diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d489007 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: CI + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened, labeled, unlabeled] + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Validate, test, and build + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout pull request branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - name: Install dependencies + run: npm ci + - name: Run importer tests + run: npm test + - name: Import submissions + if: github.event.pull_request.head.repo.full_name == github.repository + run: npm run import:submissions + - name: Validate submissions from forks + if: github.event.pull_request.head.repo.full_name != github.repository + run: npm run check:submissions + - name: Commit generated content + if: github.event.pull_request.head.repo.full_name == github.repository + run: | + if [ -n "$(git status --porcelain src/content/solutions src/content/guides public/bundles)" ]; then + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add src/content/solutions src/content/guides public/bundles + git commit -m "chore: import solution submissions" + git push + else + echo "Generated content is current." + fi + - name: Type check + run: npm run check + - name: Build site + run: npm run build + + guard-scope: + name: Guard pull request scope + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + steps: + - name: Reject mixed submission and site changes + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} + run: | + set -euo pipefail + if printf '%s' "$LABELS" | grep -q '"allow-mixed-changes"'; then + echo "Scope override label present." + exit 0 + fi + FILES="$(gh api --paginate "repos/$REPO/pulls/$PR/files" --jq '.[].filename')" + SUBMISSION="$(printf '%s\n' "$FILES" | grep -E '^submissions/' || true)" + SITE="$(printf '%s\n' "$FILES" \ + | grep -vE '^submissions/' \ + | grep -vE '^src/content/solutions/' \ + | grep -vE '^src/content/guides/' \ + | grep -vE '^public/bundles/' \ + || true)" + if [ -n "$SUBMISSION" ] && [ -n "$SITE" ]; then + echo "::error::This pull request mixes submission content with site or repository changes. Split it into separate pull requests or add the allow-mixed-changes label." + exit 1 + fi + echo "Pull request scope is valid." diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..3b270c4 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,52 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - name: Install dependencies + run: npm ci + - name: Test importer + run: npm test + - name: Regenerate submissions + run: npm run import:submissions + - name: Type check + run: npm run check + - name: Build + run: npm run build + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: dist + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index d5a18de..ccd30fc 100644 --- a/.gitignore +++ b/.gitignore @@ -427,3 +427,14 @@ FodyWeavers.xsd *.msix *.msm *.msp + +# Astro and local gallery tooling +dist/ +.astro/ +.playwright-cli/ +.DS_Store +.idea/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..17e862e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,58 @@ +## Development + +When starting the dev server, use background mode: + +``` +astro dev --background +``` + +Manage the background server with `astro dev stop`, `astro dev status`, and `astro dev logs`. + +Before building or serving the gallery, generate content from the exploded +submissions: + +```bash +npm run import:submissions +npm test +npm run check +npm run build +``` + +Generated files under `src/content/solutions/`, `src/content/guides/`, and +`public/bundles/` come from `submissions/`; do not hand-edit them. + +## Documentation + +Full documentation: https://docs.astro.build + +Consult these guides before working on related tasks: + +- [Adding pages, dynamic routes, or middleware](https://docs.astro.build/en/guides/routing/) +- [Working with Astro components](https://docs.astro.build/en/basics/astro-components/) +- [Using React, Vue, Svelte, or other framework components](https://docs.astro.build/en/guides/framework-components/) +- [Adding or managing content](https://docs.astro.build/en/guides/content-collections/) +- [Adding styles or using Tailwind](https://docs.astro.build/en/guides/styling/) +- [Supporting multiple languages](https://docs.astro.build/en/guides/internationalization/) + +## Important Notes + +- One gallery entry represents one complete exploded Power Platform solution. +- Each submission lives at `submissions//solution/` with gallery-only + `metadata.json` and optional `README.md` beside it. +- Modern workflow exports include their saved canvas under an + `associatedData.graph` object inside each `Workflows/*.json`. It contains the + nodes, edges, handles, and x/y positions needed for an accurate preview. +- The first release rejects workflows without a saved graph rather than guessing + a layout from executable actions. +- Agent architecture is derived from `bots/**` and `botcomponents/**`; component + ownership must be resolved per agent rather than applying every solution + component to every bot. +- Agent previews use a deterministic hub-and-spoke layout: primary agents are + centered, owned agents/tools/skills share radial rings, and straight edges + attach to the nearest node side. Node color is presentation-only and does not + alter the imported topology. +- The browser receives compact allowlisted graph data. Raw schemas, icons, + environment connection identifiers, and source blobs stay out of generated + content. +- Download ZIPs are deterministic rebuilds of the `solution/` contents at archive + root; gallery sidecars are excluded. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..20cdaea --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,99 @@ +# Contributing a solution + +Contribute one complete Copilot Studio / Power Platform solution by adding an +exploded solution folder to `submissions/` and opening a pull request. + +## Export and unpack + +1. Export the solution from Power Platform as an **unmanaged** solution. +2. Unpack the exported ZIP with `pac solution unpack`, Power Platform CLI, or a + standard ZIP tool. +3. Copy the unpacked files into `submissions//solution/`. +4. Add the gallery sidecar files described below. + +Do not submit a ZIP. Keeping the payload exploded makes agents, workflows, +connectors, and code reviewable in the pull request. + +## Submission layout + +```text +submissions// +├── metadata.json # required gallery metadata +├── README.md # optional human-facing overview +└── solution/ # required exploded payload + ├── solution.xml + ├── customizations.xml + ├── Workflows/*.json + ├── bots/*/ + ├── botcomponents/*/ + └── ...other solution files +``` + +The slug must use lowercase letters, numbers, and single hyphens. + +### `metadata.json` + +```json +{ + "name": "Customer Service Operations", + "description": "Agents and workflows for triage, policy review, and assisted resolution.", + "tags": ["customer-service", "triage", "human-in-the-loop"], + "author": "Your Name", + "authorUrl": "https://github.com/your-login", + "createdAt": "2026-08-12", + "updatedAt": "2026-08-12", + "featured": false +} +``` + +Required fields: `name`, `description`, `tags`, and `author`. Optional fields: +`authorUrl`, `authorGithub`, `createdAt`, `updatedAt`, and `featured`. + +Solution identity, version, publisher, component counts, agents, and workflows +are derived from the exploded payload and cannot be overridden in metadata. + +### `README.md` + +The optional README is written for gallery visitors. Explain the scenario, +agents, workflows, prerequisites, and import-time configuration. It becomes the +Overview tab and is never included in the downloadable solution ZIP. + +## Workflow preview requirement + +Each submitted `Workflows/*.json` must include the saved designer graph produced +by the modern workflow designer. The importer reads the graph's nodes, edges, +handles, and positions from the workflow JSON. A workflow without that graph +fails validation. + +## Safety and privacy + +- Remove secrets, tokens, customer data, tenant-specific email addresses, and + other sensitive content before submitting. +- The repository never executes submitted solution code. +- Symlinks, path escapes, oversized files, and oversized payloads are rejected. +- The browser receives a compact normalized preview, not raw parameter schemas, + icons, connection identifiers, or source blobs. +- The downloadable ZIP contains the submitted `solution/` contents verbatim, so + review those files carefully. + +## Validate locally + +```bash +npm install +npm test +npm run check:submissions +npm run import:submissions +npm run astro -- check +npm run build +``` + +Copy [`submissions/_template/`](submissions/_template/) to start. + +## Pull-request scope + +Keep submission changes separate from site, importer, workflow, or repository +configuration changes. CI rejects mixed-scope pull requests unless maintainers +apply the `allow-mixed-changes` label. + +By contributing, you agree that the submitted content is shared under this +repository's [MIT license](LICENSE). diff --git a/README.md b/README.md new file mode 100644 index 0000000..f2c369d --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# CAT Agents & Workflows Gallery + +A community gallery for complete **Copilot Studio agent and workflow +solutions**. Each entry is generated from an exploded Power Platform solution, +so visitors can inspect its agent architecture and saved workflow canvases before +downloading and importing it. + +The site uses Astro, Tailwind CSS, React Flow, and static GitHub Pages deployment. + +## Features + +- Searchable, filterable gallery of complete solutions. +- One entry per solution, with Overview, Agents, Workflows, and Files tabs. +- Interactive agent architecture graphs showing agents, tools, skills, and + connected-agent delegation. +- Interactive workflow previews rendered from each export's saved designer graph. +- Deterministic downloadable ZIP rebuilt from the submitted exploded solution. +- Contributor directory and JSON catalog endpoint. +- Pull-request validation for metadata, solution structure, graph availability, + unsafe filesystem entries, and generated output. + +## Local development + +```bash +npm install +npm run import:submissions +npm run dev +``` + +The site is configured for GitHub Pages at: + +```text +http://localhost:4321/copilot-studio-gallery/ +``` + +Useful commands: + +```bash +npm test +npm run seed:demos +npm run check:submissions +npm run import:submissions +npm run astro -- check +npm run build +npm run preview +``` + +`npm run seed:demos` recreates the 12 deterministic demo submissions used to +develop and preview the gallery's category-led discovery experience. + +## Submit a solution + +Add one folder under [`submissions/`](submissions/) and open a pull request: + +```text +submissions// +├── metadata.json +├── README.md +└── solution/ + ├── solution.xml + ├── customizations.xml + ├── Workflows/ + ├── bots/ + └── ... +``` + +Submit the solution **exploded**, not as a ZIP. The gallery validates and +rebuilds the installable ZIP. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full +contract. + +## Deployment + +Pull requests run validation, tests, type checks, and a production build. +Merges to `main` deploy the static site to: + + + +## License + +[MIT](LICENSE) diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..85d64b6 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,8 @@ +# Support + +The gallery and contributed solutions are community examples. File a GitHub +issue for site bugs, importer failures, or problems with a specific submission. +Include the solution slug and the exact validation or browser error. + +Microsoft product support continues through the normal Power Platform and +Copilot Studio support channels. diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..02431af --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,19 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; +import { unified } from '@astrojs/markdown-remark'; +import tailwindcss from '@tailwindcss/vite'; +import rehypeSanitize from 'rehype-sanitize'; + +export default defineConfig({ + site: 'https://microsoft.github.io', + base: '/copilot-studio-gallery', + trailingSlash: 'ignore', + integrations: [react()], + markdown: { + processor: unified({ rehypePlugins: [rehypeSanitize] }), + }, + vite: { + plugins: [tailwindcss()], + }, +}); diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..a863c5a --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,22 @@ +# Roadmap + +The first release focuses on trustworthy static discovery, interactive graph +previews, deterministic downloads, and pull-request validation. + +## Deferred + +- **Ratings and discussion** — GitHub Discussions-backed reactions, comments, + and a top-rated sort. +- **Download telemetry** — privacy-reviewed download counts and a most-downloaded + sort. +- **Site analytics** — explicit analytics configuration and consent review. +- **Contributor automation** — badges, first-submission recognition, and bot + comments that mention the submitted solution's author. +- **Graph reconstruction** — best-effort workflow layout for older exports that + do not contain `metadata.associatedData.graph`. +- **Richer connector previews** — operation and schema summaries with a stricter + privacy model. +- **Compatibility reporting** — surfaced import prerequisites, missing + dependencies, connection references, and supported solution versions. +- **Graph comparison** — visual differences between two versions of the same + submitted solution. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7901a3d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,8142 @@ +{ + "name": "copilot-studio-gallery", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "copilot-studio-gallery", + "version": "0.1.0", + "dependencies": { + "@astrojs/markdown-remark": "^7.2.2", + "@astrojs/react": "^6.0.2", + "@xyflow/react": "^12.11.2", + "adm-zip": "^0.6.0", + "astro": "^7.1.6", + "fast-xml-parser": "^5.10.1", + "gray-matter": "^4.0.3", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "rehype-sanitize": "^6.0.0", + "yaml": "^2.9.0" + }, + "devDependencies": { + "@astrojs/check": "^0.9.10", + "@tailwindcss/typography": "^0.5.20", + "@tailwindcss/vite": "^4.3.3", + "@types/adm-zip": "^0.5.8", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "tailwindcss": "^4.3.3", + "tsx": "^4.23.5", + "typescript": "^6.0.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/check": { + "version": "0.9.10", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/check/-/check-0.9.10.tgz", + "integrity": "sha1-dllc256pkOFVv1vmlaZeyz7OM00=", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/language-server": "^2.16.7", + "chokidar": "^4.0.3", + "kleur": "^4.1.5", + "yargs": "^18.0.0" + }, + "bin": { + "astro-check": "bin/astro-check.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/@astrojs/check/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha1-e+N6TAPJruHs/oYqSiOyxwwgXTA=", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@astrojs/check/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha1-64WAFDX78qfuWPGeCSGwaPxplI0=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha1-088m7ZjhnT0QDNvrZhznuFZxnKc=", + "dev": true, + "license": "MIT" + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding/-/compiler-binding-0.3.2.tgz", + "integrity": "sha1-hnDIKUSk2xf8NX8FuKqiQddIfLA=", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.2", + "@astrojs/compiler-binding-darwin-x64": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.2", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.2", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.2", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.2", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.2.tgz", + "integrity": "sha1-5XZWZ4zmpUyg66GiI6fdLehNkUA=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.2.tgz", + "integrity": "sha1-Aw7sVhaXUjrZa+83RJuo7P94RWw=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha1-hjWPGxTnHECkFTpCaAF7wCYuhzY=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha1-zo83TLzOS2vbOVCgAngxvLGgtG0=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha1-Cl/Up6PT3Vj9qv0KpTnyFRqj9Zs=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.2.tgz", + "integrity": "sha1-yZNQ/tw5GYCHCBwhRRE+arkkvmE=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.2.tgz", + "integrity": "sha1-tprquJiw2x6blGK9zIZh6baD8L8=", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha1-sqnC1I8xFWetZUdygCLXZ7o72nw=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha1-JtBeSJeMRIt7qBz38PR+SoXVIaI=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/compiler-rs/-/compiler-rs-0.3.2.tgz", + "integrity": "sha1-SlZnBYKEX7IdvegWBVjfAL2SbKg=", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.2" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.10.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/internal-helpers/-/internal-helpers-0.10.2.tgz", + "integrity": "sha1-XhWbDiabiBLdsagdZJ1xTdXs6Z4=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.3.0", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/language-server": { + "version": "2.16.13", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/language-server/-/language-server-2.16.13.tgz", + "integrity": "sha1-oCkAGrVifjyI1Mf+Wnb4SGmuxek=", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.13.1", + "@astrojs/yaml2ts": "^0.2.4", + "@jridgewell/sourcemap-codec": "^1.5.5", + "@volar/kit": "~2.4.28", + "@volar/language-core": "~2.4.28", + "@volar/language-server": "~2.4.28", + "@volar/language-service": "~2.4.28", + "muggle-string": "^0.4.1", + "tinyglobby": "^0.2.16", + "volar-service-css": "0.0.71", + "volar-service-emmet": "0.0.71", + "volar-service-html": "0.0.71", + "volar-service-prettier": "0.0.71", + "volar-service-typescript": "0.0.71", + "volar-service-typescript-twoslash-queries": "0.0.71", + "volar-service-yaml": "0.0.71", + "vscode-html-languageservice": "^5.6.2", + "vscode-uri": "^3.1.0" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } + } + }, + "node_modules/@astrojs/markdown-remark": { + "version": "7.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/markdown-remark/-/markdown-remark-7.2.2.tgz", + "integrity": "sha1-xxhWwH8Y3ie6rxkuZcNSJ1MaQbE=", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.1.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/markdown-satteri/-/markdown-satteri-0.3.5.tgz", + "integrity": "sha1-fXMAIQZZLGxqV6rbm8jQARam+6I=", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "satteri": "^0.9.1" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha1-XcByWmHqb+ZlpIR05lyWgHmlH0A=", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/react": { + "version": "6.0.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/react/-/react-6.0.2.tgz", + "integrity": "sha1-xX3ZiFp0sRfTHGoCI5zx4aD6C6c=", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@vitejs/plugin-react": "^5.2.0", + "devalue": "^5.8.1", + "ultrahtml": "^1.6.0", + "vite": "^8.0.13" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", + "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", + "react": "^17.0.2 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha1-JOW88gPRrAd0fALj8HEBNycggrg=", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", + "integrity": "sha1-iZ6N552mFFtRTY+3wXVFJvj211E=", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha1-8vu/6ofESiFZDsUVt3iywm2IZuc=", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha1-bwI38PNtLlHAVwpjb67Z0tDv5ik=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha1-gMELFySAgpaLV6hXuRZAlx8gcPc=", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha1-SwuIeIVCJkMzngkCIUikxOuqSXk=", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha1-eh3vcEMCQBxH9k+oVYnpdK4hcEI=", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha1-8EqW+9hHMkGxB5JD9bPwOjAQq3s=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha1-7yUEilGOgo1zk/rFiC3dc5Idc5Y=", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha1-sGJ0elmXuhOGNyATKLv/d5YFdK4=", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha1-wKB2bxoTYX2KF0B9erj51IYiXqQ=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha1-fwhx2Zgk0jE31g+G/PYTD9WhtR8=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha1-vYcITO0MeW7Ea9pJLeboPSnon8I=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha1-zzFb6UAhOzVOtKvMC9Aevj9zvCo=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha1-Rav951SJl+NDdsPmn+tHXP+0pgc=", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha1-llNxai8Qxne5j7xj1L+wAMMCzxc=", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha1-wkQkUnhYIgYk/Vmlseq0+kE8gDo=", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha1-XPJaNomQa1ji8KLys3R4nmYnsV8=", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha1-TZ1ABPZFzdME3pWMclFieE7KxwA=", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha1-QREBTNxxoPldlHGQdZC6oLimsoo=", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha1-Einu8x2FFW1w+j9M2Fk3bQ6vaGM=", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha1-pcWWW56qHQao6pJP2bSrAY9LVnM=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha1-O89jnHPIOCin43q7+AGfan65/lg=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha1-CF3WnAH5+J9kE9gC0BjjBHFLlXI=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha1-Yts0XlGnqK7CBKDzbzjctb03rp0=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha1-Quq2YdUoEBC48gO+X+r8/TppUgQ=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha1-UOdkjpOU+bYsfWQwJO8GppodZFI=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha1-zST/vhZQV35ivtes0hxItMXrrR0=", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha1-ueEGTzprFjHiQeY460jXNr/TcqY=", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha1-WPHz1dgamxL3k6tojJY3GQECfCQ=", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha1-TJO+z1v6OxPRu9zAau44MhrYE5o=", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.9.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha1-DYOFJTX2PFCrkCU/4gnlzlz/6wk=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha1-FUinVztYRp3GM+zDhArqwZSJNxA=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha1-X8Xzo3GpOmnXZOgaCbrfAUM4Fzk=", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.4.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@clack/core/-/core-1.4.3.tgz", + "integrity": "sha1-HoMZdPgR1zNwfsZQ5Y9pDX10xfo=", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.7.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@clack/prompts/-/prompts-1.7.0.tgz", + "integrity": "sha1-LWztNjpgi6I/a5YRIFuDBshbvw0=", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.3", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha1-7SuI/je5ciktYCbHxUCq+IfOy24=", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha1-t4UxNIbrpst+tiOtOTeMThBj3AA=", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-parser": { + "version": "0.4.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha1-DSl1uR26WGEuXDXjaogO9CQGfsM=", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha1-Q7enG5HNxRHLaZy+nGe7XUyrZ1Q=", + "dev": true, + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha1-6c3GcZT9kfi36xQQFL5PLQhsFfE=", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI=", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha1-JEywLHfsLnT3ipvTGCGKvJxQCmE=", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha1-XpU0ikLNHgbwuao4DNdAkdqk1SA=", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha1-hCV647BTHrKuwf+iPXBwDaAHupU=", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha1-yb9y/UvluSiu6JSCDo2BTtc5Fuk=", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha1-egGo0uwvuy2seK2tCbD6eB5Agr4=", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha1-cEvSl95tdi3lTqu+r79V9nVqvi8=", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha1-tUCifRTkr9BYSWpNvsTT9BTbEQo=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha1-0csWbTSw+/D+irRgpVlPJKN4cB4=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha1-EDSyZFf8iGNo/mG70J9lP2r6jlQ=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha1-ZVVqQyoeTXIDLYIYwZMvzKGkl3I=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha1-LmHgWS+QMNfj2uGO4l68U1kYrvY=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha1-yV7CiZWe+AecTcqBeh4sS+Zrm9M=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha1-wJoPZ5F1kqwN6JKpvk04FN69Kmw=", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha1-QLIhdd2gYYLz7oFBGGxf8wTEpxc=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha1-pYD5xnZ5eDOJHlGfx6EzfIr9jbM=", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha1-RkUs8yHcf56Rwvp4Cla7Vuec1os=", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha1-QhGzGE3WYI9T3LIuOfXTTuCIUsg=", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha1-aXhXwqYcubC2u2ZS5AwdxeHKjl0=", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha1-0ZKUPrFGpArExkl9DPe+NbmGvwg=", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha1-rOoDVtoODrwI+Xz3ucLkAeHmSNw=", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha1-bww84MtkxTS3DExF7LLBbTTjXf0=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha1-i813B3oNzjN4tXT+2ybSolO3PTY=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha1-5/sqAemcgwyU5mI82f77TI+1g0c=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha1-xSkJNy24uG4sVeBaiUADO1Zgo7I=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha1-xCe5vlpkwmL/mn63C1+7qt9EbGw=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha1-3JsUe6yi5sSzyFVxdB70hgpIkJc=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha1-zoZtEt8TwV5MmfBzo9Rm9uBkmzo=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha1-dGjjaS0B1inVlB5dg4F7uA+eObQ=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha1-pbwAY/sryrbQ7WPyoVN5WLwmnsY=", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha1-EAZO5E9DR7kMmgK0Rrv4CpFjKxI=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha1-sMLC+mYa33Xv/WtJZEl82AAQu50=", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha1-iydAiEvFixJ/wwIEeaASlPoQlcs=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha1-kt+RMg+vV8xUszEYV3DVqT1RAEk=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha1-SAGME3mo9QfWgdbNjb5D2XldyAk=", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha1-IntB/8bJlhK86rpW+ZShkz13lXM=", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha1-aUkD7EEMAJRc5bDCbayD1xhMi4Y=", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha1-5g4IjIBr3hrCi+ZItgw0ZfQcGKc=", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha1-Sd3xVnKGZqId7tBxbzu3K7NRF54=", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha1-vjFhqv1llBez4mN037vNLaK/tiw=", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha1-vwCKZ3nZvitWpN9nt1OUH3Z8lX0=", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha1-lYOBS421iInIW62eXgt4JSV/85Q=", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha1-q8mjEUSVtwW6ILfBVoue7NYq67s=", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha1-5Y+xSnh58V2ecKmChw84TzmoMqI=", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha1-VhFID8t0Zd1TFgRNtTrXqEPtSvg=", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha1-yNpQDEAWiTqJ1G6YMtCKBu73fyc=", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha1-RLZYI+zJd9RYWzCAcP/zlHJW3gQ=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha1-6h19uBj1KvHh4FfoLVXyOy3jhkw=", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha1-civXmUZYeRsC0QN+oJyly3gDDY0=", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha1-6ClIF9faSVVBpKhw9W5rXQ+GQ80=", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha1-mEPDLzmurEtg3WD540FlIKTk3kY=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha1-nP7k7MPUGrmidOAZ3+e0BihAUQw=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha1-IE0GeMjDsVMA2aJuy5wNzaEcpd4=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha1-QviXm9vhpUGi6bmdO1vgfmtxx8A=", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha1-gjqqk9FNuEmZ1bXcln/1bPGWbZ8=", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha1-81VNRcviRTKgrepzbsV2WPdKKRE=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha1-CUKyZDvLV+SEGf5BGd6EB4rgrHQ=", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha1-iiXKzcdajCYHfAf7pR6AVqrkdPE=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha1-Y0Khn0Q0dRjJPkOxrGnes8Rlah8=", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha1-N1xHbRlylHhRuh4Vro8SMEdEWqE=", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha1-eg7mAfYPmaIMfHxf8MgDiMEYm9Y=", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha1-aRKwDSxjHA0Vzhp6tXzWV/Ko+Lo=", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha1-2xXWeByTHzolGj2sOVAcmKYIL9A=", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha1-xwcGUy5YJ8CTLKa/Q+4sUS8pxjk=", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@nodable/entities": { + "version": "3.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha1-aUcDvIZNMOrtVcLj3vANvWFJNnA=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha1-VfPZpZdDCgHype9jxrQvdp+c404=", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.142.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha1-C0vXhB7z3SZ6aRhLl0UswLMT+1I=", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.2.tgz", + "integrity": "sha1-t9I11Fs4yE8miSrIBLtPF0OKu/4=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.2.tgz", + "integrity": "sha1-kCHbIkFV8VpBq2mDMu2zADEY/c0=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.2.tgz", + "integrity": "sha1-f521YY0M5GP5sLuu7qSNGzC7lpw=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.2.tgz", + "integrity": "sha1-X7uSUp+43XP+bJITqaC54f2LiuE=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.2.tgz", + "integrity": "sha1-qDaq36OOp3i9xVCO79UDb1u/gFI=", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.2.tgz", + "integrity": "sha1-x321wkOoHYJBteTvapmoLIrPmkA=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.2.tgz", + "integrity": "sha1-BuzpiHLrPFsSln+qjgfvKwPgLQY=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.2.tgz", + "integrity": "sha1-tvVbzIOFIID4R47F2g6IsuP5ptE=", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.2.tgz", + "integrity": "sha1-f6QW0sN69lTyi0MuJOW+lIO4nxk=", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.2.tgz", + "integrity": "sha1-mu+mfnIkz9RGQ3OppPU4HNGcrjs=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.2.tgz", + "integrity": "sha1-jNcWCutbS6wz/Orz1+DGegx4yh0=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.2.tgz", + "integrity": "sha1-DDkaCKOaCXlK+XPuznghbIDhRPY=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.2.tgz", + "integrity": "sha1-aJUmXiRhZO0ugWLLqRwMP8K/NoQ=", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.2.tgz", + "integrity": "sha1-Nd7ZIEMvp7FoFukELTZ5z9GxKYo=", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha1-iojMkqD3Qb78e8EJyxpMa5QI4cU=", + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha1-rCOinO0CRwYKIQgV/KOcF95NLyY=", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@shikijs/core": { + "version": "4.4.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/core/-/core-4.4.1.tgz", + "integrity": "sha1-kerwBzuEimaNvRshy7aje69rexY=", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.4.1", + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.4.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/engine-javascript/-/engine-javascript-4.4.1.tgz", + "integrity": "sha1-VcPID5tLvxXToEO83TESE6sCUDU=", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.4.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.1.tgz", + "integrity": "sha1-FU0JVq/v6xkP8V5wRh3pTW3HrN4=", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.4.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/langs/-/langs-4.4.1.tgz", + "integrity": "sha1-voEWO+hFSZyEl+JUMLDuNE8sCgY=", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.4.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/primitive/-/primitive-4.4.1.tgz", + "integrity": "sha1-ounc1UHCRo0FyUJGijqr3jYa7kg=", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.4.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/themes/-/themes-4.4.1.tgz", + "integrity": "sha1-a63xfOOcEdpdSdbNYd5kY78UCjo=", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.4.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/types/-/types-4.4.1.tgz", + "integrity": "sha1-cTIsJIJ7hXpEHr6ANF2zuu22ik4=", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha1-qQqzHQzB37VMZqaeUVv2JPp7IiQ=", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha1-OP8EMJ/wNuo1iae62QacROydOIM=", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha1-uFqulkhtyxv0mnyFcSISc/Tx5Kk=", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha1-8DOIURbf79nG9UeHUj41FLYeGWg=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha1-ULcYcbAcgZlYS2SeKSVH+up6+bU=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha1-NfPpczLRMLnKGB4RtWje1q68bV4=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha1-l3enZHK2Ttb/lDQq1kx7r9eUpXU=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha1-E65lLhq3O5E117faFy9mbEEK1T0=", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha1-QXhYeVqUWS9oASOhsfnaig4e8zU=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha1-a+NmkugQtxgECAL9gJYjz/5zITM=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha1-C3gDr06yHP043Tn+Kru1PH3QkfY=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha1-iNyLqGXd3bGsXvBLDxYYBEGMFjs=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha1-TzC6P6XpJfW3n5RejMDRdsOxqzg=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha1-FBqlYFZFBkkokCu0rwRfp9n0Igo=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha1-VnY+wJoPqAkd8nh5/ZTRkHjADZE=", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha1-YmYQnQJc/LBPjkxYlUpvYwpBW38=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha1-hI+TA0FV2veJIYUCjfsYFDv8fQc=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha1-XHeeMsHDYb63UTb9jixif8uaWyg=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha1-uikv9S+jJkE59/54dHGc4KRmaHU=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha1-B+WJSHuaY2I1pK3kjO/B+e7uxX8=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha1-DIq8Io2eGeAGXdtwfrpS4hhVs/8=", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha1-UGfcev0V0rl63HepEXfcS+yNG4s=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha1-Ka5fJ5vizmTbNocRmFtq2ORWLlc=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha1-fWk7QPaYdXRLSZSBNZsif9OsOjw=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha1-alXWZPR8X/mtP0a/Bf4H1BC4z9g=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha1-QIqbxiDmjxqvDf6jPae9O2X54u8=", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha1-PVgrABgPpGgOC2yQvmn6X0ogkJI=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha1-zkxmP+87T95nYRpME5GGb4wKp5s=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.20", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/typography/-/typography-0.5.20.tgz", + "integrity": "sha1-n+t/sdXy97U2DCLiRlEhDbdMNN8=", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >=4.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha1-H0A3uty15sa7V009igFyyD8muX8=", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha1-AVy6np3UfOFNA9KoxdVHv7FpZl0=", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/adm-zip": { + "version": "0.5.8", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/adm-zip/-/adm-zip-0.5.8.tgz", + "integrity": "sha1-1O6SDZ++94Bh0Dn6fEd23noLsa8=", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha1-PfFfJ7qFMZyqB7oI0HIYibs5wBc=", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha1-tYGSlMUReZV6+uw0FEL5NB5BCKk=", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha1-VnJRNwHBshmbxtrWNqnXSRWGdm8=", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha1-B9cT1szg0mXJhJ2wy+YtP2Hzb3Q=", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha1-NoyWGhjech2oIA6AvzlD+1MTavI=", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha1-sTq6iyRCtAaMmp5tHYL4vOp3/AI=", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha1-QSuQ6EhwKF8v+KhGxutgNE8SpBw=", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha1-vXpF/AqMMWemMWdeYbwsorBY1KM=", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha1-ETa8V+nds8OQ3MybX/O30rjZRwY=", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha1-3Msy0cVrHhxuDxGA2ZSJbwOLxAs=", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha1-ItHMnVQtNZPK6nZPl0MGqzYobuc=", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha1-zz8Oh2177hWpOrkluCv1cKOQSiQ=", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha1-hYqI6iDzT+ZREfAFpon6Hr9w3Bg=", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha1-SAIN5MDmNJL0yp20IGjBCPaLf48=", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha1-fM9y7dLxqn3TQ34YDGQ3NYWATdY=", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha1-BSqmekjszEMJ1/AZG35BQ0uQu3g=", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha1-McrTRuqrSKmopYRl09BeJTDdp2I=", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/node/-/node-26.1.2.tgz", + "integrity": "sha1-2nlwjx+cYpT0zeyPRVowMrAogIo=", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/react/-/react-19.2.18.tgz", + "integrity": "sha1-6wtqH7Y10alpLV+Eo0lb2K0VNwc=", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha1-Flq16X37CBhHsPZ1UGDPQKP2Zms=", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha1-rKqw+RnOaczmKcLU7S60rcG2wgw=", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha1-CUBB4aTLGYfwODNUISgayL45C8w=", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha1-EIvQ9WbyiM41Zpgt9O/xN97XsV8=", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@volar/kit": { + "version": "2.4.28", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@volar/kit/-/kit-2.4.28.tgz", + "integrity": "sha1-O7yVf6SZTHVXL+i28o0SkHq2KHc=", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha1-wh82WpHB3/6L1yZP1JF3DI10/vM=", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.28", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@volar/language-server/-/language-server-2.4.28.tgz", + "integrity": "sha1-Q/oowcO9zPpWqnmj+mpLaZb85HM=", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "path-browserify": "^1.0.1", + "request-light": "^0.7.0", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.28", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@volar/language-service/-/language-service-2.4.28.tgz", + "integrity": "sha1-HKKY7O7B/qBZH12E3egZvl3XIJk=", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha1-tAJU6MlhmeXx4Hlnd8WTxhetJw4=", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha1-g/hjVuhOsQG4CBpEwQTy8s7YQR8=", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha1-elPk/bFzKcwu2IA2kFx42BHSMdY=", + "dev": true, + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha1-WVSRULEz8u+sykj+nOHsBlmvI0I=", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha1-kW06XpYNurR8HFb1iny1CHsTXJU=", + "dev": true, + "license": "MIT" + }, + "node_modules/@xyflow/react": { + "version": "12.11.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@xyflow/react/-/react-12.11.2.tgz", + "integrity": "sha1-HEksftZUSjP+yAFIYunmYqwgCE4=", + "license": "MIT", + "dependencies": { + "@xyflow/system": "0.0.79", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "@types/react": ">=17", + "@types/react-dom": ">=17", + "react": ">=17", + "react-dom": ">=17" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.79", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@xyflow/system/-/system-0.0.79.tgz", + "integrity": "sha1-NFmgs2V3b0pXnfVJYuQuIBVnwZI=", + "license": "MIT", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.6.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha1-u8XGwzN1XpZ6Bt2YdH9DHh1To88=", + "license": "MIT", + "engines": { + "node": ">=14.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha1-MEs2Nq3Yi6fZNnYN1Q7OAG3qlfk=", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha1-O2R2GyaLoLnmaPC0G6U/zgrXf8g=", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-i18n": { + "version": "4.2.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ajv-i18n/-/ajv-i18n-4.2.0.tgz", + "integrity": "sha1-1IdQumDig7Pe4x48IsjJ90EOMm8=", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.0-beta.0" + } + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha1-Tbp96V+brbPmGg8Fk/rEOEpKvH4=", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha1-YCFu6kZNhkWXzigyAAc4oFiWUME=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha1-wETV3MUhoHZBNHJZehrLHxA8QEE=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha1-eQxYsZuhcgqEIFtXxhjVrYUklz4=", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha1-WpQpFeJrNy3A8OZ1MUmhbmscVgE=", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha1-KqwA4I3603JsHUYuYNvC+DFlmkQ=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg=", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha1-k/gaQ0gOM6M48ZFjo9EKUMAdzVk=", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha1-bv1D+ClbP+4GJR09YurUvZgF3SQ=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astro": { + "version": "7.1.6", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/astro/-/astro-7.1.6.tgz", + "integrity": "sha1-MyQAvqA1TTxWo0+tL+b8hoSdwaI=", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.3.2", + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/markdown-satteri": "0.3.5", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "am-i-vibing": "^0.4.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.8.1", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.3.0", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.0.0", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.2" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/astro/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", + "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha1-KHaMdtDjz/IbxiqeLQtqwwBCoe4=", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/bail/-/bail-2.0.2.tgz", + "integrity": "sha1-0m9c2P5db4MqMVF7n3w1YEC6bV0=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.12", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", + "integrity": "sha1-QqxIdwv3PSkvYM6LpNxefrskLsM=", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha1-QJBGUX/M0uUc3CDwd0VLcUEYQCg=", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha1-G8jlArcj+jk0Vd++3VzOwMKbt04=", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha1-F6O/gjAuCHDW2kOgExGovAKj7PU=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha1-LQnC5yzZUjB2zLIRV9/2atQ/zCI=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha1-HxrblAyXGksiujndymthjcblays=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha1-dryDqQc4kB17wiOp6TdZ/dVgEls=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha1-lJwSapI4qAeSvpoCZZNPCYrzaaU=", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha1-fVTv+fVLRbYkAcJgMmlutZyL0Yw=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha1-jCCfNZqTrDAkBKEBYbUB66nAnHc=", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha1-b3iQ84b28feZU63B943sRvzC0pE=", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha1-tbuOIWXOJ11NQ0dt0nAK2Qkdttw=", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha1-7tOXyf2L2IK/sY3qtxAgSaLzKZk=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha1-TonJRYrLYbyP7xn0UplzsjkoOe4=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/commander/-/commander-11.1.0.tgz", + "integrity": "sha1-Yv3OdgBqaOXBqzMU3JLoAOuD2QY=", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha1-8dNhrqkjaq1bkqD/W53xQi3TYP8=", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha1-S1YPZJ/E6RjdCrdc9JYei8iC2Co=", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha1-H8fpvd84+pVnzU11CCAIfNtdbpg=", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha1-Bso8X181MWhKIFlmajYRc/dKicg=", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha1-2q0zHUQUjqZQAJi8hYhp86WrgaY=", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha1-Abbo0WNje7LdbJgspO1lhjaCeG4=", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha1-hsrHARVhJysw5rHgQrps4EeqdRg=", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha1-zcyPm2l3cZ/fvR3nrsJKv3Vrneo=", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/csso/-/csso-5.0.5.tgz", + "integrity": "sha1-+bf+bMasC32QeBuxbV6YdDA+LKY=", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha1-NhFdOC1gr9Jx43f5xfZ9Ar1IwDI=", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha1-XsSOe+8SBlRTkGnhrk3cgcpJDro=", + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha1-7EjA8+mT5QZIyG2lWeJhCZXPmJo=", + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha1-OVsoM9+scVB/EqwvevI7+BneJOI=", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha1-X8dShOnCN1w2yDlBGgz1UMv8TV4=", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha1-mUqunNI8cZ9TteEOOgphCMaWB7o=", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha1-llisOKIUDVnTRhYPH2ww/aC9EvQ=", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha1-PEeqWzLFs9+1bvP9Q0IHimMrQA0=", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha1-wlM4IH76csxbm9FFihpBkB8eGzE=", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha1-YoTSonCChbGrt+IB7aQ4CvNeY7A=", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha1-aGn93hRIhoB3/dWYkgDLYbKhZF8=", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha1-0T9BZccyF//qpUKVzWlps+eu6PM=", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha1-PkBgN2CHTC5YZ2kbWZ1zp9oltT8=", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/defu/-/defu-6.1.7.tgz", + "integrity": "sha1-clQ1Z8jp+X/xPOQCttvgmsWuTSM=", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha1-JkQhTxmX057Q7g7OcjNUkKesZ74=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/destr/-/destr-2.0.5.tgz", + "integrity": "sha1-fREv8bkl+40gefrFvbSpCXO1H9s=", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha1-aJxdzcGQDvVYOky59te0c3QgdK0=", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.9.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/devalue/-/devalue-5.9.0.tgz", + "integrity": "sha1-XTDbQaDbkXHPTunb9mF+C3fNfCo=", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha1-TbfCyk3G4Og0wwvnDJS7yXbccBg=", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/diff/-/diff-8.0.4.tgz", + "integrity": "sha1-T1uvMYi5skMRF7li6yC6Mw+t9pY=", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha1-5BuALh7t+fbK4YPOXmIteJ19jlM=", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/entities/-/entities-4.5.0.tgz", + "integrity": "sha1-XSaOpecRPsdMTQM7eepaNaSI+0g=", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha1-XEXo6GmVJiYzHXqrMm0B2vZdWJ0=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha1-zDhff3UfHR/GUMITdIBCVFOMfTE=", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha1-7b/itmiwwdl8JLrw8QYrEyIhvHg=", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/dset/-/dset-3.1.4.tgz", + "integrity": "sha1-+Or18CPwaKA20IzQfcn/t9AGUkg=", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.401", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/electron-to-chromium/-/electron-to-chromium-1.5.401.tgz", + "integrity": "sha1-PpMZJtj9wRf2qDTljap0wRFCANg=", + "license": "ISC" + }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha1-szH1ct83olI2Dr7n3ERiyNLjL1w=", + "dev": true, + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha1-vz1uj3+P0ipl2XA0dbwBRzV6aw0=", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha1-tNrTJVt1RfB7pVNRiYaOn4X0dXM=", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/entities/-/entities-6.0.1.tgz", + "integrity": "sha1-wow0pDN5yn9h0HQTCy9fcCCjBpQ=", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha1-W/LfBpmdu+XwBqX0ahH7n1t7ORs=", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha1-70W0Y0ycnZeilq6kEUpfmED5VXg=", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha1-ARo/aYVroYnf+n3I/M6Z0qh5A+U=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha1-RoMSa1ALYXYvLb66zhgG6L4xscg=", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha1-UvAQF4wqTBF6d1fP6UKtt9LaTKw=", + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha1-qG1mFwQzcS3egUcHrFK1JxzrH+s=", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/extend/-/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha1-I6/g2mfXUsoHJ1OPHmlndZcozkk=", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha1-FturtJHOVYW17LZ1tlwWXXFojus=", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-uri": { + "version": "3.1.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha1-YQ83QZoDAnBDDOzWjXTj1NlnJdA=", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha1-lelSoBRbzj9ZrVbhefhMSNQHKTU=", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.3.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz", + "integrity": "sha1-vISYBHlv5Hv5FQIt2Tmw/DC6RV0=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.10.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", + "integrity": "sha1-GTE/fJOGxH+kod5SdUe3PtLPzt4=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^3.0.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.1", + "xml-naming": "^0.3.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha1-7Sq5Z6MxreYvGNB32uGSaE1Q01A=", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha1-iBgiNXIxE2Z9NiF/7FU1knXW/j0=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha1-3ip2zzYQiAFpAabyqPK9AWya6EQ=", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha1-u6/e3bseBUln8IRv3HV6zKuIu2g=", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha1-ysZAd4XQNnWipeGlMFxpezR9kNY=", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha1-MqbudsPX9S1GsrGuXZP+qFgKJeA=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha1-T5RBKoLbMvNuOwuXQfipf+sDH34=", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha1-IWkA+R3xGossGYw+HZPWwDWndrk=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha1-SOHISRnRb9Ht/r0E6JWNqUKUXCU=", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha1-Us8vknmiHrbFndOFtBDwwK3ajxo=", + "license": "ISC" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha1-QYPk6L8Iu24Fu7L30uDI9xLKQOM=", + "dev": true, + "license": "ISC" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha1-6JPAZIJd5z6h9ffYjHqfcnQoh5g=", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.15.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha1-JLyVAo82HNqqhHRbBqEJxEhnc8A=", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/h3/-/h3-1.15.11.tgz", + "integrity": "sha1-gxF5/GtLwG3orRB356XH1jt5ZXc=", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha1-SFx0eFNYvrgMS6Y0YpkxGsTEnII=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha1-gwo1Ai//KMP+o2l6mML0zGuDWi4=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha1-bjGmUywhfltTOEjH5Sydk2nKCTI=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha1-NSh5+obiVhYDYDfdiTH7XzTLSic=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha1-ebZrJvb2j7UN+0cWss3KkNkq3y4=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-sanitize": { + "version": "5.0.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", + "integrity": "sha1-7bJg2U5buiAw65N1eQqHU+W/OR8=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "unist-util-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha1-zMZzpVu46Fd1sIrCg4D3LUcWcAU=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha1-lao5HMBRS0lRQY0ByIPRA4r0L10=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha1-V7Z2kx5xv5y4UkU2eElbMIC/rj4=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha1-d3jtnTyS3Z6MXI9kiknCH8UctiE=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha1-28hL72BR1ACENCwinEUc2dxWff8=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha1-TTNmdGUr6x3Lwp72trp/a+b9/tY=", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha1-/J29hK+edHJJA01NYmAt72UX8dc=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha1-IF9Ntk+FYrdqT/kjWqUnmDmgndU=", + "license": "BSD-2-Clause" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha1-qmD/KqEFUGMPTAsR/SRCvs2zWm8=", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha1-aquHx3ow3f85xGCvNy7heVrueEg=", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha1-1lAl7ew2V84DL9fbY8l4g+rtcfA=", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unsafe": { + "version": "2.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-unsafe/-/is-unsafe-2.0.0.tgz", + "integrity": "sha1-wNzk4GdCZi3eJjYBYOQU6kh9ouk=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha1-l0Io8vTKK8IYhaF5e0X+po6VDGQ=", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha1-ASFsAB1n9I4s1WDXCMevIQkKOEg=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha1-dNM1ojT2ftGZB/2t+sfM+dQJgl0=", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha1-rnvLNlard6c7pcSb9lTzjmtoYOI=", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json5/-/json5-2.2.3.tgz", + "integrity": "sha1-eM1vGhm9wStz21rQxh79ZsHikoM=", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha1-8qUktPf9EePXkeVZl3rWC5i3mLQ=", + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha1-lRBhAXlfcFDGxlDzUMaD/r3bF4A=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha1-wIhn1xp5OFxuGQIU/XL+8+X5Xws=", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha1-mmhB+IrlD8g1ApA4krQa9BvCuQc=", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha1-wPLDHAv9GfpN0/GOlXofGhUgl9Y=", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha1-ywcFllrLU4xmg5Sc5pJfs833w2E=", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha1-djU4gosmurJoDa2vzITueLDrUCs=", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha1-aGLjF2ozGu297B7TUrTX0N0HhN4=", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha1-xqOi7RUUHa9r3CYokw+OOb30c6o=", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha1-f6EzSXH8goRfmCffbvigsgkUusY=", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha1-i5J4YuqMK7xoMaRlCSRLUNmTblU=", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha1-DFJbsHff2UQEwFnP5C2teX6Wrq8=", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha1-hQ7hED2smJz6tQ46wi0aaeOU5j0=", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha1-40OuFS7tNgncbhGUnRo785oclG8=", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha1-YvpnzZWHQqFXSvnzmGY2QQLZDNQ=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "1.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/magic-string/-/magic-string-1.1.0.tgz", + "integrity": "sha1-VKhHCqvyoEuXAXfybAMIyr6nSj8=", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/magicast/-/magicast-0.5.4.tgz", + "integrity": "sha1-u+ON/WA3ZwBX8zq/IriqedXpnQo=", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha1-/kTW1BD/nW8uoXl6P2CqTStjHCo=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha1-wbtwbl52u5P5oJ3XrxdAAq5prCQ=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha1-cKMXTIlOFN9yKr9DvCUMuuRLEd8=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha1-yVgiuRqrdfGKTL6LL1G4c+0s8Mc=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha1-LN9juSwqMxQGsPsNtMB3wbAzF1E=", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha1-q9VXYwM3vTCm1aS9glLhwtwIddU=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha1-d3jp2co99yOMwr0/orG/amWxlAM=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha1-1E756O0oOsjBFlqw0N/QWMJ2TBY=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha1-ekNftiI6crCGKzOvvXErba6HjTg=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha1-5oCV0vikMD7yQJSrZC4QR7mRqTY=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha1-fMCo3sMOrwS3salmGpKtszgqpuM=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha1-1/+EykmaV+LAYK5nVIrZUOaJoFM=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha1-+RD/5giX8Eu0t+fuQ0SG92KINhs=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha1-elEhR1VWoE5+3etnsmSq550xKBQ=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha1-43ucUIgLdTZsTUCsY9m7ys22Hw4=", + "license": "CC0-1.0" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha1-kTlaPhiEoZjmIRbjPJxWjjmTb9s=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha1-xpFjDkhQIaaM8o28Kyyifr9njNQ=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha1-PhM3arld16XP0OKVYN/pmWV7PFs=", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha1-Yoau6WhsRGLB41UqnVBf7dzuuTU=", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha1-TatW1OOYuYU/b+TvrE/JNh8+B1A=", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha1-hhBt+LOmkrX2qSKA04eb5r5G2SM=", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha1-+scLy/Uf5l9fRAMxGNOb6Km1lAs=", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha1-8m2KeAe1mF+6E89hRltYyl/33Fc=", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha1-vMNNgFY5gpmQ7BdcPuoSu1t4Hyw=", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha1-j++OD3CB8EdPvdkt61DJkKAmRjk=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha1-UmfvqX8eUlTvx/ILRZo4yyEFi6E=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha1-NtAhLpYrKzEh+FJfx6PHwCnzNPw=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha1-I35KpdWKlYY/AQMtnumwkPHebpQ=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha1-BrJrKYPE0nv8xlezPiUTTUhosLE=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha1-L5h4MaQNTFEKwmHomFLE6XA8zaY=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha1-R/vNk0caP8yrhs/wOEf8NVLbEFE=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha1-05n6+cRcoUyLS+mLHqSBvO2Htik=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha1-Kg9JCrCL/1zC/V7sbdDKBPibMKk=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha1-/PFbZgl5OI5vEYzba/fXnXPSb+U=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha1-bLmVguXScehO/KjmGoB5lNcWHrI=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha1-DVHRwJVVHPqsNoMmljz1XxX1QLg=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha1-5AQDCWSBmGtBwQZif5j3LU0QuCU=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha1-ww13sugyrPZSb4vxqke8nJQ4wW0=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha1-4aLWLN0jcjCirhGDkCexk4HjHos=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha1-q4l4m4GKWHUrc9a1UjhiG3+qj9c=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha1-2K3lug8xl6HPaimZ+7/mNXoaGe4=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha1-5dpJTo6ysHGg0I+zT2zv7GwKGbg=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha1-8AIl9fWg68MlT5bDa2YFxLOTkI4=", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha1-vD6H95h4U6VMmFDusfEHjNRK3dw=", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ms/-/ms-2.1.3.tgz", + "integrity": "sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=", + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha1-OzZr1Dsy+AncIGWVNN0w58ig0yg=", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.17", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha1-8cOqJTxSVHlWpSxQv/dUMW9hA3o=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha1-fIm0P2UE74WSjHGPV4xoYhV20ZQ=", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha1-BVEehGHr+0FZUusLfpoafUBHG9Q=", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha1-nQnKYwZsxIQjIR7UyvXXAHXXanE=", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/node-mock-http/-/node-mock-http-1.0.5.tgz", + "integrity": "sha1-SXvC890gjm4/fAbLtKNCX4mf6Bg=", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.52", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/node-releases/-/node-releases-2.0.52.tgz", + "integrity": "sha1-2Cg7ol5Xek2XVtW0Xso1O6VQAwA=", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha1-yeq0KO/842zWuSySS9sADvHx7R0=", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/obug/-/obug-2.1.4.tgz", + "integrity": "sha1-kJDYpUilIlF5FdKqaq6QcZesbPg=", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha1-XEPMVuAzmLJzAUlXBgNEJUUFxcc=", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha1-YLEejP9iyp3uiNE3R6W6oUX1kAs=", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha1-4nykRvf88JaWYqOrm09DF21isTk=", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha1-Q+ZAKAJBsNaHoxTnpkHUdkB6HE0=", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-limit/-/p-limit-7.3.1.tgz", + "integrity": "sha1-3tSMv6ELFhqZKBICYfqCyaKC6z8=", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha1-xeUox+s2G3uo61qUBpAvfy51/I8=", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha1-lWgKaqaTxTDxSsM3uL0y1Oxq5PA=", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha1-cMmixL0aUT3NbK0Aap/OvsIqElM=", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha1-jfrKwm+mA/dkF/NiM/xFYCoyPh0=", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha1-1+Ik+nI5nHoXUJn0X8KtAksF7AU=", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha1-2YRUqcN1PVeQhg8W9ohnueRr4f0=", + "dev": true, + "license": "MIT" + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha1-VnxzwHGX6dzvJOkO3NxXEFZZkWg=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha1-7zP2GA5qN7Nf4SpFdlqQAXHPrtw=", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha1-PTIa8+q5ObCDyPkpodEs2oHCa2s=", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha1-UepXoX2G9gX4EDlZX7xA7QalX6s=", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.25", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha1-UBKlmOqqiX8hu+hVO+PLe9K9eMs=", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha1-ebYeLA0b/CYC1UnhHQh2JW+N+I0=", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha1-s+pRRlFdQPxT8YqmP3Tfqx4Q2/Y=", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha1-2XCZadnU4WQD9vNIxjVTsZ8Jdak=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha1-BqGLsGxBPYJ1AhkEWPx2BO5rPe0=", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha1-CAmzQmTplcC/zTInAooeNSEK+Ao=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha1-/SfSrziWxr9Lzfq2Qnxpwq/GnsA=", + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react/-/react-19.2.8.tgz", + "integrity": "sha1-qAZj27WNacb+P9KR08syTop9/y0=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha1-O0a57tqHfN/yzxPSdw//SuNsLsI=", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha1-Lc6X9P6TKk2BQvoWMOR1wXKcgGI=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha1-+/H3GnJ4kdaFuxeG+bp0CE9uL5E=", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/regex/-/regex-6.1.0.tgz", + "integrity": "sha1-186Y+O4y2nSXwT9mAfyivEpqeAM=", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha1-oLGXenTIfwczd7k42+36supYKzM=", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha1-hxY1EqFdzikIzweciWDVFY/0MoA=", + "license": "MIT" + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha1-Wdc0j9Xb7zgHu6odRD79LdhezuQ=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-sanitize": { + "version": "6.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", + "integrity": "sha1-FulfSmemnL8PeeETyODfSCA9tzw=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-sanitize": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha1-LsHrxWxqugeQXTtEcL3w9oTzC3U=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha1-MyJ7KnQ5dnDTV78FwJjq+FE/DWs=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha1-qmB0P8s36/awaSBOtNowTkDbRaE=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha1-Kt2q3agMqb2aoNp2PnTRYydoOzc=", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/remark-smartypants/-/remark-smartypants-3.0.3.tgz", + "integrity": "sha1-MrTyC0ZtFrPC9Mx38xAZJw6pems=", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha1-TFsB3XEcJp3xqq4RdD634udjb9M=", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha1-iFYouy+AQMJkAevyWOxRxK6YrCo=", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha1-iaf92TgmEmcxjq/hT5wy5ZjDaQk=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha1-YWs9wsVwVrVYjDHN9LPWTbEzcg8=", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/retext/-/retext-9.0.0.tgz", + "integrity": "sha1-q1zXKDaJQWewymrnD9z6oWYmf3o=", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha1-0CSYqh/TnxvwDi/1mxOEwF0MfOM=", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha1-ToUsKXTPLPolPu7EJ8l+/EO10Vg=", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha1-UB1UQL1NEh41HHxQn4UH3pYR4Vk=", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rolldown": { + "version": "1.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/rolldown/-/rolldown-1.2.2.tgz", + "integrity": "sha1-8oB2cWk2vO2gXg0FWVAqXOsKbp4=", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.142.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.2", + "@rolldown/binding-darwin-arm64": "1.2.2", + "@rolldown/binding-darwin-x64": "1.2.2", + "@rolldown/binding-freebsd-x64": "1.2.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.2", + "@rolldown/binding-linux-arm64-gnu": "1.2.2", + "@rolldown/binding-linux-arm64-musl": "1.2.2", + "@rolldown/binding-linux-ppc64-gnu": "1.2.2", + "@rolldown/binding-linux-s390x-gnu": "1.2.2", + "@rolldown/binding-linux-x64-gnu": "1.2.2", + "@rolldown/binding-linux-x64-musl": "1.2.2", + "@rolldown/binding-openharmony-arm64": "1.2.2", + "@rolldown/binding-win32-arm64-msvc": "1.2.2", + "@rolldown/binding-win32-x64-msvc": "1.2.2" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha1-4/zuCT+7XOdl4a0Ij/TeKIn2+b4=", + "license": "MIT" + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha1-J8h+v3YIuxYfTLUQi5YchDjBiLc=", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/sax/-/sax-1.6.1.tgz", + "integrity": "sha1-TCPPYIwLaTq1S0tYiOks/pd7mEM=", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha1-DE74LWfR5cHjWej8dtOofwRf5b0=", + "license": "MIT" + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha1-6QQZU1BngOwB1Z8pKhnHuFC4QWc=", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-6.3.1.tgz", + "integrity": "sha1-VW0u+GiRRuRtzqS/3QlfNDTf/LQ=", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha1-Qe0hpGQGvhY+rNC+ezZLQvzyiF8=", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", + "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shiki": { + "version": "4.4.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/shiki/-/shiki-4.4.1.tgz", + "integrity": "sha1-l7FkbtxxaK3scLZVEc1I8N/O/So=", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.4.1", + "@shikijs/engine-javascript": "4.4.1", + "@shikijs/engine-oniguruma": "4.4.1", + "@shikijs/langs": "4.4.1", + "@shikijs/themes": "4.4.1", + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha1-E01oEpd1ZDfMBcoBNw06elcQde0=", + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.7.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/smol-toml/-/smol-toml-1.7.1.tgz", + "integrity": "sha1-uj4o0uQ0iHSoJP2OHXP+xhjLdjs=", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha1-HOVlD93YerwJnto33P8CTCZnrkY=", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha1-Hs2dI1CjhEVyw/SjErzrAYNIhZ8=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "license": "BSD-3-Clause" + }, + "node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha1-cxBRZJPfV1dC/pivb66H2F1e0Kw=", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha1-s7ee9fJ3zErHPK6wI2xbqTmzpPM=", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha1-0iomlSKDamJ6+NBLXD/Sx/o+MuM=", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "2.4.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha1-hUF/aDETut6g/n4XInZ2+In/flg=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/svgo": { + "version": "4.0.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha1-piJG8KnWccAxTQTzzBX3ixvQZn8=", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha1-wAaGFhHCE8GHeJOrWyPaoWviu1U=", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha1-XafJmSxGA4IhJnmFqyhCGoh58WA=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha1-EicVSUkToYBRZqr3yTRnkz7qJsQ=", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "0.1.15", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha1-2zXqor1f5iez7y6jjYsEB/K8Le8=", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha1-qswdux1Ok+atjdZJROCfmtFHpHQ=", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha1-ViqabJ6ys7Ej05cZ+a9btE/NdjE=", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha1-2ALjMqB9+GHEiALAQyEBexvYczg=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/trough/-/trough-2.2.0.tgz", + "integrity": "sha1-lKYL1r03XBUsHfkRpLEdWwJW9Q8=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha1-YS7+TtI11Wfoq6Xypfq3AoCt6D8=", + "license": "0BSD", + "optional": true + }, + "node_modules/tsx": { + "version": "4.23.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tsx/-/tsx-4.23.5.tgz", + "integrity": "sha1-+/eCQf8+YWxdyW5hEyALDmiZ59w=", + "devOptional": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha1-kaQ2aBsvUUutsRQGG2peXCuJQ7E=", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha1-kCUdwAeRbpcnhsuU100VsYVXfSE=", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.6", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha1-efA3XW+hbTETPPNdNgJbWCN8ScE=", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, + "node_modules/typescript-auto-import-cache/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", + "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha1-eo+4dfzGOC0sfQs2knOLBQCpJGc=", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha1-jn1ZfjOKSB6oKFKpul6AIcVK7RA=", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha1-4SiNYJIm8tAtjWnuhh+iDYNI7ys=", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha1-ROn8nzJEZIzeo15Pm7LWgelBCAk=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unified/-/unified-11.0.5.tgz", + "integrity": "sha1-9mZ3YQpcCp7pDKsrjU1mA3Am2eE=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha1-YravaPS2Ato0n8ENySUl/5O/8yk=", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha1-P8zBsIa1bzTIt5jh/5C1xURo6JY=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha1-0KP4by3Q23rNfYwkeAgLXGf5xqk=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha1-mB1jCOiHsAXR9JGBHTy8wlSzFek=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha1-Z48gq1yhIHqX1+qKOINzyc+Ja+Q=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha1-/qaKJWWECclGBAi8a0mRuWW1IWM=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha1-RJxuIaiA4IVb9aq63rOnQDFKusI=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha1-mioosKp2oV4NpwoIpYY6LwYOJGg=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha1-S87Rmbcdfzw5dUPqbMOeen833H4=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha1-d333+5hlLOFrS3zZmdChpA76OgI=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha1-52yC/cHSwEyw4sCh3giqCLIlP1E=", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha1-AOFmZckMYg+6FKPDaHMql2ST92A=", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha1-ZNdttYcTE2rL60xJEUNmzGzC6A0=", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha1-sXS/plyytSZzLZ8qwKQIAnh28y0=", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true, + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha1-NlKrHEllMYUr9VprrFevmB68OKs=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha1-y56s0g8rZCbRlFHg6vo9CoRiJcM=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha1-h7RN3de3DwZBwuPtCGS6c+LqjfQ=", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.2.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vite/-/vite-8.2.0.tgz", + "integrity": "sha1-kC/NPcAxL1U8hbbLxGJdysotjfg=", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha1-WbmIWxwgCFYxnX6c6w6ZoGVDAAk=", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/volar-service-css": { + "version": "0.0.71", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/volar-service-css/-/volar-service-css-0.0.71.tgz", + "integrity": "sha1-P5t7RMm7t+OV+58nkO5UQPgrLHk=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.71", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz", + "integrity": "sha1-IRkR6CT6ATGailWxTNVf55Dyvtc=", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.1", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.71", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/volar-service-html/-/volar-service-html-0.0.71.tgz", + "integrity": "sha1-x2cl8EyFMkWnpmgU8ENUpwUjApc=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.71", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz", + "integrity": "sha1-mIw5zJN/bHTF8N9ArpGry+U5mTg=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.71", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz", + "integrity": "sha1-4iVTSY46Mk04hlPn32McZ+Fg/J0=", + "dev": true, + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.71", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz", + "integrity": "sha1-2GwbONHv2RBVq1665rxy+d9wsfQ=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", + "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.71", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz", + "integrity": "sha1-dIyKhgORqavi+j57axE74KaJE2Q=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.23.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha1-JCNlrD/y62n3fKdQOk5pQTUYAwI=", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha1-MnNnbwzy6rQLP0TQhay7fwijnYo=", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-html-languageservice": { + "version": "5.6.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", + "integrity": "sha1-MvUyYdpdD61PachdwA3mZJ4hC9E=", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha1-OXo5I41Jbj4IpUSouT3yzRM0fQw=", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz", + "integrity": "sha1-XoSKSt3wBLYzcVb3hYoAbgg4tPU=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha1-UArvggl+uU35DQCGeLC2tfR0AVs=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.2.tgz", + "integrity": "sha1-5/s25royvHumIiV623imEmJz3cU=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.1", + "vscode-languageserver-types": "3.18.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha1-RX7gQnGrOJmKCTxowjQvU/bkpjE=", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz", + "integrity": "sha1-EyMhIpYEg2urcQyXSH5s2vub17s=", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha1-9D36NftR52PRfNlNzKDJRY81q/k=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha1-hkqLjzkINVcvThO9n4MT0OOsS+o=", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha1-MnNnbwzy6rQLP0TQhay7fwijnYo=", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha1-PLaJPdm9aVJE2KAkvfdG7qZlzD8=", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha1-3QnsWmaji1w//8d0AVcTSW0U4Jw=", + "dev": true, + "license": "MIT" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha1-EBD/fGUOzLJZLOvur5obJT/UBpI=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha1-lWgy3qlJQwbm0gnrhxZDu4c9fJg=", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha1-tbuOIWXOJ11NQ0dt0nAK2Qkdttw=", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha1-RsHhi/4oWEeZgt0qzPNNFudJ7aI=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha1-/+fwuYIgpK+sFx4/ubbR+HcfAV4=", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha1-f0k00PfKjFb5UxSTndzS3ZHOHVU=", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha1-eCdK/ZNZih391hMN9qVm3vy/mqQ=", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-language-server": { + "version": "1.23.0", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yaml-language-server/-/yaml-language-server-1.23.0.tgz", + "integrity": "sha1-hSrDGSQ35V1sMafDDZed2X4t2cY=", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "ajv-i18n": "^4.2.0", + "prettier": "^3.8.1", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.2", + "yaml": "2.8.3" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha1-i/c6ByQrnntgH6wvpdwioJSrzCc=", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha1-oNa9Lvs90DxZNwIjcBg05gQJvX0=", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha1-zX6YxwPvUWlbu/Bi7VjyjpQpG1Y=", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha1-h7gglAUbBWdxc0bs0A/RSASzV8g=", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha1-PgnJXT8aqJpYwRTJkiPt9jkVLAA=", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/zod/-/zod-4.4.3.tgz", + "integrity": "sha1-toDxcohdGLvr8hqDTqJeVaG781Y=", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha1-fWuyAmoUJBXdi+iJHXhw5tvmX1U=", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha1-yCfUsKy3b8PmhaTG7CkC1RBw6dc=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c0a054c --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "copilot-studio-gallery", + "type": "module", + "version": "0.1.0", + "private": true, + "description": "A community gallery for Copilot Studio agents and workflows.", + "engines": { + "node": ">=22.12.0" + }, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro", + "check": "astro check", + "check:submissions": "tsx scripts/import-submissions.ts --check", + "import:submissions": "tsx scripts/import-submissions.ts", + "seed:demos": "tsx scripts/seed-demo-submissions.ts", + "test": "node --import tsx --test scripts/*.test.ts" + }, + "dependencies": { + "@astrojs/markdown-remark": "^7.2.2", + "@astrojs/react": "^6.0.2", + "@xyflow/react": "^12.11.2", + "adm-zip": "^0.6.0", + "astro": "^7.1.6", + "fast-xml-parser": "^5.10.1", + "gray-matter": "^4.0.3", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "rehype-sanitize": "^6.0.0", + "yaml": "^2.9.0" + }, + "devDependencies": { + "@astrojs/check": "^0.9.10", + "@tailwindcss/typography": "^0.5.20", + "@tailwindcss/vite": "^4.3.3", + "@types/adm-zip": "^0.5.8", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "tailwindcss": "^4.3.3", + "tsx": "^4.23.5", + "typescript": "^6.0.3" + } +} diff --git a/public/bundles/blastbox-demo-agents.zip b/public/bundles/blastbox-demo-agents.zip new file mode 100644 index 0000000..80fc85f Binary files /dev/null and b/public/bundles/blastbox-demo-agents.zip differ diff --git a/public/bundles/blastbox-deploy.zip b/public/bundles/blastbox-deploy.zip new file mode 100644 index 0000000..11da442 Binary files /dev/null and b/public/bundles/blastbox-deploy.zip differ diff --git a/public/bundles/blastbox-omega.zip b/public/bundles/blastbox-omega.zip new file mode 100644 index 0000000..a7d69f6 Binary files /dev/null and b/public/bundles/blastbox-omega.zip differ diff --git a/public/bundles/campaign-content-engine.zip b/public/bundles/campaign-content-engine.zip new file mode 100644 index 0000000..ea724f8 Binary files /dev/null and b/public/bundles/campaign-content-engine.zip differ diff --git a/public/bundles/contract-review-assistant.zip b/public/bundles/contract-review-assistant.zip new file mode 100644 index 0000000..16c7036 Binary files /dev/null and b/public/bundles/contract-review-assistant.zip differ diff --git a/public/bundles/customer-support-triage.zip b/public/bundles/customer-support-triage.zip new file mode 100644 index 0000000..5cb4b71 Binary files /dev/null and b/public/bundles/customer-support-triage.zip differ diff --git a/public/bundles/employee-onboarding-guide.zip b/public/bundles/employee-onboarding-guide.zip new file mode 100644 index 0000000..59f8c89 Binary files /dev/null and b/public/bundles/employee-onboarding-guide.zip differ diff --git a/public/bundles/field-service-dispatch.zip b/public/bundles/field-service-dispatch.zip new file mode 100644 index 0000000..a21d3a6 Binary files /dev/null and b/public/bundles/field-service-dispatch.zip differ diff --git a/public/bundles/incident-response-coordinator.zip b/public/bundles/incident-response-coordinator.zip new file mode 100644 index 0000000..6541b52 Binary files /dev/null and b/public/bundles/incident-response-coordinator.zip differ diff --git a/public/bundles/invoice-exception-handler.zip b/public/bundles/invoice-exception-handler.zip new file mode 100644 index 0000000..a561dc3 Binary files /dev/null and b/public/bundles/invoice-exception-handler.zip differ diff --git a/public/bundles/knowledge-gap-finder.zip b/public/bundles/knowledge-gap-finder.zip new file mode 100644 index 0000000..15d2eac Binary files /dev/null and b/public/bundles/knowledge-gap-finder.zip differ diff --git a/public/bundles/project-status-narrator.zip b/public/bundles/project-status-narrator.zip new file mode 100644 index 0000000..a21456c Binary files /dev/null and b/public/bundles/project-status-narrator.zip differ diff --git a/public/bundles/sales-account-brief.zip b/public/bundles/sales-account-brief.zip new file mode 100644 index 0000000..6696d0b Binary files /dev/null and b/public/bundles/sales-account-brief.zip differ diff --git a/public/bundles/security-access-review.zip b/public/bundles/security-access-review.zip new file mode 100644 index 0000000..d474414 Binary files /dev/null and b/public/bundles/security-access-review.zip differ diff --git a/public/bundles/store-feedback-analyzer.zip b/public/bundles/store-feedback-analyzer.zip new file mode 100644 index 0000000..ead7d17 Binary files /dev/null and b/public/bundles/store-feedback-analyzer.zip differ diff --git a/public/bundles/store-modern-agent.zip b/public/bundles/store-modern-agent.zip new file mode 100644 index 0000000..2c0b41f Binary files /dev/null and b/public/bundles/store-modern-agent.zip differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..7f48a94 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..27ae0d5 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/scripts/fixtures/reduced-solution/README.md b/scripts/fixtures/reduced-solution/README.md new file mode 100644 index 0000000..1d83f63 --- /dev/null +++ b/scripts/fixtures/reduced-solution/README.md @@ -0,0 +1,3 @@ +# Demo agent + +This guide came from the submitted README. diff --git a/scripts/fixtures/reduced-solution/metadata.json b/scripts/fixtures/reduced-solution/metadata.json new file mode 100644 index 0000000..4c37154 --- /dev/null +++ b/scripts/fixtures/reduced-solution/metadata.json @@ -0,0 +1,8 @@ +{ + "name": "Catalog name wins", + "description": "A compact imported solution.", + "tags": ["support", "demo"], + "author": "Example Author", + "authorGithub": "example-author", + "createdAt": "2026-01-01" +} diff --git a/scripts/fixtures/reduced-solution/solution/Workflows/branched.json b/scripts/fixtures/reduced-solution/solution/Workflows/branched.json new file mode 100644 index 0000000..83f01cd --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/Workflows/branched.json @@ -0,0 +1,22 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Saved workflow", + "nodes": [ + { "id": "start", "name": "When a request arrives", "type": "trigger", "position": { "x": 0, "y": 0 }, "data": { "config": { "method": "POST" } } }, + { "id": "route", "name": "Route request", "type": "condition", "position": { "x": 200, "y": 0 }, "data": { "config": { "expression": "priority" } } }, + { "id": "respond", "name": "Respond", "type": "connector", "position": { "x": 400, "y": -80 }, "data": { "config": { "displayName": "Reply", "apiName": "shared_teams", "operationName": "postMessage", "operationType": "action", "parameters": { "message": "Done", "connectionName": "do-not-publish", "outputSchema": "omitted" } } } }, + { "id": "escalate", "name": "Escalate", "type": "agent", "position": { "x": 400, "y": 80 }, "data": { "config": { "botSchemaName": "manager_agent", "mode": "delegate", "tools": [{ "name": "Search" }] } } } + ], + "edges": [ + { "id": "one", "source": "start", "target": "route" }, + { "id": "two", "source": "route", "target": "respond", "sourceHandle": "yes" }, + { "id": "three", "source": "route", "target": "escalate", "sourceHandle": "no" } + ] + } + } + } + } +} diff --git a/scripts/fixtures/reduced-solution/solution/botcomponents/other_agent.tool.helper/botcomponent.xml b/scripts/fixtures/reduced-solution/solution/botcomponents/other_agent.tool.helper/botcomponent.xml new file mode 100644 index 0000000..4a2d236 --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/botcomponents/other_agent.tool.helper/botcomponent.xml @@ -0,0 +1 @@ +ToolOther Tool diff --git a/scripts/fixtures/reduced-solution/solution/botcomponents/support-tools/botcomponent.xml b/scripts/fixtures/reduced-solution/solution/botcomponents/support-tools/botcomponent.xml new file mode 100644 index 0000000..e9d9c0f --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/botcomponents/support-tools/botcomponent.xml @@ -0,0 +1 @@ +ToolKnowledge Search diff --git a/scripts/fixtures/reduced-solution/solution/botcomponents/support-tools/data b/scripts/fixtures/reduced-solution/solution/botcomponents/support-tools/data new file mode 100644 index 0000000..6053ec5 --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/botcomponents/support-tools/data @@ -0,0 +1,6 @@ +components: + - kind: Skill + name: Triage Skill + - kind: ConnectedAgentTool + name: Manager Escalation + botSchemaName: manager_agent diff --git a/scripts/fixtures/reduced-solution/solution/bots/other/bot.xml b/scripts/fixtures/reduced-solution/solution/bots/other/bot.xml new file mode 100644 index 0000000..de34356 --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/bots/other/bot.xml @@ -0,0 +1 @@ +Other Agent diff --git a/scripts/fixtures/reduced-solution/solution/bots/other/configuration.json b/scripts/fixtures/reduced-solution/solution/bots/other/configuration.json new file mode 100644 index 0000000..031b5f6 --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/bots/other/configuration.json @@ -0,0 +1,6 @@ +{ + "agentSettings": { + "model": { "series": "gpt-4o-mini" }, + "channels": [{ "channelId": "web" }] + } +} diff --git a/scripts/fixtures/reduced-solution/solution/bots/support/bot.xml b/scripts/fixtures/reduced-solution/solution/bots/support/bot.xml new file mode 100644 index 0000000..5f21539 --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/bots/support/bot.xml @@ -0,0 +1 @@ +Support Agent diff --git a/scripts/fixtures/reduced-solution/solution/bots/support/configuration.json b/scripts/fixtures/reduced-solution/solution/bots/support/configuration.json new file mode 100644 index 0000000..6067ed9 --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/bots/support/configuration.json @@ -0,0 +1,7 @@ +{ + "agentSettings": { + "model": { "series": "gpt-4.1" }, + "instructions": { "segments": [{ "value": "Help customers" }, { "value": "with support requests." }] }, + "channels": [{ "channelId": "web" }, { "id": "teams" }] + } +} diff --git a/scripts/fixtures/reduced-solution/solution/customizations.xml b/scripts/fixtures/reduced-solution/solution/customizations.xml new file mode 100644 index 0000000..2eb8e48 --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/customizations.xml @@ -0,0 +1,5 @@ + + + Branched workflowbranched.jsonworkflow-001 + + diff --git a/scripts/fixtures/reduced-solution/solution/solution.xml b/scripts/fixtures/reduced-solution/solution/solution.xml new file mode 100644 index 0000000..60571dc --- /dev/null +++ b/scripts/fixtures/reduced-solution/solution/solution.xml @@ -0,0 +1,9 @@ + + + Demo_Agent_Solution + 2.3.4.5 + Contoso + + + + diff --git a/scripts/import-submissions.test.ts b/scripts/import-submissions.test.ts new file mode 100644 index 0000000..35cd9e7 --- /dev/null +++ b/scripts/import-submissions.test.ts @@ -0,0 +1,213 @@ +import assert from "node:assert/strict"; +import { cp, lstat, mkdir, rm, symlink, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import test from "node:test"; +import AdmZip from "adm-zip"; +import { + SubmissionImportError, + deriveSolutionFields, + deterministicZip, + extractSavedGraph, + importSubmissions, + layoutAgentGraph, + normalizeAgentComponents, + normalizeWorkflowGraph, + parseXml, +} from "./import-submissions"; + +const fixture = join(process.cwd(), "scripts/fixtures/reduced-solution"); +const workspace = join(process.cwd(), "scripts/fixtures/.import-test-workspace"); + +async function setup(): Promise { + await rm(workspace, { recursive: true, force: true }); + await mkdir(join(workspace, "submissions"), { recursive: true }); + await cp(fixture, join(workspace, "submissions/demo-agent"), { recursive: true }); +} + +test.beforeEach(setup); +test.after(async () => rm(workspace, { recursive: true, force: true })); + +test("imports metadata while retaining derived solution identity", async () => { + const report = await importSubmissions({ root: workspace }); + assert.deepEqual(report.imported, ["demo-agent"]); + const record = JSON.parse(await (await import("node:fs/promises")).readFile(join(workspace, "src/content/solutions/demo-agent.json"), "utf8")); + assert.equal(record.name, "Catalog name wins"); + assert.equal(record.uniqueName, "Demo_Agent_Solution"); + assert.equal(record.version, "2.3.4.5"); + assert.equal(record.publisher, "Contoso"); + assert.equal(record.bundle, "bundles/demo-agent.zip"); + assert.equal(record.componentCount, 1); + const support = record.agents.find((agent: { schemaName: string }) => agent.schemaName === "support_agent"); + const other = record.agents.find((agent: { schemaName: string }) => agent.schemaName === "other_agent"); + assert.deepEqual(support, { + id: "support", + schemaName: "support_agent", + name: "Support Agent", + model: "gpt-4.1", + instructions: "Help customers\nwith support requests.", + channels: ["web", "teams"], + toolCount: 1, + skillCount: 1, + connectedAgentCount: 1, + }); + assert.equal(other.toolCount, 1); + assert.equal(other.skillCount, 0); + assert.equal(other.connectedAgentCount, 0); + assert.deepEqual(record.agentGraph.nodes.find((node: { id: string }) => node.id === "agent:support").details, { + model: "gpt-4.1", + instructions: "Help customers\nwith support requests.", + channels: ["web", "teams"], + toolCount: 1, + skillCount: 1, + connectedAgentCount: 1, + }); + assert.deepEqual(record.agentGraph.nodes.find((node: { name: string }) => node.name === "Knowledge Search").details, { + kind: "tool", + schemaName: "support_agent.tool.knowledge", + }); + assert.ok(record.agentGraph.nodes.every((node: { position: { x: number; y: number } }) => Number.isFinite(node.position.x) && Number.isFinite(node.position.y))); + assert.equal(record.agentGraph.edges.filter((edge: { label: string; source: string }) => edge.source === "agent:support").length, 3); + assert.equal(record.workflows[0].name, "Branched workflow"); + const workflowNodes = record.workflows[0].graph.nodes; + assert.deepEqual(workflowNodes.find((node: { id: string }) => node.id === "respond").details, { + displayName: "Reply", + apiName: "shared_teams", + operationName: "postMessage", + operationType: "action", + parameters: { message: "Done" }, + }); + assert.deepEqual(workflowNodes.find((node: { id: string }) => node.id === "escalate").details, { + botSchemaName: "manager_agent", + mode: "delegate", + tools: ["Search"], + }); +}); + +test("check mode validates inputs without requiring or writing generated artifacts", async () => { + const report = await importSubmissions({ root: workspace, check: true }); + assert.deepEqual(report.imported, ["demo-agent"]); + await assert.rejects(() => lstat(join(workspace, "src/content/solutions/demo-agent.json"))); + await assert.rejects(() => lstat(join(workspace, "src/content/guides/demo-agent.md"))); + await assert.rejects(() => lstat(join(workspace, "public/bundles/demo-agent.zip"))); + await mkdir(join(workspace, "src/content/solutions"), { recursive: true }); + await writeFile(join(workspace, "src/content/solutions/stale.json"), "{}"); + await importSubmissions({ root: workspace, check: true }); + await lstat(join(workspace, "src/content/solutions/stale.json")); +}); + +test("ignores hidden and template submission directories", async () => { + await mkdir(join(workspace, "submissions/_template"), { recursive: true }); + await mkdir(join(workspace, "submissions/.draft"), { recursive: true }); + const report = await importSubmissions({ root: workspace, check: true }); + assert.deepEqual(report.imported, ["demo-agent"]); +}); + +test("maps XML components and normalizes tools, skills, and delegation", () => { + const xml = parseXml("Example"); + assert.equal((xml as { root: { Thing: { Name: string } } }).root.Thing.Name, "Example"); + const normalized = normalizeAgentComponents("agent:one", [ + { id: "tool", name: "Search", kind: "tool" }, + { id: "skill", name: "Triage", kind: "skill" }, + { id: "connected", name: "Escalation", kind: "connected", target: "Manager" }, + ]); + assert.equal(normalized.tools.length, 1); + assert.equal(normalized.skills.length, 1); + assert.equal(normalized.connected.length, 1); + assert.deepEqual(normalized.edges.map((edge) => edge.label), ["uses", "includes"]); +}); + +test("centers primary agents and arranges capabilities around them", () => { + const graph = layoutAgentGraph([ + { id: "agent:primary", name: "Primary", type: "agent", position: { x: 0, y: 0 }, details: {} }, + { id: "tool:search", name: "Search", type: "tool", position: { x: 0, y: 0 }, details: {} }, + { id: "skill:triage", name: "Triage", type: "skill", position: { x: 0, y: 0 }, details: {} }, + { id: "agent:delegate", name: "Delegate", type: "agent", position: { x: 0, y: 0 }, details: {} }, + { id: "tool:notify", name: "Notify", type: "tool", position: { x: 0, y: 0 }, details: {} }, + ], [ + { id: "primary-search", source: "agent:primary", target: "tool:search", label: "uses" }, + { id: "primary-triage", source: "agent:primary", target: "skill:triage", label: "includes" }, + { id: "primary-delegate", source: "agent:primary", target: "agent:delegate", label: "delegates" }, + { id: "delegate-notify", source: "agent:delegate", target: "tool:notify", label: "uses" }, + ]); + const position = (id: string) => graph.nodes.find((node) => node.id === id)?.position; + assert.deepEqual(position("agent:primary"), { x: -112, y: -36 }); + assert.notDeepEqual(position("tool:search"), position("agent:primary")); + assert.notDeepEqual(position("skill:triage"), position("agent:primary")); + assert.ok((position("agent:delegate")?.y ?? 0) < -300); + assert.ok((position("tool:notify")?.y ?? 0) < (position("agent:delegate")?.y ?? 0)); + assert.deepEqual(graph.edges.map((edge) => edge.id), [ + "delegate-notify", + "primary-delegate", + "primary-search", + "primary-triage", + ]); +}); + +test("derives display and publisher only from the solution manifest", () => { + const solution = parseXml(` + solution_name1.0.0.0 + + publisher_unique + `); + assert.deepEqual(deriveSolutionFields(solution, {}), { + uniqueName: "solution_name", + displayName: "Solution title", + version: "1.0.0.0", + publisher: "Publisher title", + }); +}); + +test("extracts one saved graph and retains unknown workflow node types", () => { + const graph = extractSavedGraph({ + unrelated: { name: "Not the saved graph", nodes: [], edges: [] }, + trigger: { + metadata: { + associatedData: { + graph: { + name: "Saved", + nodes: [ + { id: "a", name: "Start", type: "trigger" }, + { id: "b", name: "Novel", type: "future-widget", position: { x: 9, y: 3 } }, + ], + edges: [{ source: "a", target: "b", sourceHandle: "yes" }], + }, + }, + }, + }, + }); + const normalized = normalizeWorkflowGraph(graph); + assert.equal(normalized.nodes[1].type, "future-widget"); + assert.equal(normalized.edges[0].sourceHandle, "yes"); +}); + +test("rejects a workflow without exactly one saved graph", () => { + assert.throws(() => extractSavedGraph({ name: "no graph", nodes: [] }), /exactly one saved designer graph/); +}); + +test("rejects invalid metadata, unsafe slugs, and symlinks", async () => { + await writeFile(join(workspace, "submissions/demo-agent/metadata.json"), "{}"); + await assert.rejects(() => importSubmissions({ root: workspace }), SubmissionImportError); + await setup(); + await symlink("../metadata.json", join(workspace, "submissions/demo-agent/solution/linked.xml")); + await assert.rejects(() => importSubmissions({ root: workspace }), /symbolic links are not allowed/); + assert.equal((await lstat(join(workspace, "submissions/demo-agent/solution/linked.xml"))).isSymbolicLink(), true); +}); + +test("builds deterministic solution-only zip and cleans stale outputs", async () => { + await importSubmissions({ root: workspace }); + const solutionRoot = join(workspace, "submissions/demo-agent/solution"); + const files = [ + { absolutePath: join(solutionRoot, "customizations.xml"), path: "customizations.xml", size: 1 }, + { absolutePath: join(solutionRoot, "solution.xml"), path: "solution.xml", size: 1 }, + ]; + assert.deepEqual(deterministicZip(files, solutionRoot), deterministicZip([...files].reverse(), solutionRoot)); + const zip = new AdmZip(await (await import("node:fs/promises")).readFile(join(workspace, "public/bundles/demo-agent.zip"))); + assert.equal(zip.getEntry("metadata.json"), null); + assert.equal(zip.getEntry("README.md"), null); + await writeFile(join(workspace, "src/content/solutions/stale.json"), "{}"); + await writeFile(join(workspace, "src/content/guides/stale.md"), "stale"); + await mkdir(join(workspace, "public/bundles"), { recursive: true }); + await writeFile(join(workspace, "public/bundles/stale.zip"), "stale"); + await importSubmissions({ root: workspace }); + await assert.rejects(() => lstat(join(workspace, "src/content/solutions/stale.json"))); +}); diff --git a/scripts/import-submissions.ts b/scripts/import-submissions.ts new file mode 100644 index 0000000..bf38c94 --- /dev/null +++ b/scripts/import-submissions.ts @@ -0,0 +1,1002 @@ +import { readFileSync } from "node:fs"; +import { readFile, readdir, lstat, mkdir, rm, writeFile } from "node:fs/promises"; +import { basename, dirname, join, relative, resolve, sep } from "node:path"; +import { fileURLToPath } from "node:url"; +import AdmZip from "adm-zip"; +import { XMLParser } from "fast-xml-parser"; +import matter from "gray-matter"; +import { parse as parseYaml } from "yaml"; +import { + submissionMetadataSchema, + type AgentPreview, + type FileSummary, + type GraphData, + type GraphEdge, + type GraphNode, + type SolutionRecord, + type SubmissionMetadata, + type WorkflowPreview, +} from "../src/lib/schema"; + +const MAX_FILE_BYTES = 2 * 1024 * 1024; +const MAX_TOTAL_BYTES = 25 * 1024 * 1024; +const MAX_TEXT_BYTES = 512 * 1024; +const FIXED_ZIP_DATE = new Date(1980, 0, 1, 0, 0, 0); +const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; + +type UnknownRecord = Record; + +interface SolutionFile { + absolutePath: string; + path: string; + size: number; +} + +interface ComponentItem { + id: string; + name: string; + kind: "tool" | "skill" | "connected"; + target?: string; + owner?: string; + schemaName?: string; + operation?: string; + description?: string; +} + +interface BuiltSubmission { + slug: string; + record: SolutionRecord; + guide: string; + bundle: Buffer; +} + +export interface ImportReport { + imported: string[]; + errors: Record; +} + +export class SubmissionImportError extends Error { + constructor(readonly errors: Record) { + super( + Object.entries(errors) + .map(([slug, messages]) => `${slug}:\n${messages.map((message) => ` - ${message}`).join("\n")}`) + .join("\n"), + ); + } +} + +function record(value: unknown): UnknownRecord | undefined { + return value !== null && typeof value === "object" && !Array.isArray(value) ? (value as UnknownRecord) : undefined; +} + +function strings(value: unknown): string[] { + if (typeof value === "string") return [value]; + if (Array.isArray(value)) return value.flatMap(strings); + return []; +} + +function array(value: unknown): unknown[] { + return Array.isArray(value) ? value : []; +} + +function valuesForKey(input: unknown, key: string): unknown[] { + const result: unknown[] = []; + const visit = (value: unknown): void => { + if (Array.isArray(value)) { + value.forEach(visit); + return; + } + const item = record(value); + if (!item) return; + for (const [candidate, child] of Object.entries(item)) { + if (candidate.replace(/^@/, "").toLowerCase() === key.replace(/^@/, "").toLowerCase()) result.push(child); + visit(child); + } + }; + visit(input); + return result; +} + +function firstText(input: unknown, keys: string[]): string | undefined { + for (const key of keys) { + const value = valuesForKey(input, key).flatMap(strings).find((text) => text.trim()); + if (value) return value.trim(); + } + return undefined; +} + +function field(item: UnknownRecord, ...names: string[]): unknown { + for (const [key, value] of Object.entries(item)) { + if (names.some((name) => key.replace(/^@/, "").toLowerCase() === name.replace(/^@/, "").toLowerCase())) return value; + } + return undefined; +} + +function scalar(item: UnknownRecord, ...names: string[]): string | undefined { + const value = field(item, ...names); + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value); + return undefined; +} + +function decodeXmlEntities(value: string): string { + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll(""", "\"") + .replaceAll("'", "'"); +} + +function safeId(value: string, fallback: string): string { + const normalized = value.trim().replace(/[^A-Za-z0-9_.:-]+/g, "-").replace(/^-|-$/g, ""); + return normalized || fallback; +} + +function textFromNode(value: unknown): string | undefined { + if (typeof value === "string") return value.trim() || undefined; + const item = record(value); + if (!item) return undefined; + return scalar(item, "#text", "text", "description", "@description", "name", "@name")?.trim() || undefined; +} + +function directLocalizedDescription(parent: UnknownRecord): string | undefined { + const sections = field(parent, "LocalizedNames"); + const localized = (Array.isArray(sections) ? sections : [sections]) + .map(record) + .flatMap((section) => (section ? [field(section, "LocalizedName")] : [])) + .flatMap((names) => (Array.isArray(names) ? names : [names])) + .map(textFromNode) + .find((value): value is string => Boolean(value)); + return localized; +} + +function parser(): XMLParser { + return new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: "@", + processEntities: false, + trimValues: true, + parseTagValue: false, + }); +} + +export function parseXml(xml: string): unknown { + return parser().parse(xml); +} + +function assertDescendant(root: string, candidate: string): void { + const path = resolve(candidate); + if (path !== root && !path.startsWith(`${root}${sep}`)) throw new Error(`unsafe path: ${candidate}`); +} + +async function collectFiles(root: string): Promise { + const resolvedRoot = resolve(root); + const files: SolutionFile[] = []; + let total = 0; + const walk = async (directory: string): Promise => { + assertDescendant(resolvedRoot, directory); + const entries = await readdir(directory, { withFileTypes: true }); + for (const entry of entries) { + const absolutePath = join(directory, entry.name); + assertDescendant(resolvedRoot, absolutePath); + const stat = await lstat(absolutePath); + if (stat.isSymbolicLink()) throw new Error(`symbolic links are not allowed: ${relative(resolvedRoot, absolutePath)}`); + if (stat.isDirectory()) { + await walk(absolutePath); + } else if (stat.isFile()) { + if (stat.size > MAX_FILE_BYTES) { + throw new Error(`file exceeds ${MAX_FILE_BYTES} byte limit: ${relative(resolvedRoot, absolutePath)}`); + } + total += stat.size; + if (total > MAX_TOTAL_BYTES) throw new Error(`solution exceeds ${MAX_TOTAL_BYTES} byte total limit`); + files.push({ absolutePath, path: relative(resolvedRoot, absolutePath).split(sep).join("/"), size: stat.size }); + } else { + throw new Error(`unsupported filesystem entry: ${relative(resolvedRoot, absolutePath)}`); + } + } + }; + await walk(resolvedRoot); + return files.sort((a, b) => a.path.localeCompare(b.path)); +} + +async function rejectSymlinks(root: string): Promise { + const resolvedRoot = resolve(root); + const walk = async (directory: string): Promise => { + for (const entry of await readdir(directory, { withFileTypes: true })) { + const path = join(directory, entry.name); + assertDescendant(resolvedRoot, path); + const stat = await lstat(path); + if (stat.isSymbolicLink()) throw new Error(`symbolic links are not allowed: ${relative(resolvedRoot, path)}`); + if (stat.isDirectory()) await walk(path); + } + }; + const stat = await lstat(resolvedRoot); + if (stat.isSymbolicLink()) throw new Error("submission directory must not be a symbolic link"); + await walk(resolvedRoot); +} + +function requiredFile(files: SolutionFile[], name: string): SolutionFile { + const file = files.find((item) => item.path.toLowerCase() === name.toLowerCase()); + if (!file) throw new Error(`missing required solution/${name}`); + return file; +} + +async function readText(file: SolutionFile): Promise { + if (file.size > MAX_TEXT_BYTES) throw new Error(`text file is too large to inspect: ${file.path}`); + return readFile(file.absolutePath, "utf8"); +} + +export function deriveSolutionFields(solutionXml: unknown, _customizationsXml: unknown): { + uniqueName: string; + displayName: string; + version: string; + publisher?: string; +} { + const manifest = valuesForKey(solutionXml, "SolutionManifest").map(record).find((value): value is UnknownRecord => Boolean(value)); + const uniqueName = manifest ? scalar(manifest, "UniqueName") : undefined; + const version = manifest ? scalar(manifest, "Version") : undefined; + const localized = manifest ? directLocalizedDescription(manifest) : undefined; + const publisherNode = manifest ? record(field(manifest, "Publisher")) : undefined; + const publisher = publisherNode + ? directLocalizedDescription(publisherNode) ?? scalar(publisherNode, "PublisherName", "UniqueName") + : undefined; + if (!uniqueName) throw new Error("solution.xml does not contain SolutionManifest/UniqueName"); + if (!version) throw new Error("solution.xml does not contain SolutionManifest/Version"); + return { uniqueName, displayName: localized ?? uniqueName, version, publisher }; +} + +function categoryFor(path: string): string { + const lower = path.toLowerCase(); + if (lower === "solution.xml" || lower === "customizations.xml") return "solution"; + if (lower.startsWith("bots/")) return "bot"; + if (lower.startsWith("botcomponents/")) return "component"; + if (lower.startsWith("workflows/")) return "workflow"; + if (/\.(png|jpe?g|gif|svg|ico|zip|pdf)$/i.test(lower)) return "asset"; + return "other"; +} + +export function filesManifest(files: SolutionFile[]): FileSummary[] { + return files.map(({ path, size }) => ({ path, category: categoryFor(path), size })); +} + +function inspectComponentValue(value: unknown, source: string): ComponentItem[] { + const items: ComponentItem[] = []; + const visit = (candidate: unknown, index: number): void => { + if (Array.isArray(candidate)) { + candidate.forEach((child, childIndex) => visit(child, childIndex)); + return; + } + const item = record(candidate); + if (!item) return; + const type = scalar(item, "$kind", "kind", "type", "@type") ?? ""; + const name = scalar(item, "name", "displayName", "toolName", "schemaName", "@name") ?? ""; + const combined = `${type} ${name}`.toLowerCase(); + const target = scalar(item, "agentName", "targetAgent", "connectedAgent", "botSchemaName", "schemaName"); + const kind: ComponentItem["kind"] | undefined = combined.includes("connectedagenttool") + ? "connected" + : combined.includes("skill") + ? "skill" + : combined.includes("tool") + ? "tool" + : undefined; + if (kind) { + const label = name || type || `${kind}-${index}`; + const schemaName = scalar(item, "schemaName", "toolSchemaName"); + const operation = scalar(item, "operationName", "operation"); + items.push({ + id: safeId(`${source}-${label}`, `${source}-${index}`), + name: label, + kind, + target, + ...(schemaName ? { schemaName } : {}), + ...(operation ? { operation } : {}), + }); + } + Object.values(item).forEach((child, childIndex) => visit(child, childIndex)); + }; + visit(value, 0); + return items; +} + +function componentOwner(value: string): string { + return value.match(/^(.+?)\.(?:tool|skill|component)(?:\.|$)/i)?.[1]?.toLowerCase() ?? value.toLowerCase(); +} + +async function componentItems(files: SolutionFile[]): Promise { + const result: ComponentItem[] = []; + const directories = [...new Set(files.filter((file) => file.path.toLowerCase().startsWith("botcomponents/")).map((file) => dirname(file.path)))]; + for (const directory of directories) { + const componentFiles = files.filter((file) => dirname(file.path) === directory); + const definition = componentFiles.find((file) => basename(file.path).toLowerCase() === "botcomponent.xml"); + if (!definition) continue; + try { + const parsed = record(parseXml(await readText(definition))); + const component = parsed ? record(field(parsed, "botcomponent")) : undefined; + if (!component) continue; + const componentType = scalar(component, "componenttype"); + if (componentType && componentType !== "9") continue; + + const schemaName = scalar(component, "@schemaname", "schemaname") ?? basename(directory); + const parentBot = record(field(component, "parentbotid")); + const owner = (parentBot ? scalar(parentBot, "schemaname", "@schemaname") : undefined) ?? componentOwner(schemaName); + if (!owner) continue; + + const dataFile = componentFiles.find((file) => basename(file.path).toLowerCase() === "data"); + let data: UnknownRecord = {}; + if (dataFile) { + const parsedData = parseYaml(await readText(dataFile)); + data = record(parsedData) ?? {}; + } + if (!componentType) { + const rootItems = inspectComponentValue(component, definition.path).map((item) => ({ + ...item, + owner, + schemaName, + })); + const dataItems = dataFile + ? inspectComponentValue(data, dataFile.path).map((item) => ({ ...item, owner })) + : []; + result.push(...rootItems, ...dataItems); + continue; + } + const dataKind = scalar(data, "kind", "$kind") ?? ""; + const combined = `${schemaName} ${dataKind}`.toLowerCase(); + const kind: ComponentItem["kind"] | undefined = + combined.includes("connectedagenttool") || combined.includes(".tool.connected-agent.") + ? "connected" + : combined.includes(".skill.") || combined.includes("inlineagentskill") + ? "skill" + : combined.includes(".tool.") || combined.includes("mcptool") + ? "tool" + : undefined; + if (!kind) continue; + + const rawName = scalar(component, "name") ?? schemaName; + const description = scalar(component, "description"); + const target = scalar(data, "botSchemaName", "targetAgent", "agentName"); + const operation = scalar(data, "operationId", "operationName"); + result.push({ + id: safeId(schemaName, basename(directory)), + name: decodeXmlEntities(rawName), + kind, + owner, + schemaName, + ...(target ? { target } : {}), + ...(operation ? { operation } : {}), + ...(description ? { description: decodeXmlEntities(description) } : {}), + }); + } catch (error) { + throw new Error(`cannot parse component ${definition.path}: ${error instanceof Error ? error.message : String(error)}`); + } + } + return result; +} + +export function layoutAgentGraph(nodes: GraphNode[], edges: GraphEdge[]): GraphData { + const ordered = [...nodes].sort((a, b) => a.type.localeCompare(b.type) || a.name.localeCompare(b.name) || a.id.localeCompare(b.id)); + const nodeById = new Map(ordered.map((node) => [node.id, node])); + const agents = ordered.filter((node) => node.type === "agent"); + const agentIds = new Set(agents.map((agent) => agent.id)); + const incomingAgents = new Set( + edges + .filter((edge) => agentIds.has(edge.source) && agentIds.has(edge.target)) + .map((edge) => edge.target), + ); + const roots = agents.filter((agent) => !incomingAgents.has(agent.id)); + if (roots.length === 0 && agents[0]) roots.push(agents[0]); + + const outgoing = new Map(); + for (const edge of edges) { + const targets = outgoing.get(edge.source) ?? []; + targets.push(edge.target); + outgoing.set(edge.source, targets); + } + for (const targets of outgoing.values()) { + targets.sort((a, b) => { + const left = nodeById.get(a); + const right = nodeById.get(b); + return (left?.type ?? "").localeCompare(right?.type ?? "") + || (left?.name ?? "").localeCompare(right?.name ?? "") + || a.localeCompare(b); + }); + } + + const positions = new Map(); + const centerPosition = (x: number, y: number) => ({ x: x - 112, y: y - 36 }); + const pointOnCircle = (center: { x: number; y: number }, radius: number, angle: number) => ({ + x: center.x + Math.cos(angle) * radius, + y: center.y + Math.sin(angle) * radius, + }); + + const placeAgent = ( + agentId: string, + center: { x: number; y: number }, + depth: number, + outwardAngle = -Math.PI / 2, + ): void => { + if (positions.has(agentId)) return; + positions.set(agentId, centerPosition(center.x, center.y)); + const children = (outgoing.get(agentId) ?? []) + .filter((id) => nodeById.has(id) && !positions.has(id)); + const components = children.filter((id) => !agentIds.has(id)); + const connectedAgents = children.filter((id) => agentIds.has(id)); + const ringAngle = (id: string): number => { + const index = children.indexOf(id); + if (children.length === 1) return -Math.PI / 2; + if (children.length === 2) return Math.PI * index; + return -Math.PI / 2 + (Math.PI * 2 * index) / children.length; + }; + + components.forEach((id, index) => { + const angle = depth === 0 + ? ringAngle(id) + : outwardAngle - Math.PI / 3 + (Math.PI * 2 / 3) * ((index + 1) / (components.length + 1)); + const point = pointOnCircle(center, depth === 0 ? 220 : 170, angle); + positions.set(id, centerPosition(point.x, point.y)); + }); + + connectedAgents.forEach((id, index) => { + const angle = depth === 0 + ? ringAngle(id) + : connectedAgents.length === 1 + ? outwardAngle + : outwardAngle - Math.PI / 3 + (Math.PI * 2 / 3) * ((index + 1) / (connectedAgents.length + 1)); + const point = pointOnCircle(center, depth === 0 ? 360 : 300, angle); + placeAgent(id, point, depth + 1, angle); + }); + }; + + roots.forEach((root, index) => { + const centerX = (index - (roots.length - 1) / 2) * 1_350; + placeAgent(root.id, { x: centerX, y: 0 }, 0); + }); + + agents.filter((agent) => !positions.has(agent.id)).forEach((agent, index) => { + placeAgent(agent.id, { x: index * 1_350, y: 1_100 }, 0); + }); + + const unplaced = ordered.filter((node) => !positions.has(node.id)); + unplaced.forEach((node, index) => { + const point = pointOnCircle({ x: 0, y: 0 }, 580, -Math.PI / 2 + (Math.PI * 2 * index) / Math.max(unplaced.length, 1)); + positions.set(node.id, centerPosition(point.x, point.y)); + }); + + return { + nodes: ordered.map((node) => ({ ...node, position: positions.get(node.id) ?? node.position })), + edges: [...edges].sort((a, b) => a.id.localeCompare(b.id)), + }; +} + +export function normalizeAgentComponents(agentId: string, items: ComponentItem[]): { + tools: ComponentItem[]; + skills: ComponentItem[]; + connected: ComponentItem[]; + nodes: GraphNode[]; + edges: GraphEdge[]; +} { + const unique = (kind: ComponentItem["kind"]) => + [ + ...new Map( + items + .filter((item) => item.kind === kind) + .map((item) => [item.schemaName ?? item.id, item]), + ).values(), + ]; + const tools = unique("tool"); + const skills = unique("skill"); + const connected = unique("connected"); + const nodes: GraphNode[] = [ + ...tools.map((item) => ({ + id: `tool:${item.id}`, + name: item.name, + type: "tool", + position: { x: 0, y: 0 }, + details: { + kind: item.kind, + ...(item.schemaName ? { schemaName: item.schemaName } : {}), + ...(item.operation ? { operation: item.operation } : {}), + ...(item.description ? { description: item.description } : {}), + }, + })), + ...skills.map((item) => ({ + id: `skill:${item.id}`, + name: item.name, + type: "skill", + position: { x: 0, y: 0 }, + details: { + kind: item.kind, + ...(item.schemaName ? { schemaName: item.schemaName } : {}), + ...(item.description ? { description: item.description } : {}), + }, + })), + ]; + const edges: GraphEdge[] = [ + ...tools.map((item) => ({ id: `${agentId}-uses-${item.id}`, source: agentId, target: `tool:${item.id}`, label: "uses" })), + ...skills.map((item) => ({ id: `${agentId}-includes-${item.id}`, source: agentId, target: `skill:${item.id}`, label: "includes" })), + ]; + return { tools, skills, connected, nodes, edges }; +} + +function readConfigStrings(config: unknown, keys: string[]): string[] { + const found: string[] = []; + for (const key of keys) found.push(...valuesForKey(config, key).flatMap(strings).map((value) => value.trim()).filter(Boolean)); + return [...new Set(found)]; +} + +function valuesAtPath(value: unknown, path: string[]): unknown[] { + let values = [value]; + for (const segment of path) { + values = values.flatMap((candidate) => { + const item = record(candidate); + const next = item ? field(item, segment) : undefined; + return next === undefined ? [] : Array.isArray(next) ? next : [next]; + }); + } + return values; +} + +function configurationModel(config: unknown): string | undefined { + return [ + ...valuesAtPath(config, ["agentSettings", "model", "series"]).flatMap(strings), + ...readConfigStrings(config, ["modelName"]), + ] + .map((value) => value.trim()) + .find(Boolean); +} + +function configurationInstructions(config: unknown): string | undefined { + const segments = valuesAtPath(config, ["agentSettings", "instructions", "segments"]) + .flatMap((segment) => (Array.isArray(segment) ? segment : [segment])) + .map(record) + .flatMap((segment) => (segment ? [scalar(segment, "value")] : [])) + .filter((value): value is string => Boolean(value)); + return [...segments, ...readConfigStrings(config, ["systemPrompt", "instruction"])] + .map((value) => value.trim()) + .filter(Boolean) + .join("\n") || undefined; +} + +function configurationChannels(config: unknown): string[] { + const candidates = [...valuesAtPath(config, ["agentSettings", "channels"]), ...valuesForKey(config, "channels")]; + const channels = candidates.flatMap((candidate) => { + const entries = Array.isArray(candidate) ? candidate : [candidate]; + return entries.map(record).flatMap((entry) => (entry ? [scalar(entry, "channelId", "id", "name")] : strings(entry))); + }); + return [...new Set(channels.filter((value): value is string => Boolean(value)).map((value) => value.trim()).filter(Boolean))]; +} + +function componentBelongsToAgent(component: ComponentItem, schemaName: string, directory: string): boolean { + if (!component.owner) return false; + const owner = component.owner.toLowerCase(); + return owner === schemaName.toLowerCase() || owner === basename(directory).toLowerCase(); +} + +export async function inspectAgents(files: SolutionFile[]): Promise<{ agents: AgentPreview[]; graph: GraphData }> { + const botDirectories = [...new Set(files.filter((file) => /^bots\/[^/]+\/bot\.xml$/i.test(file.path)).map((file) => dirname(file.path)))].sort(); + const components = await componentItems(files); + const graphNodes: GraphNode[] = []; + const graphEdges: GraphEdge[] = []; + const agents: AgentPreview[] = []; + for (const directory of botDirectories) { + const bot = files.find((file) => file.path.toLowerCase() === `${directory}/bot.xml`.toLowerCase()); + if (!bot) continue; + const id = safeId(basename(directory), `agent-${agents.length + 1}`); + let botXml: unknown; + try { + botXml = parseXml(await readText(bot)); + } catch (error) { + throw new Error(`cannot parse ${bot.path}: ${error instanceof Error ? error.message : String(error)}`); + } + const configFile = files.find((file) => file.path.toLowerCase() === `${directory}/configuration.json`.toLowerCase()); + let config: unknown = {}; + if (configFile) { + try { + config = JSON.parse(await readText(configFile)); + } catch (error) { + throw new Error(`cannot parse ${configFile.path}: ${error instanceof Error ? error.message : String(error)}`); + } + } + const schemaName = firstText(botXml, ["SchemaName", "UniqueName"]) ?? id; + const name = decodeXmlEntities(firstText(botXml, ["Name", "DisplayName"]) ?? id); + const componentData = [ + ...components.filter((component) => componentBelongsToAgent(component, schemaName, directory)), + ]; + const normalized = normalizeAgentComponents(`agent:${id}`, componentData); + const model = configurationModel(config); + const instructions = configurationInstructions(config); + const channels = configurationChannels(config); + const agent: AgentPreview = { + id, + schemaName, + name, + ...(model ? { model } : {}), + ...(instructions ? { instructions: instructions.slice(0, 2_000) } : {}), + channels, + toolCount: normalized.tools.length, + skillCount: normalized.skills.length, + connectedAgentCount: normalized.connected.length, + }; + agents.push(agent); + graphNodes.push({ + id: `agent:${id}`, + name, + type: "agent", + position: { x: 0, y: 0 }, + details: { + ...(agent.model ? { model: agent.model } : {}), + ...(agent.instructions ? { instructions: agent.instructions } : {}), + channels: agent.channels, + toolCount: agent.toolCount, + skillCount: agent.skillCount, + connectedAgentCount: agent.connectedAgentCount, + }, + }, ...normalized.nodes); + graphEdges.push(...normalized.edges); + for (const connection of normalized.connected) { + const targetId = `agent:${safeId(connection.target ?? connection.name, connection.id)}`; + if (!graphNodes.some((node) => node.id === targetId)) { + graphNodes.push({ + id: targetId, + name: connection.target ?? connection.name, + type: "agent", + position: { x: 0, y: 0 }, + details: { channels: [], toolCount: 0, skillCount: 0, connectedAgentCount: 0 }, + }); + } + graphEdges.push({ id: `agent:${id}-delegates-${connection.id}`, source: `agent:${id}`, target: targetId, label: "delegates" }); + } + } + return { agents, graph: layoutAgentGraph([...new Map(graphNodes.map((node) => [node.id, node])).values()], graphEdges) }; +} + +function workflowMappings(customizations: unknown): Map { + const mappings = new Map(); + const visit = (value: unknown): void => { + if (Array.isArray(value)) return value.forEach(visit); + const item = record(value); + if (!item) return; + const jsonFileName = scalar(item, "JsonFileName"); + const name = scalar(item, "Name"); + const id = scalar(item, "WorkflowId") ?? name; + if (jsonFileName && name && id) mappings.set(basename(jsonFileName).toLowerCase(), { id, name }); + Object.values(item).forEach(visit); + }; + visit(customizations); + return mappings; +} + +export function extractSavedGraph(value: unknown): UnknownRecord { + const preferred: UnknownRecord[] = []; + const fallback: UnknownRecord[] = []; + const isGraph = (candidate: unknown): candidate is UnknownRecord => { + const item = record(candidate); + return Boolean(item && typeof item.name === "string" && Array.isArray(item.nodes) && Array.isArray(item.edges)); + }; + const visit = (candidate: unknown): void => { + if (Array.isArray(candidate)) return candidate.forEach(visit); + const item = record(candidate); + if (!item) return; + if (isGraph(item)) fallback.push(item); + const associatedData = record(field(item, "associatedData")); + const graph = associatedData ? field(associatedData, "graph") : undefined; + if (isGraph(graph)) preferred.push(graph); + Object.values(item).forEach(visit); + }; + visit(value); + const matches = preferred.length > 0 ? preferred : fallback; + if (matches.length !== 1) throw new Error(`expected exactly one saved designer graph, found ${matches.length}`); + return matches[0]; +} + +function safeValue(value: unknown): string | undefined { + if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") return undefined; + return String(value).slice(0, 400); +} + +function allowedScalars(config: UnknownRecord, names: string[]): Record { + const details: Record = {}; + for (const name of names) { + const value = safeValue(field(config, name)); + if (value !== undefined) details[name] = value; + } + return details; +} + +function safeParameters(value: unknown): Record { + const parameterEntries: [string, unknown][] = []; + if (Array.isArray(value)) { + for (const item of value.map(record)) { + const name = item ? scalar(item, "name") : undefined; + const parameterValue = item ? field(item, "value") : undefined; + if (name && parameterValue !== undefined) parameterEntries.push([name, parameterValue]); + } + } else { + const parameters = record(value); + if (!parameters) return {}; + parameterEntries.push(...Object.entries(parameters)); + } + const safe: Record = {}; + for (const [key, parameter] of parameterEntries) { + if (/connection|schema|icon|(?:id|guid)$/i.test(key)) continue; + const scalarValue = safeValue(parameter); + if (scalarValue !== undefined && Object.keys(safe).length < 25) safe[key] = scalarValue; + } + return safe; +} + +function toolsSummary(value: unknown): string[] { + const tools = Array.isArray(value) ? value : []; + return tools + .map(record) + .flatMap((tool) => (tool ? [scalar(tool, "displayName", "name", "schemaName", "botSchemaName")] : [])) + .filter((name): name is string => Boolean(name)) + .slice(0, 20); +} + +function configText(config: UnknownRecord, name: string): string | undefined { + const value = field(config, name); + const direct = safeValue(value); + if (direct !== undefined) return direct; + const item = record(value); + if (!item) return undefined; + const series = safeValue(field(item, "series", "value")); + if (series !== undefined) return series; + const segments = array(field(item, "segments")) + .map(record) + .flatMap((segment) => (segment ? [safeValue(field(segment, "value"))] : [])) + .filter((segment): segment is string => Boolean(segment)); + return segments.length > 0 ? segments.join("\n").slice(0, 400) : undefined; +} + +function safeWorkflowDetails(node: UnknownRecord, type: string): Record { + const data = record(field(node, "data")); + const config = data ? record(field(data, "config")) ?? data : record(field(node, "config")) ?? node; + const lower = type.toLowerCase(); + let details: Record; + if (lower.includes("note") || scalar(config, "markdown") !== undefined) { + details = allowedScalars(config, ["markdown", "color"]); + } else if (lower.includes("connector") || lower.includes("openapi") || scalar(config, "apiName") !== undefined) { + details = allowedScalars(config, ["displayName", "apiName", "operationName", "operationType"]); + const parameters = safeParameters(field(config, "parameters")); + if (Object.keys(parameters).length > 0) details.parameters = parameters; + } else if (lower.includes("agent") || scalar(config, "botSchemaName") !== undefined) { + details = allowedScalars(config, ["botSchemaName", "mode"]); + for (const name of ["model", "instructions", "inlineInstructions"]) { + const value = configText(config, name); + if (value !== undefined) details[name] = value; + } + const tools = toolsSummary(field(config, "tools")); + if (tools.length > 0) details.tools = tools; + } else if (lower.includes("condition")) { + details = allowedScalars(config, ["expression", "condition"]); + } else if (lower.includes("loop") || lower.includes("foreach")) { + details = allowedScalars(config, ["expression"]); + } else if (lower.includes("variable")) { + details = allowedScalars(config, ["name", "type", "value"]); + } else if (lower.includes("end")) { + details = allowedScalars(config, ["runStatus"]); + } else if (lower.includes("classif") || lower.includes("input") || lower.includes("model")) { + details = allowedScalars(config, ["category", "input", "model"]); + } else if (lower.includes("trigger")) { + details = allowedScalars(config, ["method", "operation", "description"]); + } else { + details = allowedScalars(config, ["category", "input", "model", "description"]); + } + return details; +} + +export function normalizeWorkflowGraph(graph: UnknownRecord): GraphData { + const rawNodes = array(graph.nodes).map(record).filter((node): node is UnknownRecord => Boolean(node)); + const nodes = rawNodes.map((node, index): GraphNode => { + const id = safeId(scalar(node, "id", "nodeId") ?? `node-${index + 1}`, `node-${index + 1}`); + const data = record(field(node, "data")); + const name = scalar(node, "name", "displayName", "label") ?? (data ? scalar(data, "name", "displayName", "label") : undefined) ?? id; + const type = scalar(node, "type", "kind") ?? (data ? scalar(data, "type", "kind") : undefined) ?? "generic"; + const position = record(field(node, "position", "layout")); + const x = Number(scalar(position ?? {}, "x") ?? scalar(node, "x") ?? index * 220); + const y = Number(scalar(position ?? {}, "y") ?? scalar(node, "y") ?? 0); + return { id, name, type, position: { x: Number.isFinite(x) ? x : index * 220, y: Number.isFinite(y) ? y : 0 }, details: safeWorkflowDetails(node, type) }; + }); + const knownIds = new Set(nodes.map((node) => node.id)); + const edges = array(graph.edges).map(record).filter((edge): edge is UnknownRecord => Boolean(edge)).flatMap((edge, index): GraphEdge[] => { + const source = safeId(scalar(edge, "source") ?? "", ""); + const target = safeId(scalar(edge, "target") ?? "", ""); + if (!source || !target || !knownIds.has(source) || !knownIds.has(target)) return []; + const sourceHandle = scalar(edge, "sourceHandle"); + const targetHandle = scalar(edge, "targetHandle"); + return [{ id: safeId(scalar(edge, "id") ?? `${source}-${target}-${index}`, `edge-${index}`), source, target, ...(sourceHandle ? { sourceHandle } : {}), ...(targetHandle ? { targetHandle } : {}) }]; + }); + return { nodes, edges }; +} + +export async function inspectWorkflows(files: SolutionFile[], customizations: unknown): Promise { + const mappings = workflowMappings(customizations); + const workflowFiles = files.filter((file) => /^workflows\/[^/]+\.json$/i.test(file.path)); + const workflows: WorkflowPreview[] = []; + for (const file of workflowFiles) { + let parsed: unknown; + try { + parsed = JSON.parse(await readText(file)); + } catch (error) { + throw new Error(`cannot parse workflow ${file.path}: ${error instanceof Error ? error.message : String(error)}`); + } + let saved: UnknownRecord; + try { + saved = extractSavedGraph(parsed); + } catch (error) { + throw new Error(`${file.path}: ${error instanceof Error ? error.message : String(error)}`); + } + const graph = normalizeWorkflowGraph(saved); + const mapping = mappings.get(basename(file.path).toLowerCase()); + const starting = graph.nodes.find((node) => !graph.edges.some((edge) => edge.target === node.id) || /trigger|start/i.test(node.type)); + workflows.push({ + id: mapping?.id ?? safeId(basename(file.path, ".json"), `workflow-${workflows.length + 1}`), + name: mapping?.name ?? String(saved.name), + ...(starting ? { trigger: starting.name } : {}), + nodeCount: graph.nodes.length, + edgeCount: graph.edges.length, + graph, + }); + } + return workflows.sort((a, b) => a.name.localeCompare(b.name)); +} + +function rootComponentCount(customizations: unknown): number | undefined { + const roots = valuesForKey(customizations, "RootComponents"); + for (const root of roots) { + const components = valuesForKey(root, "RootComponent"); + const count = components.reduce( + (total, component) => total + (Array.isArray(component) ? component.length : 1), + 0, + ); + if (count > 0) return count; + } + return undefined; +} + +export function deterministicZip(files: SolutionFile[], solutionRoot: string): Buffer { + const zip = new AdmZip(); + for (const file of [...files].sort((a, b) => a.path.localeCompare(b.path))) { + const archivePath = file.path.replaceAll("\\", "/"); + if (archivePath.startsWith("/") || archivePath.split("/").includes("..")) throw new Error(`unsafe archive path: ${archivePath}`); + assertDescendant(resolve(solutionRoot), file.absolutePath); + const data = readFileSync(file.absolutePath); + zip.addFile(archivePath, data, "", 0o100644); + } + for (const entry of zip.getEntries()) { + entry.header.time = FIXED_ZIP_DATE; + entry.header.attr = 0o100644; + } + return zip.toBuffer(); +} + +async function buildSubmission(repositoryRoot: string, slug: string): Promise { + if (!slugPattern.test(slug)) throw new Error("slug must be lowercase kebab-case"); + const submissionPath = resolve(repositoryRoot, "submissions", slug); + const solutionRoot = resolve(submissionPath, "solution"); + assertDescendant(resolve(repositoryRoot, "submissions"), submissionPath); + await rejectSymlinks(submissionPath); + const metadataPath = join(submissionPath, "metadata.json"); + let metadata: SubmissionMetadata; + try { + metadata = submissionMetadataSchema.parse(JSON.parse(await readFile(metadataPath, "utf8"))); + } catch (error) { + const description = error instanceof Error ? error.message : String(error); + throw new Error(`invalid metadata.json: ${description}`); + } + let files: SolutionFile[]; + try { + const stat = await lstat(solutionRoot); + if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error("solution must be a directory and not a symbolic link"); + files = await collectFiles(solutionRoot); + } catch (error) { + throw new Error(`invalid solution payload: ${error instanceof Error ? error.message : String(error)}`); + } + const solutionXml = parseXml(await readText(requiredFile(files, "solution.xml"))); + const customizationsXml = parseXml(await readText(requiredFile(files, "customizations.xml"))); + const derived = deriveSolutionFields(solutionXml, customizationsXml); + const [{ agents, graph }, workflows] = await Promise.all([inspectAgents(files), inspectWorkflows(files, customizationsXml)]); + if (agents.length === 0 && workflows.length === 0) throw new Error("solution must contain at least one agent or workflow"); + const manifest = filesManifest(files); + const componentCount = rootComponentCount(solutionXml) ?? manifest.filter((file) => ["bot", "component", "workflow"].includes(file.category)).length; + const record: SolutionRecord = { + ...metadata, + uniqueName: derived.uniqueName, + version: derived.version, + ...(derived.publisher ? { publisher: derived.publisher } : {}), + bundle: `bundles/${slug}.zip`, + agentCount: agents.length, + workflowCount: workflows.length, + componentCount, + agents, + agentGraph: graph, + workflows, + files: manifest, + }; + const readmePath = join(submissionPath, "README.md"); + let body = `# ${metadata.name}\n\n${metadata.description}\n`; + try { + body = matter(await readFile(readmePath, "utf8")).content.trim() || body; + } catch { + // README is optional. + } + const guideBody = body.trim().replace(/^#\s+.+?(?:\r?\n){1,2}/, ""); + const guide = matter.stringify(`${guideBody}\n`, { title: metadata.name, slug, solution: derived.uniqueName }); + return { slug, record, guide, bundle: deterministicZip(files, solutionRoot) }; +} + +async function existingSlugs(directory: string, extension: string): Promise { + try { + const entries = await readdir(directory, { withFileTypes: true }); + return entries.filter((entry) => entry.isFile() && entry.name.endsWith(extension)).map((entry) => basename(entry.name, extension)); + } catch { + return []; + } +} + +async function writeIfChanged(path: string, content: Buffer | string): Promise { + let existing: Buffer | undefined; + try { + existing = await readFile(path); + } catch { + // Missing is handled below. + } + const expected = typeof content === "string" ? Buffer.from(content) : content; + if (!existing || !existing.equals(expected)) { + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, expected); + } +} + +export async function importSubmissions(options: { root?: string; check?: boolean } = {}): Promise { + const root = resolve(options.root ?? process.cwd()); + const submissionsRoot = join(root, "submissions"); + const errors: Record = {}; + let entries: string[] = []; + try { + entries = (await readdir(submissionsRoot, { withFileTypes: true })) + .filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".")) + .map((entry) => entry.name) + .sort(); + } catch { + entries = []; + } + const built: BuiltSubmission[] = []; + for (const slug of entries) { + try { + built.push(await buildSubmission(root, slug)); + } catch (error) { + (errors[slug] ??= []).push(error instanceof Error ? error.message : String(error)); + } + } + if (Object.keys(errors).length > 0) throw new SubmissionImportError(errors); + if (options.check) return { imported: built.map((item) => item.slug), errors }; + const solutionDir = join(root, "src/content/solutions"); + const guideDir = join(root, "src/content/guides"); + const bundleDir = join(root, "public/bundles"); + for (const submission of built) { + await writeIfChanged(join(solutionDir, `${submission.slug}.json`), `${JSON.stringify(submission.record, null, 2)}\n`); + await writeIfChanged(join(guideDir, `${submission.slug}.md`), submission.guide); + await writeIfChanged(join(bundleDir, `${submission.slug}.zip`), submission.bundle); + } + const expectedSlugs = new Set(built.map((item) => item.slug)); + for (const [directory, extension] of [[solutionDir, ".json"], [guideDir, ".md"], [bundleDir, ".zip"]] as const) { + for (const slug of await existingSlugs(directory, extension)) { + if (expectedSlugs.has(slug) || slug === ".gitkeep") continue; + await rm(join(directory, `${slug}${extension}`)); + } + } + if (Object.keys(errors).length > 0) throw new SubmissionImportError(errors); + return { imported: built.map((item) => item.slug), errors }; +} + +const isCli = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url); +if (isCli) { + const check = process.argv.slice(2).includes("--check"); + importSubmissions({ check }) + .then((report) => console.log(`${check ? "Checked" : "Imported"} ${report.imported.length} submission(s).`)) + .catch((error: unknown) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; + }); +} diff --git a/scripts/seed-demo-submissions.ts b/scripts/seed-demo-submissions.ts new file mode 100644 index 0000000..44e5607 --- /dev/null +++ b/scripts/seed-demo-submissions.ts @@ -0,0 +1,285 @@ +import { mkdir, rm, writeFile } from "node:fs/promises"; +import { join } from "node:path"; + +type DemoSubmission = { + slug: string; + name: string; + description: string; + tags: string[]; + author: string; + authorGithub?: string; + createdAt: string; + featured?: boolean; + kind: "agent" | "workflow" | "both"; + workflow?: string; + trigger?: string; + agent?: string; + model?: string; + steps: string[]; +}; + +const demos: DemoSubmission[] = [ + { + slug: "customer-support-triage", + name: "Customer Support Triage", + description: "Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue.", + tags: ["support", "customer-service", "ai", "human-in-the-loop", "demo"], + author: "Maya Chen", + authorGithub: "mayachen", + createdAt: "2026-08-13", + featured: true, + kind: "both", + workflow: "Triage and route support cases", + trigger: "When a support case is created", + agent: "Support Triage Agent", + model: "GPT-5", + steps: ["Classify case", "Search knowledge", "Draft response", "Human review", "Update case"], + }, + { + slug: "sales-account-brief", + name: "Sales Account Brief Generator", + description: "Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief.", + tags: ["sales", "crm", "research", "productivity", "demo"], + author: "Jon Bell", + authorGithub: "jonbell", + createdAt: "2026-08-12", + featured: true, + kind: "both", + workflow: "Prepare account brief", + trigger: "Before a customer meeting", + agent: "Account Research Agent", + model: "Sonnet 4.6", + steps: ["Load CRM account", "Find recent signals", "Summarize risks", "Create brief"], + }, + { + slug: "employee-onboarding-guide", + name: "Employee Onboarding Guide", + description: "Coordinates a new hire checklist and answers policy, benefits, and first-week questions.", + tags: ["hr", "onboarding", "knowledge", "beginner", "demo"], + author: "Priya Raman", + authorGithub: "priyaraman", + createdAt: "2026-08-10", + kind: "both", + workflow: "New hire onboarding", + trigger: "When an employee joins", + agent: "Onboarding Guide", + model: "GPT-4.1", + steps: ["Create checklist", "Provision access", "Send welcome plan", "Check progress"], + }, + { + slug: "incident-response-coordinator", + name: "Incident Response Coordinator", + description: "Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution.", + tags: ["it-ops", "incident-response", "teams", "automation", "demo"], + author: "Elliot Stone", + authorGithub: "elliotstone", + createdAt: "2026-08-09", + featured: true, + kind: "both", + workflow: "Coordinate service incident", + trigger: "When an alert is escalated", + agent: "Incident Coordinator", + model: "GPT-5", + steps: ["Collect telemetry", "Assess severity", "Open bridge", "Post updates", "Close incident"], + }, + { + slug: "contract-review-assistant", + name: "Contract Review Assistant", + description: "Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval.", + tags: ["document-ops", "legal", "compliance", "human-in-the-loop", "demo"], + author: "Sofia Alvarez", + authorGithub: "sofiaalvarez", + createdAt: "2026-08-08", + featured: true, + kind: "both", + workflow: "Review contract package", + trigger: "When a contract is uploaded", + agent: "Contract Review Agent", + model: "Sonnet 4.6", + steps: ["Extract clauses", "Compare playbook", "Score risk", "Request approval"], + }, + { + slug: "campaign-content-engine", + name: "Campaign Content Engine", + description: "Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls.", + tags: ["marketing", "content", "brand", "approval", "demo"], + author: "Maya Chen", + authorGithub: "mayachen", + createdAt: "2026-08-07", + kind: "both", + workflow: "Create campaign content kit", + trigger: "When a campaign brief is approved", + agent: "Campaign Content Agent", + model: "GPT-5", + steps: ["Read brief", "Load brand voice", "Draft channels", "Review content", "Publish kit"], + }, + { + slug: "invoice-exception-handler", + name: "Invoice Exception Handler", + description: "Validates invoice data, resolves routine mismatches, and routes material exceptions for finance review.", + tags: ["finance", "document-ops", "automation", "approval", "demo"], + author: "Noah Williams", + authorGithub: "noahwilliams", + createdAt: "2026-08-06", + kind: "workflow", + workflow: "Resolve invoice exception", + trigger: "When invoice validation fails", + steps: ["Read invoice", "Match purchase order", "Classify exception", "Request approval", "Post result"], + }, + { + slug: "field-service-dispatch", + name: "Field Service Dispatch", + description: "Prioritizes service requests, finds the right technician, and keeps customers informed of arrival changes.", + tags: ["operations", "field-service", "scheduling", "customer-service", "demo"], + author: "Amina Yusuf", + authorGithub: "aminayusuf", + createdAt: "2026-08-05", + kind: "workflow", + workflow: "Dispatch field service visit", + trigger: "When a service request is approved", + steps: ["Assess request", "Find technician", "Reserve slot", "Notify customer"], + }, + { + slug: "knowledge-gap-finder", + name: "Knowledge Gap Finder", + description: "Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines.", + tags: ["knowledge", "support", "analytics", "ai", "demo"], + author: "Priya Raman", + authorGithub: "priyaraman", + createdAt: "2026-08-04", + kind: "agent", + agent: "Knowledge Gap Analyst", + model: "GPT-5", + steps: ["Cluster questions", "Find missing topics", "Prioritize gaps", "Draft article outlines"], + }, + { + slug: "security-access-review", + name: "Security Access Review", + description: "Explains access anomalies, gathers manager evidence, and produces an auditable review decision.", + tags: ["security", "it-ops", "compliance", "approval", "demo"], + author: "Elliot Stone", + authorGithub: "elliotstone", + createdAt: "2026-08-03", + kind: "both", + workflow: "Quarterly access review", + trigger: "On a quarterly schedule", + agent: "Access Review Analyst", + model: "Sonnet 4.6", + steps: ["Load access list", "Detect anomalies", "Collect evidence", "Record decision"], + }, + { + slug: "store-feedback-analyzer", + name: "Store Feedback Analyzer", + description: "Summarizes store feedback, detects recurring product issues, and prepares action themes for operations.", + tags: ["retail", "analytics", "customer-service", "beginner", "demo"], + author: "Amina Yusuf", + authorGithub: "aminayusuf", + createdAt: "2026-08-02", + kind: "agent", + agent: "Feedback Insights Agent", + model: "GPT-4.1", + steps: ["Collect feedback", "Detect sentiment", "Group themes", "Recommend actions"], + }, + { + slug: "project-status-narrator", + name: "Project Status Narrator", + description: "Combines delivery signals into a plain-language status update with risks, decisions, and next actions.", + tags: ["productivity", "project-management", "reporting", "beginner", "demo"], + author: "Noah Williams", + authorGithub: "noahwilliams", + createdAt: "2026-08-01", + kind: "workflow", + workflow: "Generate weekly project update", + trigger: "Every Friday", + steps: ["Collect delivery signals", "Summarize progress", "Highlight risks", "Send update"], + }, +]; + +const root = process.cwd(); + +function xml(value: string): string { + return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"); +} + +function graphFor(demo: DemoSubmission) { + const types = ["start", "agent", "connector", "ifElse", "connector", "end"]; + const names = [demo.trigger ?? "Start", ...demo.steps, "Complete"]; + const nodes = names.map((name, index) => ({ + id: `node-${index + 1}`, + name, + type: types[index] ?? (index === names.length - 1 ? "end" : index % 2 ? "agent" : "connector"), + position: { x: index * 240, y: index === 3 ? 90 : index === 4 ? -70 : 0 }, + data: { + config: { + displayName: name, + operationType: index === 0 ? "trigger" : index === names.length - 1 ? "end" : "action", + }, + }, + })); + const edges = nodes.slice(1).map((node, index) => ({ + id: `edge-${index + 1}`, + source: nodes[index].id, + target: node.id, + })); + return { trigger: { metadata: { associatedData: { graph: { name: demo.workflow, nodes, edges } } } } }; +} + +async function write(path: string, content: string): Promise { + await mkdir(join(path, ".."), { recursive: true }); + await writeFile(path, content); +} + +for (const demo of demos) { + const base = join(root, "submissions", demo.slug); + await rm(base, { recursive: true, force: true }); + const uniqueName = `Demo_${demo.slug.replaceAll("-", "_")}`; + const hasAgent = demo.kind !== "workflow"; + const hasWorkflow = demo.kind !== "agent"; + const rootComponents = [hasAgent ? `` : "", hasWorkflow ? `` : ""].filter(Boolean).join(""); + + await write( + join(base, "metadata.json"), + `${JSON.stringify({ + name: demo.name, + description: demo.description, + tags: demo.tags, + author: demo.author, + ...(demo.authorGithub ? { authorGithub: demo.authorGithub } : {}), + createdAt: demo.createdAt, + featured: demo.featured ?? false, + }, null, 2)}\n`, + ); + await write( + join(base, "README.md"), + `# ${demo.name}\n\n${demo.description}\n\n## How it works\n\n${demo.steps.map((step) => `- ${step}`).join("\n")}\n\n> Demo submission for gallery design and discovery testing.\n`, + ); + await write( + join(base, "solution", "solution.xml"), + `${uniqueName}1.0.0.0CAT Gallery Demos${rootComponents}\n`, + ); + await write( + join(base, "solution", "customizations.xml"), + hasWorkflow + ? `${xml(demo.workflow ?? demo.name)}workflow.json${demo.slug}-workflow\n` + : "\n", + ); + if (hasWorkflow) { + await write(join(base, "solution", "Workflows", "workflow.json"), `${JSON.stringify(graphFor(demo), null, 2)}\n`); + } + if (hasAgent) { + const schemaName = `${demo.slug.replaceAll("-", "_")}_agent`; + await write(join(base, "solution", "bots", schemaName, "bot.xml"), `${xml(demo.agent ?? demo.name)}\n`); + await write( + join(base, "solution", "bots", schemaName, "configuration.json"), + `${JSON.stringify({ + agentSettings: { + model: { series: demo.model ?? "GPT-5" }, + instructions: { segments: [{ value: demo.description }] }, + channels: [{ channelId: "MsTeams" }], + }, + }, null, 2)}\n`, + ); + } +} + +console.log(`Seeded ${demos.length} demo submissions.`); diff --git a/src/components/SolutionCard.astro b/src/components/SolutionCard.astro new file mode 100644 index 0000000..b8f9169 --- /dev/null +++ b/src/components/SolutionCard.astro @@ -0,0 +1,63 @@ +--- +import type { CollectionEntry } from "astro:content"; +import { authorKey, solutionMakeup } from "../lib/schema"; +import { withBase } from "../lib/url"; +import TopologyPreview from "./TopologyPreview.astro"; + +interface Props { + solution: CollectionEntry<"solutions">; + compact?: boolean; +} + +const { solution, compact = false } = Astro.props; +const data = solution.data; +const graph = data.workflows[0]?.graph ?? data.agentGraph; +const makeup = solutionMakeup(data.agentCount, data.workflowCount); +const isDemo = data.tags.includes("demo"); +const buildingBlocks = [ + ...data.agents.map((agent) => agent.name), + ...data.workflows.flatMap((workflow) => workflow.graph.nodes.filter((node) => node.type !== "canvasNote").map((node) => node.name)), +]; +--- + + +
+
+
+ +
+
+
+
+
+

{makeup}

+

{data.name}

+
+ { + data.featured && ( + + Featured + + ) + } +
+ {!compact &&

{data.description}

} +
+ {(compact ? buildingBlocks.slice(0, 3) : data.tags.filter((tag) => tag !== "demo").slice(0, 3)).map((label) => ( + {label} + ))} + {compact && buildingBlocks.length > 3 && +{buildingBlocks.length - 3}} + {isDemo && Demo} +
+
+ by {data.author} + + {data.agentCount} agent{data.agentCount === 1 ? "" : "s"} + {data.workflowCount} workflow{data.workflowCount === 1 ? "" : "s"} + +
+
+
diff --git a/src/components/TopologyPreview.astro b/src/components/TopologyPreview.astro new file mode 100644 index 0000000..57f8164 --- /dev/null +++ b/src/components/TopologyPreview.astro @@ -0,0 +1,138 @@ +--- +import type { GraphData } from "../lib/schema"; +import { graphVisualColor, graphVisualKind } from "../lib/graphVisuals"; + +interface Props { + graph?: GraphData; + label?: string; + compact?: boolean; +} + +const { graph, label = "Solution topology", compact = false } = Astro.props; +const nodes = graph?.nodes.filter((node) => node.type !== "canvasNote").slice(0, compact ? 9 : 18) ?? []; +const nodeIds = new Set(nodes.map((node) => node.id)); +const edges = graph?.edges.filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)) ?? []; +const nodeById = new Map(nodes.map((node) => [node.id, node])); +const xs = nodes.map((node) => node.position.x); +const ys = nodes.map((node) => node.position.y); +const minX = xs.length ? Math.min(...xs) : 0; +const maxX = xs.length ? Math.max(...xs) : 1; +const minY = ys.length ? Math.min(...ys) : 0; +const maxY = ys.length ? Math.max(...ys) : 1; +const width = 540; +const height = compact ? 116 : 210; +const pad = compact ? 18 : 28; +const gradientId = `topology-${label.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${compact ? "compact" : "full"}`; +const scaleX = (x: number) => pad + ((x - minX) / Math.max(maxX - minX, 1)) * (width - pad * 2); +const scaleY = (y: number) => pad + ((y - minY) / Math.max(maxY - minY, 1)) * (height - pad * 2); +const fallback = [ + { x: 35, y: height / 2 }, + { x: 145, y: height / 2 }, + { x: 270, y: height / 3 }, + { x: 270, y: (height * 2) / 3 }, + { x: 405, y: height / 2 }, + { x: 505, y: height / 2 }, +]; +--- + + + + + + + + + + + { + nodes.length ? ( + <> + {edges.map((edge) => { + const source = nodeById.get(edge.source); + const target = nodeById.get(edge.target); + if (!source || !target) return null; + const x1 = scaleX(source.position.x); + const y1 = scaleY(source.position.y); + const x2 = scaleX(target.position.x); + const y2 = scaleY(target.position.y); + const bend = Math.max(24, Math.abs(x2 - x1) * 0.35); + const color = graphVisualColor(graphVisualKind(source)); + return ( + + ); + })} + {nodes.map((node) => { + const x = scaleX(node.position.x); + const y = scaleY(node.position.y); + const kind = graphVisualKind(node); + const color = graphVisualColor(kind); + const isTerminal = node.type === "start" || node.type === "end"; + if (isTerminal) return ( + + ); + if (kind === "decision") return ( + + ); + return ( + + ); + })} + + ) : ( + <> + {fallback.slice(0, -1).map((point, i) => { + const next = fallback[i + 1]; + return ( + + ); + })} + {fallback.map((point, i) => + i === 0 || i === fallback.length - 1 ? ( + + ) : ( + + ) + )} + + ) + } + diff --git a/src/components/graph/GraphCanvas.css b/src/components/graph/GraphCanvas.css new file mode 100644 index 0000000..18410e5 --- /dev/null +++ b/src/components/graph/GraphCanvas.css @@ -0,0 +1,276 @@ +@import "@xyflow/react/dist/style.css"; + +.catalog-flow { + --xy-background-color: var(--cp-bg-elevated); + --xy-background-pattern-color: var(--cp-border); + --xy-edge-stroke: var(--cp-border-strong); + --xy-edge-stroke-selected: var(--cp-violet); + --xy-minimap-background-color: var(--cp-surface); + --xy-minimap-mask-background-color: var(--cp-overlay); + --xy-minimap-node-background-color: var(--cp-border-strong); + --xy-controls-button-background-color: var(--cp-surface); + --xy-controls-button-background-color-hover: var(--cp-surface-soft); + --xy-controls-button-color: var(--cp-text); + --xy-controls-button-color-hover: var(--cp-accent); + --xy-controls-button-border-color: var(--cp-border); + --xy-controls-box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.14); + --xy-attribution-background-color: var(--cp-panel); + --xy-handle-background-color: var(--cp-violet); + --xy-handle-border-color: var(--cp-surface); + width: 100%; + height: 34rem; + background: + radial-gradient(circle at 80% 10%, var(--cp-coral-glow), transparent 28%), + radial-gradient(circle at 12% 88%, var(--cp-canvas-glow), transparent 32%), + var(--cp-bg-elevated); +} + +.catalog-flow:fullscreen { + height: 100%; +} + +.catalog-flow--compact { + height: 16rem; +} + +.solution-hero-preview .catalog-flow--compact { + height: 21rem; +} + +.catalog-flow .react-flow__minimap { + overflow: hidden; + border: 1px solid var(--cp-border); + border-radius: 0.625rem; +} + +.catalog-flow .react-flow__edge-text { + fill: var(--cp-text-muted); + font-family: "Segoe UI Variable Text", "Segoe UI", sans-serif; + font-size: 10px; + font-weight: 600; +} + +.catalog-flow .react-flow__edge-textbg { + fill: var(--cp-surface); +} + +.catalog-node { + --node-color: var(--graph-action); + width: 14rem; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--node-color) 74%, var(--cp-border)); + border-radius: 0.75rem; + background: var(--cp-surface); + color: var(--cp-text); + box-shadow: 0 2px 7px rgba(0, 0, 0, 0.1); +} + +.catalog-node[data-selected="true"] { + border-color: var(--node-color); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--node-color) 18%, transparent); +} + +.catalog-node[data-visual="trigger"] { --node-color: var(--graph-trigger); } +.catalog-node[data-visual="ai"] { --node-color: var(--graph-ai); } +.catalog-node[data-visual="decision"] { --node-color: var(--graph-decision); } +.catalog-node[data-visual="loop"] { --node-color: var(--graph-loop); } +.catalog-node[data-visual="data"] { --node-color: var(--graph-data); } +.catalog-node[data-visual="communication"] { --node-color: var(--graph-communication); } +.catalog-node[data-visual="approval"] { --node-color: var(--graph-approval); } +.catalog-node[data-visual="content"] { --node-color: var(--graph-content); } +.catalog-node[data-visual="action"] { --node-color: var(--graph-action); } +.catalog-node[data-visual="tool"] { --node-color: var(--graph-tool); } +.catalog-node[data-visual="skill"] { --node-color: var(--graph-skill); } +.catalog-node[data-visual="end"] { --node-color: var(--graph-end); } +.catalog-node[data-visual="note"] { --node-color: var(--graph-note); } + +.catalog-flow--agent .catalog-node[data-primary="true"] { + border-width: 2px; + box-shadow: + 0 0 0 4px color-mix(in srgb, var(--graph-ai) 12%, transparent), + 0 6px 18px rgba(0, 0, 0, 0.13); +} + +.catalog-flow--agent .catalog-node[data-primary="true"] .catalog-node__icon { + background: var(--graph-ai); + color: var(--cp-accent-fg); +} + +.catalog-flow--agent .react-flow__handle { + opacity: 0; +} + +.catalog-node[data-kind="start"], +.catalog-node[data-kind="end"] { + width: 10rem; + border-radius: 9999px; +} + +.catalog-node[data-kind="canvasNote"] { + border-style: dashed; + background: color-mix(in srgb, var(--cp-coral) 8%, var(--cp-surface)); +} + +.catalog-node__body { + display: flex; + align-items: center; + gap: 0.65rem; + padding: 0.7rem 0.8rem; +} + +.catalog-node__icon { + display: grid; + width: 1.8rem; + height: 1.8rem; + flex: none; + place-items: center; + border-radius: 0.5rem; + background: color-mix(in srgb, var(--node-color) 15%, var(--cp-surface)); + color: var(--node-color); + font-family: "Segoe UI Variable Text", "Segoe UI", sans-serif; + font-size: 0.68rem; + font-weight: 700; + text-transform: uppercase; +} + +.catalog-node__copy { + min-width: 0; +} + +.catalog-node__type { + overflow: hidden; + color: var(--cp-text-soft); + font-family: "Segoe UI Variable Text", "Segoe UI", sans-serif; + font-size: 0.58rem; + letter-spacing: 0.08em; + text-overflow: ellipsis; + text-transform: uppercase; + white-space: nowrap; +} + +.catalog-node__name { + display: -webkit-box; + overflow: hidden; + margin-top: 0.12rem; + font-size: 0.78rem; + font-weight: 650; + line-height: 1.25; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.catalog-node .react-flow__handle { + width: 8px; + height: 8px; +} + +.graph-toolbar { + position: absolute; + top: 0.75rem; + left: 0.75rem; + z-index: 5; + display: flex; + gap: 0.4rem; +} + +.graph-toolbar button { + min-height: 2.25rem; + border: 1px solid var(--cp-border); + border-radius: 0.5rem; + background: var(--cp-surface); + color: var(--cp-text-muted); + padding: 0.4rem 0.65rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); + cursor: pointer; + font-size: 0.72rem; + font-weight: 600; +} + +.graph-toolbar button:hover, +.graph-toolbar button[aria-pressed="true"] { + border-color: var(--cp-accent); + color: var(--cp-accent); +} + +.graph-detail { + position: absolute; + top: 0.75rem; + right: 0.75rem; + z-index: 10; + width: min(23rem, calc(100% - 1.5rem)); + max-height: calc(100% - 1.5rem); + overflow-y: auto; + border: 1px solid var(--cp-border); + border-radius: 1rem; + background: var(--cp-panel-strong); + color: var(--cp-text); + box-shadow: var(--cp-shadow); +} + +.graph-detail__header { + position: sticky; + top: 0; + display: flex; + align-items: start; + justify-content: space-between; + gap: 1rem; + border-bottom: 1px solid var(--cp-border); + background: var(--cp-panel-strong); + padding: 1rem; +} + +.graph-detail__close { + display: grid; + width: 2rem; + height: 2rem; + flex: none; + place-items: center; + border: 1px solid var(--cp-border); + border-radius: 0.625rem; + background: var(--cp-surface); + color: var(--cp-text-muted); + cursor: pointer; +} + +.graph-detail__content { + display: grid; + gap: 1rem; + padding: 1rem; +} + +.graph-detail__field dt { + color: var(--cp-text-soft); + font-family: "Segoe UI Variable Text", "Segoe UI", sans-serif; + font-size: 0.65rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.graph-detail__field dd { + margin: 0.3rem 0 0; + overflow-wrap: anywhere; + color: var(--cp-text-muted); + font-size: 0.78rem; + line-height: 1.5; + white-space: pre-wrap; +} + +@media (max-width: 640px) { + .catalog-flow { + height: 30rem; + } + + .solution-hero-preview .catalog-flow--compact { + height: 18rem; + } + + .graph-detail { + top: auto; + bottom: 0.75rem; + max-height: 62%; + } + + .catalog-flow .react-flow__minimap { + display: none; + } +} diff --git a/src/components/graph/GraphCanvas.tsx b/src/components/graph/GraphCanvas.tsx new file mode 100644 index 0000000..f9070d8 --- /dev/null +++ b/src/components/graph/GraphCanvas.tsx @@ -0,0 +1,315 @@ +import { + Background, + Handle, + MarkerType, + MiniMap, + Position, + ReactFlow, + ReactFlowProvider, + useReactFlow, + type Edge, + type Node, + type NodeProps, +} from "@xyflow/react"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { graphVisualColor, graphVisualKind } from "../../lib/graphVisuals"; +import type { GraphData, GraphNode } from "../../lib/schema"; +import "./GraphCanvas.css"; + +type CatalogNodeData = { + source: GraphNode; + primary: boolean; +}; + +type CatalogNode = Node; + +const ICONS: Record = { + agent: "AI", + tool: "TL", + skill: "SK", + start: "IN", + end: "OUT", + connector: "CN", + ifElse: "IF", + loop: "LP", + variable: "VR", + builtinFunction: "FN", + classifyOnInlineAgent: "CL", + canvasNote: "NT", +}; + +function CatalogNodeView({ data, selected }: NodeProps) { + const source = data.source; + return ( +
+ + + + +
+ + + {humanize(source.type)} + {source.name} + +
+ + + + +
+ ); +} + +const nodeTypes = { catalog: CatalogNodeView }; + +function humanize(value: string): string { + return value + .replace(/^category:/, "") + .replace(/[-_]+/g, " ") + .replace(/([a-z])([A-Z])/g, "$1 $2") + .trim(); +} + +function displayValue(value: unknown): string { + if (typeof value === "string") return value; + if (typeof value === "number" || typeof value === "boolean") return String(value); + if (Array.isArray(value)) { + return value.map((item) => displayValue(item)).join("\n"); + } + if (value && typeof value === "object") { + return Object.entries(value) + .map(([key, child]) => `${humanize(key)}: ${displayValue(child)}`) + .join("\n"); + } + return "Not specified"; +} + +function branchLabel(handle: string | undefined): string | undefined { + if (!handle) return undefined; + if (handle === "else") return "Else"; + if (handle === "default-category") return "Default"; + if (handle.startsWith("condition:")) return "Condition"; + if (handle.startsWith("category:")) return "Category"; + return humanize(handle); +} + +function nearestHandles(source: GraphNode, target: GraphNode): { + sourceHandle: string; + targetHandle: string; +} { + const dx = target.position.x - source.position.x; + const dy = target.position.y - source.position.y; + if (Math.abs(dx) > Math.abs(dy)) { + return dx >= 0 + ? { sourceHandle: "source-right", targetHandle: "target-left" } + : { sourceHandle: "source-left", targetHandle: "target-right" }; + } + return dy >= 0 + ? { sourceHandle: "source-bottom", targetHandle: "target-top" } + : { sourceHandle: "source-top", targetHandle: "target-bottom" }; +} + +function Canvas({ + graph, + label, + showNotesInitially, + compact, + variant, +}: { + graph: GraphData; + label: string; + showNotesInitially: boolean; + compact: boolean; + variant: "workflow" | "agent"; +}) { + const wrapper = useRef(null); + const { fitView, zoomIn, zoomOut } = useReactFlow(); + const [selected, setSelected] = useState(null); + const [showNotes, setShowNotes] = useState(showNotesInitially); + + useEffect(() => { + const onKey = (event: KeyboardEvent) => { + if (event.key === "Escape") setSelected(null); + }; + document.addEventListener("keydown", onKey); + return () => document.removeEventListener("keydown", onKey); + }, []); + + const hiddenIds = useMemo( + () => new Set(showNotes ? [] : graph.nodes.filter((node) => node.type === "canvasNote").map((node) => node.id)), + [graph.nodes, showNotes], + ); + const primaryAgentIds = useMemo(() => { + if (variant !== "agent") return new Set(); + const agentIds = new Set(graph.nodes.filter((node) => node.type === "agent").map((node) => node.id)); + const delegatedIds = new Set( + graph.edges + .filter((edge) => agentIds.has(edge.source) && agentIds.has(edge.target)) + .map((edge) => edge.target), + ); + return new Set([...agentIds].filter((id) => !delegatedIds.has(id))); + }, [graph.edges, graph.nodes, variant]); + const nodes = useMemo( + () => + graph.nodes + .filter((node) => !hiddenIds.has(node.id)) + .map((node) => ({ + id: node.id, + type: "catalog", + position: node.position, + data: { source: node, primary: primaryAgentIds.has(node.id) }, + width: node.type === "start" || node.type === "end" ? 160 : 224, + height: 72, + draggable: false, + selectable: !compact, + })), + [compact, graph.nodes, hiddenIds, primaryAgentIds], + ); + const graphNodeById = useMemo(() => new Map(graph.nodes.map((node) => [node.id, node])), [graph.nodes]); + const edges = useMemo( + () => + graph.edges + .filter((edge) => !hiddenIds.has(edge.source) && !hiddenIds.has(edge.target)) + .map((edge) => { + const sourceNode = graphNodeById.get(edge.source); + const color = sourceNode + ? graphVisualColor(graphVisualKind(sourceNode)) + : "var(--cp-border-strong)"; + const targetNode = graphNodeById.get(edge.target); + const handles = variant === "agent" && sourceNode && targetNode + ? nearestHandles(sourceNode, targetNode) + : {}; + return { + id: edge.id, + source: edge.source, + target: edge.target, + label: edge.label ?? branchLabel(edge.sourceHandle), + type: variant === "agent" ? "straight" : "smoothstep", + ...handles, + markerEnd: { type: MarkerType.ArrowClosed, color }, + style: { strokeWidth: 1.8, stroke: color }, + }; + }), + [graph.edges, graphNodeById, hiddenIds, variant], + ); + + const toggleFullscreen = async () => { + if (!wrapper.current) return; + if (document.fullscreenElement) await document.exitFullscreen(); + else await wrapper.current.requestFullscreen(); + }; + + return ( +
+
+ + + + {!compact && graph.nodes.some((node) => node.type === "canvasNote") && ( + + )} + {!compact && } +
+ + nodes={nodes} + edges={edges} + nodeTypes={nodeTypes} + fitView + fitViewOptions={{ padding: 0.18 }} + minZoom={0.15} + maxZoom={1.8} + nodesDraggable={false} + nodesConnectable={false} + onNodeClick={(_, node) => { + if (!compact) setSelected(node.data.source); + }} + onPaneClick={() => setSelected(null)} + proOptions={{ hideAttribution: true }} + > + + {!compact && ( + graphVisualColor(graphVisualKind((node as CatalogNode).data.source))} + /> + )} + + {!compact && selected && ( + + )} +
+ ); +} + +export default function GraphCanvas({ + graph, + label, + showNotesInitially = true, + compact = false, + variant = "workflow", +}: { + graph: GraphData; + label: string; + showNotesInitially?: boolean; + compact?: boolean; + variant?: "workflow" | "agent"; +}) { + return ( + + + + ); +} diff --git a/src/components/graph/WorkflowExplorer.tsx b/src/components/graph/WorkflowExplorer.tsx new file mode 100644 index 0000000..3b1d480 --- /dev/null +++ b/src/components/graph/WorkflowExplorer.tsx @@ -0,0 +1,48 @@ +import { useState } from "react"; +import type { WorkflowPreview } from "../../lib/schema"; +import GraphCanvas from "./GraphCanvas"; + +export default function WorkflowExplorer({ workflows }: { workflows: WorkflowPreview[] }) { + const [activeId, setActiveId] = useState(workflows[0]?.id ?? ""); + const active = workflows.find((workflow) => workflow.id === activeId) ?? workflows[0]; + + if (!active) { + return ( +
+

No workflows in this solution

+

The agent architecture is still available in the Agents tab.

+
+ ); + } + + return ( +
+
+ +
+ {active.trigger && Trigger: {active.trigger}} + {active.nodeCount} nodes + {active.edgeCount} connections +
+
+
+ +
+
+ ); +} diff --git a/src/content.config.ts b/src/content.config.ts new file mode 100644 index 0000000..1fa2b02 --- /dev/null +++ b/src/content.config.ts @@ -0,0 +1,16 @@ +import { defineCollection } from "astro:content"; +import { glob } from "astro/loaders"; +import { z } from "astro/zod"; +import { solutionSchema } from "./lib/schema"; + +const solutions = defineCollection({ + loader: glob({ pattern: "**/*.json", base: "./src/content/solutions" }), + schema: solutionSchema, +}); + +const guides = defineCollection({ + loader: glob({ pattern: "**/*.md", base: "./src/content/guides" }), + schema: z.looseObject({}), +}); + +export const collections = { solutions, guides }; diff --git a/src/content/guides/.gitkeep b/src/content/guides/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/content/guides/blastbox-demo-agents.md b/src/content/guides/blastbox-demo-agents.md new file mode 100644 index 0000000..0e859cf --- /dev/null +++ b/src/content/guides/blastbox-demo-agents.md @@ -0,0 +1,20 @@ +--- +title: BlastBox Demo Agent Team +slug: blastbox-demo-agents +solution: BlastBoxDemo +--- +This solution demonstrates how a retail assistant can coordinate a complete store +conversation while relying on specialist agents for inventory and policy. + +## Agents + +The primary store assistant delegates focused questions to inventory and policy +agents. Its supporting capabilities include membership and warehouse MCP tools, +points reconciliation, prorated refund calculation, card reissue, and PDF slip +generation. + +## Import + +Download the rebuilt solution ZIP and import it through Power Platform. Review +the included MCP connections and replace environment-specific configuration +before publishing the agents. diff --git a/src/content/guides/blastbox-deploy.md b/src/content/guides/blastbox-deploy.md new file mode 100644 index 0000000..ae8b2f8 --- /dev/null +++ b/src/content/guides/blastbox-deploy.md @@ -0,0 +1,21 @@ +--- +title: BlastBox Deploy +slug: blastbox-deploy +solution: BlastBoxDeploy +--- +BlastBox Deploy packages a coordinated retail-agent team for import into another +Power Platform environment. It combines conversational agents with MCP-backed +store operations and reusable file-based skills. + +## Agents + +The store assistant works with inventory and policy specialists and includes +capabilities for membership, orders, warehouse inventory, refunds, points, and +customer documents. + +## Import + +Import the rebuilt ZIP through Power Platform, then review all connection +references and MCP endpoints before publishing. File-based skills are included +in the solution package and should be reviewed with the same care as other +submitted source files. diff --git a/src/content/guides/blastbox-omega.md b/src/content/guides/blastbox-omega.md new file mode 100644 index 0000000..16819d0 --- /dev/null +++ b/src/content/guides/blastbox-omega.md @@ -0,0 +1,48 @@ +--- +title: BlastBox Omega Store Operations +slug: blastbox-omega +solution: WorkflowsBlastbox +--- +BlastBox Omega is a retro-gaming store solution built around one store-associate +assistant and two specialist agents. Together they handle returns, warranty +swaps, membership changes, stock questions, fulfillment alternatives, policy +checks, and settlement. + +The solution includes two modern workflows that reuse the same agent and MCP +data layer. They show how classification, loops, conditions, human review, and +agent nodes combine into practical store operations. + +## Agents + +- **Store Associate Assistant** coordinates the customer's complete visit and + delegates stock and policy questions to its specialist agents. +- **Inventory & Fulfillment Agent** checks stock, alternatives, restock timing, + compatibility, and inventory aging. +- **Store Policy Agent** grounds returns, warranty, membership, and markdown + decisions in store policy. + +The primary assistant also includes inline skills for prorated refunds, points +reconciliation, and PDF slip generation, plus MCP-backed order and membership +tools. + +## BlastBox Post-Sales Service + +An email with the subject **BlastBox Customer Service** starts the workflow. A +classification step separates shipping issues, defective products, and general +post-sale questions. Each branch calls the appropriate agents, checks order, +stock, weather, and policy context, and asks for human input only when the +customer's choice or an exception requires it. + +## BlastBox Weekly Inventory Health + +This workflow reviews active SKUs with the inventory agent, loops through stock +aging results, and branches between price adjustment, reorder, and keep-price +paths. Markdown policy and human review protect consequential discount +decisions. The run ends with an HTML inventory report sent by email. + +## Import + +Download the rebuilt solution ZIP from this page and import it through Power +Platform. The package contains the original exploded solution files, including +its agents, workflows, connectors, skills, and connection references. Review and +replace environment-specific connections during import. diff --git a/src/content/guides/campaign-content-engine.md b/src/content/guides/campaign-content-engine.md new file mode 100644 index 0000000..62c66c4 --- /dev/null +++ b/src/content/guides/campaign-content-engine.md @@ -0,0 +1,16 @@ +--- +title: Campaign Content Engine +slug: campaign-content-engine +solution: Demo_campaign_content_engine +--- +Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls. + +## How it works + +- Read brief +- Load brand voice +- Draft channels +- Review content +- Publish kit + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/contract-review-assistant.md b/src/content/guides/contract-review-assistant.md new file mode 100644 index 0000000..5a1aaa2 --- /dev/null +++ b/src/content/guides/contract-review-assistant.md @@ -0,0 +1,15 @@ +--- +title: Contract Review Assistant +slug: contract-review-assistant +solution: Demo_contract_review_assistant +--- +Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval. + +## How it works + +- Extract clauses +- Compare playbook +- Score risk +- Request approval + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/customer-support-triage.md b/src/content/guides/customer-support-triage.md new file mode 100644 index 0000000..945d5af --- /dev/null +++ b/src/content/guides/customer-support-triage.md @@ -0,0 +1,16 @@ +--- +title: Customer Support Triage +slug: customer-support-triage +solution: Demo_customer_support_triage +--- +Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue. + +## How it works + +- Classify case +- Search knowledge +- Draft response +- Human review +- Update case + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/employee-onboarding-guide.md b/src/content/guides/employee-onboarding-guide.md new file mode 100644 index 0000000..5b56106 --- /dev/null +++ b/src/content/guides/employee-onboarding-guide.md @@ -0,0 +1,15 @@ +--- +title: Employee Onboarding Guide +slug: employee-onboarding-guide +solution: Demo_employee_onboarding_guide +--- +Coordinates a new hire checklist and answers policy, benefits, and first-week questions. + +## How it works + +- Create checklist +- Provision access +- Send welcome plan +- Check progress + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/field-service-dispatch.md b/src/content/guides/field-service-dispatch.md new file mode 100644 index 0000000..493cfb6 --- /dev/null +++ b/src/content/guides/field-service-dispatch.md @@ -0,0 +1,15 @@ +--- +title: Field Service Dispatch +slug: field-service-dispatch +solution: Demo_field_service_dispatch +--- +Prioritizes service requests, finds the right technician, and keeps customers informed of arrival changes. + +## How it works + +- Assess request +- Find technician +- Reserve slot +- Notify customer + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/incident-response-coordinator.md b/src/content/guides/incident-response-coordinator.md new file mode 100644 index 0000000..0038d4d --- /dev/null +++ b/src/content/guides/incident-response-coordinator.md @@ -0,0 +1,16 @@ +--- +title: Incident Response Coordinator +slug: incident-response-coordinator +solution: Demo_incident_response_coordinator +--- +Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution. + +## How it works + +- Collect telemetry +- Assess severity +- Open bridge +- Post updates +- Close incident + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/invoice-exception-handler.md b/src/content/guides/invoice-exception-handler.md new file mode 100644 index 0000000..79860c4 --- /dev/null +++ b/src/content/guides/invoice-exception-handler.md @@ -0,0 +1,16 @@ +--- +title: Invoice Exception Handler +slug: invoice-exception-handler +solution: Demo_invoice_exception_handler +--- +Validates invoice data, resolves routine mismatches, and routes material exceptions for finance review. + +## How it works + +- Read invoice +- Match purchase order +- Classify exception +- Request approval +- Post result + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/knowledge-gap-finder.md b/src/content/guides/knowledge-gap-finder.md new file mode 100644 index 0000000..fd16d79 --- /dev/null +++ b/src/content/guides/knowledge-gap-finder.md @@ -0,0 +1,15 @@ +--- +title: Knowledge Gap Finder +slug: knowledge-gap-finder +solution: Demo_knowledge_gap_finder +--- +Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines. + +## How it works + +- Cluster questions +- Find missing topics +- Prioritize gaps +- Draft article outlines + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/project-status-narrator.md b/src/content/guides/project-status-narrator.md new file mode 100644 index 0000000..3999092 --- /dev/null +++ b/src/content/guides/project-status-narrator.md @@ -0,0 +1,15 @@ +--- +title: Project Status Narrator +slug: project-status-narrator +solution: Demo_project_status_narrator +--- +Combines delivery signals into a plain-language status update with risks, decisions, and next actions. + +## How it works + +- Collect delivery signals +- Summarize progress +- Highlight risks +- Send update + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/sales-account-brief.md b/src/content/guides/sales-account-brief.md new file mode 100644 index 0000000..7dc631a --- /dev/null +++ b/src/content/guides/sales-account-brief.md @@ -0,0 +1,15 @@ +--- +title: Sales Account Brief Generator +slug: sales-account-brief +solution: Demo_sales_account_brief +--- +Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief. + +## How it works + +- Load CRM account +- Find recent signals +- Summarize risks +- Create brief + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/security-access-review.md b/src/content/guides/security-access-review.md new file mode 100644 index 0000000..85b502b --- /dev/null +++ b/src/content/guides/security-access-review.md @@ -0,0 +1,15 @@ +--- +title: Security Access Review +slug: security-access-review +solution: Demo_security_access_review +--- +Explains access anomalies, gathers manager evidence, and produces an auditable review decision. + +## How it works + +- Load access list +- Detect anomalies +- Collect evidence +- Record decision + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/store-feedback-analyzer.md b/src/content/guides/store-feedback-analyzer.md new file mode 100644 index 0000000..c7cf256 --- /dev/null +++ b/src/content/guides/store-feedback-analyzer.md @@ -0,0 +1,15 @@ +--- +title: Store Feedback Analyzer +slug: store-feedback-analyzer +solution: Demo_store_feedback_analyzer +--- +Summarizes store feedback, detects recurring product issues, and prepares action themes for operations. + +## How it works + +- Collect feedback +- Detect sentiment +- Group themes +- Recommend actions + +> Demo submission for gallery design and discovery testing. diff --git a/src/content/guides/store-modern-agent.md b/src/content/guides/store-modern-agent.md new file mode 100644 index 0000000..5e9a1b0 --- /dev/null +++ b/src/content/guides/store-modern-agent.md @@ -0,0 +1,21 @@ +--- +title: Store Modern Agent +slug: store-modern-agent +solution: StoreModernAgent +--- +Store Modern Agent is a compact example of delegation in Copilot Studio. A +customer-facing assistant stays at the center of the conversation and calls +specialist agents when a question needs inventory or store-policy expertise. + +## Agents + +- The primary store assistant coordinates the conversation and owns reusable + store skills. +- The inventory specialist handles stock and fulfillment questions. +- The policy specialist handles returns and other policy-grounded decisions. + +## Import + +Download and import the rebuilt solution ZIP through Power Platform. Review the +connected-agent references and configure any required environment connections +before publishing. diff --git a/src/content/solutions/.gitkeep b/src/content/solutions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/content/solutions/blastbox-demo-agents.json b/src/content/solutions/blastbox-demo-agents.json new file mode 100644 index 0000000..cfc576a --- /dev/null +++ b/src/content/solutions/blastbox-demo-agents.json @@ -0,0 +1,576 @@ +{ + "name": "BlastBox Demo Agent Team", + "description": "A retail service team that coordinates store questions across specialist agents, MCP tools, and reusable skills.", + "tags": [ + "retail", + "customer-service", + "multi-agent", + "mcp", + "skills" + ], + "author": "BlastBox team", + "featured": false, + "uniqueName": "BlastBoxDemo", + "version": "1.0.0.1", + "publisher": "CAT", + "bundle": "bundles/blastbox-demo-agents.zip", + "agentCount": 4, + "workflowCount": 0, + "componentCount": 4, + "agents": [ + { + "id": "Default_InventoryFullfilmentAgent_X-w2GP", + "schemaName": "Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "model": "Sonnet46", + "instructions": "You are the Inventory & Fulfillment Agent for BlastBox Omega stores. You support the Returns & Service Assistant with stock, alternatives, restock, and game compatibility.​‌\n\nTools:​‌\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.​‌\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.​‌\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.​‌\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.​‌\nRules:​‌\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.​‌\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).​‌\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.​‌\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.​‌\nBe concise. Always include SKUs, prices, and quantities you looked up.​‌\n\n\nInventory aging (merchandising reviews):​‌\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.​‌\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.​‌", + "channels": [], + "toolCount": 1, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "Default_StorePolicyAgent_s9s-u8", + "schemaName": "Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "model": "Sonnet46", + "instructions": "You are the Store Policy Agent — the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns & Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won't power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns & Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn't specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "Default_draft_IsBewO", + "schemaName": "Default_draft_IsBewO", + "name": "Returns & Service Assistant", + "model": "Sonnet46", + "instructions": "You are the BlastPass Assistant, a friendly self-serve helper that BlastBox customers talk to directly. You help members with their BlastPass membership and their membership card.\n\nTone: warm, upbeat, and concise. Speak directly to the customer like a helpful concierge - one friendly idea per turn, never a wall of text. A single light emoji is fine; don't overdo it. Ask for one thing at a time, and follow the steps in your skills when one applies.\n\nA couple of house rules: always verify identity before making any change to a membership or card, and never invent membership details, serials, or numbers - look them up with your tools or ask the customer.", + "channels": [], + "toolCount": 1, + "skillCount": 2, + "connectedAgentCount": 0 + }, + { + "id": "cr26e_storeassociateassistant_ZQHUx1", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1", + "name": "Store Associate Assistant", + "model": "Sonnet46", + "instructions": "You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 3, + "connectedAgentCount": 2 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "type": "agent", + "position": { + "x": 563, + "y": -396 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Inventory & Fulfillment Agent for BlastBox Omega stores. You support the Returns & Service Assistant with stock, alternatives, restock, and game compatibility.​‌\n\nTools:​‌\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.​‌\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.​‌\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.​‌\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.​‌\nRules:​‌\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.​‌\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).​‌\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.​‌\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.​‌\nBe concise. Always include SKUs, prices, and quantities you looked up.​‌\n\n\nInventory aging (merchandising reviews):​‌\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.​‌\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.​‌", + "channels": [], + "toolCount": 1, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "agent:Default_draft_IsBewO", + "name": "Returns & Service Assistant", + "type": "agent", + "position": { + "x": -787, + "y": -36 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the BlastPass Assistant, a friendly self-serve helper that BlastBox customers talk to directly. You help members with their BlastPass membership and their membership card.\n\nTone: warm, upbeat, and concise. Speak directly to the customer like a helpful concierge - one friendly idea per turn, never a wall of text. A single light emoji is fine; don't overdo it. Ask for one thing at a time, and follow the steps in your skills when one applies.\n\nA couple of house rules: always verify identity before making any change to a membership or card, and never invent membership details, serials, or numbers - look them up with your tools or ask the customer.", + "channels": [], + "toolCount": 1, + "skillCount": 2, + "connectedAgentCount": 0 + } + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1", + "name": "Store Associate Assistant", + "type": "agent", + "position": { + "x": 563, + "y": -36 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 3, + "connectedAgentCount": 2 + } + }, + { + "id": "agent:Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "type": "agent", + "position": { + "x": 905.3803458662553, + "y": -147.24611797498108 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Store Policy Agent — the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns & Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won't power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns & Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn't specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "skill:Default_draft_IsBewO.skill.card-reissue", + "name": "card-reissue", + "type": "skill", + "position": { + "x": -787, + "y": -256 + }, + "details": { + "kind": "skill", + "schemaName": "Default_draft_IsBewO.skill.card-reissue", + "description": "Replace (reissue) a member's BlastPass card when it is lost, stolen, or damaged. Use whenever the person wants a new/replacement BlastPass card. The skill verifies identity (name + last 4 of the console serial) before deactivating the old card, mints a new one with reissue_card, and hands over a digital card. Do not use this to cancel a membership." + } + }, + { + "id": "skill:Default_draft_IsBewO.skill.membership-card-png", + "name": "membership-card-png", + "type": "skill", + "position": { + "x": -596.4744111674235, + "y": 73.99999999999996 + }, + "details": { + "kind": "skill", + "schemaName": "Default_draft_IsBewO.skill.membership-card-png", + "description": "Generate a digital BlastPass membership card as a PNG image by running the bundled card_png.py script. Use when a member reissues a lost, stolen, or damaged card, or any time a BlastPass card needs to be shown on screen. Pass the member details (name, id, tier, tier code, card serial, console serial, dates) as command-line flags to the bundled Python (matplotlib) script, which renders a CR80-sized card to a real PNG. It only draws the card from values you already have." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation", + "name": "points-reconciliation", + "type": "skill", + "position": { + "x": 692.3127555043441, + "y": 141.98373876248843 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation", + "description": "Reconcile a customer's BlastPoints loyalty balance after a visit (purchases, returns/clawback, promo multipliers, tier bonus, expiry) and convert the net balance to store credit. Use when an associate asks what a member's BlastPoints are worth, or after a return that should claw back earned points. The skill generates and runs a short Python snippet that applies the rules exactly and prints a points ledger plus the redeemable store-credit value." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator", + "name": "prorated-refund-calculator", + "type": "skill", + "position": { + "x": 433.68724449565593, + "y": 141.98373876248843 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator", + "description": "Calculate the exact prorated refund when a customer cancels a BlastPass membership for the BlastBox Omega console. Use after the Store Policy Agent confirms a prorated refund is allowed for the member's tier and the Membership MCP has returned the membership details (months remaining, annual price, fees). The skill writes and runs a short Python snippet to compute the precise refund and shows a line-by-line breakdown." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator", + "name": "slip-pdf-generator", + "type": "skill", + "position": { + "x": 353.7675664150662, + "y": -103.9837387624884 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator", + "description": "Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill fills a fixed markdown slip template with the agreed values, then generates and runs a short Python (reportlab) snippet to render that markdown into a real PDF file." + } + }, + { + "id": "tool:Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server", + "name": "Membership MCP v2 — Membership MCP v2 Server", + "type": "tool", + "position": { + "x": -977.5255888325764, + "y": 74.00000000000007 + }, + "details": { + "kind": "tool", + "schemaName": "Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server", + "operation": "InvokeMembershipMCPv2", + "description": "MCP server for BlastPass memberships. Tools: get_membership, cancel_membership, reissue_card." + } + }, + { + "id": "tool:Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer", + "name": "Warehouse MCP — Warehouse MCP Server", + "type": "tool", + "position": { + "x": 563, + "y": -566 + }, + "details": { + "kind": "tool", + "schemaName": "Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer", + "operation": "InvokeMCP", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date." + } + } + ], + "edges": [ + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-delegates-cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "label": "delegates" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-delegates-cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "agent:Default_StorePolicyAgent_s9s-u8", + "label": "delegates" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation", + "label": "includes" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator", + "label": "includes" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator", + "label": "includes" + }, + { + "id": "agent:Default_draft_IsBewO-includes-Default_draft_IsBewO.skill.card-reissue", + "source": "agent:Default_draft_IsBewO", + "target": "skill:Default_draft_IsBewO.skill.card-reissue", + "label": "includes" + }, + { + "id": "agent:Default_draft_IsBewO-includes-Default_draft_IsBewO.skill.membership-card-png", + "source": "agent:Default_draft_IsBewO", + "target": "skill:Default_draft_IsBewO.skill.membership-card-png", + "label": "includes" + }, + { + "id": "agent:Default_draft_IsBewO-uses-Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server", + "source": "agent:Default_draft_IsBewO", + "target": "tool:Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server", + "label": "uses" + }, + { + "id": "agent:Default_InventoryFullfilmentAgent_X-w2GP-uses-Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer", + "source": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "target": "tool:Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer", + "label": "uses" + } + ] + }, + "workflows": [], + "files": [ + { + "path": "[Content_Types].xml", + "category": "other", + "size": 2030 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/botcomponent.xml", + "category": "component", + "size": 861 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/data", + "category": "component", + "size": 4358 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/botcomponent.xml", + "category": "component", + "size": 843 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/data", + "category": "component", + "size": 6783 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/botcomponent.xml", + "category": "component", + "size": 799 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/data", + "category": "component", + "size": 7632 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml", + "category": "component", + "size": 797 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data", + "category": "component", + "size": 122 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json", + "category": "component", + "size": 72 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml", + "category": "component", + "size": 784 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data", + "category": "component", + "size": 113 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json", + "category": "component", + "size": 63 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml", + "category": "component", + "size": 629 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py", + "category": "component", + "size": 6809 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml", + "category": "component", + "size": 617 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md", + "category": "component", + "size": 4575 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml", + "category": "component", + "size": 712 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.card-reissue/data", + "category": "component", + "size": 2372 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml", + "category": "component", + "size": 849 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.membership-card-png/data", + "category": "component", + "size": 98 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/botcomponent.xml", + "category": "component", + "size": 513 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/data", + "category": "component", + "size": 285 + }, + { + "path": "botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/botcomponent.xml", + "category": "component", + "size": 568 + }, + { + "path": "botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/data", + "category": "component", + "size": 255 + }, + { + "path": "botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/botcomponent.xml", + "category": "component", + "size": 409 + }, + { + "path": "botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/data", + "category": "component", + "size": 524 + }, + { + "path": "bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml", + "category": "bot", + "size": 35406 + }, + { + "path": "bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json", + "category": "bot", + "size": 1328 + }, + { + "path": "bots/Default_draft_IsBewO/bot.xml", + "category": "bot", + "size": 35396 + }, + { + "path": "bots/Default_draft_IsBewO/configuration.json", + "category": "bot", + "size": 1083 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml", + "category": "bot", + "size": 35418 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json", + "category": "bot", + "size": 2012 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/bot.xml", + "category": "bot", + "size": 35394 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/configuration.json", + "category": "bot", + "size": 1393 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx", + "category": "other", + "size": 52839 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_iconblob.Png", + "category": "asset", + "size": 2895 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_openapidefinition.json", + "category": "other", + "size": 840 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_order-20management-20mcp_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_order-20management-20mcp_customcodeblobcontent.csx", + "category": "other", + "size": 54856 + }, + { + "path": "Connector/new_order-20management-20mcp_openapidefinition.json", + "category": "other", + "size": 1027 + }, + { + "path": "Connector/new_order-20management-20mcp_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_policy-20rag-20mcp_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_policy-20rag-20mcp_customcodeblobcontent.csx", + "category": "other", + "size": 55320 + }, + { + "path": "Connector/new_policy-20rag-20mcp_openapidefinition.json", + "category": "other", + "size": 1064 + }, + { + "path": "Connector/new_policy-20rag-20mcp_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_warehouse-20mcp_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_warehouse-20mcp_customcodeblobcontent.csx", + "category": "other", + "size": 61348 + }, + { + "path": "Connector/new_warehouse-20mcp_iconblob.Png", + "category": "asset", + "size": 2895 + }, + { + "path": "Connector/new_warehouse-20mcp_openapidefinition.json", + "category": "other", + "size": 1024 + }, + { + "path": "Connector/new_warehouse-20mcp_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 4441 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 4504 + } + ] +} diff --git a/src/content/solutions/blastbox-deploy.json b/src/content/solutions/blastbox-deploy.json new file mode 100644 index 0000000..ad84ce9 --- /dev/null +++ b/src/content/solutions/blastbox-deploy.json @@ -0,0 +1,564 @@ +{ + "name": "BlastBox Deploy", + "description": "A packaged multi-agent retail assistant with connected specialists, operational MCP tools, and portable skill files.", + "tags": [ + "retail", + "multi-agent", + "mcp", + "skills", + "deployment" + ], + "author": "BlastBox team", + "featured": false, + "uniqueName": "BlastBoxDeploy", + "version": "1.0.0.5", + "publisher": "CDS Default Publisher", + "bundle": "bundles/blastbox-deploy.zip", + "agentCount": 4, + "workflowCount": 0, + "componentCount": 4, + "agents": [ + { + "id": "Default_InventoryFullfilmentAgent_X-w2GP", + "schemaName": "Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "model": "Sonnet46", + "instructions": "You are the Inventory & Fulfillment Agent for BlastBox Omega stores. You support the Returns & Service Assistant with stock, alternatives, restock, and game compatibility.​‌\n\nTools:​‌\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.​‌\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.​‌\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.​‌\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.​‌\nRules:​‌\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.​‌\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).​‌\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.​‌\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.​‌\nBe concise. Always include SKUs, prices, and quantities you looked up.​‌\n\n\nInventory aging (merchandising reviews):​‌\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.​‌\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.​‌", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "Default_StorePolicyAgent_s9s-u8", + "schemaName": "Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "model": "Sonnet46", + "instructions": "You are the Store Policy Agent — the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns & Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won't power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns & Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn't specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "Default_draft_IsBewO", + "schemaName": "Default_draft_IsBewO", + "name": "Returns & Service Assistant", + "model": "Sonnet46", + "instructions": "You are the BlastPass Assistant, a friendly self-serve helper that BlastBox customers talk to directly. You help members with their BlastPass membership and their membership card.\n\nTone: warm, upbeat, and concise. Speak directly to the customer like a helpful concierge - one friendly idea per turn, never a wall of text. A single light emoji is fine; don't overdo it. Ask for one thing at a time, and follow the steps in your skills when one applies.\n\nA couple of house rules: always verify identity before making any change to a membership or card, and never invent membership details, serials, or numbers - look them up with your tools or ask the customer.", + "channels": [], + "toolCount": 0, + "skillCount": 2, + "connectedAgentCount": 0 + }, + { + "id": "cr26e_storeassociateassistant_ZQHUx1", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1", + "name": "Store Associate Assistant", + "model": "Sonnet46", + "instructions": "You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 3, + "connectedAgentCount": 2 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "type": "agent", + "position": { + "x": 563, + "y": -396 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Inventory & Fulfillment Agent for BlastBox Omega stores. You support the Returns & Service Assistant with stock, alternatives, restock, and game compatibility.​‌\n\nTools:​‌\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.​‌\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.​‌\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.​‌\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.​‌\nRules:​‌\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.​‌\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).​‌\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.​‌\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.​‌\nBe concise. Always include SKUs, prices, and quantities you looked up.​‌\n\n\nInventory aging (merchandising reviews):​‌\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.​‌\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.​‌", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "agent:Default_draft_IsBewO", + "name": "Returns & Service Assistant", + "type": "agent", + "position": { + "x": -787, + "y": -36 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the BlastPass Assistant, a friendly self-serve helper that BlastBox customers talk to directly. You help members with their BlastPass membership and their membership card.\n\nTone: warm, upbeat, and concise. Speak directly to the customer like a helpful concierge - one friendly idea per turn, never a wall of text. A single light emoji is fine; don't overdo it. Ask for one thing at a time, and follow the steps in your skills when one applies.\n\nA couple of house rules: always verify identity before making any change to a membership or card, and never invent membership details, serials, or numbers - look them up with your tools or ask the customer.", + "channels": [], + "toolCount": 0, + "skillCount": 2, + "connectedAgentCount": 0 + } + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1", + "name": "Store Associate Assistant", + "type": "agent", + "position": { + "x": 563, + "y": -36 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 3, + "connectedAgentCount": 2 + } + }, + { + "id": "agent:Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "type": "agent", + "position": { + "x": 905.3803458662553, + "y": -147.24611797498108 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Store Policy Agent — the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns & Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won't power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns & Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn't specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "skill:Default_draft_IsBewO.skill.card-reissue", + "name": "card-reissue", + "type": "skill", + "position": { + "x": -567, + "y": -36 + }, + "details": { + "kind": "skill", + "schemaName": "Default_draft_IsBewO.skill.card-reissue", + "description": "Replace (reissue) a member's BlastPass card when it is lost, stolen, or damaged. Use whenever the person wants a new/replacement BlastPass card. The skill verifies identity (name + last 4 of the console serial) before deactivating the old card, mints a new one with reissue_card, and hands over a digital card. Do not use this to cancel a membership." + } + }, + { + "id": "skill:Default_draft_IsBewO.skill.membership-card-png", + "name": "membership-card-png", + "type": "skill", + "position": { + "x": -1007, + "y": -35.99999999999997 + }, + "details": { + "kind": "skill", + "schemaName": "Default_draft_IsBewO.skill.membership-card-png", + "description": "Generate a digital BlastPass membership card as a PNG image by running the bundled card_png.py script. Use when a member reissues a lost, stolen, or damaged card, or any time a BlastPass card needs to be shown on screen. Pass the member details (name, id, tier, tier code, card serial, console serial, dates) as command-line flags to the bundled Python (matplotlib) script, which renders a CR80-sized card to a real PNG. It only draws the card from values you already have." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "name": "points-reconciliation", + "type": "skill", + "position": { + "x": 692.3127555043441, + "y": 141.98373876248843 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "description": "Reconcile a customer's BlastPoints loyalty balance after a complex visit (purchases, returns, promo multipliers, expiry) and convert the net balance to store credit. Use when an associate asks about a member's points, or after a return that should claw back previously earned points. The skill runs a bundled Python script that applies the base earn rate, promo multipliers, tier bonus, clawbacks, and expiry, then prints a points ledger and the redeemable store-credit value." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "name": "prorated-refund-calculator", + "type": "skill", + "position": { + "x": 433.68724449565593, + "y": 141.98373876248843 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "description": "Calculate the exact prorated refund when a customer cancels a BlastPass membership, and the net settlement when a defective unit is traded up to a pricier model. Use after the Store Policy Agent confirms the refund rule and the Membership MCP has returned the membership details. The skill runs a bundled Python script so the arithmetic is exact." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "name": "slip-pdf-generator", + "type": "skill", + "position": { + "x": 353.7675664150662, + "y": -103.9837387624884 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "description": "Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill runs a bundled Python script that renders the confirmed slip values into a real PDF file." + } + } + ], + "edges": [ + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-delegates-cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "label": "delegates" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-delegates-cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "agent:Default_StorePolicyAgent_s9s-u8", + "label": "delegates" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "label": "includes" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "label": "includes" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "label": "includes" + }, + { + "id": "agent:Default_draft_IsBewO-includes-Default_draft_IsBewO.skill.card-reissue", + "source": "agent:Default_draft_IsBewO", + "target": "skill:Default_draft_IsBewO.skill.card-reissue", + "label": "includes" + }, + { + "id": "agent:Default_draft_IsBewO-includes-Default_draft_IsBewO.skill.membership-card-png", + "source": "agent:Default_draft_IsBewO", + "target": "skill:Default_draft_IsBewO.skill.membership-card-png", + "label": "includes" + } + ] + }, + "workflows": [], + "files": [ + { + "path": "[Content_Types].xml", + "category": "other", + "size": 1579 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml", + "category": "component", + "size": 892 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data", + "category": "component", + "size": 99 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml", + "category": "component", + "size": 772 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data", + "category": "component", + "size": 104 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml", + "category": "component", + "size": 727 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data", + "category": "component", + "size": 96 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml", + "category": "component", + "size": 797 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data", + "category": "component", + "size": 122 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json", + "category": "component", + "size": 72 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml", + "category": "component", + "size": 784 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data", + "category": "component", + "size": 113 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json", + "category": "component", + "size": 63 + }, + { + "path": "botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml", + "category": "component", + "size": 594 + }, + { + "path": "botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py", + "category": "component", + "size": 4979 + }, + { + "path": "botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml", + "category": "component", + "size": 604 + }, + { + "path": "botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py", + "category": "component", + "size": 3257 + }, + { + "path": "botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml", + "category": "component", + "size": 561 + }, + { + "path": "botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md", + "category": "component", + "size": 3791 + }, + { + "path": "botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml", + "category": "component", + "size": 556 + }, + { + "path": "botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md", + "category": "component", + "size": 3603 + }, + { + "path": "botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml", + "category": "component", + "size": 553 + }, + { + "path": "botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md", + "category": "component", + "size": 4152 + }, + { + "path": "botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml", + "category": "component", + "size": 562 + }, + { + "path": "botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py", + "category": "component", + "size": 5872 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml", + "category": "component", + "size": 629 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py", + "category": "component", + "size": 6809 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml", + "category": "component", + "size": 617 + }, + { + "path": "botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md", + "category": "component", + "size": 4575 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml", + "category": "component", + "size": 712 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.card-reissue/data", + "category": "component", + "size": 2372 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml", + "category": "component", + "size": 849 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.membership-card-png/data", + "category": "component", + "size": 98 + }, + { + "path": "bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml", + "category": "bot", + "size": 35406 + }, + { + "path": "bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json", + "category": "bot", + "size": 1150 + }, + { + "path": "bots/Default_draft_IsBewO/bot.xml", + "category": "bot", + "size": 35396 + }, + { + "path": "bots/Default_draft_IsBewO/configuration.json", + "category": "bot", + "size": 1083 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml", + "category": "bot", + "size": 35418 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json", + "category": "bot", + "size": 2012 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/bot.xml", + "category": "bot", + "size": 35394 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/configuration.json", + "category": "bot", + "size": 1393 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx", + "category": "other", + "size": 52839 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_iconblob.Png", + "category": "asset", + "size": 2895 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_openapidefinition.json", + "category": "other", + "size": 1080 + }, + { + "path": "Connector/cat_membership-20mcp-20v2_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_order-20management-20mcp_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_order-20management-20mcp_customcodeblobcontent.csx", + "category": "other", + "size": 54856 + }, + { + "path": "Connector/new_order-20management-20mcp_openapidefinition.json", + "category": "other", + "size": 1027 + }, + { + "path": "Connector/new_order-20management-20mcp_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx", + "category": "other", + "size": 55320 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json", + "category": "other", + "size": 972 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_warehouse-20mcp_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_warehouse-20mcp_customcodeblobcontent.csx", + "category": "other", + "size": 61348 + }, + { + "path": "Connector/new_warehouse-20mcp_iconblob.Png", + "category": "asset", + "size": 2895 + }, + { + "path": "Connector/new_warehouse-20mcp_openapidefinition.json", + "category": "other", + "size": 1024 + }, + { + "path": "Connector/new_warehouse-20mcp_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 4469 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 4594 + } + ] +} diff --git a/src/content/solutions/blastbox-omega.json b/src/content/solutions/blastbox-omega.json new file mode 100644 index 0000000..10cc204 --- /dev/null +++ b/src/content/solutions/blastbox-omega.json @@ -0,0 +1,1428 @@ +{ + "name": "BlastBox Omega Store Operations", + "description": "A multi-agent retail solution pairing customer service and inventory agents with two human-aware operational workflows.", + "tags": [ + "retail", + "customer-service", + "inventory", + "multi-agent", + "human-in-the-loop" + ], + "author": "Adriana Trujillo", + "createdAt": "2026-08-11", + "updatedAt": "2026-08-11", + "featured": true, + "uniqueName": "WorkflowsBlastbox", + "version": "1.0.0.3", + "publisher": "AdrianaTrujillo", + "bundle": "bundles/blastbox-omega.zip", + "agentCount": 3, + "workflowCount": 2, + "componentCount": 5, + "agents": [ + { + "id": "Default_InventoryFullfilmentAgent_X-w2GP", + "schemaName": "Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "model": "Sonnet46", + "instructions": "You are the Inventory & Fulfillment Agent for BlastBox Omega stores. You support the Returns & Service Assistant with stock, alternatives, restock, and game compatibility.​‌\n\nTools:​‌\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.​‌\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.​‌\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.​‌\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.​‌\nRules:​‌\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.​‌\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).​‌\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.​‌\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.​‌\nBe concise. Always include SKUs, prices, and quantities you looked up.​‌\n\n\nInventory aging (merchandising reviews):​‌\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.​‌\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.​‌", + "channels": [], + "toolCount": 1, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "Default_StorePolicyAgent_s9s-u8", + "schemaName": "Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "model": "Sonnet46", + "instructions": "You are the Store Policy Agent — the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns & Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won't power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns & Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn't specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.", + "channels": [], + "toolCount": 1, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "cr26e_storeassociateassistant_ZQHUx1", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1", + "name": "Store Associate Assistant", + "model": "Sonnet46", + "instructions": "You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer.", + "channels": [ + "MsTeams" + ], + "toolCount": 2, + "skillCount": 3, + "connectedAgentCount": 2 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "type": "agent", + "position": { + "x": -111.99999999999997, + "y": -396 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Inventory & Fulfillment Agent for BlastBox Omega stores. You support the Returns & Service Assistant with stock, alternatives, restock, and game compatibility.​‌\n\nTools:​‌\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.​‌\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.​‌\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.​‌\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.​‌\nRules:​‌\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.​‌\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).​‌\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.​‌\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.​‌\nBe concise. Always include SKUs, prices, and quantities you looked up.​‌\n\n\nInventory aging (merchandising reviews):​‌\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.​‌\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.​‌", + "channels": [], + "toolCount": 1, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1", + "name": "Store Associate Assistant", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer.", + "channels": [ + "MsTeams" + ], + "toolCount": 2, + "skillCount": 3, + "connectedAgentCount": 2 + } + }, + { + "id": "agent:Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "type": "agent", + "position": { + "x": 169.45933368849074, + "y": -260.45632866914406 + }, + "details": { + "model": "Sonnet46", + "instructions": "You are the Store Policy Agent — the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns & Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won't power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns & Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn't specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.", + "channels": [], + "toolCount": 1, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "name": "points-reconciliation", + "type": "skill", + "position": { + "x": 102.48414068000119, + "y": 12.954605470389168 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "description": "Reconcile a customer's BlastPoints loyalty balance after a complex visit (purchases, returns, promo multipliers, expiry) and convert the net balance to store credit. Use when an associate asks about a member's points, or after a return that should claw back previously earned points. The skill runs a bundled Python script that applies the base earn rate, promo multipliers, tier bonus, clawbacks, and expiry, then prints a points ledger and the redeemable store-credit value." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "name": "prorated-refund-calculator", + "type": "skill", + "position": { + "x": -16.545577394137197, + "y": 162.2131509385322 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "description": "Calculate the exact prorated refund when a customer cancels a BlastPass membership, and the net settlement when a defective unit is traded up to a pricier model. Use after the Store Policy Agent confirms the refund rule and the Membership MCP has returned the membership details. The skill runs a bundled Python script so the arithmetic is exact." + } + }, + { + "id": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "name": "slip-pdf-generator", + "type": "skill", + "position": { + "x": -207.45442260586276, + "y": 162.2131509385322 + }, + "details": { + "kind": "skill", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "description": "Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill runs a bundled Python script that renders the confirmed slip values into a real PDF file." + } + }, + { + "id": "tool:cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac", + "name": "Membership MCP v2 — Membership MCP v2 Server", + "type": "tool", + "position": { + "x": -326.4841406800012, + "y": 12.95460547038919 + }, + "details": { + "kind": "tool", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac", + "operation": "InvokeMembershipMCPv2", + "description": "MCP server for BlastPass memberships. Tools: get_membership, cancel_membership, reissue_card." + } + }, + { + "id": "tool:cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG", + "name": "Order Management MCP — Order Management MCP Server", + "type": "tool", + "position": { + "x": -284.00292614296654, + "y": -173.16775640892135 + }, + "details": { + "kind": "tool", + "schemaName": "cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG", + "operation": "InvokeMCP", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status." + } + }, + { + "id": "tool:Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V", + "name": "Policy RAG MCP v2 — Policy RAG MCP v2 Server", + "type": "tool", + "position": { + "x": 302.3706857080558, + "y": -366.4495949851288 + }, + "details": { + "kind": "tool", + "schemaName": "Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V", + "operation": "InvokeMCP", + "description": "MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy, get_markdown_policy." + } + }, + { + "id": "tool:Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG", + "name": "Warehouse MCP — Warehouse MCP Server", + "type": "tool", + "position": { + "x": -111.99999999999993, + "y": -566 + }, + "details": { + "kind": "tool", + "schemaName": "Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG", + "operation": "InvokeMCP", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date." + } + } + ], + "edges": [ + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-delegates-cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "label": "delegates" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-delegates-cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "agent:Default_StorePolicyAgent_s9s-u8", + "label": "delegates" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK", + "label": "includes" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe", + "label": "includes" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-includes-cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "skill:cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4", + "label": "includes" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-uses-cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "tool:cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac", + "label": "uses" + }, + { + "id": "agent:cr26e_storeassociateassistant_ZQHUx1-uses-cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG", + "source": "agent:cr26e_storeassociateassistant_ZQHUx1", + "target": "tool:cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG", + "label": "uses" + }, + { + "id": "agent:Default_InventoryFullfilmentAgent_X-w2GP-uses-Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG", + "source": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "target": "tool:Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG", + "label": "uses" + }, + { + "id": "agent:Default_StorePolicyAgent_s9s-u8-uses-Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V", + "source": "agent:Default_StorePolicyAgent_s9s-u8", + "target": "tool:Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V", + "label": "uses" + } + ] + }, + "workflows": [ + { + "id": "{62d468f3-4a55-1ba5-2ec3-ae1a4b3a4b35}", + "name": "BlastBox Post Sales Service", + "trigger": "When a new email arrives", + "nodeCount": 23, + "edgeCount": 22, + "graph": { + "nodes": [ + { + "id": "start-ee6d2ba4-6711-46df-873b-8ff4e5362436", + "name": "When a new email arrives", + "type": "start", + "position": { + "x": -173.36318146106169, + "y": 560.3551575805192 + }, + "details": {} + }, + { + "id": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "name": "Classify", + "type": "classifyOnInlineAgent", + "position": { + "x": 239.63619816708766, + "y": 478.71031516103835 + }, + "details": { + "model": "GPT55Chat" + } + }, + { + "id": "canvasNote-a592e707-ed7b-4419-b23f-4a1265cb05c5", + "name": "Note", + "type": "canvasNote", + "position": { + "x": -184.00976257403167, + "y": 269.8319960199096 + }, + "details": { + "markdown": "Customer email arrives in Inbox with enquiry. Subject filter is \"BlastBox Customer Service\", a Post-Sales entry point", + "color": "yellow" + } + }, + { + "id": "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "name": "Review Order, Stock & Weather", + "type": "agent", + "position": { + "x": 667.6865691304283, + "y": 229.55219617704242 + }, + "details": { + "botSchemaName": "", + "mode": "inline", + "instructions": "", + "inlineInstructions": "Explore the possible issues with the shipment of the order, such as lack of stock, as per the customer enquiry:\n\nEnquiry: {triggerOutputs()?['body/body']}\n\nEstimate a new ETA considering the weather conditions of the place of the destination address, and indicate if a substitute product exists that the customer can order instead. If no substitute exists, propose a refund of 100% of the original am" + } + }, + { + "id": "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "name": "Review Order & Policy", + "type": "agent", + "position": { + "x": 671.7037587803834, + "y": 561.1407517560767 + }, + "details": { + "botSchemaName": "", + "mode": "inline", + "instructions": "\nee", + "inlineInstructions": "Explore the complain of the customer and the options to help them according to our policy:\n\nComplain: {triggerOutputs()?['body/body']}\n\nOutput the following:\nReturn: 'applicable' if policy makes a return, refund or exchange applicable straightaway, 'exception' if the conditions are not fully covered by policy but the case is extreme, shows enough ambiguity or has grey areas not covered explicitly " + } + }, + { + "id": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "name": "Policy Condition", + "type": "ifElse", + "position": { + "x": 1041.2437396594803, + "y": 490 + }, + "details": {} + }, + { + "id": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "name": "Trigger Order Return/Exchange", + "type": "agent", + "position": { + "x": 1966.9371349815933, + "y": 459.85290511248394 + }, + "details": { + "botSchemaName": "", + "mode": "inline", + "instructions": "", + "inlineInstructions": "Initiate a return request for the item(s) in the customer order below that have issues with shipment or that present defects:\n\n{body('connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e')}\n\nSend an email to the customer address indicated below (never other email) indicating that the return request has been initiated:\n\nCustomer email: {triggerOutputs()?['body/from']}\nSubject: 'BlastBox Service - Your en" + } + }, + { + "id": "connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "name": "Human Review: Exception", + "type": "connector", + "position": { + "x": 1342.2819165478081, + "y": 592.5812911489419 + }, + "details": { + "displayName": "Human review", + "apiName": "shared_advancedapprovals", + "operationName": "RequestForInformation", + "operationType": "OpenApiConnectionWebhook", + "parameters": { + "RequestForInformationInput/channel": "Outlook", + "RequestForInformationInput/title": "Action needed: Review Return Policy Exception", + "RequestForInformationInput/message": "Dear manager,\n\nPlease review the following case where return policy has been flagged as applicable.\n\nAssessment Summary:\n@{body('agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b')?['structuredOutput/Summary']}\n\nCustomer Email:\n@{triggerOutputs()?['body/body']}", + "RequestForInformationInput/assignedTo": "@{triggerOutputs()?['body/from']}" + } + } + }, + { + "id": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "name": "Approval Condition", + "type": "ifElse", + "position": { + "x": 1689.5534197048107, + "y": 562.7220352780549 + }, + "details": {} + }, + { + "id": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "name": "Send reply: No refund", + "type": "connector", + "position": { + "x": 1974.0114277344587, + "y": 777.9796628872746 + }, + "details": { + "displayName": "Office 365 Outlook", + "apiName": "shared_office365", + "operationName": "ReplyToV3", + "operationType": "OpenApiConnection", + "parameters": { + "replyParameters/Importance": "Low", + "replyParameters/Body": "
Dear customer,

We have assessed your enquiry and have concluded that your case does not apply for a refund or exchange under our current policy.

Findings summary:@{body('agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b')?['structuredOutput/Summary']}​

We apologize for any inconvenience this may cause to you. If you need furt", + "replyParameters/Subject": "BlastBox Service - Your enquiry", + "replyParameters/To": "@{triggerOutputs()?['body/from']}" + } + } + }, + { + "id": "connector-5f867ed3-6c32-4c2d-b747-251877850d50", + "name": "Send reply", + "type": "connector", + "position": { + "x": 1036.526545065823, + "y": 896.9996168290575 + }, + "details": { + "displayName": "Office 365 Outlook", + "apiName": "shared_office365", + "operationName": "ReplyToV3", + "operationType": "OpenApiConnection", + "parameters": { + "replyParameters/Importance": "Low", + "replyParameters/Body": "
Dear customer,

Thank you for reaching out. We have assessed your enquiry. Here is some useful information for you:

@{body('agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13')?['result']}

Further questions? Please reach out to us or visit our site. 

Thank you​
", + "replyParameters/To": "@{triggerOutputs()?['body/from']}", + "replyParameters/Subject": "BlastBox Service - Your enquiry" + } + } + }, + { + "id": "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e", + "name": "Send context and options", + "type": "connector", + "position": { + "x": 1043.9965195642978, + "y": 228.67374640118007 + }, + "details": { + "displayName": "Office 365 Outlook", + "apiName": "shared_office365", + "operationName": "SendMailWithOptions", + "operationType": "OpenApiConnectionWebhook", + "parameters": { + "optionsEmailSubscription/Message/Importance": "Normal", + "optionsEmailSubscription/Message/Subject": "Action Needed: Your Delivery Enquiry", + "optionsEmailSubscription/Message/Options": "Wait, Return", + "optionsEmailSubscription/Message/To": "@{triggerOutputs()?['body/from']}", + "optionsEmailSubscription/Message/Body": "Dear customer,\n\nPlease find below the findings about your recent enquiry:\n@{body('agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c')?['structuredOutput/Summary']} \n\nIndicate to us if you would be happy to wait for your order under its new ETA, or if you would prefer a refund (or exchange, if applicable).\n\nThank you\n" + } + } + }, + { + "id": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "name": "Choice Condition", + "type": "ifElse", + "position": { + "x": 1342.5961834690788, + "y": 224.4713489391661 + }, + "details": {} + }, + { + "id": "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "name": "Call Store Policy Agent", + "type": "agent", + "position": { + "x": 665.6063840283349, + "y": 898.3189906287583 + }, + "details": { + "botSchemaName": "Default_StorePolicyAgent_s9s-u8", + "mode": "invoke", + "instructions": "Review the customer query and craft a paragraph that summarizes the answer to the question, no greeting or comments, only the answer to the enquiry:\n\nEnquiry: @{triggerOutputs()?['body/body']} \n", + "inlineInstructions": "" + } + }, + { + "id": "canvasNote-55701c44-ed9e-47e5-9956-8e4262503f3b", + "name": "Note 2", + "type": "canvasNote", + "position": { + "x": 232.61791522643375, + "y": 263.65601320945666 + }, + "details": { + "markdown": "Email is classified as Order Shipment Issue, Order Defect Issue, or Other if this is a post-sale generic enquiry, not tied to a specific order", + "color": "yellow" + } + }, + { + "id": "canvasNote-3eb5c945-0bd1-4cde-9a53-b43376917c23", + "name": "Note 4", + "type": "canvasNote", + "position": { + "x": 2312.1449513388206, + "y": 227 + }, + "details": { + "markdown": "Agent builds a contextualized answer, specifying if problem comes from delivery disruptions or stock unavailability and gets new ETA. Next steps options always include \"wait\" and \"cancel/return\", and may include \"exchange\" if substitute exists.", + "color": "blue" + } + }, + { + "id": "canvasNote-c1d662dc-14df-43dc-a503-84fa55f357a4", + "name": "Note 5", + "type": "canvasNote", + "position": { + "x": 1714.3555743703328, + "y": 884.1696096472868 + }, + "details": { + "markdown": "General enquiries are checked by calling existing Store Policy Agent, followed by an email action to send a structured reply to the customer", + "color": "green" + } + }, + { + "id": "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "name": "Send reply: Thanks for waiting", + "type": "connector", + "position": { + "x": 1965.0142830129066, + "y": 134.99388135688142 + }, + "details": { + "displayName": "Office 365 Outlook", + "apiName": "shared_office365", + "operationName": "ReplyToV3", + "operationType": "OpenApiConnection", + "parameters": { + "replyParameters/Importance": "Low", + "replyParameters/Body": "
Dear customer,

Thanks for your patience. 

You can keep track of your order in the separate email sent to you. If experiencing issues with your new ETA, please reach out to us.

Thank you
", + "replyParameters/To": "@{triggerOutputs()?['body/from']}", + "replyParameters/Subject": "BlastBox Service - Your enquiry" + } + } + }, + { + "id": "canvasNote-258c5821-c40f-4722-8aaf-5467ce81b20f", + "name": "Note 4 2", + "type": "canvasNote", + "position": { + "x": 2310.9852399919387, + "y": 534 + }, + "details": { + "markdown": "Agent assesses if case qualifies for an automatic return/replacement, if the conditions may still apply due to exceptional circumstances, or if these options are unapplicable. When exception is considered, a human approves/disapproves. ", + "color": "red" + } + }, + { + "id": "end-2120af3d-79db-4382-8e87-bc95af0281b7", + "name": "End 2", + "type": "end", + "position": { + "x": 2319, + "y": 448 + }, + "details": { + "runStatus": "Succeeded" + } + }, + { + "id": "end-59c4f94f-70bb-4161-ada6-11e1284ea27d", + "name": "End 1", + "type": "end", + "position": { + "x": 2311, + "y": 135 + }, + "details": { + "runStatus": "Succeeded" + } + }, + { + "id": "end-120d8b37-4a78-416c-a48b-63bd0c976443", + "name": "End 3", + "type": "end", + "position": { + "x": 2290, + "y": 778 + }, + "details": { + "runStatus": "Succeeded" + } + }, + { + "id": "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b", + "name": "End 4", + "type": "end", + "position": { + "x": 1353, + "y": 897 + }, + "details": { + "runStatus": "Succeeded" + } + } + ], + "edges": [ + { + "id": "edge-start-ee6d2ba4-6711-46df-873b-8ff4e5362436-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "source": "start-ee6d2ba4-6711-46df-873b-8ff4e5362436", + "target": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b" + }, + { + "id": "edge-agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "source": "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "target": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4" + }, + { + "id": "edge-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b-category:1e11370e-7602-466a-904f-0242d972063c-agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "source": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "target": "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "sourceHandle": "category:1e11370e-7602-466a-904f-0242d972063c" + }, + { + "id": "edge-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4-condition:6f435d40-5285-416a-8ed1-0ad82cc517f4-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "source": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "target": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "sourceHandle": "condition:6f435d40-5285-416a-8ed1-0ad82cc517f4" + }, + { + "id": "edge-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4-condition:dcc6151b-4a09-43d6-b94a-d904a273f861-connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "source": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "target": "connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "sourceHandle": "condition:dcc6151b-4a09-43d6-b94a-d904a273f861" + }, + { + "id": "edge-connector-7492932a-4693-4b1e-91dc-313e11fd221b-ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "source": "connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "target": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339" + }, + { + "id": "edge-ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339-condition:a1be36bb-5825-4ddd-88cd-142f82444df3-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa-input", + "source": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "target": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "sourceHandle": "condition:a1be36bb-5825-4ddd-88cd-142f82444df3", + "targetHandle": "input" + }, + { + "id": "edge-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4-else-connector-9ccb141c-3277-4402-b045-e4569768d047", + "source": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "target": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "sourceHandle": "else" + }, + { + "id": "edge-ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339-else-connector-9ccb141c-3277-4402-b045-e4569768d047-input", + "source": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "target": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "sourceHandle": "else", + "targetHandle": "input" + }, + { + "id": "edge-agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c-connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e", + "source": "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "target": "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e" + }, + { + "id": "edge-connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e-ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "source": "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e", + "target": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c" + }, + { + "id": "edge-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b-default-category-agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "source": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "target": "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "sourceHandle": "default-category" + }, + { + "id": "edge-agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13-connector-5f867ed3-6c32-4c2d-b747-251877850d50", + "source": "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "target": "connector-5f867ed3-6c32-4c2d-b747-251877850d50" + }, + { + "id": "edge-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b-category:f1efd1a5-76cc-4a05-a432-8d5b2fcc0839-agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "source": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "target": "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "sourceHandle": "category:f1efd1a5-76cc-4a05-a432-8d5b2fcc0839" + }, + { + "id": "edge-canvasNote-a592e707-ed7b-4419-b23f-4a1265cb05c5-canvasNote-55701c44-ed9e-47e5-9956-8e4262503f3b-input", + "source": "canvasNote-a592e707-ed7b-4419-b23f-4a1265cb05c5", + "target": "canvasNote-55701c44-ed9e-47e5-9956-8e4262503f3b", + "targetHandle": "input" + }, + { + "id": "edge-ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c-condition:6c4633ad-a5c4-4c49-91ac-5a7d4fa0ee07-connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "source": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "target": "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "sourceHandle": "condition:6c4633ad-a5c4-4c49-91ac-5a7d4fa0ee07" + }, + { + "id": "edge-ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c-else-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa-input", + "source": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "target": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "sourceHandle": "else", + "targetHandle": "input" + }, + { + "id": "edge-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa-end-2120af3d-79db-4382-8e87-bc95af0281b7", + "source": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "target": "end-2120af3d-79db-4382-8e87-bc95af0281b7" + }, + { + "id": "edge-connector-ef8cfaa6-a984-4216-b10a-3408c1c15675-end-59c4f94f-70bb-4161-ada6-11e1284ea27d", + "source": "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "target": "end-59c4f94f-70bb-4161-ada6-11e1284ea27d" + }, + { + "id": "edge-connector-9ccb141c-3277-4402-b045-e4569768d047-end-120d8b37-4a78-416c-a48b-63bd0c976443", + "source": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "target": "end-120d8b37-4a78-416c-a48b-63bd0c976443" + }, + { + "id": "edge-connector-5f867ed3-6c32-4c2d-b747-251877850d50-end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b", + "source": "connector-5f867ed3-6c32-4c2d-b747-251877850d50", + "target": "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b" + }, + { + "id": "edge-end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b-canvasNote-c1d662dc-14df-43dc-a503-84fa55f357a4", + "source": "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b", + "target": "canvasNote-c1d662dc-14df-43dc-a503-84fa55f357a4" + } + ] + } + }, + { + "id": "{44d64f96-1130-9b19-8209-34093d4416a3}", + "name": "BlastBox Weekly Inventory Health", + "trigger": "Start", + "nodeCount": 21, + "edgeCount": 18, + "graph": { + "nodes": [ + { + "id": "start-4b8f612e-9dcd-464b-996f-33cbbf8f4799", + "name": "Start", + "type": "start", + "position": { + "x": 248, + "y": 461.54541015625 + }, + "details": {} + }, + { + "id": "canvasNote-4bf9bd4b-1e72-438a-add1-a13e7b9626e7", + "name": "Note", + "type": "canvasNote", + "position": { + "x": 238, + "y": 290 + }, + "details": { + "markdown": "Set as manual and indicate users to turn on Recurrence to recreate real life scenario without risk of missing turning off the \"Enabled\" workflow toggle", + "color": "yellow" + } + }, + { + "id": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc", + "name": "Call Inventory & Fullfilment Agent", + "type": "agent", + "position": { + "x": 534.4540209199782, + "y": 461.3158195438242 + }, + "details": { + "botSchemaName": "Default_InventoryFullfilmentAgent_X-w2GP", + "mode": "invoke", + "instructions": "Extract the following information for all gaming related SKUs (games and consoles):\n- SKUName\n- State of SKU stock aging: Stale, fresh or other if unknown\n- State of inbound stock order: 'planned' if inbound entry is true, 'unplanned' if false or unknown, 'reorder' if a note to reorder inbound exists\n", + "inlineInstructions": "" + } + }, + { + "id": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "name": "Loop over each active SKU", + "type": "loop", + "position": { + "x": 1248.5117621145687, + "y": 301.8746192221007 + }, + "details": {} + }, + { + "id": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "name": "Stock Aging Condition", + "type": "ifElse", + "position": { + "x": 68.39399609619318, + "y": 172 + }, + "details": {} + }, + { + "id": "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9", + "name": "Human Review: Discount Range", + "type": "connector", + "position": { + "x": 731.7164948835252, + "y": 80 + }, + "details": { + "displayName": "Human review", + "apiName": "shared_advancedapprovals", + "operationName": "RequestForInformation", + "operationType": "OpenApiConnectionWebhook", + "parameters": { + "RequestForInformationInput/channel": "Outlook", + "RequestForInformationInput/title": "Action needed: SKU Pricing Adjustment", + "RequestForInformationInput/message": "Dear store manager,\n\nThe following item has been nominated for a promotion or clearance application based on weekly findings.\nSKU: @{items('Loop_over_each_active_SKU')?['SKUName']} \nWeekly Findings: @{body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Findings']} \n\nThe following options are to be reviewed:\n\nIf promotion is chosen, store employees will receive the following guida", + "RequestForInformationInput/assignedTo": "email@email.com" + } + } + }, + { + "id": "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1", + "name": "Initialize variables", + "type": "variable", + "position": { + "x": 896.5085659640163, + "y": 461.53174408057924 + }, + "details": {} + }, + { + "id": "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28", + "name": "Set SKU: Adjust price", + "type": "variable", + "position": { + "x": 1063.5857849535041, + "y": 80 + }, + "details": {} + }, + { + "id": "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4", + "name": "Set SKU: Keep price", + "type": "variable", + "position": { + "x": 396.2245207435917, + "y": 439 + }, + "details": {} + }, + { + "id": "agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "name": "Review Order History & Policy", + "type": "agent", + "position": { + "x": 393.3538031286964, + "y": 80 + }, + "details": { + "botSchemaName": "", + "mode": "inline", + "instructions": "", + "inlineInstructions": "Get the potential promo or clearance new price scheme for the SKU at hand:\n{items('Loop_over_each_active_SKU')?['SKUName']}\n\nCreate a ready-to-send two lines for store employees with the new potential pricing scheme and rationale. Propose a range of discount prices but do not fix a max and min discount or price, these will be specified by the store managers later. \n\nSummarize the findings in the S" + } + }, + { + "id": "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "name": "Create HTML table", + "type": "builtinFunction", + "position": { + "x": 3252.348581866794, + "y": 617.143631857203 + }, + "details": { + "category": "providers/Microsoft.ProcessSimple/operationGroups/DataOperation", + "description": "Create an HTML table from an array of objects." + } + }, + { + "id": "connector-45078208-58d3-45f1-ba7d-b45d24a551e6", + "name": "Send an email", + "type": "connector", + "position": { + "x": 3557.8040401815115, + "y": 617.143631857203 + }, + "details": { + "displayName": "Office 365 Outlook", + "apiName": "shared_office365", + "operationName": "SendEmailV2", + "operationType": "OpenApiConnection", + "parameters": { + "emailMessage/Importance": "Normal", + "emailMessage/Body": "
Dear team,

Please notice and apply the following changes in our products in store, according to the latest review. Pay special attention to promotions, clearance, or high-visibility items to be reallocated:

@{body('builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9')}

Thank you
", + "emailMessage/Subject": "Store Pricing - Weekly Update", + "emailMessage/To": "@{variables('Email')}" + } + } + }, + { + "id": "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "name": "Send email: Reorder inbound", + "type": "connector", + "position": { + "x": 395.7750977873575, + "y": 245.8781549651269 + }, + "details": { + "displayName": "Office 365 Outlook", + "apiName": "shared_office365", + "operationName": "SendEmailV2", + "operationType": "OpenApiConnection", + "parameters": { + "emailMessage/Importance": "Normal", + "emailMessage/Body": "
Dear team,

We require to reorder the last inbound pack of the following product, given demand increase:

@{items('Loop_over_each_active_SKU')?['SKUName']}

Please provide us with an invoice and ETA within the next 2 business days.

Thank you
", + "emailMessage/To": "@{variables('Email')}", + "emailMessage/Subject": "@{items('Loop_over_each_active_SKU')?['SKUName']} - Stock reorder" + } + } + }, + { + "id": "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a", + "name": "Set SKU: Keep price, main shelf", + "type": "variable", + "position": { + "x": 732.7884644323713, + "y": 245.54526527681014 + }, + "details": {} + }, + { + "id": "canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d", + "name": "Note 2", + "type": "canvasNote", + "position": { + "x": 556.2230943460634, + "y": 286.6630664568835 + }, + "details": { + "markdown": "Extract basic inventory data for each gaming SKU in the inventory, leaving out discontinued SKUs: Stock aging state, inbound stock flag, status notes", + "color": "yellow" + } + }, + { + "id": "canvasNote-ef40d563-4df7-4aff-b6ec-72bc9a0f5f07", + "name": "Note 2 2", + "type": "canvasNote", + "position": { + "x": 892.9761614297805, + "y": 287.4124976243503 + }, + "details": { + "markdown": "Initialize array of SKU notes related to store manager approved changes in pricing, to be communicated to employees", + "color": "yellow" + } + }, + { + "id": "canvasNote-331b3285-c7e6-48a6-b6cb-306bd502888d", + "name": "Note 2 2 2", + "type": "canvasNote", + "position": { + "x": 3402.93884376098, + "y": 447.143631857203 + }, + "details": { + "markdown": "Create ready-to-send data table and communication draft, communicating relevant SKU updates to store employees", + "color": "yellow" + } + }, + { + "id": "canvasNote-77809ff8-3725-4dbb-a6f5-aecd8d9518a7", + "name": "Note 3", + "type": "canvasNote", + "position": { + "x": 1424.345573133879, + "y": 80 + }, + "details": { + "markdown": "Ask store manager for promo or clearance choice and confirm desired price ceiling/floor for SKUs with stale stock", + "color": "blue" + } + }, + { + "id": "canvasNote-5562054e-7d46-4bd8-83ed-b5b162183334", + "name": "Note 3 2", + "type": "canvasNote", + "position": { + "x": 1119.724769871198, + "y": 228.07204197032797 + }, + "details": { + "markdown": "Request reorder inbound to provider when a good-selling item has been flagged. Note for store employees to keep price and item visible.", + "color": "red" + } + }, + { + "id": "canvasNote-17b034a9-13e2-49dc-95c7-9dccaee7b587", + "name": "Note 3 2 2", + "type": "canvasNote", + "position": { + "x": 822.7977080471219, + "y": 409.4950792669853 + }, + "details": { + "markdown": "Note for store employees to keep price of other SKUs, until further notice", + "color": "green" + } + }, + { + "id": "end-2c543046-775a-471f-ab3b-5cc5c32a18bf", + "name": "End", + "type": "end", + "position": { + "x": 3874, + "y": 617 + }, + "details": { + "runStatus": "Succeeded" + } + } + ], + "edges": [ + { + "id": "edge-start-4b8f612e-9dcd-464b-996f-33cbbf8f4799-agent-2d813f06-95ff-4a50-903b-ff36ee884cdc", + "source": "start-4b8f612e-9dcd-464b-996f-33cbbf8f4799", + "target": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc" + }, + { + "id": "edge-agent-2d813f06-95ff-4a50-903b-ff36ee884cdc-variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1", + "source": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc", + "target": "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1" + }, + { + "id": "edge-variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1-loop-a4fc4348-44fb-49ea-8236-2acf87569028-external-input", + "source": "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "targetHandle": "external-input" + }, + { + "id": "edge-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-source-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "source": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "target": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "sourceHandle": "internal-source" + }, + { + "id": "edge-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8-condition:66b1bc7d-62cb-4e20-869a-5c2b0000b523-agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "source": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "target": "agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "sourceHandle": "condition:66b1bc7d-62cb-4e20-869a-5c2b0000b523" + }, + { + "id": "edge-agent-7d5d1903-f892-41df-a546-95aef444dbd9-connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9", + "source": "agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "target": "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9" + }, + { + "id": "edge-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8-else-variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4-input", + "source": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "target": "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4", + "sourceHandle": "else", + "targetHandle": "input" + }, + { + "id": "edge-connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9-variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28-input", + "source": "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9", + "target": "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28", + "targetHandle": "input" + }, + { + "id": "edge-loop-a4fc4348-44fb-49ea-8236-2acf87569028-external-output-builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "source": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "target": "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "sourceHandle": "external-output" + }, + { + "id": "edge-builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9-connector-45078208-58d3-45f1-ba7d-b45d24a551e6", + "source": "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "target": "connector-45078208-58d3-45f1-ba7d-b45d24a551e6" + }, + { + "id": "edge-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8-condition:e8648f23-63f8-4da1-a773-1c5b66317e52-connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "source": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "target": "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "sourceHandle": "condition:e8648f23-63f8-4da1-a773-1c5b66317e52" + }, + { + "id": "edge-variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-target", + "source": "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "targetHandle": "internal-target" + }, + { + "id": "edge-connector-444913ae-a97d-4bea-9bc8-6a44d3acb365-variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a", + "source": "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "target": "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a" + }, + { + "id": "edge-variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4-output-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-target", + "source": "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "sourceHandle": "output", + "targetHandle": "internal-target" + }, + { + "id": "edge-canvasNote-4bf9bd4b-1e72-438a-add1-a13e7b9626e7-canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d-input", + "source": "canvasNote-4bf9bd4b-1e72-438a-add1-a13e7b9626e7", + "target": "canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d", + "targetHandle": "input" + }, + { + "id": "edge-canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d-canvasNote-ef40d563-4df7-4aff-b6ec-72bc9a0f5f07-input", + "source": "canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d", + "target": "canvasNote-ef40d563-4df7-4aff-b6ec-72bc9a0f5f07", + "targetHandle": "input" + }, + { + "id": "edge-variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28-output-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-target", + "source": "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "sourceHandle": "output", + "targetHandle": "internal-target" + }, + { + "id": "edge-connector-45078208-58d3-45f1-ba7d-b45d24a551e6-end-2c543046-775a-471f-ab3b-5cc5c32a18bf", + "source": "connector-45078208-58d3-45f1-ba7d-b45d24a551e6", + "target": "end-2c543046-775a-471f-ab3b-5cc5c32a18bf" + } + ] + } + } + ], + "files": [ + { + "path": "[Content_Types].xml", + "category": "other", + "size": 1989 + }, + { + "path": "Assets/botcomponent_connectionreferenceset.xml", + "category": "other", + "size": 1585 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml", + "category": "component", + "size": 892 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data", + "category": "component", + "size": 99 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml", + "category": "component", + "size": 772 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data", + "category": "component", + "size": 104 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml", + "category": "component", + "size": 727 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data", + "category": "component", + "size": 96 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml", + "category": "component", + "size": 797 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data", + "category": "component", + "size": 122 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json", + "category": "component", + "size": 72 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml", + "category": "component", + "size": 784 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data", + "category": "component", + "size": 113 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json", + "category": "component", + "size": 63 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/botcomponent.xml", + "category": "component", + "size": 549 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/data", + "category": "component", + "size": 288 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/botcomponent.xml", + "category": "component", + "size": 593 + }, + { + "path": "botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/data", + "category": "component", + "size": 279 + }, + { + "path": "botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml", + "category": "component", + "size": 594 + }, + { + "path": "botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py", + "category": "component", + "size": 4979 + }, + { + "path": "botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml", + "category": "component", + "size": 604 + }, + { + "path": "botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py", + "category": "component", + "size": 3257 + }, + { + "path": "botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml", + "category": "component", + "size": 561 + }, + { + "path": "botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md", + "category": "component", + "size": 3791 + }, + { + "path": "botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml", + "category": "component", + "size": 556 + }, + { + "path": "botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md", + "category": "component", + "size": 3603 + }, + { + "path": "botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml", + "category": "component", + "size": 553 + }, + { + "path": "botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md", + "category": "component", + "size": 4152 + }, + { + "path": "botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml", + "category": "component", + "size": 562 + }, + { + "path": "botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py", + "category": "component", + "size": 5872 + }, + { + "path": "botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/botcomponent.xml", + "category": "component", + "size": 572 + }, + { + "path": "botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/data", + "category": "component", + "size": 274 + }, + { + "path": "botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/botcomponent.xml", + "category": "component", + "size": 558 + }, + { + "path": "botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/data", + "category": "component", + "size": 273 + }, + { + "path": "bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml", + "category": "bot", + "size": 35406 + }, + { + "path": "bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json", + "category": "bot", + "size": 1179 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml", + "category": "bot", + "size": 35418 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json", + "category": "bot", + "size": 2012 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/bot.xml", + "category": "bot", + "size": 35394 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/configuration.json", + "category": "bot", + "size": 1393 + }, + { + "path": "Connector/new_order-20management-20mcp_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_order-20management-20mcp_customcodeblobcontent.csx", + "category": "other", + "size": 54856 + }, + { + "path": "Connector/new_order-20management-20mcp_openapidefinition.json", + "category": "other", + "size": 1027 + }, + { + "path": "Connector/new_order-20management-20mcp_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx", + "category": "other", + "size": 55320 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json", + "category": "other", + "size": 972 + }, + { + "path": "Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_warehouse-20mcp_connectionparameters.json", + "category": "other", + "size": 2 + }, + { + "path": "Connector/new_warehouse-20mcp_customcodeblobcontent.csx", + "category": "other", + "size": 61348 + }, + { + "path": "Connector/new_warehouse-20mcp_iconblob.Png", + "category": "asset", + "size": 2895 + }, + { + "path": "Connector/new_warehouse-20mcp_openapidefinition.json", + "category": "other", + "size": 1024 + }, + { + "path": "Connector/new_warehouse-20mcp_policytemplateinstances.json", + "category": "other", + "size": 2 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 5766 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 10584 + }, + { + "path": "Workflows/BlastBoxPostSalesService-62D468F3-4A55-1BA5-2EC3-AE1A4B3A4B35.json", + "category": "workflow", + "size": 206895 + }, + { + "path": "Workflows/BlastBoxWeeklyInventoryHealth-44D64F96-1130-9B19-8209-34093D4416A3.json", + "category": "workflow", + "size": 115281 + } + ] +} diff --git a/src/content/solutions/campaign-content-engine.json b/src/content/solutions/campaign-content-engine.json new file mode 100644 index 0000000..b120b12 --- /dev/null +++ b/src/content/solutions/campaign-content-engine.json @@ -0,0 +1,209 @@ +{ + "name": "Campaign Content Engine", + "description": "Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls.", + "tags": [ + "marketing", + "content", + "brand", + "approval", + "demo" + ], + "author": "Maya Chen", + "authorGithub": "mayachen", + "createdAt": "2026-08-07", + "featured": false, + "uniqueName": "Demo_campaign_content_engine", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/campaign-content-engine.zip", + "agentCount": 1, + "workflowCount": 1, + "componentCount": 2, + "agents": [ + { + "id": "campaign_content_engine_agent", + "schemaName": "campaign_content_engine_agent", + "name": "Campaign Content Agent", + "model": "GPT-5", + "instructions": "Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:campaign_content_engine_agent", + "name": "Campaign Content Agent", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "GPT-5", + "instructions": "Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [ + { + "id": "campaign-content-engine-workflow", + "name": "Create campaign content kit", + "trigger": "When a campaign brief is approved", + "nodeCount": 7, + "edgeCount": 6, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "When a campaign brief is approved", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Read brief", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Load brand voice", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Load brand voice", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Draft channels", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Review content", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Review content", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Publish kit", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + ], + "files": [ + { + "path": "bots/campaign_content_engine_agent/bot.xml", + "category": "bot", + "size": 90 + }, + { + "path": "bots/campaign_content_engine_agent/configuration.json", + "category": "bot", + "size": 340 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 219 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 517 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3837 + } + ] +} diff --git a/src/content/solutions/contract-review-assistant.json b/src/content/solutions/contract-review-assistant.json new file mode 100644 index 0000000..199da27 --- /dev/null +++ b/src/content/solutions/contract-review-assistant.json @@ -0,0 +1,194 @@ +{ + "name": "Contract Review Assistant", + "description": "Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval.", + "tags": [ + "document-ops", + "legal", + "compliance", + "human-in-the-loop", + "demo" + ], + "author": "Sofia Alvarez", + "authorGithub": "sofiaalvarez", + "createdAt": "2026-08-08", + "featured": true, + "uniqueName": "Demo_contract_review_assistant", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/contract-review-assistant.zip", + "agentCount": 1, + "workflowCount": 1, + "componentCount": 2, + "agents": [ + { + "id": "contract_review_assistant_agent", + "schemaName": "contract_review_assistant_agent", + "name": "Contract Review Agent", + "model": "Sonnet 4.6", + "instructions": "Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:contract_review_assistant_agent", + "name": "Contract Review Agent", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "Sonnet 4.6", + "instructions": "Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [ + { + "id": "contract-review-assistant-workflow", + "name": "Review contract package", + "trigger": "When a contract is uploaded", + "nodeCount": 6, + "edgeCount": 5, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "When a contract is uploaded", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Extract clauses", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Compare playbook", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Compare playbook", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Score risk", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Request approval", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Request approval", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + ], + "files": [ + { + "path": "bots/contract_review_assistant_agent/bot.xml", + "category": "bot", + "size": 91 + }, + { + "path": "bots/contract_review_assistant_agent/configuration.json", + "category": "bot", + "size": 345 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 217 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 525 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3303 + } + ] +} diff --git a/src/content/solutions/customer-support-triage.json b/src/content/solutions/customer-support-triage.json new file mode 100644 index 0000000..dfa1ed4 --- /dev/null +++ b/src/content/solutions/customer-support-triage.json @@ -0,0 +1,209 @@ +{ + "name": "Customer Support Triage", + "description": "Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue.", + "tags": [ + "support", + "customer-service", + "ai", + "human-in-the-loop", + "demo" + ], + "author": "Maya Chen", + "authorGithub": "mayachen", + "createdAt": "2026-08-13", + "featured": true, + "uniqueName": "Demo_customer_support_triage", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/customer-support-triage.zip", + "agentCount": 1, + "workflowCount": 1, + "componentCount": 2, + "agents": [ + { + "id": "customer_support_triage_agent", + "schemaName": "customer_support_triage_agent", + "name": "Support Triage Agent", + "model": "GPT-5", + "instructions": "Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:customer_support_triage_agent", + "name": "Support Triage Agent", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "GPT-5", + "instructions": "Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [ + { + "id": "customer-support-triage-workflow", + "name": "Triage and route support cases", + "trigger": "When a support case is created", + "nodeCount": 7, + "edgeCount": 6, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "When a support case is created", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Classify case", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Search knowledge", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Search knowledge", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Draft response", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Human review", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Human review", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Update case", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + ], + "files": [ + { + "path": "bots/customer_support_triage_agent/bot.xml", + "category": "bot", + "size": 88 + }, + { + "path": "bots/customer_support_triage_agent/configuration.json", + "category": "bot", + "size": 342 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 222 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 517 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3836 + } + ] +} diff --git a/src/content/solutions/employee-onboarding-guide.json b/src/content/solutions/employee-onboarding-guide.json new file mode 100644 index 0000000..0351e2a --- /dev/null +++ b/src/content/solutions/employee-onboarding-guide.json @@ -0,0 +1,194 @@ +{ + "name": "Employee Onboarding Guide", + "description": "Coordinates a new hire checklist and answers policy, benefits, and first-week questions.", + "tags": [ + "hr", + "onboarding", + "knowledge", + "beginner", + "demo" + ], + "author": "Priya Raman", + "authorGithub": "priyaraman", + "createdAt": "2026-08-10", + "featured": false, + "uniqueName": "Demo_employee_onboarding_guide", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/employee-onboarding-guide.zip", + "agentCount": 1, + "workflowCount": 1, + "componentCount": 2, + "agents": [ + { + "id": "employee_onboarding_guide_agent", + "schemaName": "employee_onboarding_guide_agent", + "name": "Onboarding Guide", + "model": "GPT-4.1", + "instructions": "Coordinates a new hire checklist and answers policy, benefits, and first-week questions.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:employee_onboarding_guide_agent", + "name": "Onboarding Guide", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "GPT-4.1", + "instructions": "Coordinates a new hire checklist and answers policy, benefits, and first-week questions.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [ + { + "id": "employee-onboarding-guide-workflow", + "name": "New hire onboarding", + "trigger": "When an employee joins", + "nodeCount": 6, + "edgeCount": 5, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "When an employee joins", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Create checklist", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Provision access", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Provision access", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Send welcome plan", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Check progress", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Check progress", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + ], + "files": [ + { + "path": "bots/employee_onboarding_guide_agent/bot.xml", + "category": "bot", + "size": 86 + }, + { + "path": "bots/employee_onboarding_guide_agent/configuration.json", + "category": "bot", + "size": 335 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 213 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 525 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3301 + } + ] +} diff --git a/src/content/solutions/field-service-dispatch.json b/src/content/solutions/field-service-dispatch.json new file mode 100644 index 0000000..8d193c1 --- /dev/null +++ b/src/content/solutions/field-service-dispatch.json @@ -0,0 +1,150 @@ +{ + "name": "Field Service Dispatch", + "description": "Prioritizes service requests, finds the right technician, and keeps customers informed of arrival changes.", + "tags": [ + "operations", + "field-service", + "scheduling", + "customer-service", + "demo" + ], + "author": "Amina Yusuf", + "authorGithub": "aminayusuf", + "createdAt": "2026-08-05", + "featured": false, + "uniqueName": "Demo_field_service_dispatch", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/field-service-dispatch.zip", + "agentCount": 0, + "workflowCount": 1, + "componentCount": 1, + "agents": [], + "agentGraph": { + "nodes": [], + "edges": [] + }, + "workflows": [ + { + "id": "field-service-dispatch-workflow", + "name": "Dispatch field service visit", + "trigger": "When a service request is approved", + "nodeCount": 6, + "edgeCount": 5, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "When a service request is approved", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Assess request", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Find technician", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Find technician", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Reserve slot", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Notify customer", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Notify customer", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + ], + "files": [ + { + "path": "customizations.xml", + "category": "solution", + "size": 219 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 439 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3320 + } + ] +} diff --git a/src/content/solutions/incident-response-coordinator.json b/src/content/solutions/incident-response-coordinator.json new file mode 100644 index 0000000..c747c03 --- /dev/null +++ b/src/content/solutions/incident-response-coordinator.json @@ -0,0 +1,209 @@ +{ + "name": "Incident Response Coordinator", + "description": "Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution.", + "tags": [ + "it-ops", + "incident-response", + "teams", + "automation", + "demo" + ], + "author": "Elliot Stone", + "authorGithub": "elliotstone", + "createdAt": "2026-08-09", + "featured": true, + "uniqueName": "Demo_incident_response_coordinator", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/incident-response-coordinator.zip", + "agentCount": 1, + "workflowCount": 1, + "componentCount": 2, + "agents": [ + { + "id": "incident_response_coordinator_agent", + "schemaName": "incident_response_coordinator_agent", + "name": "Incident Coordinator", + "model": "GPT-5", + "instructions": "Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:incident_response_coordinator_agent", + "name": "Incident Coordinator", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "GPT-5", + "instructions": "Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [ + { + "id": "incident-response-coordinator-workflow", + "name": "Coordinate service incident", + "trigger": "When an alert is escalated", + "nodeCount": 7, + "edgeCount": 6, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "When an alert is escalated", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Collect telemetry", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Assess severity", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Assess severity", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Open bridge", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Post updates", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Post updates", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Close incident", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + ], + "files": [ + { + "path": "bots/incident_response_coordinator_agent/bot.xml", + "category": "bot", + "size": 94 + }, + { + "path": "bots/incident_response_coordinator_agent/configuration.json", + "category": "bot", + "size": 351 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 225 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 541 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3831 + } + ] +} diff --git a/src/content/solutions/invoice-exception-handler.json b/src/content/solutions/invoice-exception-handler.json new file mode 100644 index 0000000..d0b992a --- /dev/null +++ b/src/content/solutions/invoice-exception-handler.json @@ -0,0 +1,165 @@ +{ + "name": "Invoice Exception Handler", + "description": "Validates invoice data, resolves routine mismatches, and routes material exceptions for finance review.", + "tags": [ + "finance", + "document-ops", + "automation", + "approval", + "demo" + ], + "author": "Noah Williams", + "authorGithub": "noahwilliams", + "createdAt": "2026-08-06", + "featured": false, + "uniqueName": "Demo_invoice_exception_handler", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/invoice-exception-handler.zip", + "agentCount": 0, + "workflowCount": 1, + "componentCount": 1, + "agents": [], + "agentGraph": { + "nodes": [], + "edges": [] + }, + "workflows": [ + { + "id": "invoice-exception-handler-workflow", + "name": "Resolve invoice exception", + "trigger": "When invoice validation fails", + "nodeCount": 7, + "edgeCount": 6, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "When invoice validation fails", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Read invoice", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Match purchase order", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Match purchase order", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Classify exception", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Request approval", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Request approval", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Post result", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + ], + "files": [ + { + "path": "customizations.xml", + "category": "solution", + "size": 219 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 448 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3851 + } + ] +} diff --git a/src/content/solutions/knowledge-gap-finder.json b/src/content/solutions/knowledge-gap-finder.json new file mode 100644 index 0000000..c9f56b4 --- /dev/null +++ b/src/content/solutions/knowledge-gap-finder.json @@ -0,0 +1,84 @@ +{ + "name": "Knowledge Gap Finder", + "description": "Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines.", + "tags": [ + "knowledge", + "support", + "analytics", + "ai", + "demo" + ], + "author": "Priya Raman", + "authorGithub": "priyaraman", + "createdAt": "2026-08-04", + "featured": false, + "uniqueName": "Demo_knowledge_gap_finder", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/knowledge-gap-finder.zip", + "agentCount": 1, + "workflowCount": 0, + "componentCount": 1, + "agents": [ + { + "id": "knowledge_gap_finder_agent", + "schemaName": "knowledge_gap_finder_agent", + "name": "Knowledge Gap Analyst", + "model": "GPT-5", + "instructions": "Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:knowledge_gap_finder_agent", + "name": "Knowledge Gap Analyst", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "GPT-5", + "instructions": "Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [], + "files": [ + { + "path": "bots/knowledge_gap_finder_agent/bot.xml", + "category": "bot", + "size": 86 + }, + { + "path": "bots/knowledge_gap_finder_agent/configuration.json", + "category": "bot", + "size": 344 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 20 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 430 + } + ] +} diff --git a/src/content/solutions/project-status-narrator.json b/src/content/solutions/project-status-narrator.json new file mode 100644 index 0000000..904f98d --- /dev/null +++ b/src/content/solutions/project-status-narrator.json @@ -0,0 +1,150 @@ +{ + "name": "Project Status Narrator", + "description": "Combines delivery signals into a plain-language status update with risks, decisions, and next actions.", + "tags": [ + "productivity", + "project-management", + "reporting", + "beginner", + "demo" + ], + "author": "Noah Williams", + "authorGithub": "noahwilliams", + "createdAt": "2026-08-01", + "featured": false, + "uniqueName": "Demo_project_status_narrator", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/project-status-narrator.zip", + "agentCount": 0, + "workflowCount": 1, + "componentCount": 1, + "agents": [], + "agentGraph": { + "nodes": [], + "edges": [] + }, + "workflows": [ + { + "id": "project-status-narrator-workflow", + "name": "Generate weekly project update", + "trigger": "Every Friday", + "nodeCount": 6, + "edgeCount": 5, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "Every Friday", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Collect delivery signals", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Summarize progress", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Summarize progress", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Highlight risks", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Send update", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Send update", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + ], + "files": [ + { + "path": "customizations.xml", + "category": "solution", + "size": 222 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 442 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3302 + } + ] +} diff --git a/src/content/solutions/sales-account-brief.json b/src/content/solutions/sales-account-brief.json new file mode 100644 index 0000000..9852b30 --- /dev/null +++ b/src/content/solutions/sales-account-brief.json @@ -0,0 +1,194 @@ +{ + "name": "Sales Account Brief Generator", + "description": "Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief.", + "tags": [ + "sales", + "crm", + "research", + "productivity", + "demo" + ], + "author": "Jon Bell", + "authorGithub": "jonbell", + "createdAt": "2026-08-12", + "featured": true, + "uniqueName": "Demo_sales_account_brief", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/sales-account-brief.zip", + "agentCount": 1, + "workflowCount": 1, + "componentCount": 2, + "agents": [ + { + "id": "sales_account_brief_agent", + "schemaName": "sales_account_brief_agent", + "name": "Account Research Agent", + "model": "Sonnet 4.6", + "instructions": "Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:sales_account_brief_agent", + "name": "Account Research Agent", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "Sonnet 4.6", + "instructions": "Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [ + { + "id": "sales-account-brief-workflow", + "name": "Prepare account brief", + "trigger": "Before a customer meeting", + "nodeCount": 6, + "edgeCount": 5, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "Before a customer meeting", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Load CRM account", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Find recent signals", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Find recent signals", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Summarize risks", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Create brief", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Create brief", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + ], + "files": [ + { + "path": "bots/sales_account_brief_agent/bot.xml", + "category": "bot", + "size": 86 + }, + { + "path": "bots/sales_account_brief_agent/configuration.json", + "category": "bot", + "size": 346 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 209 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 511 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3307 + } + ] +} diff --git a/src/content/solutions/security-access-review.json b/src/content/solutions/security-access-review.json new file mode 100644 index 0000000..abbcbc0 --- /dev/null +++ b/src/content/solutions/security-access-review.json @@ -0,0 +1,194 @@ +{ + "name": "Security Access Review", + "description": "Explains access anomalies, gathers manager evidence, and produces an auditable review decision.", + "tags": [ + "security", + "it-ops", + "compliance", + "approval", + "demo" + ], + "author": "Elliot Stone", + "authorGithub": "elliotstone", + "createdAt": "2026-08-03", + "featured": false, + "uniqueName": "Demo_security_access_review", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/security-access-review.zip", + "agentCount": 1, + "workflowCount": 1, + "componentCount": 2, + "agents": [ + { + "id": "security_access_review_agent", + "schemaName": "security_access_review_agent", + "name": "Access Review Analyst", + "model": "Sonnet 4.6", + "instructions": "Explains access anomalies, gathers manager evidence, and produces an auditable review decision.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:security_access_review_agent", + "name": "Access Review Analyst", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "Sonnet 4.6", + "instructions": "Explains access anomalies, gathers manager evidence, and produces an auditable review decision.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [ + { + "id": "security-access-review-workflow", + "name": "Quarterly access review", + "trigger": "On a quarterly schedule", + "nodeCount": 6, + "edgeCount": 5, + "graph": { + "nodes": [ + { + "id": "node-1", + "name": "On a quarterly schedule", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "details": {} + }, + { + "id": "node-2", + "name": "Load access list", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "details": {} + }, + { + "id": "node-3", + "name": "Detect anomalies", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "details": { + "displayName": "Detect anomalies", + "operationType": "action" + } + }, + { + "id": "node-4", + "name": "Collect evidence", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "details": {} + }, + { + "id": "node-5", + "name": "Record decision", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "details": { + "displayName": "Record decision", + "operationType": "action" + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "details": {} + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + ], + "files": [ + { + "path": "bots/security_access_review_agent/bot.xml", + "category": "bot", + "size": 88 + }, + { + "path": "bots/security_access_review_agent/configuration.json", + "category": "bot", + "size": 345 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 214 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 513 + }, + { + "path": "Workflows/workflow.json", + "category": "workflow", + "size": 3307 + } + ] +} diff --git a/src/content/solutions/store-feedback-analyzer.json b/src/content/solutions/store-feedback-analyzer.json new file mode 100644 index 0000000..48d453d --- /dev/null +++ b/src/content/solutions/store-feedback-analyzer.json @@ -0,0 +1,84 @@ +{ + "name": "Store Feedback Analyzer", + "description": "Summarizes store feedback, detects recurring product issues, and prepares action themes for operations.", + "tags": [ + "retail", + "analytics", + "customer-service", + "beginner", + "demo" + ], + "author": "Amina Yusuf", + "authorGithub": "aminayusuf", + "createdAt": "2026-08-02", + "featured": false, + "uniqueName": "Demo_store_feedback_analyzer", + "version": "1.0.0.0", + "publisher": "CAT Gallery Demos", + "bundle": "bundles/store-feedback-analyzer.zip", + "agentCount": 1, + "workflowCount": 0, + "componentCount": 1, + "agents": [ + { + "id": "store_feedback_analyzer_agent", + "schemaName": "store_feedback_analyzer_agent", + "name": "Feedback Insights Agent", + "model": "GPT-4.1", + "instructions": "Summarizes store feedback, detects recurring product issues, and prepares action themes for operations.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:store_feedback_analyzer_agent", + "name": "Feedback Insights Agent", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "GPT-4.1", + "instructions": "Summarizes store feedback, detects recurring product issues, and prepares action themes for operations.", + "channels": [ + "MsTeams" + ], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + } + ], + "edges": [] + }, + "workflows": [], + "files": [ + { + "path": "bots/store_feedback_analyzer_agent/bot.xml", + "category": "bot", + "size": 91 + }, + { + "path": "bots/store_feedback_analyzer_agent/configuration.json", + "category": "bot", + "size": 350 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 20 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 439 + } + ] +} diff --git a/src/content/solutions/store-modern-agent.json b/src/content/solutions/store-modern-agent.json new file mode 100644 index 0000000..b554ad8 --- /dev/null +++ b/src/content/solutions/store-modern-agent.json @@ -0,0 +1,256 @@ +{ + "name": "Store Modern Agent", + "description": "A compact retail assistant that delegates inventory and policy questions while keeping reusable store skills close at hand.", + "tags": [ + "retail", + "customer-service", + "multi-agent", + "inventory", + "policy" + ], + "author": "BlastBox team", + "featured": false, + "uniqueName": "StoreModernAgent", + "version": "1.0.0.1", + "publisher": "CAT", + "bundle": "bundles/store-modern-agent.zip", + "agentCount": 3, + "workflowCount": 0, + "componentCount": 16, + "agents": [ + { + "id": "Default_InventoryFullfilmentAgent_X-w2GP", + "schemaName": "Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "model": "Sonnet46", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "Default_StorePolicyAgent_s9s-u8", + "schemaName": "Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "model": "Sonnet46", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + }, + { + "id": "Default_draft_IsBewO", + "schemaName": "Default_draft_IsBewO", + "name": "Returns & Service Assistant", + "model": "Sonnet46", + "channels": [], + "toolCount": 0, + "skillCount": 2, + "connectedAgentCount": 2 + } + ], + "agentGraph": { + "nodes": [ + { + "id": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "name": "Inventory & Fullfilment Agent", + "type": "agent", + "position": { + "x": -111.99999999999997, + "y": -396 + }, + "details": { + "model": "Sonnet46", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "agent:Default_draft_IsBewO", + "name": "Returns & Service Assistant", + "type": "agent", + "position": { + "x": -112, + "y": -36 + }, + "details": { + "model": "Sonnet46", + "channels": [], + "toolCount": 0, + "skillCount": 2, + "connectedAgentCount": 2 + } + }, + { + "id": "agent:Default_StorePolicyAgent_s9s-u8", + "name": "Store Policy Agent", + "type": "agent", + "position": { + "x": 248, + "y": -36 + }, + "details": { + "model": "Sonnet46", + "channels": [], + "toolCount": 0, + "skillCount": 0, + "connectedAgentCount": 0 + } + }, + { + "id": "skill:Default_draft_IsBewO.skill.skill1", + "name": "skill1", + "type": "skill", + "position": { + "x": -111.99999999999999, + "y": 184 + }, + "details": { + "kind": "skill", + "schemaName": "Default_draft_IsBewO.skill.skill1", + "description": "skill1" + } + }, + { + "id": "skill:Default_draft_IsBewO.skill.skill2", + "name": "skill2", + "type": "skill", + "position": { + "x": -332, + "y": -35.99999999999997 + }, + "details": { + "kind": "skill", + "schemaName": "Default_draft_IsBewO.skill.skill2", + "description": "skill2" + } + } + ], + "edges": [ + { + "id": "agent:Default_draft_IsBewO-delegates-Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP", + "source": "agent:Default_draft_IsBewO", + "target": "agent:Default_InventoryFullfilmentAgent_X-w2GP", + "label": "delegates" + }, + { + "id": "agent:Default_draft_IsBewO-delegates-Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8", + "source": "agent:Default_draft_IsBewO", + "target": "agent:Default_StorePolicyAgent_s9s-u8", + "label": "delegates" + }, + { + "id": "agent:Default_draft_IsBewO-includes-Default_draft_IsBewO.skill.skill1", + "source": "agent:Default_draft_IsBewO", + "target": "skill:Default_draft_IsBewO.skill.skill1", + "label": "includes" + }, + { + "id": "agent:Default_draft_IsBewO-includes-Default_draft_IsBewO.skill.skill2", + "source": "agent:Default_draft_IsBewO", + "target": "skill:Default_draft_IsBewO.skill.skill2", + "label": "includes" + } + ] + }, + "workflows": [], + "files": [ + { + "path": "[Content_Types].xml", + "category": "other", + "size": 811 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.skill1/botcomponent.xml", + "category": "component", + "size": 356 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.skill1/data", + "category": "component", + "size": 584 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.skill2/botcomponent.xml", + "category": "component", + "size": 356 + }, + { + "path": "botcomponents/Default_draft_IsBewO.skill.skill2/data", + "category": "component", + "size": 584 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml", + "category": "component", + "size": 459 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data", + "category": "component", + "size": 124 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json", + "category": "component", + "size": 72 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml", + "category": "component", + "size": 420 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data", + "category": "component", + "size": 115 + }, + { + "path": "botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json", + "category": "component", + "size": 63 + }, + { + "path": "bots/Default_draft_IsBewO/bot.xml", + "category": "bot", + "size": 35396 + }, + { + "path": "bots/Default_draft_IsBewO/configuration.json", + "category": "bot", + "size": 413 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml", + "category": "bot", + "size": 35418 + }, + { + "path": "bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json", + "category": "bot", + "size": 300 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/bot.xml", + "category": "bot", + "size": 35394 + }, + { + "path": "bots/Default_StorePolicyAgent_s9s-u8/configuration.json", + "category": "bot", + "size": 300 + }, + { + "path": "customizations.xml", + "category": "solution", + "size": 551 + }, + { + "path": "solution.xml", + "category": "solution", + "size": 3971 + } + ] +} diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro new file mode 100644 index 0000000..f289e11 --- /dev/null +++ b/src/layouts/Layout.astro @@ -0,0 +1,79 @@ +--- +import "../styles/global.css"; +import { withBase } from "../lib/url"; + +interface Props { + title?: string; + description?: string; +} + +const { + title = "CAT Agents & Workflows Gallery", + description = "Discover community-built Copilot Studio solutions through their agents and workflow canvases.", +} = Astro.props; +const fullTitle = + title === "CAT Agents & Workflows Gallery" + ? title + : `${title} · CAT Agents & Workflows Gallery`; +--- + + + + + + + + + {fullTitle} + + + + + + + +
+ +
+
+ +
+
+ +
+ + + diff --git a/src/lib/catalog.ts b/src/lib/catalog.ts new file mode 100644 index 0000000..5a046a5 --- /dev/null +++ b/src/lib/catalog.ts @@ -0,0 +1,64 @@ +import type { CollectionEntry } from "astro:content"; +import { authorKey } from "./schema"; + +export type SolutionEntry = CollectionEntry<"solutions">; + +export interface ContributorSummary { + key: string; + name: string; + github?: string; + url?: string; + count: number; + agents: number; + workflows: number; + featured: number; + latest?: string; + solutions: SolutionEntry[]; +} + +export function collectTags(solutions: SolutionEntry[]): Array<[string, number]> { + const counts = new Map(); + for (const solution of solutions) { + for (const tag of solution.data.tags) { + counts.set(tag, (counts.get(tag) ?? 0) + 1); + } + } + return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])); +} + +export function collectContributors(solutions: SolutionEntry[]): ContributorSummary[] { + const contributors = new Map(); + for (const solution of solutions) { + const key = authorKey(solution.data.authorGithub, solution.data.author); + const existing = contributors.get(key); + const updated = solution.data.updatedAt ?? solution.data.createdAt; + if (existing) { + existing.count += 1; + existing.agents += solution.data.agentCount; + existing.workflows += solution.data.workflowCount; + existing.featured += solution.data.featured ? 1 : 0; + if (updated && (!existing.latest || updated > existing.latest)) existing.latest = updated; + existing.solutions.push(solution); + continue; + } + contributors.set(key, { + key, + name: solution.data.author, + ...(solution.data.authorGithub ? { github: solution.data.authorGithub } : {}), + ...(solution.data.authorUrl ? { url: solution.data.authorUrl } : {}), + count: 1, + agents: solution.data.agentCount, + workflows: solution.data.workflowCount, + featured: solution.data.featured ? 1 : 0, + ...(updated ? { latest: updated } : {}), + solutions: [solution], + }); + } + + return [...contributors.values()] + .map((contributor) => ({ + ...contributor, + solutions: contributor.solutions.sort((a, b) => a.data.name.localeCompare(b.data.name)), + })) + .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name)); +} diff --git a/src/lib/graphVisuals.ts b/src/lib/graphVisuals.ts new file mode 100644 index 0000000..54398c5 --- /dev/null +++ b/src/lib/graphVisuals.ts @@ -0,0 +1,57 @@ +import type { GraphNode } from "./schema"; + +export type GraphVisualKind = + | "trigger" + | "ai" + | "decision" + | "loop" + | "data" + | "communication" + | "approval" + | "content" + | "action" + | "tool" + | "skill" + | "end" + | "note"; + +const KEYWORDS: Array<[GraphVisualKind, RegExp]> = [ + ["approval", /\b(approve|approval|human|review|escalat|decision)\b/i], + ["communication", /\b(send|notify|message|email|post|channel|teams|welcome|inform|update)\b/i], + ["content", /\b(draft|summar|extract|document|brief|report|article|content|response|narrat)\b/i], + ["data", /\b(search|find|load|collect|get|fetch|query|telemetry|knowledge|research|crm|signal)\b/i], + ["action", /\b(create|route|assign|open|close|provision|schedule|classify|validate|resolve|priorit)\b/i], +]; + +export function graphVisualKind(node: GraphNode): GraphVisualKind { + switch (node.type) { + case "start": + return "trigger"; + case "end": + return "end"; + case "agent": + case "classifyOnInlineAgent": + return "ai"; + case "ifElse": + return "decision"; + case "loop": + return "loop"; + case "variable": + case "builtinFunction": + return "data"; + case "tool": + return "tool"; + case "skill": + return "skill"; + case "canvasNote": + return "note"; + default: { + const text = `${node.name} ${node.summary ?? ""}`; + return KEYWORDS.find(([, pattern]) => pattern.test(text))?.[0] ?? "action"; + } + } +} + +export function graphVisualColor(kind: GraphVisualKind): string { + return `var(--graph-${kind})`; +} diff --git a/src/lib/schema.ts b/src/lib/schema.ts new file mode 100644 index 0000000..7096e3e --- /dev/null +++ b/src/lib/schema.ts @@ -0,0 +1,119 @@ +import { z } from "astro/zod"; + +export const submissionMetadataSchema = z.object({ + name: z.string().trim().min(1), + description: z.string().trim().min(1).max(320), + tags: z.array(z.string().trim().min(1).max(40)).min(1), + author: z.string().trim().min(1), + authorUrl: z.url().optional(), + authorGithub: z + .string() + .regex(/^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/i) + .optional(), + createdAt: z.iso.date().optional(), + updatedAt: z.iso.date().optional(), + featured: z.boolean().default(false), +}); + +export const graphPositionSchema = z.object({ + x: z.number(), + y: z.number(), +}); + +export const graphNodeSchema = z.object({ + id: z.string(), + name: z.string(), + type: z.string(), + position: graphPositionSchema, + summary: z.string().optional(), + details: z.record(z.string(), z.unknown()).default({}), +}); + +export const graphEdgeSchema = z.object({ + id: z.string(), + source: z.string(), + target: z.string(), + sourceHandle: z.string().optional(), + targetHandle: z.string().optional(), + label: z.string().optional(), +}); + +export const graphSchema = z.object({ + nodes: z.array(graphNodeSchema), + edges: z.array(graphEdgeSchema), +}); + +export const agentSchema = z.object({ + id: z.string(), + schemaName: z.string(), + name: z.string(), + model: z.string().optional(), + instructions: z.string().optional(), + channels: z.array(z.string()).default([]), + toolCount: z.number().int().nonnegative(), + skillCount: z.number().int().nonnegative(), + connectedAgentCount: z.number().int().nonnegative(), +}); + +export const workflowSchema = z.object({ + id: z.string(), + name: z.string(), + trigger: z.string().optional(), + nodeCount: z.number().int().nonnegative(), + edgeCount: z.number().int().nonnegative(), + graph: graphSchema, +}); + +export const fileSummarySchema = z.object({ + path: z.string(), + category: z.string(), + size: z.number().int().nonnegative(), +}); + +export const solutionSchema = z.object({ + name: z.string(), + description: z.string(), + tags: z.array(z.string()).min(1), + author: z.string(), + authorUrl: z.url().optional(), + authorGithub: z.string().optional(), + createdAt: z.iso.date().optional(), + updatedAt: z.iso.date().optional(), + featured: z.boolean().default(false), + uniqueName: z.string(), + version: z.string(), + publisher: z.string().optional(), + bundle: z.string(), + agentCount: z.number().int().nonnegative(), + workflowCount: z.number().int().nonnegative(), + componentCount: z.number().int().nonnegative(), + agents: z.array(agentSchema), + agentGraph: graphSchema, + workflows: z.array(workflowSchema), + files: z.array(fileSummarySchema), +}); + +export type SubmissionMetadata = z.infer; +export type GraphPosition = z.infer; +export type GraphNode = z.infer; +export type GraphEdge = z.infer; +export type GraphData = z.infer; +export type AgentPreview = z.infer; +export type WorkflowPreview = z.infer; +export type FileSummary = z.infer; +export type SolutionRecord = z.infer; + +export function authorKey(github: string | undefined, author: string): string { + if (github) return github.toLowerCase(); + return author + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-|-$/g, ""); +} + +export function solutionMakeup(agentCount: number, workflowCount: number): string { + if (agentCount > 0 && workflowCount > 0) return "Agents + workflows"; + if (agentCount > 0) return "Agents"; + return "Workflows"; +} diff --git a/src/lib/url.ts b/src/lib/url.ts new file mode 100644 index 0000000..d368cd8 --- /dev/null +++ b/src/lib/url.ts @@ -0,0 +1,6 @@ +const base = import.meta.env.BASE_URL.replace(/\/$/, ""); + +export function withBase(path: string): string { + const normalized = path.startsWith("/") ? path : `/${path}`; + return `${base}${normalized}` || "/"; +} diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..a8ea2e9 --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,15 @@ +--- +import Layout from "../layouts/Layout.astro"; +import { withBase } from "../lib/url"; +--- + + +
+
+

404 · Route ended

+

This path is not in the graph.

+

Return to the gallery to browse published solutions.

+ Back to gallery +
+
+
diff --git a/src/pages/contributors.astro b/src/pages/contributors.astro new file mode 100644 index 0000000..e5921a9 --- /dev/null +++ b/src/pages/contributors.astro @@ -0,0 +1,104 @@ +--- +import { getCollection } from "astro:content"; +import Layout from "../layouts/Layout.astro"; +import { collectContributors } from "../lib/catalog"; +import { withBase } from "../lib/url"; + +const contributors = collectContributors(await getCollection("solutions")); +const solutionTotal = contributors.reduce((total, contributor) => total + contributor.count, 0); +const MAX_SOLUTIONS = 8; +--- + + +
+
+
+

Community

+

Contributors

+

People and teams sharing complete Copilot Studio solutions with the community.

+

{contributors.length} contributors · {solutionTotal} solutions

+
+ +
+ +
+

All contributors

+ {contributors.length} shown +
+ + + + +
+
+ + diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..193f0ae --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,538 @@ +--- +import { getCollection } from "astro:content"; +import Layout from "../layouts/Layout.astro"; +import SolutionCard from "../components/SolutionCard.astro"; +import { collectContributors, collectTags } from "../lib/catalog"; +import { authorKey, solutionMakeup } from "../lib/schema"; +import { withBase } from "../lib/url"; + +const solutions = (await getCollection("solutions")).sort( + (a, b) => Number(b.data.featured) - Number(a.data.featured) || a.data.name.localeCompare(b.data.name), +); +const tags = collectTags(solutions); +const popularTags = tags.slice(0, 8); +const contributors = collectContributors(solutions); +const featuredSolutions = solutions.filter((solution) => solution.data.featured); +--- + + +
+ +
+ +
+ +
+ +
+ +
+
+ + diff --git a/src/pages/solutions.json.ts b/src/pages/solutions.json.ts new file mode 100644 index 0000000..6702685 --- /dev/null +++ b/src/pages/solutions.json.ts @@ -0,0 +1,24 @@ +import type { APIRoute } from "astro"; +import { getCollection } from "astro:content"; +import { solutionMakeup } from "../lib/schema"; + +export const GET: APIRoute = async () => { + const solutions = await getCollection("solutions"); + const payload = solutions + .map((solution) => ({ + slug: solution.id, + name: solution.data.name, + description: solution.data.description, + tags: solution.data.tags, + author: solution.data.author, + version: solution.data.version, + makeup: solutionMakeup(solution.data.agentCount, solution.data.workflowCount), + agentCount: solution.data.agentCount, + workflowCount: solution.data.workflowCount, + featured: solution.data.featured, + })) + .sort((a, b) => Number(b.featured) - Number(a.featured) || a.name.localeCompare(b.name)); + return new Response(JSON.stringify(payload, null, 2), { + headers: { "Content-Type": "application/json; charset=utf-8" }, + }); +}; diff --git a/src/pages/solutions/[slug].astro b/src/pages/solutions/[slug].astro new file mode 100644 index 0000000..122ffa3 --- /dev/null +++ b/src/pages/solutions/[slug].astro @@ -0,0 +1,200 @@ +--- +import { getCollection, render } from "astro:content"; +import Layout from "../../layouts/Layout.astro"; +import GraphCanvas from "../../components/graph/GraphCanvas"; +import WorkflowExplorer from "../../components/graph/WorkflowExplorer"; +import { authorKey, solutionMakeup } from "../../lib/schema"; +import { withBase } from "../../lib/url"; + +export async function getStaticPaths() { + const solutions = await getCollection("solutions"); + return solutions.map((solution) => ({ + params: { slug: solution.id }, + props: { solution }, + })); +} + +const { solution } = Astro.props; +const data = solution.data; +const guides = await getCollection("guides"); +const guide = guides.find((entry) => entry.id === solution.id); +const GuideContent = guide ? (await render(guide)).Content : null; +const makeup = solutionMakeup(data.agentCount, data.workflowCount); +const contributorKey = authorKey(data.authorGithub, data.author); +const updated = data.updatedAt ?? data.createdAt; +const categoryCounts = new Map(); +for (const file of data.files) categoryCounts.set(file.category, (categoryCounts.get(file.category) ?? 0) + 1); +const categories = [...categoryCounts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])); +const heroGraph = data.workflows[0]?.graph ?? data.agentGraph; +--- + + + + + + diff --git a/src/pages/tags/[tag].astro b/src/pages/tags/[tag].astro new file mode 100644 index 0000000..7ba4df2 --- /dev/null +++ b/src/pages/tags/[tag].astro @@ -0,0 +1,40 @@ +--- +import { getCollection } from "astro:content"; +import Layout from "../../layouts/Layout.astro"; +import SolutionCard from "../../components/SolutionCard.astro"; +import { collectTags } from "../../lib/catalog"; +import { withBase } from "../../lib/url"; + +export async function getStaticPaths() { + const solutions = await getCollection("solutions"); + return collectTags(solutions).map(([tag]) => ({ + params: { tag }, + props: { + tag, + solutions: solutions + .filter((solution) => solution.data.tags.includes(tag)) + .sort((a, b) => Number(b.data.featured) - Number(a.data.featured) || a.data.name.localeCompare(b.data.name)), + }, + })); +} + +const { tag, solutions } = Astro.props; +--- + + +
+ + All solutions + +

Tag collection

+

+ #{tag} +

+

+ {solutions.length} solution{solutions.length === 1 ? "" : "s"} tagged “{tag}”. +

+
+ {solutions.map((solution) => )} +
+
+
diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..4081fa8 --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,333 @@ +@import "tailwindcss"; +@plugin "@tailwindcss/typography"; + +@theme { + --color-page: var(--cp-bg); + --color-elevated: var(--cp-bg-elevated); + --color-surface: var(--cp-surface); + --color-surface-soft: var(--cp-surface-soft); + --color-border: var(--cp-border); + --color-border-strong: var(--cp-border-strong); + --color-ink: var(--cp-text); + --color-muted: var(--cp-text-muted); + --color-soft: var(--cp-text-soft); + --color-accent: var(--cp-accent); + --color-accent-hover: var(--cp-accent-hover); + --color-accent-soft: var(--cp-accent-soft); + --color-accent-fg: var(--cp-accent-fg); + --color-success: var(--cp-success); + --color-danger: var(--cp-danger); + --color-warning: var(--cp-warning); + --color-link: var(--cp-link); + --font-sans: "Segoe UI Variable Text", "Segoe UI", Aptos, -apple-system, BlinkMacSystemFont, sans-serif; + --font-mono: "Cascadia Code", "SFMono-Regular", Consolas, monospace; + --container-gallery: 88rem; +} + +@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *)); + +:root { + color-scheme: light; + --cp-bg: #f5f5f5; + --cp-bg-elevated: #fafafa; + --cp-surface: #ffffff; + --cp-surface-soft: #f0f0f0; + --cp-border: #d1d1d1; + --cp-border-strong: #8a8886; + --cp-text: #242424; + --cp-text-muted: #616161; + --cp-text-soft: #707070; + --cp-accent: #5b5fc7; + --cp-accent-hover: #4f52b2; + --cp-accent-soft: #efefff; + --cp-accent-fg: #ffffff; + --cp-coral: #d84a4a; + --cp-violet: #8b5cf6; + --cp-magenta: #c94f9d; + --cp-success: #107c10; + --cp-danger: #c50f1f; + --cp-warning: #f7630c; + --cp-link: #115ea3; + --cp-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); + --cp-overlay: rgba(255, 255, 255, 0.82); + --cp-panel: rgba(255, 255, 255, 0.88); + --cp-panel-strong: rgba(255, 255, 255, 0.98); + --cp-sheen: rgba(255, 255, 255, 0.72); + --cp-highlight: rgba(91, 95, 199, 0.16); + --cp-canvas-glow: rgba(139, 92, 246, 0.12); + --cp-coral-glow: rgba(232, 110, 99, 0.12); + --cp-brand-gradient: linear-gradient(112deg, #4f6bed 4%, #7856d8 43%, #c94f9d 72%, #e86e63 100%); + --graph-trigger: #107c10; + --graph-ai: #5b5fc7; + --graph-decision: #c94f9d; + --graph-loop: #8b5cf6; + --graph-data: #115ea3; + --graph-communication: #0078d4; + --graph-approval: #c50f1f; + --graph-content: #8764b8; + --graph-action: #ca5010; + --graph-tool: #6b69d6; + --graph-skill: #008272; + --graph-end: #707070; + --graph-note: #986f0b; +} + +html[data-theme="dark"] { + color-scheme: dark; + --cp-bg: #292929; + --cp-bg-elevated: #2f2f2f; + --cp-surface: #333333; + --cp-surface-soft: #3b3a39; + --cp-border: #484848; + --cp-border-strong: #707070; + --cp-text: #f5f5f5; + --cp-text-muted: #d1d1d1; + --cp-text-soft: #adadad; + --cp-accent: #9295f5; + --cp-accent-hover: #a7a9ff; + --cp-accent-soft: rgba(126, 129, 232, 0.2); + --cp-accent-fg: #171717; + --cp-coral: #ff7b78; + --cp-violet: #b69cff; + --cp-magenta: #ed86c5; + --cp-success: #4ade80; + --cp-danger: #f87171; + --cp-warning: #fce100; + --cp-link: #75b6e7; + --cp-shadow: 0 10px 28px rgba(0, 0, 0, 0.34); + --cp-overlay: rgba(41, 41, 41, 0.86); + --cp-panel: rgba(51, 51, 51, 0.9); + --cp-panel-strong: rgba(51, 51, 51, 0.98); + --cp-sheen: rgba(255, 255, 255, 0.04); + --cp-highlight: rgba(146, 149, 245, 0.2); + --cp-canvas-glow: rgba(139, 92, 246, 0.15); + --cp-coral-glow: rgba(255, 123, 120, 0.1); + --cp-brand-gradient: linear-gradient(112deg, #7890ff 4%, #9d7af2 43%, #e074b8 72%, #ff8178 100%); + --graph-trigger: #54b054; + --graph-ai: #9295f5; + --graph-decision: #ed86c5; + --graph-loop: #b69cff; + --graph-data: #75b6e7; + --graph-communication: #60a5fa; + --graph-approval: #ff7b78; + --graph-content: #c6a7e2; + --graph-action: #f0a15f; + --graph-tool: #aaa7ff; + --graph-skill: #5dd5c8; + --graph-end: #adadad; + --graph-note: #f5d47a; +} + +* { + box-sizing: border-box; +} + +html { + background: var(--cp-bg); + scroll-behavior: smooth; +} + +body { + min-width: 20rem; + margin: 0; + background: var(--cp-bg); + color: var(--cp-text); + font-family: "Segoe UI Variable Text", "Segoe UI", Aptos, -apple-system, BlinkMacSystemFont, sans-serif; + font-feature-settings: "ss01" 1, "cv05" 1; +} + +h1, +h2 { + font-family: "Segoe UI Variable Display", "Segoe UI", Aptos, -apple-system, BlinkMacSystemFont, sans-serif; +} + +button, +input, +select, +textarea { + font: inherit; +} + +a { + color: inherit; +} + +:focus-visible { + outline: 2px solid var(--cp-accent); + outline-offset: 2px; +} + +::selection { + background: var(--cp-highlight); +} + +::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +::-webkit-scrollbar-track { + background: var(--cp-surface-soft); +} + +::-webkit-scrollbar-thumb { + border: 2px solid var(--cp-surface-soft); + border-radius: 9999px; + background: var(--cp-border-strong); +} + +.card-shadow { + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); +} + +.studio-mark { + position: relative; + isolation: isolate; + overflow: hidden; + background: var(--cp-brand-gradient); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35), 0 2px 5px rgba(78, 70, 180, 0.24); +} + +.studio-mark::before, +.studio-mark::after { + position: absolute; + content: ""; + border-radius: 38%; + background: rgba(255, 255, 255, 0.72); + mix-blend-mode: soft-light; + transform: rotate(32deg); +} + +.studio-mark::before { + inset: 15% 48% 15% -8%; +} + +.studio-mark::after { + inset: 15% -8% 15% 48%; +} + +.studio-gradient-text { + background: var(--cp-brand-gradient); + background-clip: text; + color: transparent; + -webkit-background-clip: text; +} + +.studio-primary-action { + background: var(--cp-accent); + color: var(--cp-accent-fg); +} + +.studio-primary-action:hover { + background: var(--cp-accent-hover); + color: var(--cp-accent-fg); +} + +.studio-canvas { + background: + radial-gradient(circle at 16% 88%, var(--cp-canvas-glow), transparent 35%), + radial-gradient(circle at 88% 8%, var(--cp-coral-glow), transparent 30%), + linear-gradient(145deg, var(--cp-surface), var(--cp-bg-elevated)); +} + +.studio-grid { + background-image: radial-gradient(var(--cp-border) 0.7px, transparent 0.7px); + background-size: 18px 18px; +} + +.product-card { + transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; +} + +.featured-carousel { + scrollbar-width: none; +} + +.featured-carousel::-webkit-scrollbar { + display: none; +} + +.featured-slide { + width: 86%; + flex: 0 0 auto; +} + +@media (min-width: 40rem) { + .featured-slide { + width: calc((100% - 1rem) / 2); + } +} + +@media (min-width: 80rem) { + .featured-slide { + width: calc((100% - 3rem) / 4); + } +} + +.product-card:hover { + border-color: var(--cp-border-strong); + box-shadow: 0 8px 22px rgba(0, 0, 0, 0.11); + transform: translateY(-2px); +} + +html[data-theme="dark"] .product-card:hover { + box-shadow: 0 10px 26px rgba(0, 0, 0, 0.3); +} + +input, +select { + transition: border-color 140ms ease, box-shadow 140ms ease, background-color 140ms ease; +} + +input:hover, +select:hover { + border-color: var(--cp-border-strong); +} + +input:focus, +select:focus { + border-color: var(--cp-accent); + box-shadow: 0 0 0 2px var(--cp-highlight); + outline: none; +} + +.prose { + color: var(--cp-text-muted); +} + +.prose :where(h1, h2, h3, h4, strong) { + color: var(--cp-text); +} + +.prose :where(a) { + color: var(--cp-link); +} + +.prose :where(code) { + color: var(--cp-accent); +} + +.prose :where(pre) { + max-width: 100%; + overflow-x: auto; + border: 1px solid var(--cp-border); + background: var(--cp-surface-soft); + color: var(--cp-text); +} + +.prose :where(table) { + display: block; + max-width: 100%; + overflow-x: auto; +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + } +} diff --git a/submissions/README.md b/submissions/README.md new file mode 100644 index 0000000..7a1c890 --- /dev/null +++ b/submissions/README.md @@ -0,0 +1,20 @@ +# Submit an exploded solution + +Every gallery entry is one `submissions//` folder containing: + +```text +metadata.json +README.md # optional +solution/ # complete exploded Power Platform solution +``` + +The `solution/` directory must contain `solution.xml` and `customizations.xml`. +It may include agents, workflows, connectors, connection references, code, and +other normal solution assets. + +The importer derives the published agent and workflow previews and rebuilds a +deterministic `.zip` whose archive root is the contents of `solution/`. +Gallery sidecars are excluded. + +See the repository [contribution guide](../CONTRIBUTING.md) for metadata fields, +validation rules, and local commands. diff --git a/submissions/_template/README.md b/submissions/_template/README.md new file mode 100644 index 0000000..efff59e --- /dev/null +++ b/submissions/_template/README.md @@ -0,0 +1,17 @@ +# Solution display name + +Explain the business scenario and the outcome this solution supports. + +## Agents + +Describe the primary and specialist agents, tools, skills, and delegation model. + +## Workflows + +Describe each workflow's trigger, important branches, loops, approvals, and +human-in-the-loop decisions. + +## Import notes + +List prerequisites and the connections or environment variables an importer must +configure. diff --git a/submissions/_template/metadata.json b/submissions/_template/metadata.json new file mode 100644 index 0000000..c4bd157 --- /dev/null +++ b/submissions/_template/metadata.json @@ -0,0 +1,10 @@ +{ + "name": "Solution display name", + "description": "A concise summary of what the agents and workflows accomplish.", + "tags": ["replace-me"], + "author": "Your name", + "authorUrl": "https://github.com/your-login", + "createdAt": "2026-08-12", + "updatedAt": "2026-08-12", + "featured": false +} diff --git a/submissions/_template/solution/.gitkeep b/submissions/_template/solution/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/submissions/blastbox-demo-agents/README.md b/submissions/blastbox-demo-agents/README.md new file mode 100644 index 0000000..de2cf1f --- /dev/null +++ b/submissions/blastbox-demo-agents/README.md @@ -0,0 +1,17 @@ +# BlastBox Demo Agent Team + +This solution demonstrates how a retail assistant can coordinate a complete store +conversation while relying on specialist agents for inventory and policy. + +## Agents + +The primary store assistant delegates focused questions to inventory and policy +agents. Its supporting capabilities include membership and warehouse MCP tools, +points reconciliation, prorated refund calculation, card reissue, and PDF slip +generation. + +## Import + +Download the rebuilt solution ZIP and import it through Power Platform. Review +the included MCP connections and replace environment-specific configuration +before publishing the agents. diff --git a/submissions/blastbox-demo-agents/metadata.json b/submissions/blastbox-demo-agents/metadata.json new file mode 100644 index 0000000..65907b7 --- /dev/null +++ b/submissions/blastbox-demo-agents/metadata.json @@ -0,0 +1,13 @@ +{ + "name": "BlastBox Demo Agent Team", + "description": "A retail service team that coordinates store questions across specialist agents, MCP tools, and reusable skills.", + "tags": [ + "retail", + "customer-service", + "multi-agent", + "mcp", + "skills" + ], + "author": "BlastBox team", + "featured": false +} diff --git a/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_connectionparameters.json b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx new file mode 100644 index 0000000..7960808 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx @@ -0,0 +1,1195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Membership MCP Connector (inline) ║ +// ║ ║ +// ║ BlastPass membership lookup + cancellation for the BlastBox Omega console.║ +// ║ Give it a mega-blast-customer-id and it returns the membership tier and ║ +// ║ how many whole months are left on the term — the number the Returns & ║ +// ║ Service Assistant feeds into its prorated-refund Python calculation. ║ +// ║ ║ +// ║ 2 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "blastpass-membership", + Version = "1.0.0", + Title = "BlastPass Membership", + Description = "Look up a BlastPass console membership by its mega-blast-customer-id, cancel memberships, and reissue lost/stolen/damaged membership cards. Returns the membership tier, activation date, hours streamed, current blast_points balance, the card_serial on file, and how many whole months remain on the 12-month term — the inputs needed to calculate a prorated cancellation refund, reconcile loyalty points, or print a replacement card." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use get_membership with a mega-blast-customer-id (e.g. 'MEGA-BLAST-1024') to retrieve the member's BlastPass tier, annual price, activation date, hours streamed, card_serial, current blast_points balance, and months_remaining on the term. Pass months_remaining and the price to the prorated-refund calculation, and the returned blast_points to the points-reconciliation calculation — never ask the associate for the points balance, it is on the membership record. Use cancel_membership once a refund figure is agreed to close the membership and get a confirmation number. Use reissue_card for a self-serve lost/stolen/damaged card replacement: it deactivates the old card_serial and issues a new one while leaving the membership active — feed the returned new_card_serial into the membership-card-png skill to give the member a digital card to save while the physical one ships." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + // + // months_remaining is the authoritative count of WHOLE unused months left + // on the 12-month term, pre-computed so the demo is deterministic. The + // prorated refund = months_remaining x (annual_price / 12) - cancellation_fee + // - nonrefundable_credit, unless the cooling-off rule applies (activated <= 14 + // days ago AND hours_streamed < 2 -> full refund of annual_price). + + private static readonly JArray Memberships = JArray.Parse(@"[ + { + ""customer_id"":""MEGA-BLAST-1024"", + ""member_name"":""Jordan Pixel"", + ""tier"":""BlastPass Plus Extra"", + ""tier_code"":""extra"", + ""status"":""active"", + ""annual_price"":129.99, + ""cancellation_fee"":10.00, + ""nonrefundable_credit"":0.00, + ""activation_date"":""2026-02-26"", + ""term_end_date"":""2027-02-26"", + ""months_remaining"":8, + ""hours_streamed"":47.5, + ""blast_points"":4200, + ""console_serial"":""OMEGA-7F3A-1024"", + ""card_serial"":""BLAST-7F3A-1024"" + }, + { + ""customer_id"":""MEGA-BLAST-2048"", + ""member_name"":""Sam Sparkle"", + ""tier"":""BlastPass Plus Extra MEGA!!!"", + ""tier_code"":""mega"", + ""status"":""active"", + ""annual_price"":199.99, + ""cancellation_fee"":25.00, + ""nonrefundable_credit"":20.00, + ""activation_date"":""2026-05-30"", + ""term_end_date"":""2027-05-30"", + ""months_remaining"":11, + ""hours_streamed"":1.2, + ""blast_points"":15600, + ""console_serial"":""OMEGA-9B1C-2048"", + ""card_serial"":""BLAST-9B1C-2048"" + }, + { + ""customer_id"":""MEGA-BLAST-4096"", + ""member_name"":""Riley Retro"", + ""tier"":""BlastPass Plus"", + ""tier_code"":""plus"", + ""status"":""active"", + ""annual_price"":79.99, + ""cancellation_fee"":0.00, + ""nonrefundable_credit"":0.00, + ""activation_date"":""2025-12-10"", + ""term_end_date"":""2026-12-10"", + ""months_remaining"":6, + ""hours_streamed"":88.0, + ""blast_points"":9050, + ""console_serial"":""OMEGA-2D4E-4096"", + ""card_serial"":""BLAST-2D4E-4096"" + }, + { + ""customer_id"":""MEGA-BLAST-8192"", + ""member_name"":""Casey Combo"", + ""tier"":""BlastPass Plus Extra MEGA!!!"", + ""tier_code"":""mega"", + ""status"":""active"", + ""annual_price"":199.99, + ""cancellation_fee"":25.00, + ""nonrefundable_credit"":20.00, + ""activation_date"":""2026-01-05"", + ""term_end_date"":""2027-01-05"", + ""months_remaining"":5, + ""hours_streamed"":210.0, + ""blast_points"":33700, + ""console_serial"":""OMEGA-5A6F-8192"", + ""card_serial"":""BLAST-5A6F-8192"" + } + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. get_membership + handler.AddTool("get_membership", + "Look up a BlastPass membership by its mega-blast-customer-id. Returns the membership tier, annual price, activation date, hours streamed, cancellation fee, any non-refundable credit, months_remaining (the whole unused months left on the 12-month term), and the member's current blast_points balance. Feed months_remaining and annual_price into the prorated-refund calculation, and blast_points into the points-reconciliation calculation.", + schemaConfig: s => s.String("customer_id", "The mega-blast-customer-id (e.g. 'MEGA-BLAST-1024'). Case-insensitive.", required: true), + handler: async (args, ct) => + { + var id = (args.Value("customer_id") ?? "").Trim(); + foreach (var m in Memberships) + { + if (string.Equals(m["customer_id"]?.ToString(), id, StringComparison.OrdinalIgnoreCase)) + { + var activation = DateTime.Parse(m["activation_date"].ToString()); + var daysSince = (int)(DateTime.UtcNow.Date - activation.Date).TotalDays; + var hours = m["hours_streamed"]?.Value() ?? 0; + var coolingOff = daysSince <= 14 && daysSince >= 0 && hours < 2; + return new JObject + { + ["customer_id"] = m["customer_id"], + ["member_name"] = m["member_name"], + ["tier"] = m["tier"], + ["tier_code"] = m["tier_code"], + ["status"] = m["status"], + ["annual_price"] = m["annual_price"], + ["cancellation_fee"] = m["cancellation_fee"], + ["nonrefundable_credit"] = m["nonrefundable_credit"], + ["activation_date"] = m["activation_date"], + ["term_end_date"] = m["term_end_date"], + ["days_since_activation"] = daysSince, + ["months_remaining"] = m["months_remaining"], + ["hours_streamed"] = m["hours_streamed"], + ["blast_points"] = m["blast_points"], + ["within_cooling_off"] = coolingOff, + ["console_serial"] = m["console_serial"], + ["card_serial"] = m["card_serial"] + }; + } + } + throw new ArgumentException($"No BlastPass membership found for customer id \"{id}\". Check the mega-blast-customer-id (format: MEGA-BLAST-####)."); + }); + + // 2. cancel_membership + handler.AddTool("cancel_membership", + "Cancel a BlastPass membership after a refund amount has been agreed. Records the cancellation and returns a confirmation number, the effective date, and the refund amount on file. Only call this once the associate confirms the customer wants to proceed.", + schemaConfig: s => s + .String("customer_id", "The mega-blast-customer-id of the membership to cancel (e.g. 'MEGA-BLAST-1024').", required: true) + .Number("refund_amount", "The agreed prorated refund amount in dollars, as calculated by the refund step.", required: true), + handler: async (args, ct) => + { + var id = (args.Value("customer_id") ?? "").Trim(); + var refund = args.Value("refund_amount") ?? 0.0; + foreach (var m in Memberships) + { + if (string.Equals(m["customer_id"]?.ToString(), id, StringComparison.OrdinalIgnoreCase)) + { + var confirmation = "BPX-CXL-" + id.Replace("MEGA-BLAST-", "") + "-" + DateTime.UtcNow.ToString("yyyyMMdd"); + return new JObject + { + ["customer_id"] = m["customer_id"], + ["member_name"] = m["member_name"], + ["tier"] = m["tier"], + ["previous_status"] = m["status"], + ["new_status"] = "cancelled", + ["cancellation_confirmation"] = confirmation, + ["effective_date"] = DateTime.UtcNow.ToString("yyyy-MM-dd"), + ["refund_amount"] = Math.Round(refund, 2), + ["message"] = $"BlastPass {m["tier"]} membership for {m["member_name"]} is cancelled. A refund of ${Math.Round(refund, 2):0.00} will be issued to the original payment method." + }; + } + } + throw new ArgumentException($"No BlastPass membership found for customer id \"{id}\". Cannot cancel."); + }); + + // 3. reissue_card + handler.AddTool("reissue_card", + "Deactivate a member's current physical BlastPass card and issue a replacement, WITHOUT cancelling or changing the membership itself. Use this for a self-serve lost / stolen / damaged card request: it invalidates the old card_serial, mints a brand-new card_serial, keeps the membership active, and queues the new physical card for mailing. Returns the previous (now void) card_serial, the new card_serial to print on a digital card, a reissue confirmation number, and the mailing ETA for the physical card. The membership tier, term, and BlastPoints are untouched.", + schemaConfig: s => s + .String("customer_id", "The mega-blast-customer-id whose card is being replaced (e.g. 'MEGA-BLAST-1024').", required: true) + .String("reason", "Why the card is being reissued: 'lost', 'stolen', or 'damaged'. Defaults to 'lost'.", required: false), + handler: async (args, ct) => + { + var id = (args.Value("customer_id") ?? "").Trim(); + var reason = (args.Value("reason") ?? "lost").Trim().ToLowerInvariant(); + foreach (var m in Memberships) + { + if (string.Equals(m["customer_id"]?.ToString(), id, StringComparison.OrdinalIgnoreCase)) + { + if (!string.Equals(m["status"]?.ToString(), "active", StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException($"BlastPass membership for {m["member_name"]} is {m["status"]}, so a card cannot be reissued."); + + var prevCard = m["card_serial"]?.ToString() ?? ""; + // Mint a new card_serial: keep the trailing member segment, refresh the middle block. + var tail = id.Replace("MEGA-BLAST-", ""); + var seed = (prevCard + DateTime.UtcNow.Ticks).GetHashCode(); + var block = ((seed & 0x7FFFFFFF) % 0x10000).ToString("X4"); + var newCard = $"BLAST-{block}-{tail}"; + // Persist so a follow-up get_membership reflects the new card. + m["card_serial"] = newCard; + + var confirmation = "BPX-CARD-" + tail + "-" + DateTime.UtcNow.ToString("yyyyMMdd"); + var shipDays = 7; + var eta = DateTime.UtcNow.Date.AddDays(shipDays); + var last4 = prevCard.Length >= 4 ? prevCard.Substring(prevCard.Length - 4) : prevCard; + return new JObject + { + ["customer_id"] = m["customer_id"], + ["member_name"] = m["member_name"], + ["tier"] = m["tier"], + ["tier_code"] = m["tier_code"], + ["reason"] = reason, + ["previous_card_serial"] = prevCard, + ["previous_card_status"] = "deactivated", + ["new_card_serial"] = newCard, + ["reissue_confirmation"] = confirmation, + ["membership_status"] = m["status"], + ["digital_card_ready"] = true, + ["ship_method"] = "Standard mail", + ["ship_eta_days"] = shipDays, + ["estimated_delivery"] = eta.ToString("yyyy-MM-dd"), + ["message"] = $"Your old card ending {last4} is now deactivated and can no longer be used. A new {m["tier"]} card ({newCard}) has been issued — a printed copy is on its way by standard mail (arriving ~{eta:yyyy-MM-dd}). You can save the digital card now and use it in-store and at checkout right away." + }; + } + } + throw new ArgumentException($"No BlastPass membership found for customer id \"{id}\". Cannot reissue a card."); + }); + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_iconblob.Png b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_iconblob.Png new file mode 100644 index 0000000..f417fb8 Binary files /dev/null and b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_iconblob.Png differ diff --git a/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_openapidefinition.json b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_openapidefinition.json new file mode 100644 index 0000000..2fc9f18 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_openapidefinition.json @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"title":"Membership MCP v2","description":"BlastPass console membership lookup, cancellation, and card reissue. Tools: get_membership, cancel_membership, reissue_card. Runs entirely inside the connector — no external MCP server needed.","version":"1.0.0"},"host":"placeholder.azure-apim.net","basePath":"/mcp","schemes":["https"],"consumes":["application/json"],"produces":["application/json"],"paths":{"/":{"post":{"summary":"Membership MCP v2 Server","description":"MCP server for BlastPass memberships. Tools: get_membership, cancel_membership, reissue_card.","operationId":"InvokeMembershipMCPv2","x-ms-agentic-protocol":"mcp-streamable-1.0","parameters":[],"responses":{"200":{"description":"MCP response"}}}}},"definitions":{},"parameters":{},"responses":{},"security":[],"tags":[],"securityDefinitions":{}} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_policytemplateinstances.json b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/cat_membership-20mcp-20v2_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_connectionparameters.json b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx new file mode 100644 index 0000000..fc9a695 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx @@ -0,0 +1,1169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Order Management MCP Connector (inline) ║ +// ║ ║ +// ║ 5 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + private const string APP_INSIGHTS_CONNECTION_STRING = ""; + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "order-management", + Version = "1.0.0", + Title = "Order Management", + Description = "E-commerce order management with tools for searching orders, viewing details, tracking shipments, and managing returns." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use search_orders first to find orders by customer name, email, or order number. Then use the order_id from results with get_order, get_shipment, request_return, or get_return_status." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + + private static readonly JArray Orders = JArray.Parse(@"[ + {""id"":""ORD-10421"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-04-10"",""status"":""shipped"",""items"":[{""sku"":""SKU-WH1000"",""name"":""Sony WH-1000XM5 Headphones"",""quantity"":1,""unitPrice"":349.99},{""sku"":""SKU-USBC3"",""name"":""USB-C Charging Cable (3-pack)"",""quantity"":1,""unitPrice"":14.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":364.98}, + {""id"":""ORD-10422"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-04-15"",""status"":""processing"",""items"":[{""sku"":""SKU-KINDLE"",""name"":""Kindle Paperwhite (16GB)"",""quantity"":1,""unitPrice"":149.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":149.99}, + {""id"":""ORD-10318"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-03-22"",""status"":""delivered"",""items"":[{""sku"":""SKU-AIRPOD"",""name"":""AirPods Pro (2nd Gen)"",""quantity"":1,""unitPrice"":249.00},{""sku"":""SKU-CASE01"",""name"":""Silicone AirPods Case - Midnight"",""quantity"":1,""unitPrice"":19.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":268.99}, + {""id"":""ORD-10455"",""customer"":{""id"":""C-002"",""name"":""James Rivera"",""email"":""j.rivera@example.com""},""date"":""2026-04-12"",""status"":""shipped"",""items"":[{""sku"":""SKU-HOODIE"",""name"":""Nike Tech Fleece Hoodie - Black (L)"",""quantity"":1,""unitPrice"":120.00},{""sku"":""SKU-JOGGER"",""name"":""Nike Sportswear Joggers - Grey (L)"",""quantity"":1,""unitPrice"":85.00}],""shippingAddress"":""88 Sunset Blvd, Apt 4B, Los Angeles, CA 90028"",""paymentMethod"":""Mastercard ending in 8811"",""total"":205.00}, + {""id"":""ORD-10460"",""customer"":{""id"":""C-002"",""name"":""James Rivera"",""email"":""j.rivera@example.com""},""date"":""2026-04-18"",""status"":""processing"",""items"":[{""sku"":""SKU-SWITCH"",""name"":""Nintendo Switch OLED"",""quantity"":1,""unitPrice"":349.99},{""sku"":""SKU-ZELDA"",""name"":""The Legend of Zelda: Tears of the Kingdom"",""quantity"":1,""unitPrice"":59.99}],""shippingAddress"":""88 Sunset Blvd, Apt 4B, Los Angeles, CA 90028"",""paymentMethod"":""Mastercard ending in 8811"",""total"":409.98}, + {""id"":""ORD-10389"",""customer"":{""id"":""C-003"",""name"":""Emily Chen"",""email"":""emily.chen@example.com""},""date"":""2026-03-28"",""status"":""delivered"",""items"":[{""sku"":""SKU-DUNE2"",""name"":""Dune: Part Two (4K Blu-ray)"",""quantity"":1,""unitPrice"":29.99},{""sku"":""SKU-FOUNDATION"",""name"":""Foundation (Isaac Asimov) - Paperback"",""quantity"":1,""unitPrice"":12.99},{""sku"":""SKU-3BODY"",""name"":""The Three-Body Problem - Paperback"",""quantity"":1,""unitPrice"":14.99}],""shippingAddress"":""1200 Main Street, Unit 7C, Seattle, WA 98101"",""paymentMethod"":""Amex ending in 3003"",""total"":57.97}, + {""id"":""ORD-10478"",""customer"":{""id"":""C-003"",""name"":""Emily Chen"",""email"":""emily.chen@example.com""},""date"":""2026-04-14"",""status"":""shipped"",""items"":[{""sku"":""SKU-ERGOCHAIR"",""name"":""ErgoMax Office Chair - Mesh Back"",""quantity"":1,""unitPrice"":299.00}],""shippingAddress"":""1200 Main Street, Unit 7C, Seattle, WA 98101"",""paymentMethod"":""Amex ending in 3003"",""total"":299.00}, + {""id"":""ORD-10502"",""customer"":{""id"":""C-004"",""name"":""Jordan Pixel"",""email"":""jordan.pixel@example.com""},""date"":""2026-06-09"",""status"":""delivered"",""items"":[{""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""quantity"":1,""unitPrice"":399.99}],""shippingAddress"":""15 Arcade Lane, Pixel Heights, WA 98052"",""paymentMethod"":""Visa ending in 1024"",""total"":399.99} + ]"); + + private static readonly JObject Shipments = JObject.Parse(@"{ + ""ORD-10421"":{""order_id"":""ORD-10421"",""carrier"":""FedEx"",""tracking_number"":""FX-794644790132"",""tracking_url"":""https://www.fedex.com/fedextrack/?trknbr=FX-794644790132"",""estimated_delivery"":""2026-04-22"",""last_update"":""2026-04-20T14:30:00Z"",""status"":""out_for_delivery""}, + ""ORD-10455"":{""order_id"":""ORD-10455"",""carrier"":""UPS"",""tracking_number"":""1Z999AA10123456784"",""tracking_url"":""https://www.ups.com/track?tracknum=1Z999AA10123456784"",""estimated_delivery"":""2026-04-21"",""last_update"":""2026-04-19T09:15:00Z"",""status"":""in_transit""}, + ""ORD-10318"":{""order_id"":""ORD-10318"",""carrier"":""USPS"",""tracking_number"":""9400111899223100287654"",""tracking_url"":""https://tools.usps.com/go/TrackConfirmAction?tLabels=9400111899223100287654"",""estimated_delivery"":""2026-03-28"",""last_update"":""2026-03-28T10:15:00Z"",""status"":""delivered""}, + ""ORD-10478"":{""order_id"":""ORD-10478"",""carrier"":""FedEx"",""tracking_number"":""FX-794644790199"",""tracking_url"":""https://www.fedex.com/fedextrack/?trknbr=FX-794644790199"",""estimated_delivery"":""2026-04-24"",""last_update"":""2026-04-21T10:00:00Z"",""status"":""in_transit""} + }"); + + private static readonly JObject Returns = JObject.Parse(@"{ + ""RA-50012"":{""return_id"":""RA-50012"",""order_id"":""ORD-10318"",""status"":""refund_processed"",""items_returned"":[{""sku"":""SKU-CASE01"",""name"":""Silicone AirPods Case - Midnight""}],""reason"":""Wrong color received"",""refund_amount"":""$19.99"",""refund_method"":""Visa ending in 4242""}, + ""RA-50018"":{""return_id"":""RA-50018"",""order_id"":""ORD-10389"",""status"":""received"",""items_returned"":[{""sku"":""SKU-DUNE2"",""name"":""Dune: Part Two (4K Blu-ray)""}],""reason"":""Disc scratched on arrival"",""refund_amount"":""$29.99"",""refund_method"":""Amex ending in 3003""} + }"); + + private static int _returnCounter = 50019; + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. search_orders + handler.AddTool("search_orders", + "Search for orders by customer name, email, or order number. Returns a summary list. This is the entry point — use it first to find order IDs.", + schemaConfig: s => s.String("query", "Customer name, email address, or order number to search for", required: true), + handler: async (args, ct) => + { + var query = (args.Value("query") ?? "").ToLower(); + var matches = new JArray(); + foreach (var order in Orders) + { + var name = order["customer"]?["name"]?.ToString().ToLower() ?? ""; + var email = order["customer"]?["email"]?.ToString().ToLower() ?? ""; + var id = order["id"]?.ToString().ToLower() ?? ""; + if (name.Contains(query) || email.Contains(query) || id.Contains(query)) + { + matches.Add(new JObject + { + ["order_id"] = order["id"], + ["date"] = order["date"], + ["status"] = order["status"], + ["total"] = "$" + order["total"], + ["customer"] = order["customer"]?["name"], + ["item_count"] = (order["items"] as JArray)?.Count ?? 0 + }); + } + } + if (matches.Count == 0) + return new JObject { ["message"] = $"No orders found matching \"{query}\"." }; + return new JObject { ["orders"] = matches }; + }); + + // 2. get_order + handler.AddTool("get_order", + "Get full details for a specific order including line items, shipping address, and payment info. Use an order_id from search_orders results.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. ORD-10421) from search_orders results", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + foreach (var order in Orders) + { + if (order["id"]?.ToString() == orderId) + return JObject.Parse(order.ToString()); + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 3. get_shipment + handler.AddTool("get_shipment", + "Get shipment tracking info for an order. Only available for shipped/delivered orders. Returns carrier, tracking number, URL, and delivery estimate.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. ORD-10421)", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + if (Shipments[orderId] != null) + return JObject.Parse(Shipments[orderId].ToString()); + + // Check if order exists but isn't shipped + foreach (var order in Orders) + { + if (order["id"]?.ToString() == orderId) + throw new ArgumentException($"Order \"{orderId}\" has status \"{order["status"]}\" — shipment tracking is only available for shipped or delivered orders."); + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 4. request_return + handler.AddTool("request_return", + "Initiate a return for specific items in an order. Returns a return authorization with ID, label, refund estimate, and instructions.", + schemaConfig: s => s + .String("order_id", "Order ID to return items from", required: true) + .Array("item_skus", "SKUs of items to return (from get_order line items)", itemSchema: new JObject { ["type"] = "string" }, required: true) + .String("reason", "Reason for return (e.g. 'Defective', 'Wrong item')", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + var reason = args.Value("reason") ?? "Not specified"; + var skuArray = args["item_skus"] as JArray ?? new JArray(); + var skus = skuArray.Select(s => s.ToString()).ToList(); + + foreach (var order in Orders) + { + if (order["id"]?.ToString() != orderId) continue; + + var items = new JArray(); + double refundTotal = 0; + foreach (var item in order["items"] as JArray ?? new JArray()) + { + if (skus.Contains(item["sku"]?.ToString())) + { + items.Add(new JObject { ["sku"] = item["sku"], ["name"] = item["name"] }); + refundTotal += item["unitPrice"]?.Value() ?? 0; + } + } + + var returnId = $"RA-{System.Threading.Interlocked.Increment(ref _returnCounter):D5}"; + return new JObject + { + ["return_id"] = returnId, + ["order_id"] = orderId, + ["status"] = "requested", + ["items_returned"] = items, + ["reason"] = reason, + ["refund_amount"] = $"${refundTotal:F2}", + ["refund_method"] = order["paymentMethod"], + ["return_label_url"] = $"https://returns.example.com/labels/{returnId}.pdf", + ["instructions"] = "Print the return label, attach it to the package, and drop it off at any carrier location within 14 days." + }; + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 5. get_return_status + handler.AddTool("get_return_status", + "Check the status of a return request. Returns current stage, refund status, and timeline.", + schemaConfig: s => s.String("return_id", "Return ID (e.g. RA-50019) from request_return result", required: true), + handler: async (args, ct) => + { + var returnId = args.Value("return_id") ?? ""; + if (Returns[returnId] != null) + return JObject.Parse(Returns[returnId].ToString()); + + // For newly created returns, return a generic status + return new JObject + { + ["return_id"] = returnId, + ["status"] = "requested", + ["refund_status"] = "pending", + ["timeline"] = new JArray { new JObject { ["stage"] = "Return requested", ["date"] = DateTime.UtcNow.ToString("yyyy-MM-dd") } } + }; + }); + } + + // ── App Insights (optional, from Power MCP Template) ──────────────── + + private async Task LogToAppInsights(string eventName, JObject data, string correlationId) + { + if (string.IsNullOrEmpty(APP_INSIGHTS_CONNECTION_STRING)) return; + try + { + var iKey = APP_INSIGHTS_CONNECTION_STRING.Split(';').FirstOrDefault(s => s.StartsWith("InstrumentationKey="))?.Split('=')[1] ?? ""; + var endpoint = APP_INSIGHTS_CONNECTION_STRING.Split(';').FirstOrDefault(s => s.StartsWith("IngestionEndpoint="))?.Split('=')[1]?.TrimEnd('/') ?? "https://dc.services.visualstudio.com"; + var envelope = new JObject + { + ["name"] = "Microsoft.ApplicationInsights.Event", + ["time"] = DateTime.UtcNow.ToString("o"), + ["iKey"] = iKey, + ["data"] = new JObject + { + ["baseType"] = "EventData", + ["baseData"] = new JObject { ["ver"] = 2, ["name"] = eventName, ["properties"] = new JObject { ["correlationId"] = correlationId, ["details"] = data?.ToString(Newtonsoft.Json.Formatting.None) ?? "{}" } } + } + }; + var request = new HttpRequestMessage(HttpMethod.Post, $"{endpoint}/v2/track") { Content = new StringContent($"[{envelope}]", Encoding.UTF8, "application/json") }; + await this.Context.SendAsync(request, this.CancellationToken).ConfigureAwait(false); + } + catch { } + } +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_openapidefinition.json b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_openapidefinition.json new file mode 100644 index 0000000..98302dc --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Order Management MCP", + "description": "E-commerce order management with tools for searching orders, viewing details, tracking shipments, and managing returns. Runs entirely inside the connector \u2014 no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_connectionparameters.json b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_customcodeblobcontent.csx b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_customcodeblobcontent.csx new file mode 100644 index 0000000..379d78e --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_customcodeblobcontent.csx @@ -0,0 +1,1214 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Policy RAG MCP Connector (inline, MOCK) ║ +// ║ ║ +// ║ A pretend "knowledge base" for the BlastBox Omega Store Policy Agent. ║ +// ║ Modern Copilot Studio agents can't take uploaded files, so the tier ║ +// ║ policy booklets live here as static passages and a mock keyword search. ║ +// ║ ║ +// ║ No embeddings, no external store — just canned text + naive scoring. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "blastpass-policy-rag", + Version = "1.0.0", + Title = "BlastPass Policy Knowledge", + Description = "Mock policy knowledge base for BlastBox Omega. Search the BlastPass membership, returns, and loyalty policies, or fetch the exact refund rule for a tier. Use this to confirm a member's tier and whether a prorated refund is allowed before running the refund math." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Call search_policy with the associate's question (and optionally a tier_code of plus/extra/mega) to retrieve the most relevant policy passages. Call get_tier_refund_policy with a tier_code to get the structured refund rule (cooling-off window, proration method, cancellation fee, non-refundable credit) needed to calculate a prorated refund. Call get_markdown_policy with a markdown_type ('promo' or 'clearance') to get the store's pricing markdown guardrails (max discount, margin floor, clearance/manager-flag rules) for merchandising markdown decisions. This is a mock knowledge base with static text." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static "Knowledge Base" ───────────────────────────────────────── + // + // Each passage is a mock policy chunk. tier is plus/extra/mega or "all". + // Naive search scores passages by how many query words appear in the + // keywords + text. This is intentionally NOT real RAG — just a demo prop. + + private static readonly JArray Passages = JArray.Parse(@"[ + { + ""id"":""mem-plus-refund"", + ""doc"":""BlastPass Plus Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""plus"", + ""keywords"":""plus tier1 refund cancel cancellation prorate prorated proration cooling-off cooling off window months remaining price 79.99 fee"", + ""text"":""BlastPass Plus ($79.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 79.99/12 = $6.6658/mo). Plus has NO cancellation fee and no non-refundable credit. Prorated refunds ARE allowed for Plus."" + }, + { + ""id"":""mem-extra-refund"", + ""doc"":""BlastPass Plus Extra Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""extra"", + ""keywords"":""extra plus extra tier2 refund cancel cancellation prorate prorated proration cooling-off window months remaining price 129.99 fee 10 vault"", + ""text"":""BlastPass Plus Extra ($129.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 129.99/12 = $10.8325/mo), MINUS a $10 cancellation fee (never below $0). No non-refundable credit. Prorated refunds ARE allowed for Plus Extra."" + }, + { + ""id"":""mem-mega-refund"", + ""doc"":""BlastPass Plus Extra MEGA!!! Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""mega"", + ""keywords"":""mega plus extra mega tier3 refund cancel cancellation prorate prorated proration cooling-off window months remaining price 199.99 fee 25 welcome credit 20 non-refundable loot crate"", + ""text"":""BlastPass Plus Extra MEGA!!! ($199.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 199.99/12 = $16.6658/mo), MINUS a $25 cancellation fee AND MINUS the one-time $20 MEGA Welcome Credit, which is non-refundable (never below $0). Prorated refunds ARE allowed for MEGA, but remember to subtract BOTH the $25 fee and the $20 credit."" + }, + { + ""id"":""mem-perks"", + ""doc"":""BlastPass Membership Terms"", + ""section"":""Tiers & Perks"", + ""tier"":""all"", + ""keywords"":""tier tiers perks benefits vault mystery cartridge cloud save blastbuddies co-op which tier difference compare"", + ""text"":""Tiers: Plus ($79.99) — 4-player co-op, 1 Mystery Cartridge/mo, 25GB cloud saves. Plus Extra ($129.99) — Extra Vault (200+ titles), 100GB saves, 2 cartridges/mo, early demos. Plus Extra MEGA!!! ($199.99) — MEGA Vault (600+ titles incl. day-one exclusives), unlimited saves, 4 cartridges/mo + quarterly Loot Crate, MEGA Lounge, plus a one-time $20 Welcome Credit."" + }, + { + ""id"":""returns-windows"", + ""doc"":""Store Returns & Exchanges Policy"", + ""section"":""Return Windows"", + ""tier"":""all"", + ""keywords"":""return returns exchange window days console accessory game sealed opened receipt eligible eligibility"", + ""text"":""Return windows (with receipt): Consoles & hardware — 30 days, like-new. Accessories (controllers, headsets) — 30 days. Sealed physical games — 30 days. Opened physical games — 14 days, exchange only for the same title if defective. Digital/redeemed codes — non-returnable once revealed. Day-one MEGA exclusives — NON-RETURNABLE, all sales final."" + }, + { + ""id"":""returns-restocking"", + ""doc"":""Store Returns & Exchanges Policy"", + ""section"":""Restocking Fees & Store Credit"", + ""tier"":""all"", + ""keywords"":""restocking fee store credit refund opened bundle settlement defective warranty percent bonus"", + ""text"":""Restocking fee: 15% on opened consoles/hardware returned in like-new condition; waived if the item is defective. Defective hardware within 30 days is a free warranty swap, not a return. Refunds go to the original tender; choosing STORE CREDIT instead adds a 10% goodwill bonus on the eligible merchandise total (the bonus does NOT apply to membership-proration refunds or to fees)."" + }, + { + ""id"":""loyalty-blastpoints"", + ""doc"":""BlastPoints Loyalty Program Terms"", + ""section"":""Earning, Promos & Expiry"", + ""tier"":""all"", + ""keywords"":""blastpoints loyalty points earn redeem expiry expire promo multiplier triple weekend tier bonus reconcile balance"", + ""text"":""BlastPoints: earn 10 points per $1 on merchandise. Tier bonus: Extra members +20%, MEGA members +50% on earned points. Promo events (e.g. Triple BLAST Weekend) multiply BASE earn by 3x before the tier bonus. Points from returned items are clawed back. Points expire 90 days after they are earned if the account is inactive. Redemption: 1,000 points = $5 store credit."" + } + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. search_policy — mock keyword search over the static passages + handler.AddTool("search_policy", + "Search the BlastBox Omega policy knowledge base (membership, returns, and loyalty terms) and return the most relevant policy passages. Use this to confirm a member's tier, check whether a prorated refund is allowed, look up return windows, restocking fees, or loyalty rules.", + schemaConfig: s => s + .String("query", "What you want to know, in natural language (e.g. 'is a prorated refund allowed for Plus Extra?' or 'return window for an opened console').", required: true) + .String("tier_code", "Optional tier filter: 'plus', 'extra', or 'mega'. Omit to search all policies.", required: false), + handler: async (args, ct) => + { + var query = (args.Value("query") ?? "").Trim(); + var tier = (args.Value("tier_code") ?? "").Trim().ToLowerInvariant(); + var terms = Tokenize(query); + + var scored = new List(); + foreach (var p in Passages) + { + var pTier = p["tier"]?.ToString() ?? "all"; + if (tier.Length > 0 && pTier != "all" && pTier != tier) + continue; + + var hay = ((p["keywords"]?.ToString() ?? "") + " " + (p["text"]?.ToString() ?? "")).ToLowerInvariant(); + int score = 0; + foreach (var t in terms) + if (hay.Contains(t)) score++; + if (tier.Length > 0 && pTier == tier) score += 2; // tier match boost + + if (score > 0) + { + scored.Add(new JObject + { + ["id"] = p["id"], + ["document"] = p["doc"], + ["section"] = p["section"], + ["tier"] = pTier, + ["text"] = p["text"], + ["score"] = score + }); + } + } + + var top = scored.OrderByDescending(x => (int)x["score"]).Take(3).ToList(); + if (top.Count == 0) + { + return new JObject + { + ["query"] = query, + ["matches"] = new JArray(), + ["note"] = "No matching policy passage. Try mentioning the tier (Plus, Plus Extra, MEGA) or a topic like 'refund', 'return window', or 'BlastPoints'." + }; + } + + var results = new JArray(); + foreach (var m in top) results.Add(m); + return new JObject { ["query"] = query, ["matches"] = results }; + }); + + // 2. get_tier_refund_policy — structured refund rule for a tier + handler.AddTool("get_tier_refund_policy", + "Return the exact, structured membership refund rule for a BlastPass tier: the cooling-off window, the proration method, the cancellation fee, and any non-refundable credit. Use this once the tier is known so the refund can be calculated precisely.", + schemaConfig: s => s + .String("tier_code", "The tier: 'plus' (BlastPass Plus), 'extra' (BlastPass Plus Extra), or 'mega' (BlastPass Plus Extra MEGA!!!).", required: true), + handler: async (args, ct) => + { + var tier = (args.Value("tier_code") ?? "").Trim().ToLowerInvariant(); + switch (tier) + { + case "plus": + return RefundRule("BlastPass Plus", 79.99, 0.00, 0.00); + case "extra": + return RefundRule("BlastPass Plus Extra", 129.99, 10.00, 0.00); + case "mega": + return RefundRule("BlastPass Plus Extra MEGA!!!", 199.99, 25.00, 20.00); + default: + throw new ArgumentException("Unknown tier_code \"" + tier + "\". Use 'plus', 'extra', or 'mega'."); + } + }); + + // 3. get_markdown_policy + handler.AddTool("get_markdown_policy", + "Return the store's pricing markdown guardrails. Pass markdown_type='ask' FIRST to get the clarifying question the manager must answer (promo vs clearance). Once known, pass markdown_type='promo' (temporary promotion: capped discount, margin floor, no clearance) or 'clearance' (discontinuation: no cap/floor, requires a manager flag). Pass the returned guardrails into the markdown-optimizer; never hardcode them.", + schemaConfig: s => s + .String("markdown_type", "The markdown type: 'promo' (temporary promotion), 'clearance' (discontinuation), or 'ask' to receive the clarifying question to put to the manager.", required: true), + handler: async (args, ct) => + { + var type = (args.Value("markdown_type") ?? "").Trim().ToLowerInvariant(); + switch (type) + { + case "": + case "ask": + return new JObject + { + ["needs_clarification"] = true, + ["question"] = "Are these markdowns a temporary end-of-quarter PROMOTION, or a CLEARANCE because we're discontinuing the items? Promotions are capped and must stay above the margin floor; clearance has no floor but needs manager approval.", + ["options"] = new JArray("promo", "clearance"), + ["rule"] = "The markdown type determines the discount cap and margin floor, so confirm it with the manager before recommending any discounts." + }; + case "promo": + return new JObject + { + ["markdown_type"] = "promo", + ["max_discount_pct"] = 30, + ["margin_floor_pct"] = 15, + ["min_useful_promo_discount_pct"] = 10, + ["clearance_allowed"] = false, + ["requires_manager_flag"] = false, + ["rule"] = "Temporary promotion: discount each item by up to 30% off its current price, but never below a 15% gross margin. If the deepest discount allowed by the margin floor is under min_useful_promo_discount_pct (10%), the item cannot be meaningfully promoted and should be flagged for clearance instead." + }; + case "clearance": + return new JObject + { + ["markdown_type"] = "clearance", + ["max_discount_pct"] = null, + ["margin_floor_pct"] = null, + ["min_useful_promo_discount_pct"] = null, + ["clearance_allowed"] = true, + ["requires_manager_flag"] = true, + ["rule"] = "Clearance / discontinuation: no discount cap and no margin floor (may sell below cost to clear), but every clearance markdown requires explicit manager approval (manager flag)." + }; + default: + return new JObject + { + ["needs_clarification"] = true, + ["question"] = "I didn't recognize that markdown type. Is this a temporary PROMOTION or a CLEARANCE (discontinuation)?", + ["options"] = new JArray("promo", "clearance") + }; + } + }); + } + + private static JObject RefundRule(string name, double annual, double fee, double credit) + { + return new JObject + { + ["tier"] = name, + ["annual_price"] = annual, + ["monthly_value"] = Math.Round(annual / 12.0, 4), + ["term_months"] = 12, + ["cooling_off_days"] = 14, + ["cooling_off_max_hours_streamed"] = 2, + ["cooling_off_rule"] = "If cancelled within 14 days of activation AND under 2 hours streamed, issue a FULL refund of the annual price (no fee).", + ["proration_rule"] = "Otherwise refund = whole UNUSED months remaining x (annual_price / 12), then subtract the cancellation fee and any non-refundable credit; never below $0.", + ["cancellation_fee"] = fee, + ["nonrefundable_credit"] = credit, + ["prorated_refund_allowed"] = true + }; + } + + // Naive tokenizer: lowercase, split on non-alphanumeric, drop short/stop words. + private static List Tokenize(string s) + { + var stop = new HashSet { "the","a","an","is","are","for","to","of","and","or","my","i","do","does","can","what","whats","how","on","in","it","be","with","please" }; + var sb = new StringBuilder(); + foreach (var ch in (s ?? "").ToLowerInvariant()) + sb.Append(char.IsLetterOrDigit(ch) ? ch : ' '); + var tokens = new List(); + foreach (var w in sb.ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) + if (w.Length >= 3 && !stop.Contains(w) && !tokens.Contains(w)) + tokens.Add(w); + return tokens; + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_openapidefinition.json b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_openapidefinition.json new file mode 100644 index 0000000..af0fcf4 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_openapidefinition.json @@ -0,0 +1,29 @@ +{ + "swagger": "2.0", + "info": { + "title": "Policy RAG MCP", + "description": "BlastPass Policy Knowledge — a mock policy knowledge base for BlastBox Omega. Tools: search_policy (natural-language search over membership/returns/loyalty terms, optional tier filter) and get_tier_refund_policy (structured refund rule per tier). Runs entirely inside the connector — no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": ["https"], + "consumes": ["application/json"], + "produces": ["application/json"], + "paths": { + "/": { + "post": { + "summary": "Policy RAG MCP Server", + "description": "MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { "description": "MCP response" } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_policytemplateinstances.json b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_policy-20rag-20mcp_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_connectionparameters.json b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx new file mode 100644 index 0000000..18dbcb2 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx @@ -0,0 +1,1209 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Warehouse MCP Connector (inline) ║ +// ║ ║ +// ║ 7 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "warehouse-fulfillment", + Version = "1.0.0", + Title = "Warehouse & Fulfillment", + Description = "Warehouse inventory and fulfillment tools for checking stock, tracking fulfillment pipeline stages, finding product alternatives, and looking up restock dates." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use check_stock to look up inventory for a product SKU. If out of stock, use get_restock_date for restock info and find_alternatives for similar products. Use get_fulfillment_status with an order_id to check where an order is in the warehouse pipeline. Use check_game_compatibility to find which BlastBox Omega model a game requires. Use get_inventory_aging to see how long a product's stock has been sitting and whether more is inbound, to inform markdown/clearance decisions. Use get_console_exclusives to list the MEGA-only AAA titles (with an associate upsell pitch) when helping a customer decide between the base console and the MEGA Edition." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + + private static readonly JArray Stock = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""category"":""console"",""price"":399.99,""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-20"",""availableForShipping"":false}, + {""sku"":""SKU-OMEGA-MEGA"",""name"":""BlastBox Omega MEGA Edition"",""category"":""console"",""price"":499.99,""quantity"":5,""warehouse"":""Seattle-WA1"",""aisle"":""E-21"",""availableForShipping"":true}, + {""sku"":""SKU-APEX5"",""name"":""Apex NoiseGuard 5 Headphones"",""category"":""electronics"",""quantity"":3,""warehouse"":""Seattle-WA1"",""aisle"":""E-14"",""availableForShipping"":true}, + {""sku"":""SKU-USBC3"",""name"":""USB-C Charging Cable (3-pack)"",""category"":""electronics"",""quantity"":142,""warehouse"":""Seattle-WA1"",""aisle"":""A-02"",""availableForShipping"":true}, + {""sku"":""SKU-LUMI"",""name"":""LumiRead E-Reader (16GB)"",""category"":""electronics"",""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-08"",""availableForShipping"":false}, + {""sku"":""SKU-PULSE"",""name"":""PulseWave Pro Earbuds"",""category"":""electronics"",""quantity"":17,""warehouse"":""Seattle-WA1"",""aisle"":""E-12"",""availableForShipping"":true}, + {""sku"":""SKU-VORTEX"",""name"":""Vortex Gaming Console OLED"",""category"":""electronics"",""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-22"",""availableForShipping"":false}, + {""sku"":""SKU-REALM"",""name"":""Realm of Legends: The Lost Crown"",""category"":""electronics"",""quantity"":24,""warehouse"":""Chicago-IL1"",""aisle"":""G-05"",""availableForShipping"":true}, + {""sku"":""SKU-APEX4"",""name"":""Apex NoiseGuard 4 Headphones (Previous Gen)"",""category"":""electronics"",""quantity"":8,""warehouse"":""Seattle-WA1"",""aisle"":""E-14"",""availableForShipping"":true}, + {""sku"":""SKU-CLAR7"",""name"":""Clarity NC700 Headphones"",""category"":""electronics"",""quantity"":6,""warehouse"":""Seattle-WA1"",""aisle"":""E-15"",""availableForShipping"":true}, + {""sku"":""SKU-PWMAX"",""name"":""PulseWave Max Headphones - Space Gray"",""category"":""electronics"",""quantity"":2,""warehouse"":""Seattle-WA1"",""aisle"":""E-13"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE"",""name"":""TrailMark Fleece Hoodie - Black (L)"",""category"":""clothing"",""quantity"":4,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE-XL"",""name"":""TrailMark Fleece Hoodie - Black (XL)"",""category"":""clothing"",""quantity"":7,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE-GRY"",""name"":""TrailMark Fleece Hoodie - Grey (L)"",""category"":""clothing"",""quantity"":2,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-JOGGER"",""name"":""TrailMark Sport Joggers - Grey (L)"",""category"":""clothing"",""quantity"":11,""warehouse"":""Dallas-TX1"",""aisle"":""C-09"",""availableForShipping"":true}, + {""sku"":""SKU-STAR2"",""name"":""Starfall: Part Two (4K Blu-ray)"",""category"":""media"",""quantity"":9,""warehouse"":""Chicago-IL1"",""aisle"":""M-03"",""availableForShipping"":true}, + {""sku"":""SKU-COMFT"",""name"":""ComfortEdge Pro - Matte Black"",""category"":""furniture"",""quantity"":1,""warehouse"":""Seattle-WA1"",""aisle"":""F-01"",""availableForShipping"":true}, + {""sku"":""SKU-COMFT-W"",""name"":""ComfortEdge Pro - White"",""category"":""furniture"",""quantity"":3,""warehouse"":""Seattle-WA1"",""aisle"":""F-01"",""availableForShipping"":true}, + {""sku"":""SKU-DESKMAT"",""name"":""Felt Desk Mat - Dark Grey (90x40cm)"",""category"":""furniture"",""quantity"":30,""warehouse"":""Seattle-WA1"",""aisle"":""F-04"",""availableForShipping"":true} + ]"); + + private static readonly JObject Fulfillment = JObject.Parse(@"{ + ""ORD-10422"":{""order_id"":""ORD-10422"",""warehouse"":""Chicago-IL1"",""assigned_worker"":""Mike Torres"",""current_stage"":""received"",""estimated_ship_date"":""2026-04-23"",""notes"":""LumiRead E-Reader — awaiting restock. Item reserved from incoming shipment."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-15T09:30:00Z""}]}, + ""ORD-10460"":{""order_id"":""ORD-10460"",""warehouse"":""Chicago-IL1"",""assigned_worker"":""Lisa Park"",""current_stage"":""picked"",""estimated_ship_date"":""2026-04-21"",""notes"":""Vortex Console on backorder, Realm of Legends picked, holding for bundle ship."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-18T10:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-19T14:20:00Z""}]}, + ""ORD-10421"":{""order_id"":""ORD-10421"",""warehouse"":""Seattle-WA1"",""assigned_worker"":""Carlos Mendez"",""current_stage"":""handed_to_carrier"",""estimated_ship_date"":""2026-04-13"",""notes"":""Shipped via SwiftShip. Two items in single box."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-11T08:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-11T11:30:00Z""},{""stage"":""packed"",""completed_at"":""2026-04-12T09:15:00Z""},{""stage"":""labeled"",""completed_at"":""2026-04-12T10:00:00Z""},{""stage"":""handed_to_carrier"",""completed_at"":""2026-04-13T08:45:00Z""}]}, + ""ORD-10455"":{""order_id"":""ORD-10455"",""warehouse"":""Dallas-TX1"",""assigned_worker"":""Rachel Kim"",""current_stage"":""handed_to_carrier"",""estimated_ship_date"":""2026-04-14"",""notes"":""Shipped via PrimeFreight. Hoodie + joggers in one package."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-12T11:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-13T09:00:00Z""},{""stage"":""packed"",""completed_at"":""2026-04-13T14:30:00Z""},{""stage"":""labeled"",""completed_at"":""2026-04-14T08:00:00Z""},{""stage"":""handed_to_carrier"",""completed_at"":""2026-04-14T15:00:00Z""}]} + }"); + + private static readonly JArray Restock = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""current_quantity"":0,""next_shipment_date"":""2026-06-11"",""expected_quantity"":40,""supplier"":""BlastBox Manufacturing"",""notes"":""Standard replenishment — next batch arrives in about 4 days. MEGA Edition is in stock now as an upgrade alternative.""}, + {""sku"":""SKU-LUMI"",""name"":""LumiRead E-Reader (16GB)"",""current_quantity"":0,""next_shipment_date"":""2026-04-22"",""expected_quantity"":50,""supplier"":""LumiRead Distribution Co."",""notes"":""Delayed from original April 18 date. Supplier confirmed new ETA.""}, + {""sku"":""SKU-VORTEX"",""name"":""Vortex Gaming Console OLED"",""current_quantity"":0,""next_shipment_date"":""2026-04-28"",""expected_quantity"":30,""supplier"":""Vortex Games Inc."",""notes"":""High demand — limited allocation. Next batch after this is mid-May.""}, + {""sku"":""SKU-APEX5"",""name"":""Apex NoiseGuard 5 Headphones"",""current_quantity"":3,""next_shipment_date"":""2026-05-05"",""expected_quantity"":20,""supplier"":""Apex Audio Corp."",""notes"":""Regular replenishment cycle. Current stock sufficient for 1-2 weeks.""}, + {""sku"":""SKU-COMFT"",""name"":""ComfortEdge Pro - Matte Black"",""current_quantity"":1,""next_shipment_date"":""2026-04-30"",""expected_quantity"":10,""supplier"":""ComfortEdge Furniture Co."",""notes"":""Low stock alert triggered. Express shipment arranged.""} + ]"); + + private static readonly JArray Games = JArray.Parse(@"[ + {""title"":""MEGA Lizards from Outer Space"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA title — requires the MEGA Edition GPU. Will NOT run on the base BlastBox Omega.""}, + {""title"":""Galactic Tax Evader VII: Audit Protocol"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA open-world heist sim — the real-time audit physics only run on the MEGA Edition co-processor.""}, + {""title"":""Mecha-Granny: Knitpocalypse"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA action roguelite — the 4K yarn engine requires the MEGA Edition. Will NOT run on the base console.""}, + {""title"":""Realm of Legends: The Lost Crown"",""required_model"":""BlastBox Omega Console"",""required_sku"":""SKU-OMEGA"",""runs_on_base"":true,""note"":""Runs on any BlastBox Omega model, including the base console.""}, + {""title"":""Galaxy Smash"",""required_model"":""BlastBox Omega Console"",""required_sku"":""SKU-OMEGA"",""runs_on_base"":true,""note"":""Runs on any BlastBox Omega model.""} + ]"); + + // Exclusive AAA titles per console model, with a ready-to-say associate upsell pitch. + private static readonly JObject Exclusives = JObject.Parse(@"{ + ""SKU-OMEGA-MEGA"":{ + ""model"":""BlastBox Omega MEGA Edition"", + ""sku"":""SKU-OMEGA-MEGA"", + ""tagline"":""Three AAA exclusives the base console literally can't load."", + ""titles"":[ + {""title"":""MEGA Lizards from Outer Space"",""genre"":""Co-op chaos shooter"",""pitch"":""Picture two hundred neon space-lizards on screen at once and a co-op buddy screaming next to you. It's the #1 couch-chaos shooter of the year — and it physically will not run on the base console.""}, + {""title"":""Galactic Tax Evader VII: Audit Protocol"",""genre"":""Open-world heist sim"",""pitch"":""You're an interstellar accountant on the run, dodging audits in real time across a living open galaxy. Critics call it 'Grand Theft Spreadsheet.' MEGA-exclusive, because the audit physics melt anything smaller.""}, + {""title"":""Mecha-Granny: Knitpocalypse"",""genre"":""Action roguelite"",""pitch"":""A sweet old lady in a battle-mech knits the apocalypse back together, one 4K scarf at a time. It's hilarious, it's brutal, and the yarn engine only spins up on MEGA.""} + ] + }, + ""SKU-OMEGA"":{ + ""model"":""BlastBox Omega Console"", + ""sku"":""SKU-OMEGA"", + ""tagline"":""Plays the full shared BlastBox library, but none of the MEGA-only AAA exclusives."", + ""titles"":[] + } + }"); + + // Inventory aging / inbound status for merchandising markdown decisions. + private static readonly JArray Aging = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA-MEGA"",""name"":""BlastBox Omega MEGA Edition"",""weeks_in_stock"":3,""received_date"":""2026-05-11"",""inbound_po"":true,""next_inbound_date"":""2026-06-15"",""aging_bucket"":""fresh"",""note"":""Hot seller. Replenishment PO inbound.""}, + {""sku"":""SKU-MEGALIZARDS"",""name"":""MEGA Lizards from Outer Space"",""weeks_in_stock"":4,""received_date"":""2026-05-04"",""inbound_po"":true,""next_inbound_date"":""2026-06-12"",""aging_bucket"":""fresh"",""note"":""AAA title driving MEGA Edition attach. Reorder inbound.""}, + {""sku"":""SKU-PULSE-CTRL"",""name"":""PulseGrip Pro Controller"",""weeks_in_stock"":5,""received_date"":""2026-04-27"",""inbound_po"":true,""next_inbound_date"":""2026-06-20"",""aging_bucket"":""fresh"",""note"":""Steady accessory attach.""}, + {""sku"":""SKU-OMEGA-CORE"",""name"":""BlastBox Omega Core (1st-gen)"",""weeks_in_stock"":16,""received_date"":""2026-02-16"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""aging"",""note"":""Being phased out by the MEGA Edition. No further inbound — candidate for discontinuation/clearance.""}, + {""sku"":""SKU-RETRO-CADET"",""name"":""BlastBox Cadet Bundle"",""weeks_in_stock"":22,""received_date"":""2026-01-05"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""stale"",""note"":""Last-gen bundle. No inbound. Strong markdown candidate.""}, + {""sku"":""SKU-GALAXY-SMASH"",""name"":""Galaxy Smash"",""weeks_in_stock"":14,""received_date"":""2026-03-02"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""aging"",""note"":""Overstocked at launch. No inbound. Markdown candidate.""}, + {""sku"":""SKU-VR-GOGGLES"",""name"":""OmegaVision VR Headset"",""weeks_in_stock"":18,""received_date"":""2026-02-02"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""stale"",""note"":""Slow launch. No inbound. Strong markdown/clearance candidate.""} + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. check_stock + handler.AddTool("check_stock", + "Check warehouse inventory for a product by SKU. Returns quantity on hand, warehouse location, aisle, and whether the item is available for shipping. If quantity is 0, use get_restock_date for restock info.", + schemaConfig: s => s.String("sku", "Product SKU (e.g. 'SKU-APEX5'). Use SKUs from order line items.", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + foreach (var item in Stock) + { + if (item["sku"]?.ToString() == sku) + { + var qty = item["quantity"]?.Value() ?? 0; + return new JObject + { + ["sku"] = item["sku"], + ["name"] = item["name"], + ["category"] = item["category"], + ["price"] = item["price"], + ["quantity_on_hand"] = qty, + ["warehouse"] = item["warehouse"], + ["aisle"] = item["aisle"], + ["available_for_shipping"] = item["availableForShipping"], + ["status"] = qty == 0 ? "OUT_OF_STOCK" : qty <= 3 ? "LOW_STOCK" : "IN_STOCK" + }; + } + } + throw new ArgumentException($"SKU \"{sku}\" not found in warehouse inventory."); + }); + + // 2. get_fulfillment_status + handler.AddTool("get_fulfillment_status", + "Get the fulfillment pipeline status for an order. Shows which warehouse stage the order is at (received → picked → packed → labeled → handed_to_carrier), assigned worker, and estimated ship date.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. 'ORD-10422') from the order management system", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + if (Fulfillment[orderId] != null) + return JObject.Parse(Fulfillment[orderId].ToString()); + throw new ArgumentException($"No fulfillment record found for order \"{orderId}\". The order may not have entered the warehouse yet."); + }); + + // 3. find_alternatives + handler.AddTool("find_alternatives", + "Find alternative products similar to a given SKU. Returns items in the same category that are in stock. Useful when a customer wants a different size, color, or comparable product, or when an item is out of stock.", + schemaConfig: s => s.String("sku", "Product SKU to find alternatives for (e.g. 'SKU-HOODIE')", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + JToken original = null; + foreach (var item in Stock) + { + if (item["sku"]?.ToString() == sku) { original = item; break; } + } + if (original == null) + throw new ArgumentException($"SKU \"{sku}\" not found in inventory."); + + var category = original["category"]?.ToString(); + var alternatives = new JArray(); + foreach (var item in Stock) + { + if (item["category"]?.ToString() == category && item["sku"]?.ToString() != sku && (item["quantity"]?.Value() ?? 0) > 0) + { + alternatives.Add(new JObject + { + ["sku"] = item["sku"], + ["name"] = item["name"], + ["price"] = item["price"], + ["quantity_available"] = item["quantity"], + ["warehouse"] = item["warehouse"], + ["available_for_shipping"] = item["availableForShipping"] + }); + } + } + + return new JObject + { + ["original"] = new JObject { ["sku"] = original["sku"], ["name"] = original["name"], ["category"] = category }, + ["alternatives"] = alternatives + }; + }); + + // 4. get_restock_date + handler.AddTool("get_restock_date", + "Get the next restock date and supplier info for a product. Use this when check_stock shows an item is out of stock or low stock. Returns expected delivery date, quantity, and supplier details.", + schemaConfig: s => s.String("sku", "Product SKU (e.g. 'SKU-LUMI'). Typically used after check_stock shows low/no stock.", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + foreach (var item in Restock) + { + if (item["sku"]?.ToString() == sku) + return JObject.Parse(item.ToString()); + } + return new JObject { ["message"] = $"No restock schedule found for SKU \"{sku}\". The item may be regularly stocked or discontinued." }; + }); + + // 5. check_game_compatibility + handler.AddTool("check_game_compatibility", + "Check which BlastBox Omega console model a game requires. Use when a customer asks whether a game will run on their console, or when deciding between the base console and the MEGA Edition. Returns the required model/SKU and whether it runs on the base console.", + schemaConfig: s => s.String("game_title", "The game title (e.g. 'MEGA Lizards from Outer Space'). Case-insensitive, partial match allowed.", required: true), + handler: async (args, ct) => + { + var q = (args.Value("game_title") ?? "").Trim().ToLowerInvariant(); + foreach (var g in Games) + { + var title = (g["title"]?.ToString() ?? "").ToLowerInvariant(); + if (title == q || (q.Length > 0 && title.Contains(q))) + return JObject.Parse(g.ToString()); + } + return new JObject { ["message"] = $"No compatibility record found for game \"{args.Value("game_title")}\"." }; + }); + + // 6. get_inventory_aging + handler.AddTool("get_inventory_aging", + "Return inventory aging and inbound-replenishment status for a product SKU: how many weeks the stock has been sitting, its received date, whether there is an inbound purchase order, and an aging bucket (fresh / aging / stale). Use this to judge whether a slow-selling item is a markdown or clearance candidate. Omit sku to return aging for all tracked products.", + schemaConfig: s => s.String("sku", "Optional. Product SKU (e.g. 'SKU-VR-GOGGLES'). Omit to return all tracked products."), + handler: async (args, ct) => + { + var sku = (args.Value("sku") ?? "").Trim(); + if (sku.Length == 0) + return new JObject { ["count"] = Aging.Count, ["items"] = JArray.Parse(Aging.ToString()) }; + foreach (var item in Aging) + { + if (string.Equals(item["sku"]?.ToString(), sku, StringComparison.OrdinalIgnoreCase)) + return JObject.Parse(item.ToString()); + } + return new JObject { ["message"] = $"No aging record found for SKU \"{sku}\"." }; + }); + + // 7. get_console_exclusives + handler.AddTool("get_console_exclusives", + "Return the AAA game titles that run ONLY on a given BlastBox Omega console model, each with a ready-to-say associate upsell pitch. Use this to help an associate upsell a customer to the MEGA Edition: it surfaces the marquee titles the base console cannot run plus a short, attractive blurb to read to the customer. Accepts a SKU (e.g. 'SKU-OMEGA-MEGA') or a model name (e.g. 'mega', 'base').", + schemaConfig: s => s.String("model", "Console SKU or model name. Use 'SKU-OMEGA-MEGA' or 'mega' for the MEGA Edition; 'SKU-OMEGA' or 'base' for the base console.", required: true), + handler: async (args, ct) => + { + var q = (args.Value("model") ?? "").Trim().ToLowerInvariant(); + string key = null; + if (q.Contains("mega")) key = "SKU-OMEGA-MEGA"; + else if (q == "base" || q == "sku-omega" || q.Contains("base")) key = "SKU-OMEGA"; + else if (q == "sku-omega-mega") key = "SKU-OMEGA-MEGA"; + if (key != null && Exclusives[key] != null) + return JObject.Parse(Exclusives[key].ToString()); + return new JObject { ["message"] = $"No console-exclusive list found for model \"{args.Value("model")}\". Try 'mega' or 'base'." }; + }); + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_iconblob.Png b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_iconblob.Png new file mode 100644 index 0000000..f417fb8 Binary files /dev/null and b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_iconblob.Png differ diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_openapidefinition.json b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_openapidefinition.json new file mode 100644 index 0000000..d624a9f --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Warehouse MCP", + "description": "Warehouse inventory and fulfillment with tools for checking stock, tracking pipeline stages, finding alternatives, and restock dates. Runs entirely inside the connector \u2014 no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Warehouse MCP Server", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/[Content_Types].xml b/submissions/blastbox-demo-agents/solution/[Content_Types].xml new file mode 100644 index 0000000..bfb4918 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/[Content_Types].xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/botcomponent.xml new file mode 100644 index 0000000..1f719e3 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date. + 0 + Warehouse MCP — Warehouse MCP Server + + Default_InventoryFullfilmentAgent_X-w2GP + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/data b/submissions/blastbox-demo-agents/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/data new file mode 100644 index 0000000..63bdad7 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer/data @@ -0,0 +1,4 @@ +kind: McpTool +connectorId: /providers/Microsoft.PowerApps/apis/shared_new-5fwarehouse-20mcp-5fee08b8354fad177a +connectionReference: Default_InventoryFullfilmentAgent_X-w2GP.cr.shared_new-5fwarehou.89650fa4b3604076a2e730bec67b7f07 +operationId: InvokeMCP \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/botcomponent.xml new file mode 100644 index 0000000..c89a8c8 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/botcomponent.xml @@ -0,0 +1,10 @@ + + 9 + 0 + Policy RAG MCP v2 - Policy RAG MCP v2 Server + + Default_StorePolicyAgent_s9s-u8 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/data b/submissions/blastbox-demo-agents/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/data new file mode 100644 index 0000000..13d6772 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.action.PolicyRAGMCPv2-PolicyRAGMCPv2Server/data @@ -0,0 +1,12 @@ +kind: TaskDialog +modelDisplayName: Policy RAG MCP v2 Server +modelDescription: "MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy, get_markdown_policy." +action: + kind: InvokeExternalAgentTaskAction + connectionReference: Default_StorePolicyAgent_s9s-u8.shared_new-5fpolicy-20rag-20mcp-20v2-5fee08b8354fad177a.1d7ba24296314fd18b7ce9d4fb97d766 + connectionProperties: + mode: Invoker + + operationDetails: + kind: ModelContextProtocolMetadata + operationId: InvokeMCP \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml new file mode 100644 index 0000000..f85df1a --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml @@ -0,0 +1,15 @@ + + 14 + This knowledge source searches information contained in SKILL.md + SKILL.md + 0 + SKILL.md + + Default_draft_IsBewO.skill.membership-card-png + + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md new file mode 100644 index 0000000..3dba17b --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md @@ -0,0 +1,95 @@ +--- +name: membership-card-png +description: Generate a digital BlastPass membership card as a PNG image by running the bundled card_png.py script. Use when a member self-serves a lost/stolen/damaged card replacement, or any time a card needs to be shown on screen (vs. printed). The skill takes the member details returned by the Membership MCP (get_membership) plus the new_card_serial returned by reissue_card and passes them as command-line flags to a bundled Python (matplotlib) script that renders a CR80-sized membership card to a real PNG. It only renders the card from already-retrieved values — it never looks anything up or invents data. +--- + +# BlastPass Membership Card PNG Generator + +This skill produces a **digital BlastPass membership card** as a shareable image by +running the **bundled `card_png.py` script**. It is the self-serve companion to +`membership-card-pdf`: when a member reissues a lost, stolen, or damaged card, you +mint a new serial via the Membership MCP and hand the member a PNG to save to their +phone **while the physical card ships**. + +You do **not** write any Python. You take the member record you already pulled from +the **Membership MCP** (`get_membership`) and the **new serial** returned by +`reissue_card`, then **run the bundled script** with those values as CLI flags. + +Never invent a member, tier, or serial number — every value on the card comes from +the MCP results. The script only *draws* the card; it does not decide anything. + +## When to use + +Use this skill in the **self-serve card reissue** scenario, after: + +1. `get_membership(customer_id)` confirmed the member and returned their details. +2. The member verified their identity (last 4 of the console serial). +3. `reissue_card(customer_id, reason)` deactivated the old card and returned a + `new_card_serial`. + +Use the PDF skill instead when an in-store associate needs a **printable** card. +Use this PNG skill when the member needs an **on-screen / saveable** card. + +## Inputs (map MCP results → CLI flags) + +| Script flag | Source | +| --- | --- | +| `--member-name` | `get_membership.member_name` | +| `--member-id` | `get_membership.customer_id` | +| `--tier` | `get_membership.tier` (e.g. `"BlastPass Plus Extra"`) | +| `--tier-code` | `get_membership.tier_code` (`plus` \| `extra` \| `mega`) — drives accent color & perks | +| `--card-serial` | **`reissue_card.new_card_serial`** (the freshly minted card; falls back to `get_membership.card_serial` if not a reissue) | +| `--console-serial` | `get_membership.console_serial` | +| `--member-since` | `get_membership.activation_date` | +| `--valid-through` | `get_membership.term_end_date` | +| `--out` | optional output path (default `blastpass_card.png`) | + +The `--tier-code` selects the card's accent color, header badge, and the perks line +printed beneath the card: + +- `plus` → blue, "PLUS", 5% off + 10 BlastPoints/$1 +- `extra` → purple, "PLUS EXTRA", 10% off + 20% BlastPoints bonus +- `mega` → orange, "MEGA!!!", 15% off + 50% BlastPoints + day-one exclusives + +## Workflow + +### 1. Gather the confirmed values + +From `get_membership` (tier, dates, console serial, name, id) and from +`reissue_card` (`new_card_serial`). Do **not** pass any value the MCP didn't return. + +### 2. Run the bundled script + +Pass the confirmed values as flags. The script writes `blastpass_card.png` to the +working directory (or `--out`) and prints a one-line confirmation: + +```bash +python3 card_png.py \ + --member-name "Jordan Pixel" \ + --member-id MEGA-BLAST-1024 \ + --tier "BlastPass Plus Extra" \ + --tier-code extra \ + --card-serial BLAST-A1B2-1024 \ + --console-serial OMEGA-7F3A-1024 \ + --member-since 2026-02-26 \ + --valid-through 2027-02-26 +``` + +`--card-serial` must be the **new** serial from `reissue_card` — never the old +(now-deactivated) one. + +### 3. Hand back the image + +Tell the member their new digital card is ready (`blastpass_card.png`), read back +the **new card serial** and tier so they can confirm it, and remind them the old +card is now deactivated and the physical replacement ships in ~7 days. + +## Notes + +- The bundled script is `card_png.py`, validated against the four mock members in + the Membership MCP. It depends only on `matplotlib` (available in the runtime). +- The card serial shown is the **new** one from `reissue_card`; the old serial is + already deactivated server-side, so it must never appear on the card. +- The barcode is decorative (deterministic from the card serial) — it is not a real + scannable symbology. +- All data is mock; the card is for demo purposes only. diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml new file mode 100644 index 0000000..5e2c1fb --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml @@ -0,0 +1,15 @@ + + 14 + This knowledge source searches information contained in card_png.py + card_png.py + 0 + card_png.py + + Default_draft_IsBewO.skill.membership-card-png + + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py new file mode 100644 index 0000000..9673be9 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +"""Render a BlastPass membership card to a PNG. + +Bundled skill script: the agent runs this with the member's confirmed values +(from the Membership MCP get_membership + reissue_card results) as CLI flags. The +script only *draws* the card — it never looks anything up or invents data. + +Usage: + python3 card_png.py \ + --member-name "Jordan Pixel" \ + --member-id MEGA-BLAST-1024 \ + --tier "BlastPass Plus Extra" \ + --tier-code extra \ + --card-serial BLAST-A1B2-1024 \ + --console-serial OMEGA-7F3A-1024 \ + --member-since 2026-02-26 \ + --valid-through 2027-02-26 \ + [--out blastpass_card.png] + +--card-serial must be the NEW serial from reissue_card (new_card_serial) on a +reissue, otherwise the card_serial on file from get_membership. +""" + +import argparse +import hashlib +import sys + +import matplotlib +matplotlib.use("Agg") # headless: render straight to a file, no display needed +import matplotlib.pyplot as plt +from matplotlib.patches import FancyBboxPatch, Rectangle + +# Tier styling: accent color, header badge text, perks blurb (printed under card) +TIERS = { + "plus": ("#3aa0ff", "PLUS", + "Free 2-day shipping | 5% off accessories | 10 BlastPoints / $1"), + "extra": ("#9b51e0", "PLUS EXTRA", + "Free next-day shipping | 10% off accessories | +20% BlastPoints bonus"), + "mega": ("#ff7a18", "MEGA!!!", + "Same-day delivery | 15% off everything | +50% BlastPoints | Day-one exclusives"), +} +INK = "#1b1030" +MUTED = "#9d90bd" + + +def parse_args(argv): + p = argparse.ArgumentParser(description="Render a BlastPass membership card PNG.") + p.add_argument("--member-name", required=True) + p.add_argument("--member-id", required=True) + p.add_argument("--tier", required=True, help='e.g. "BlastPass Plus Extra"') + p.add_argument("--tier-code", required=True, choices=["plus", "extra", "mega"]) + p.add_argument("--card-serial", required=True, + help="NEW serial from reissue_card, else card_serial on file") + p.add_argument("--console-serial", required=True) + p.add_argument("--member-since", required=True, help="activation_date (YYYY-MM-DD)") + p.add_argument("--valid-through", required=True, help="term_end_date (YYYY-MM-DD)") + p.add_argument("--out", default="blastpass_card.png") + return p.parse_args(argv) + + +def render(card, out_path): + accent, tier_badge, perks = TIERS.get(card["tier_code"], TIERS["plus"]) + + # Card geometry in inches (CR80 ratio 3.375 x 2.125), drawn on a small canvas. + CARD_W, CARD_H = 3.375, 2.125 + fig_w, fig_h = CARD_W + 1.4, CARD_H + 1.0 + fig = plt.figure(figsize=(fig_w, fig_h), dpi=300) + ax = fig.add_axes([0, 0, 1, 1]) + ax.set_xlim(0, fig_w) + ax.set_ylim(0, fig_h) + ax.axis("off") + + x0 = (fig_w - CARD_W) / 2 + y0 = (fig_h - CARD_H) / 2 + 0.18 # nudge up to leave room for perks footer + + # Card body (rounded) + ax.add_patch(FancyBboxPatch( + (x0, y0), CARD_W, CARD_H, + boxstyle="round,pad=0,rounding_size=0.12", + linewidth=0, facecolor=INK, zorder=1)) + + # Accent header band (rounded top, squared bottom via an overlapping rectangle) + band_h = 0.55 + ax.add_patch(FancyBboxPatch( + (x0, y0 + CARD_H - band_h), CARD_W, band_h, + boxstyle="round,pad=0,rounding_size=0.12", + linewidth=0, facecolor=accent, zorder=2)) + ax.add_patch(Rectangle( + (x0, y0 + CARD_H - band_h), CARD_W, band_h * 0.45, + linewidth=0, facecolor=accent, zorder=2)) + + pad = 0.22 + # Brand + ax.text(x0 + pad, y0 + CARD_H - 0.34, "BlastBox Omega", + color="white", fontsize=12, fontweight="bold", va="center", zorder=3) + # Tier badge pill (right side of band) + badge_w = 0.16 + 0.085 * len(tier_badge) + bx = x0 + CARD_W - pad - badge_w + ax.add_patch(FancyBboxPatch( + (bx, y0 + CARD_H - 0.45), badge_w, 0.26, + boxstyle="round,pad=0,rounding_size=0.06", + linewidth=0, facecolor="white", alpha=0.22, zorder=3)) + ax.text(bx + badge_w / 2, y0 + CARD_H - 0.32, tier_badge, + color="white", fontsize=7.5, fontweight="bold", + ha="center", va="center", zorder=4) + + # Member name + ax.text(x0 + pad, y0 + CARD_H - 0.80, card["member_name"], + color="white", fontsize=13.5, fontweight="bold", va="center", zorder=3) + # Card serial (mono, accent) + ax.text(x0 + pad, y0 + CARD_H - 1.06, card["card_serial"], + color=accent, fontsize=12.5, fontweight="bold", + family="monospace", va="center", zorder=3) + + # Detail rows + details = [ + ("MEMBER ID", card["member_id"]), + ("MEMBER SINCE", card["member_since"]), + ("VALID THROUGH", card["valid_through"]), + ("CONSOLE S/N", card["console_serial"]), + ] + dy = y0 + CARD_H - 1.30 + for label, val in details: + ax.text(x0 + pad, dy, label, color=MUTED, fontsize=6, va="center", zorder=3) + ax.text(x0 + pad + 0.95, dy, str(val), color="white", fontsize=8, + fontweight="bold", va="center", zorder=3) + dy -= 0.165 + + # Faux barcode strip (deterministic from the card serial) + seed = int(hashlib.md5(card["card_serial"].encode()).hexdigest(), 16) + bx2 = x0 + pad + by2 = y0 + 0.14 + bar_h = 0.14 + w = 0.0 + i = 0 + max_w = CARD_W - 2 * pad + while w < max_w: + bar = 0.012 + ((seed >> (i % 32)) & 0x3) * 0.006 + if i % 2 == 0: + ax.add_patch(Rectangle((bx2 + w, by2), bar, bar_h, + linewidth=0, facecolor="white", zorder=3)) + w += bar + i += 1 + + # Perks footer (under the card) — shrink font if the perks line is long + perks_fs = 6.5 if len(perks) <= 80 else 5.6 + ax.text(fig_w / 2, y0 - 0.16, perks, color="#666666", fontsize=perks_fs, + style="italic", ha="center", va="center") + ax.text(fig_w / 2, y0 - 0.34, + "Present this card in-store or scan at checkout. " + "A printed copy is on its way by mail.", + color="#888888", fontsize=6.5, ha="center", va="center") + + fig.savefig(out_path, dpi=300, facecolor="white") + plt.close(fig) + + +def main(argv): + args = parse_args(argv) + card = { + "member_name": args.member_name, + "member_id": args.member_id, + "tier": args.tier, + "tier_code": args.tier_code, + "card_serial": args.card_serial, + "console_serial": args.console_serial, + "member_since": args.member_since, + "valid_through": args.valid_through, + } + render(card, args.out) + print(f"WROTE {args.out}") + print(f" {card['member_name']} · {card['tier']} · card {card['card_serial']}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml new file mode 100644 index 0000000..ad792ee --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Replace (reissue) a member's BlastPass card when it is lost, stolen, or damaged. Use whenever the person wants a new/replacement BlastPass card. The skill verifies identity (name + last 4 of the console serial) before deactivating the old card, mints a new one with reissue_card, and hands over a digital card. Do not use this to cancel a membership. + 0 + card-reissue + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/data b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/data new file mode 100644 index 0000000..5f7c834 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/data @@ -0,0 +1,54 @@ +kind: InlineAgentSkill +content: | + --- + name: card-reissue + description: Replace (reissue) a member's BlastPass card when it is lost, stolen, or damaged. Use whenever the person wants a new/replacement BlastPass card. The skill verifies identity (name + last 4 of the console serial) before deactivating the old card, mints a new one with reissue_card, and hands over a digital card. Do not use this to cancel a membership. + --- + + # BlastPass Card Reissue + + Use this when someone wants to replace a **lost, stolen, or damaged** BlastPass + card. It is a card swap only — the membership stays active. Never use + `cancel_membership` here. + + Follow these steps in order. Do not skip the identity check, and do not change + anything until identity is verified. + + ## 1. Look up the membership + + Ask for the membership ID if you don't have it, then call **`get_membership`** + with it. This is read-only — nothing changes yet. Keep the details you get back + (name, console serial, tier, dates) to yourself for now. + + ## 2. Verify identity (two factors) + + Before deactivating any card, confirm it's really the account holder. Ask for + **two things**: + + 1. The **name** on the membership, and + 2. The **last 4 digits of the console serial** (printed on the bottom of the + BlastBox Omega). + + **Do not reveal** the name, serial, or any on-file detail while asking — make + them tell you. Continue **only if both match** the record (`member_name`, and the + last 4 of `console_serial`). If either one doesn't match, politely stop and do + **not** reissue. + + ## 3. Reissue the card + + Once both factors match, call **`reissue_card(membership_id, reason)`** (reason = + lost / stolen / damaged). This deactivates the old card, mints a **new serial**, + keeps the membership active, and queues the physical card for standard mail + (~7 days). + + ## 4. Hand over the digital card + + Run the **`membership-card-png`** skill with the member's details and the + **new** serial from `reissue_card` to generate `blastpass_card.png`. Never put + the old (now-deactivated) serial on the card. + + ## 5. Confirm + + In one warm, short message: the new card is ready (the PNG), the new serial, that + the old card is now deactivated, the mail ETA, and the reissue confirmation + number. Note their membership, tier, and points are unchanged. diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml new file mode 100644 index 0000000..feffbee --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Generate a digital BlastPass membership card as a PNG image by running the bundled card_png.py script. Use when a member reissues a lost, stolen, or damaged card, or any time a BlastPass card needs to be shown on screen. Pass the member details (name, id, tier, tier code, card serial, console serial, dates) as command-line flags to the bundled Python (matplotlib) script, which renders a CR80-sized card to a real PNG. It only draws the card from values you already have. + 0 + membership-card-png + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/data b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/data new file mode 100644 index 0000000..ba412cd --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/botcomponent.xml new file mode 100644 index 0000000..7341252 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + MCP server for BlastPass memberships. Tools: get_membership, cancel_membership, reissue_card. + 0 + Membership MCP v2 — Membership MCP v2 Server + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/data b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/data new file mode 100644 index 0000000..4a639ce --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/Default_draft_IsBewO.tool.MembershipMCPv2-MembershipMCPv2Server/data @@ -0,0 +1,5 @@ +kind: McpTool +authMode: Invoker +connectionReference: Default_draft_IsBewO.cr.shared_membership-20.cd48113e28c44025952903ba1e42cd0b +connectorId: /providers/Microsoft.PowerApps/apis/shared_membership-20mcp-20v2-5fee08b8354fad177a-5f8dc9e3504a9e5333 +operationId: InvokeMembershipMCPv2 \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/botcomponent.xml new file mode 100644 index 0000000..8c9f237 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Reconcile a customer's BlastPoints loyalty balance after a visit (purchases, returns/clawback, promo multipliers, tier bonus, expiry) and convert the net balance to store credit. Use when an associate asks what a member's BlastPoints are worth, or after a return that should claw back earned points. The skill generates and runs a short Python snippet that applies the rules exactly and prints a points ledger plus the redeemable store-credit value. + 0 + points-reconciliation + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/data b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/data new file mode 100644 index 0000000..5c21639 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation/data @@ -0,0 +1,101 @@ +kind: InlineAgentSkill +content: |+ + --- + name: points-reconciliation + description: Reconcile a customer's BlastPoints loyalty balance after a visit (purchases, returns/clawback, promo multipliers, tier bonus, expiry) and convert the net balance to store credit. Use when an associate asks what a member's BlastPoints are worth, or after a return that should claw back earned points. The skill generates and runs a short Python snippet that applies the rules exactly and prints a points ledger plus the redeemable store-credit value. + --- + + # BlastPoints Loyalty Reconciliation (BlastBox Omega) + + This skill reconciles a member's **BlastPoints** after a visit that involves any + mix of earning, returns (clawback), promo events, and expiry, and converts the + net balance to store credit. The points math must be exact, so you **generate and + run a short Python snippet** - never estimate points by hand. + + ## When to use + + Use this skill when an associate asks things like: + - "How many BlastPoints does this member have after today?" + - "They returned a console - claw back the points they earned on it." + - "What's that worth in store credit?" + + Confirm the member's **tier** with the Membership MCP / Store Policy Agent first, + and confirm any **promo multiplier** in effect with the Store Policy Agent. + + ## The points rules (single source of truth) + + 1. **Base earn:** 10 points per $1 of merchandise. + 2. **Promo multiplier:** promo events (e.g. **Triple BLAST Weekend** = 3x) + multiply the **base** earn **before** the tier bonus is applied. + 3. **Tier bonus on earned points:** Plus **+0%**, Plus Extra **+20%**, MEGA **+50%**. + 4. **Clawback:** returned items remove points using the **same** formula (the + promo multiplier and tier bonus that applied to the original purchase). + 5. **Expiry:** points older than 90 days while the account was inactive are + removed (provided as a number). + 6. **Redemption:** **1,000 points = $5.00** store credit, in whole 1,000-point + blocks. The net balance never goes below 0. + + ## Workflow + + ### 1. Gather the inputs + + Get the **tier** (`plus` / `extra` / `mega`), the **prior balance**, the list of + **purchases** (`amount`, `promo_multiplier`), the list of **returns** to claw back + (`amount`, `promo_multiplier`), and any **expired points**. + + ### 2. Generate and run this Python + + ```python + TIER_BONUS = {"plus": 0.0, "extra": 0.20, "mega": 0.50} + + def earn(amount, promo, bonus): + return int(round(10.0 * amount * promo * (1.0 + bonus))) + + # --- inputs (from the Membership MCP, Policy RAG MCP, and the associate) --- + tier = "extra" + prior_balance = 4200 + purchases = [ + {"amount": 499.99, "promo_multiplier": 3.0}, # Triple BLAST Weekend + ] + returns = [] + expired_points = 800 + + bonus = TIER_BONUS[tier] + lines, earned, clawback = [], 0, 0 + for p in purchases: + pts = earn(p["amount"], p["promo_multiplier"], bonus) + earned += pts + lines.append(f"Earn ${p['amount']:.2f} x{p['promo_multiplier']:g} promo +{int(bonus*100)}% tier = +{pts}") + for r in returns: + pts = earn(r["amount"], r["promo_multiplier"], bonus) + clawback += pts + lines.append(f"Clawback ${r['amount']:.2f} (returned) = -{pts}") + if expired_points: + lines.append(f"Expired points = -{int(expired_points)}") + + net = max(int(prior_balance + earned - clawback - int(expired_points)), 0) + blocks = net // 1000 + store_credit = blocks * 5.0 + + print("\n".join(lines)) + print(f"Prior balance: {int(prior_balance)}") + print(f"Net balance: {net} BlastPoints") + print(f"Redeemable: {int(blocks)} x 1,000 = ${store_credit:.2f} store credit") + ``` + + ### 3. Report the result + + - State the **net BlastPoints balance** and the **store-credit value**. + - Show the ledger lines (earn / clawback / expired) so the associate can verify. + - Offer to apply the redeemable store credit to the current transaction. + + ## Worked example (flagship scenario) + + Plus Extra member, prior balance 4,200, a Triple BLAST Weekend purchase of + $499.99 (3x), no clawback (defective warranty swap, not a points return), and 800 + expired points: + + - Earn: $499.99 x3 x1.2 = **18,000** + - Expired: **-800** + - Net = 4,200 + 18,000 - 800 = **21,400 BlastPoints** + - Redeemable: 21 x 1,000 = **$105.00** store credit diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/botcomponent.xml new file mode 100644 index 0000000..e0e3ebe --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Calculate the exact prorated refund when a customer cancels a BlastPass membership for the BlastBox Omega console. Use after the Store Policy Agent confirms a prorated refund is allowed for the member's tier and the Membership MCP has returned the membership details (months remaining, annual price, fees). The skill writes and runs a short Python snippet to compute the precise refund and shows a line-by-line breakdown. + 0 + prorated-refund-calculator + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/data b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/data new file mode 100644 index 0000000..d2630be --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator/data @@ -0,0 +1,157 @@ +kind: InlineAgentSkill +content: |+ + --- + name: prorated-refund-calculator + description: Calculate the exact prorated refund when a customer cancels a BlastPass membership for the BlastBox Omega console. Use after the Store Policy Agent confirms a prorated refund is allowed for the member's tier and the Membership MCP has returned the membership details (months remaining, annual price, fees). The skill writes and runs a short Python snippet to compute the precise refund and shows a line-by-line breakdown. + --- + + # Prorated Refund Calculator (BlastPass) + + This skill computes the **exact** refund owed when a customer cancels their + BlastPass membership. The math must be exact and consistent, so you **generate + and run a short Python snippet** to do the arithmetic — never compute the refund + in your head or with mental math. + + ## When to use + + Use this skill in a BlastPass cancellation, once you have: + + 1. The **Store Policy Agent** has confirmed the member's tier and that a + **prorated refund** applies (it reads the tier policy documents). + 2. The **Membership MCP** (`get_membership`) has returned the membership record + for the customer's `mega-blast-customer-id`, including `annual_price`, + `months_remaining`, `cancellation_fee`, `nonrefundable_credit`, and + `within_cooling_off`. + + Do **not** invent any of these numbers. They come from the Membership MCP and the + Store Policy Agent. + + ## The refund rules (single source of truth) + + These rules mirror the BlastPass policy documents the Store Policy Agent holds: + + 1. **Cooling-off (full refund).** If `within_cooling_off` is true (membership + activated 14 days ago or less **and** under 2 hours streamed), refund the full + `annual_price`. No fee, no proration. + 2. **Prorated cancellation (the usual case).** Otherwise refund the value of the + whole unused months left on the 12-month term, then subtract the tier + cancellation fee: + - `monthly_value = annual_price / 12` + - `prorated = round(months_remaining * monthly_value, 2)` + - `refund = prorated - cancellation_fee` + 3. **MEGA welcome credit.** Subtract `nonrefundable_credit` (only the Plus Extra + MEGA!!! tier has one — $20). For other tiers it is 0. + 4. The refund is **never negative**. If fees exceed the prorated value, the refund + is `$0.00`. + + ## Workflow + + ### 1. Collect the inputs + + From the Membership MCP `get_membership` result, take: + `annual_price`, `months_remaining`, `cancellation_fee`, `nonrefundable_credit`, + and `within_cooling_off`. Confirm the tier matches what the Store Policy Agent + quoted. + + ### 2. Generate and run this Python + + Write and execute a snippet like the following, substituting the real values: + + ```python + def money(v): + cents = int(v * 100 + (0.5 if v >= 0 else -0.5)) + return cents / 100.0 + + # --- inputs from the Membership MCP --- + annual_price = 129.99 # get_membership.annual_price + months_remaining = 8 # get_membership.months_remaining + cancellation_fee = 10.00 # get_membership.cancellation_fee + nonrefundable_credit = 0.00 # get_membership.nonrefundable_credit + within_cooling_off = False # get_membership.within_cooling_off + + lines = [] + if within_cooling_off: + refund = money(annual_price) + lines.append(f"Within 14-day cooling-off and under 2h used -> full refund of ${annual_price:.2f}") + else: + monthly = annual_price / 12 + prorated = money(months_remaining * monthly) + lines.append(f"Monthly value: ${annual_price:.2f} / 12 = ${monthly:.4f}") + lines.append(f"Prorated: {months_remaining} unused months x ${monthly:.4f} = ${prorated:.2f}") + refund = prorated + if cancellation_fee > 0: + lines.append(f"Cancellation fee: -${cancellation_fee:.2f}") + refund = money(refund - cancellation_fee) + if nonrefundable_credit > 0: + lines.append(f"Non-refundable MEGA welcome credit: -${nonrefundable_credit:.2f}") + refund = money(refund - nonrefundable_credit) + refund = max(refund, 0.0) + + print("\n".join(lines)) + print(f"REFUND DUE: ${refund:.2f}") + ``` + + ### 3. Report the result + + - State the **refund due** clearly (e.g. "The customer is owed **$76.66**"). + - Show the breakdown lines so the associate can verify how it was derived. + - Offer to cancel the membership via the Membership MCP (`cancel_membership`) + using the agreed refund amount, and to generate a return/RMA slip. + + ## Worked example + + BlastPass Plus Extra, `annual_price = 129.99`, `months_remaining = 8`, + `cancellation_fee = 10`, `nonrefundable_credit = 0`, not in cooling-off: + + - Monthly value = 129.99 / 12 = $10.8325 + - Prorated = 8 x $10.8325 = $86.66 + - Minus $10 fee = **$76.66** + + ## Upgrade settlement (defective swap → higher model) + + Sometimes the membership cancellation happens alongside a **console upgrade** — the + customer's defective unit is credited under warranty and they put the value toward a + pricier model, applying the membership refund as store credit. The **same exact-Python + rule** applies: never compute the net in your head. + + ### When to use + + After (a) the Store Policy Agent confirms the defective unit is credited at its + purchase price with no restocking fee, (b) the Warehouse MCP returns the upgrade + model's price, and (c) the prorated membership refund above is known. + + ### Generate and run this Python + + ```python + def money(v): + cents = int(v * 100 + (0.5 if v >= 0 else -0.5)) + return cents / 100.0 + + # --- inputs --- + defective_credit = 399.99 # warranty credit for the defective unit (its purchase price) + upgrade_price = 499.99 # Warehouse MCP price of the new (MEGA) model + membership_credit = 76.66 # prorated refund from the section above, applied as store credit + + upgrade_difference = money(upgrade_price - defective_credit) + net = money(upgrade_difference - membership_credit) + + print(f"Defective unit warranty credit: ${defective_credit:.2f}") + print(f"Upgrade model price: ${upgrade_price:.2f}") + print(f"Upgrade difference: ${upgrade_price:.2f} - ${defective_credit:.2f} = ${upgrade_difference:.2f}") + print(f"BlastPass refund applied as store credit: -${membership_credit:.2f}") + if net >= 0: + print(f"NET DUE FROM CUSTOMER: ${net:.2f}") + else: + print(f"REFUND TO CUSTOMER: ${-net:.2f}") + ``` + + ### Worked example (eval scenario) + + Defective base console credited $399.99, MEGA edition $499.99, membership refund + $76.66 applied as credit: + + - Upgrade difference = 499.99 − 399.99 = **$100.00** + - Net = 100.00 − 76.66 = **$23.34 due from the customer** + + Hand the final numbers to the `slip-pdf-generator` skill to print the slip. + diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/botcomponent.xml new file mode 100644 index 0000000..65fd9df --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill fills a fixed markdown slip template with the agreed values, then generates and runs a short Python (reportlab) snippet to render that markdown into a real PDF file. + 0 + slip-pdf-generator + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/data b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/data new file mode 100644 index 0000000..9d54c59 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator/data @@ -0,0 +1,188 @@ +kind: InlineAgentSkill +content: | + --- + name: slip-pdf-generator + description: Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill fills a fixed markdown slip template with the agreed values, then generates and runs a short Python (reportlab) snippet to render that markdown into a real PDF file. + --- + + # Return / RMA Slip PDF Generator + + This skill produces the **printable slip** that closes out a transaction at the + returns desk. You first fill the **markdown slip template** below with the final, + already-confirmed numbers, then you **generate and run a short Python snippet** that + renders the slip to a **PDF** using `reportlab`. Never hand-draw the PDF or invent + amounts — every value comes from earlier steps (Policy Agent ruling, Membership MCP, + Warehouse MCP, and the `refund-calculator` skill). + + ## When to use + + Use this skill **last**, after every amount is final: + + - the eligibility / warranty ruling is decided (Store Policy Agent), + - any membership refund is computed (`refund-calculator`), + - any upgrade settlement / net-due is computed (`refund-calculator`), + - the customer has agreed to the outcome. + + Do **not** use it to *decide* anything — it only renders the agreed result. + + ## Slip types + + Set `SLIP_TYPE` to the right label: + + - `WARRANTY SWAP` — defective item replaced under warranty. + - `RETURN / REFUND` — item returned for a refund. + - `EXCHANGE / UPGRADE` — item swapped for a different model with a price difference. + - `MEMBERSHIP CANCELLATION` — BlastPass cancelled (often combined with the above). + + For the eval scenario the slip is an **EXCHANGE / UPGRADE** (defective base console + → MEGA edition) that also records the BlastPass cancellation credit. + + ## The markdown slip template (single source of truth) + + Fill every `{{...}}` placeholder. Omit a whole line only if it does not apply. + + ```markdown + # BlastBox — Returns & Service Slip + + **Slip type:** {{SLIP_TYPE}} + **RMA / Ref #:** {{RMA_REF}} + **Date:** {{DATE}} + **Store / Associate:** {{STORE}} / {{ASSOCIATE}} + + ## Customer + - Name: {{CUSTOMER_NAME}} + - BlastPass ID: {{MEMBER_ID}} ({{MEMBER_TIER}}) + + ## Items + | Item | SKU | Disposition | Amount | + | --- | --- | --- | --- | + {{ITEM_ROWS}} + + ## Settlement + {{SETTLEMENT_ROWS}} + + **{{NET_LABEL}}: {{NET_AMOUNT}}** + + ## Notes + {{NOTES}} + + _Keep this slip for your records. Warranty swaps carry the remaining original warranty._ + ``` + + ### Field guidance + + - `ITEM_ROWS` — one markdown table row per line item, e.g. + `| BlastBox Omega (defective) | SKU-OMEGA | Warranty credit | $399.99 |` + - `SETTLEMENT_ROWS` — one `- label: amount` bullet per settlement line (upgrade + difference, membership refund applied as credit, etc.). + - `NET_LABEL` / `NET_AMOUNT` — e.g. `NET DUE FROM CUSTOMER` / `$23.34`, or + `REFUND TO CUSTOMER` / `$76.66`. + + ## Workflow + + ### 1. Fill the template + + Substitute the confirmed values into the markdown above. Keep amounts formatted to + 2 decimals with a `$` prefix. + + ### 2. Generate and run this Python to render the PDF + + Build a `slip` dict and run a snippet like the following. It parses the simple + markdown the agent produced and lays it out with `reportlab`. Write the PDF to the + working directory and report the path. + + ```python + from reportlab.lib.pagesizes import LETTER + from reportlab.lib.units import inch + from reportlab.lib import colors + from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table, + TableStyle, HRFlowable) + from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle + + # --- confirmed values (fill these in) --- + slip = { + "slip_type": "EXCHANGE / UPGRADE", + "rma_ref": "RMA-OMEGA-1024", + "date": "2026-06-07", + "store": "BlastBox #214 — Seattle", + "associate": "Associate Desk", + "customer_name": "Jordan Pixel", + "member_id": "MEGA-BLAST-1024", + "member_tier": "BlastPass Plus Extra", + "items": [ + # (item, sku, disposition, amount) + ("BlastBox Omega (defective)", "SKU-OMEGA", "Warranty credit", "$399.99"), + ("BlastBox Omega MEGA Edition", "SKU-OMEGA-MEGA", "Upgrade (new)", "$499.99"), + ], + "settlement": [ + ("Upgrade difference ($499.99 - $399.99)", "$100.00"), + ("BlastPass Plus Extra refund applied as store credit", "-$76.66"), + ], + "net_label": "NET DUE FROM CUSTOMER", + "net_amount": "$23.34", + "notes": ("Manufacturing defect confirmed within 30 days - no restocking fee. " + "'MEGA Lizards from Outer Space' requires the MEGA Edition. " + "BlastPass Plus Extra (MEGA-BLAST-1024) cancelled; 8 unused months prorated."), + } + + styles = getSampleStyleSheet() + h1 = ParagraphStyle("h1", parent=styles["Title"], fontSize=18, spaceAfter=6) + h2 = ParagraphStyle("h2", parent=styles["Heading2"], fontSize=12, spaceBefore=10, spaceAfter=4) + body = styles["BodyText"] + + doc = SimpleDocTemplate("blastbox_slip.pdf", pagesize=LETTER, + topMargin=0.7*inch, bottomMargin=0.7*inch) + flow = [] + flow.append(Paragraph("BlastBox - Returns & Service Slip", h1)) + flow.append(HRFlowable(width="100%", color=colors.HexColor("#6b2fb5"))) + meta = (f"Slip type: {slip['slip_type']}    " + f"RMA / Ref #: {slip['rma_ref']}
" + f"Date: {slip['date']}    " + f"Store / Associate: {slip['store']} / {slip['associate']}") + flow.append(Paragraph(meta, body)) + + flow.append(Paragraph("Customer", h2)) + flow.append(Paragraph(f"{slip['customer_name']}  |  " + f"BlastPass {slip['member_id']} ({slip['member_tier']})", body)) + + flow.append(Paragraph("Items", h2)) + item_data = [["Item", "SKU", "Disposition", "Amount"]] + [list(r) for r in slip["items"]] + t = Table(item_data, colWidths=[2.6*inch, 1.3*inch, 1.5*inch, 1.0*inch]) + t.setStyle(TableStyle([ + ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#6b2fb5")), + ("TEXTCOLOR", (0, 0), (-1, 0), colors.white), + ("FONTSIZE", (0, 0), (-1, -1), 9), + ("GRID", (0, 0), (-1, -1), 0.5, colors.grey), + ("ALIGN", (-1, 0), (-1, -1), "RIGHT"), + ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.HexColor("#f3eefb")]), + ])) + flow.append(t) + + flow.append(Paragraph("Settlement", h2)) + for label, amount in slip["settlement"]: + flow.append(Paragraph(f"{label}: {amount}", body)) + flow.append(Spacer(1, 6)) + flow.append(Paragraph(f"{slip['net_label']}: {slip['net_amount']}", h2)) + + flow.append(Paragraph("Notes", h2)) + flow.append(Paragraph(slip["notes"], body)) + flow.append(Spacer(1, 10)) + flow.append(Paragraph("Keep this slip for your records. Warranty swaps carry " + "the remaining original warranty.", body)) + + doc.build(flow) + print("WROTE blastbox_slip.pdf") + ``` + + ### 3. Report + + - Confirm the PDF was written and give its filename. + - Restate the **net amount** (due or refunded) and the slip type. + - Offer to email or reprint the slip if needed. + + ## Notes + + - Use `reportlab` (it is the available PDF library). Do not depend on + `weasyprint`, `fpdf`, or external network calls. + - Keep the layout to a single page for a normal transaction. + - Escape `&` as `&` in any text passed to `Paragraph`. diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml new file mode 100644 index 0000000..f30b9d9 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Delegate to the Inventory & Fulfillment Agent for anything about stock and consoles - whether a SKU is in stock locally, restock/ETA dates, in-stock alternatives or upgrades, which BlastBox Omega model a game requires, and the MEGA-only exclusive titles used to upsell a customer choosing between the base console and the MEGA Edition. + 0 + Inventory & Fullfilment Agent + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data new file mode 100644 index 0000000..e1c3b0a --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_InventoryFullfilmentAgent_X-w2GP +historyType: + kind: ConversationHistory diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json new file mode 100644 index 0000000..52b1d1d --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_InventoryFullfilmentAgent_X-w2GP"}] \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml new file mode 100644 index 0000000..6a2d654 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Delegate to the Store Policy Agent for any policy ruling - returns and warranty eligibility, defective-vs-accidental damage, return windows and restocking fees, and BlastPass membership refund/proration rules. Ask it before ruling on a return; it establishes the defect cause first and may return a clarifying question to relay back to the associate. + 0 + Store Policy Agent + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data new file mode 100644 index 0000000..6f26d5d --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_StorePolicyAgent_s9s-u8 +historyType: + kind: ConversationHistory diff --git a/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json new file mode 100644 index 0000000..789e87d --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_StorePolicyAgent_s9s-u8"}] \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml b/submissions/blastbox-demo-agents/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml new file mode 100644 index 0000000..5c9f448 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Inventory & Fullfilment Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json b/submissions/blastbox-demo-agents/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json new file mode 100644 index 0000000..ea735b3 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the Inventory \u0026 Fulfillment Agent for BlastBox Omega stores. You support the Returns \u0026 Service Assistant with stock, alternatives, restock, and game compatibility.\u200B\u200C\n\nTools:\u200B\u200C\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.\u200B\u200C\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.\u200B\u200C\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.\u200B\u200C\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.\u200B\u200C\nRules:\u200B\u200C\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.\u200B\u200C\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).\u200B\u200C\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.\u200B\u200C\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.\u200B\u200C\nBe concise. Always include SKUs, prices, and quantities you looked up.\u200B\u200C\n\n\nInventory aging (merchandising reviews):\u200B\u200C\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.\u200B\u200C\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.\u200B\u200C" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml b/submissions/blastbox-demo-agents/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml new file mode 100644 index 0000000..b58beff --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + iVBORw0KGgoAAAANSUhEUgAAAJoAAACaCAYAAABR/1EXAAAQAElEQVR4AbT9abeubXaWh53n+wGJMYzoDISEDxaqKjn/IsY01Yr8AAjIRqAG9bEBx0mc2LHBtqCkkiAew07+QcCoOmHnd0TU+wrHqEFIEBqNYUl82TmOOa/7fp61u5IZ6FqzOec5zznvZ+261rP3XnvXfj/4E3/jH736E3/jH+Mn/3Wy/hP/6NWfxP8Ebv6TP0797F/45Vd/8gtwP/bLr/53z/6j1M/+eerxX3r1p/7aL+Nm/K++5j/yS6/+NP6n/kvys/8X//DVn372//wfvvp2/E/j3/5XwJf/ZfD4L776dvN/9ouv/h382/9T8rP/36if/T/5B6/+3f/kF/GT/2Oy/n8lP/v/5R+8+jOX/0fg/+gXXv0Z8/+ZfPw7zP+nX3j1Hc/+f6TW/8Off/Ud/yGY/Gfx7/g//PyrP3v5fwC+/C+Bj/8581/8uVd/7tn/ArX+75OPfyf5O/+9n3u1/rOb//dk/YfJx7+L/F0/9LOvXvgP/v1X3/WDcD/w91999w/87Kvvmvz3X33397/m30etf+/ff/U9+Hd/7/84+Xv+PPnZv4da/+7/8dWfx78HN3+QlI9wNhvHK9XTA2PFNXPplKIl6lOD5d7q9LCgG2em4CYTzeFAByKe5Ur57NvppIn0KYAYuHjwJsZJhqkMsJNO3t4y4RQeb9I2TW7PdSRhTW0HGTMo9+lB5rbBQoQ1djIhrTgxCsezp8tSiEoOTDMaQvP6aQpV4niTtmk8G0W6VemUoiWOEeCgiOKQn1wK77DhiHrnGy24VVy0V3mFLK+I+FZiSOppUlINXF3AF0keHZNmhDAjQ6TBYPauxmD0dIUrRqONBkCenhrdQqfl06FIijZNTe+VGmhYq5MgtLsHTb0zAqXk6fO6yNiINtO/TAIpzXnZ8zwXDT+NVQKlHi0JWpK6DV16antspZa+/QJo5lnT9zWqp8BGQjk2OvYM+bpO8agU7OtXJ82ccEkJJWRNP2Orobj0EDwFsaJBB6t5+AflzpW65OBXDmdwA/vsEAnc5kVgja8KEj3ZzLEeADtYjT51idt9jqVAkvH0/ggoc3rH0VBih3tGi8vc6kQjg9kcUOPpoJasSw0zYEJPNNdeY0yI2uRkIK3NANaBAfGY13vYK1NqiiJoPAU+eeFYaAKNXdjcUedFtIJI23iMDwdhyQRSQTpwkHk9z4dd2ChU65kqIcXTE9R94FfJODfT2yw2b+kt5YIysDx4brL8w597tmeWmTGJ46vj1lM/MCpWObNuf5+jRvf1wK5wtARtnM7Jt479gydfuxxXqxg8DyOjuZ9xsDXQQQQYI7tPvDvULEdTMYmBNfBQkycg3TwCn43AHaTpmZdWd56zxOlfz92erXECE6y1bzKvMwjx0Ps85Ev7QAaXG4B2M9O3RtnO2Lt2iVHRdP5yh6DYs7rFqzvvaN1LyNUrmjEAdkH6W7VkfRgwCuOzQ9E16gU3YabxA3hyONChjKcTOqVxfcjh8hSdG4dr8hSpbCTLWSbgzOkbSAZv0jZNbs91JGFNbdPiKd1OzIu4FZKMpwle3ByOuJOT0VBg8TQ14QWVnMmrKzj36aAOZyx127Q4OGmej9XtaGzXcHxxTnVyE4ka4hEVUBg9m5sAtMxhPwYsXPMB9y1zBbmk3khv7+VSF7c6pbBeVdybDLNyaIFJ7pozjw69z9l0q6Ruf+49ZtyGHnPXPAO8WkYF1CCEgKnJVoMBmNDZ1S2xHJ8ZAKMltodf5iqdYTX369riqBBoOLJ9mdOnPfk0puljeA4tzbZ0LskUdGjMs4AupHQQh1B7JRoYU5LsnQKMOYdsjXpaKIfYgn2nAc80NSY1IgKY1hOpCnLmxWgUQJnGh6InBwHKvKOFOxeOd0/oTezUoRQ98qKmPZ6kyXg4YhLW5W5dH9x2TsxkZBmPOjiLCz9l6fHs6emFvNiowyBs9gAXjC4T7bVNi8dzZXHS7DGXCpkRsglFS05ivB0g3WHD6e2F61QFAbDiYwDnQgc4sROT0Gg8xLWQjvfOAV0VMG3jMT5ctOwgNI0fmdiEvJ7no45m6V6ewblPRQRsOpszeH6NNjef2zdfQZO9wNzDwRMkblf38ENzc7XdtTNqrM364lwDloPtPbsNa7M7Fyv1NemL7W/vbc9bzfbPM0nXjPxLp+lXKclnro4C7tItt896YJ+jv+N1uYwdl37harf1+j57z76vQa07dCZIz5rFkL6Q4yaUPBCjJX5wEJgcsyy/NGbK07uefXQ0rx8LMyKNpaujDWYfgMga4oy+8h0N0TFvoNDcuYdJW0OSzkc4favDYkE1zlzBTSaaw4EORDzLlfLZt9NJE+lTADFw8eBNjJMMUxlgJ528vWXCKTzepG2a3J7rSMKa2g4yZlDu04PMbYOFCGvsZEJacWIUjmdPl6UQlRyYZjSE5vXTFKrE8SZt03g2inSr0ilFSxwjwEERxSE/uRTeYcMR9c43WnCrwn7s1gFyJJM/4NpxC6VP4jbKcRcPOVeSJow9WJj7Jouhpu/cNNThTEy5YVToyA7hSKZY3U0M99jFCK2p7ThkPQQAm/3k/SpDhCkbyYZdIomrIw23mYHLJNhFc9dau8Os3zoU6nBpdyo7Q6hoSJCmBzMltfn2C7BkdNPnR+Tkq+34+Oh49mlQUiDG5tkjIlDbYxN9a4krg4XuAK4OQqOWnl1iit1BgZDIPiJGC8zQYFT0JUCmxwokH3jdnm9eJWhcOdSDC+LKkmDA2WN9ocFqdFQd3+5zLAWSjKf3R0CZ0zuOhhI73DNaXOZWJxoZzOaAGk8HtWRdapgBE3qiufYaY0LUJicDaW0GsA4MiMe83sNemVJTFEHjKfDJC8dCE2jswuaOOi+iFUTaxmN8OAhLJpAK0oGDzOt5PuzCRqFaz1QJKZ6eoC6QHT+oZD175h1tLyRR81a+5tdXmtmWNxnpbpA4bt9rbNYXI1N8O/cdbP/y0blUp2c9ELwavkCuZzwayBRszzYEthwsD8ZouIMG5rMv3x5xpaOjmrwaBjD36fTm2cgtb19wPYMuwgfHutlhnnEBbUhKALWzD3dc3oyvirW+JpN53R20JXGMXdaTJlyc+x7O9I6iwRBhAiSnIQFECX+9NgiNHq9r+CvPII9WT/vuLXboA6Tcw+BcvyQtWU/nI5y+5pT0jHrBTZhp/ACeHA50KOPphE5pXB9yuDxF58bhmqS5ItlGsh3LBJw5fQPJ4E3apsntuY4krKltWjyl24l5EbdCkvE0wYubwxF3cjIaCiyepia8oJIzeXUF5z4d1OGMpW6bFgcnzfOxuh2N7RqOL86pTm4iUUM8ogIKo2dzE4CWOezHgIUrOeSXHk5LD+cdjds4RqDh7bvcmzwUgUvK9RyjwiQcIY+ObHeTDTSa8Phz7zFzRKMhaMf5EuGlWBzNEjwGDmp3ALDdDVAzab+6WIA9Y5o7DsDEOnPu2J0QWyDQrM9jheMGereOmh1jcFZ0eTbR4nIFUDbmWTcGMDdttZb46nj2aczMrTvC0aGh5DO1uoqTaTA/PeA1PkJqWkcnowpyROLlVgOmRRy9EgAtSE2f5rIDFeHn+2jhNnLz4unglqxD9TiJnlHvYjX6VIcbnBM3I8l4Gk9r1gPzyNLj2VO6D1RgYfSk3RwOkKgtZyxl2ycdGO6yHmBu+GiMCTFnLhzo3A6gRQ2gF9DlBTeZaA7nypBxLikfG0UNh8ZkOwBMdLx3DuiqgGkbj/HhomUHoWn8yMQm5PU8H3U0S/fyDM59KiJg09mcxRSsAGdOb2RZ/mSA2+ZXzbP7VfNwbufc1AmnEHPbz+yldYeUmWs+yXD1r3xx5tW5z9W70x1q7V/5GctdGrE77MdDQ+5Np0mPyIOIYAx8PdPXsHh20XSHu4FQ9pS/pnsIXmic2dbqaWKL3fvwfS1qEfiAA33eS7/6m5Ve+y585Ys/mY2+nn2mz7t04tW4c/uPZy6nRpe/Mtid+oxT8wCMkSGeMjPoPmib6Ol8hNO3OiwWVOPMFNxkojkc6EDEs1wpn307nTSRPgUQAxcP3sQ4yTCVAXbSydtbJpzC403apsntuY4krKntIGMG5T49yNw2WIiwxk4mpBUnRuF49nRZClHJgWlGQ2heP02hShxv0jaNZ6NItyqdUrTEMQIcFFEc8pNL4R02HFHvfKMFtyrsx24dIEdycjcrwqsABxKT82u0p1sITcWNXCAG+SWAg/bagrnF3FTLFdPTHDg+PTW6ha6GWps5tEOTbflVNb0JkNhDR4Gtjjh4wrye1fG6WIgNtxntZRIzok6JBT48+dbZo4DalsDajD+9vnnNtKTUmm+/AI3Rzeg+e18vg3KksdHx7DNHSYEAG/2ICNT22ETfWuLKYKE7gKuD0KilZ5eYYndQICSyj4jRAjM0GBV9CZDpsULJ1TPrcGMz60XjHhYf4/qV7uVAaRPexWp0KpnSed3kkGQ8vT8CypzecTSU2OGe0eIytzrRyGA2B9R4Oqgl61LDDJjQE8211xgTojY5GUhrM4B1YEA85vUe9sqUmqIIGk+BT144FppAYxc2d9R5Ea0g0jYe48NBWDKBVJAOHGRez/NhFzYK1XqmSkjx9AR1gex4Mrkweva0FKcfcjgyQlvzjnZ9pZnnMnILMaSYxHH7XmOzvljNs3PfGbZ/+eiuLw961pa79qGXp+KrBRFN51cHCzV9eDlEPBSjXp2aZ98ecaWjo5q8usc+lmMjJCOxdXyJ6xmj8QUgkiOhc5+76Q4hhnuho6aHKcLV4Gh4ArV9k3mdxRBoHELk85Av/YKbJtrNTN8aZTtj79olRkXTnZc7BMWe1S1enSRICQ5ixWOvNcQMMDsNJqAQY/xZp7Q379kvzhyuZPFwLc1N0vnIHOhQxtMJndK4PuRweYrOjcM1eYpUNpLlLBNw5vQNJIM3aZsmt+c6krCmtmnxlG4n5kXcCknG0wQvbg5H3MnJaCiweJqa8IJKzuTVFZz7dFCHM5a6bVocnDTPx+p2NLZrOL44pzq5iUQN8YgKKIyezU0AWuawHwMWruSQX3o4LT2cSPXSoCHKXPk1Wrh5+ty+xZQACIzriAHmtnpz6Q4ma7S8wPrQExyncTDVGIsmS+s7o+5pkQ0dyq80NcAZXYz+ECMjqCOx+3p9Co4r1xlWo3aWbfEQoZFCdtoStCV1G7o0NYnmPJIAlNDVULpknnVjAHPTHt2OrU4s6esn3zowY2PApQESWzBILWYx09SYlBodTOuJVAXpDA5CRcRWR6nZk4N0guRLNdklT/PsPdiOUFdhnj28o5Xm5UBun1HvYq5lKy5kl3sRMxWSjKfxtGY9MI8sPZ49pftABRZGT9rN4QCJ2nLGUrZ90oHhLusB5oaPxpgQc+bCgc7tAFrUAHoBXV5wk4nmcK4MGeeS8rFR1HBoTLYDwETHe+eArgqYtvEYHy5adhCaxo9MbEJez/NRR7N0L8/g3Kcindh9HAAAEABJREFUAjadzVlMwQpw5vRGljUsA1xdpx5M+IALd24q1w/bAkBjviJPfmB2wnFhR2qw9+wXZ16d+7z8fG0wi9GS25rCJunBXRr3uoMmD8ZoyL3p2yOyizi6Kz+e49zsmr7PQ26aWjwFks0w2GJnkU3P7OuSgxjNhc0P39eg9tYNta/podtnXBr19qx91mIf89Atxx7El8ZMyZi6eZBD4+qfHZFGb3U7yz4AkTVE11iPIyU75J7NzKK8MX17D9+ZD4punYhl7yGpIB04KHO4nKGMpwaKvnBJGfKIQzcxNCEV3zyxSW4dxVpIx0XhPHLpONKKQrWe61TQ5UcjLmSHA4x1YoZrGyzEZGITcjitOFMJx7Ony1KISg5MMxpC8/ppClXieJO2aTwbRbpV6ZSiJY4R4KCI4pCfXArvsOGIeucbLbhVYT926wA5kpO7WRFeBTiQqHX6IYrkxxOY8CcDOYeb6MX0Zl7OfR644dKRueVqHeG+AlFa6LTlIEkINXxqet70xYecRNCcJyPbRw6ecHaB0biDNNxmJ45LIKM5O9SKT3FEJDRKfS3m0cE9dFOcUfHLGefGJ9Bnyb1jsHp5M37Z6fEjBkNEss+0tCBrQKRsF0BsweuxxqXojlFiMJhgnIAxgAQw8zwPyeN5FLQQIKOnhgJEjdHDEIlFdNTowxKWHkBXIetBhzfRzLyjCcK9azhcw7ZTbYR7zUqNJOPp/RFQ5vSOo6HEDveMFpe51YlGBrM5oMbTQS1ZlxpmwISeaK69xpgQtcnJQFqbAawDA+Ixr/ewV6bUFEXQeAp88sKx0AQau7C5o86LaAWRtvEYHw7CkgmkgnTgIPN6ng+7sFGo1jNVQoqnJ6gLZMcPKlnPnpbi9EMOR0Y4LUD1Jm3T7LnyfB+NK3jfRLFfmfpiBryWt+9ttX/56PhKYMmYtaUXfzXQW0Cz6MKTtyd0Tj2i81CTz9sZew/fHpEFRBZg4NWre+xzHo2LhS98i0vPAoQPzp32zDMuoD27lwA+nmkb4l4jUC5nb3etXs4VV7ZnfekWM70EqxYzvSPwGDwmoH0aEkCU8O7VITR6for2rjyDPG45tYyNTuzQlZnA0Gv4c0+Nc5NRaWI1H4S7VzxzCwfF2OyBDkQ8w0GYC7m+nZrgTGJkGU/5CN5kItlGMhVBGw+nbyAZvEnbNLk915GENbVNi6d0OzEv4lZIMp4meHFzOOJOTkZDgcXT1IQXVHImr67g3KeDOpyx1G3T4uCkeT5Wt6OxXcPxxTnVyU0kaohHVEBh9GxuAtAyh/0YsHAlh/zSw2np4USqlwYNUebwJm3T5PZweqp5R7tunVfYG+kNRLM2txZIfu49ZujxtaDN3NGtlo0COBBCwNRkq8EATDg7NiDfry53+qztXxwDl7lKZ06NWmduHx0CDUd2WhTYKVBNwXMxFllBTtuZ2wfQUYODHDg6prBbYhMNBrWNx+ujaYM0RtuSz3DKWQi3ecDuAK5uZZA8H3yTbrhEYnqKoEzjQ9GTgwDNjnsF/eHIj+db4M7ozpkZ8nMiseOeYowmBjlDH8RbqKcQnZgXcSskGU/jac16YB5Zejx7SveBCiyMnrSbwwESteWMpWz7pAPDXdYDzA0fjTEh5syFA53bAbSoAfQCurzgJhPN4VwZMs4l5WOjqOHQmGwHgImO984BXRUwbeMxPly07CA0jR+Z2IS8nuejjmbpXp7BuU9FBGw6m7OYghXgzOmNLGtYBri6Tj2Y0FFkuHBaGAyINfc7mreSa6hxCbmKGi5h79kvzszFJiHUuNarc8UQp/cSP2vE7kCYOfcOvjpeYLrURJYTwRj4oXPH7nt+3khoqRM/98A+3EW4s6Rbuy00Sw5PwNx1+b4WtTR8wIFX/5Gv/malu3vXix+cGl/PZDZeGjMl9OoBDo2rf3Z7l/6Bz+thCUgaByHEBhMw98vz+aGF0N7yHCl0apD6fIVXHprF87vOcg+LSPMiUgpPKvnZbVmTRxz6iaEJqfjmiU1y6yjGoJIAcWM4j1zZJm3T5PZcRxLW1HaQMYNynx5kbhssRFhjJxPSihOjcDx7uiyFqOTANKMhNK+fplAljjdpm8azUaRblU4pWuIYAQ6KKA75yaXwDhuOqHe+0YJbFfZjtw6QIzm5mxXhVYADiVqnH6JIfjyB0RtPX6tOyffRuLjeQEXjU4PIXMQBc18tdBju8NjMXTqyrb3JgwiQ2ENHgdEYam77BvbRcD/uDtJwm504LoGU5uxQKz7FEZHQKHW9eXRwD90UZ1T8csa58Qn0WfK8g5JZ+Z0jrtHAmKJnnETA5tmrYhaD88cW2VWcTAPd1dt9EJqtcQLGAOOAEc0ElDViEk1qMT01EKDhLJXQ5aWJ6AwplsWF+j23GmVQGiLWSejDMIDBjknNO9oon0LBc1sB5UpeHnDm9I4P3VAotpdBiysuDGJSPFeOvXg6qCXrUsMMmNATzbXXGBOiNjkZSGszgHVgQDzm9R72ypSaoggaT4FPXjgWmkBjFzZ31HkRrSDSNh7jw0FYMoFUkA4cZF7P82EXNgrVeqZKSPH0BHWB7PhBJevZ01KcfsjhyAinBajepG2aPXeGiz407NpVXe9o1N7AcW8sF5Hb6VewznXnUk6ThlqwNv7Q3zpmB09mhIwJoB1iBxuNEJice56d7gyZdXtXFj9mWMBueu5ceqnBE6g3r3Cxn5uPuPKMD8EuMk9BThTfvj214wS2HZ3poeehEA+9z0G+NPsWMz34kdlwa2w9dNeu1brv2R269A/MtkOCpHEQKx57rSFGxzOmwUuHQowB6O2z5NErw+UQYKtxlAIRBiVW/cFcQm4fM5kEYS7V+nZqgjOJkWU85SN4k4lkG8lUBG08nL6BZPAmbdPk9lxHEtbUNi2e0u3EvIhbIcl4mk988zfk2/7QN+WH/91//eF/Bqx/B/n4D03+PTH/0HeY8T/7r+eH/uzvyef+yDelvZ6XOT3R3PiRtGQ9nhrGP/Et3xD9B7/n9+Szf+yb8tlP/vZ85pPflE987Bvpq2v8oCDn4U0saohHVEBh9GxuAtAyx9cgB1NcroRnp0wLgxMtXzg0dTMfTdqmye3h9FQFRyzQEytCyfV3neHqrXNpwV5DE3kIel5TSm/nQPC0JhuO5kXz2mFPTEYKAmAswCi57wDsYHv4Zc7o7FZzfQXta5kGSrKGI5vWJ/6N35If/vbfnf/qP/79+eF/53fns3/oX+PC/Zav738QzQvnglB/5g//tvyNv/y/zCf+4DfwOnkk5uN82LwuH4w/Xp+CV/Fy/fUf+QP5ge/+PeMf58J9mot2+Q9w8X7ir/2BuXT8SMzu3ce85kPGCZiC1VEgJKIiYjzeNjVmTw5y9bSoMZpg+AUwaAcbKLfF1GCzThOdFIhPG4S5afzCZp0lm5x9xUVjqjqBCzu3jzItRPQQH1l6PHtK94EKLIyetJvDARK15YylbPukA8Nd1gPMDR+NMSHmzIUDndsB38rF8HJ94pt/C91/tfaZP/LbHs9idal6cgZv9YmPfUN+8Fyu/AbOp3l3+4E//3t5h/sGtmQ8z2c+38D39q1yn4oI2NGErDeMr2cPzIANnTSRMNqUj2QwodlzZ7hchQCMjchcuFKNjPDi+2h+NepcV4z7yA3mYh7MvaW2Tzrc3lYKm6SdedaI3UGTx2IMy73p2yOyizi6Kz+e49zsmr7PQ26aOvnsv/WvzTsZk78p5jsaj+Ml7Gvy9VwOeb2Y/MB3/Z747vU/50V8nMv5/d/7e2N25/3jxufmbrlnX44n7AuynOePhmFe4XBTswOjRgyQo9B2wXBCpgZf2ZVg9tFF/3J+aPQ0NMVQ6OF8Bgl+3tE6d+8RXWdF5iYS6RObySU2ecQpDIcDYvStdatwHrl0Xd2KQrWe61TQ5UcjLmSHA4x1YoZrG9/JPvdv/bb8Zp6v/b1f3+cRqzdpm8az0Xcyq39Z97J94uP+2s0NZXcBndwLLYCD0HwNcjDFhyL09g7bNsGJ8QBNeFNiiNUL0hMqnSIBm0sOpxlAEmwqlL5ME+r4Uye3zzu4Vy97vLWg4eaSEqgxbyly0TpSruz0Hz3K4ZGseInZx/PI2HCb1R2XcJa5gQbwPmMaKwS+bL3K5/43v7mXzAd/+DO/vi/F14Tva/DF0KX4DL/++p/7TsbkG/ZpfrPAenh+vATs9sE8aX7cpF46OjWQIDQYYowdYhEdNfqwhKUH0FXIY0CHN9EcjgCUwTHEYzxy+KWcxWFszqNofFDu2+UBZ07v6I0fHyYotrdocWULg5AUz5VjL54Oasm61DADJvREc+01xoSoTU4G0toMYN38rvI349dkOcd3ss//V/8oX/o7v7LPfYoRz+toPvNHvyn/Ks7HP/4N+cynfzube3sGJaR4eoKffyA7flDJeva0FKcfcjgywmkBqjdpm2bPneGiDw27dlXpfISIo2v2ACP5wb4LeQO5dl7NSQRtnN7J3mgqLuoQlGZLWCAEBsBgM4crfT/jYGsgkhGSUUJgYHetswxDY4NEcw081OQJ82sztrzTfuS//sf5zv/gF/Ln/tLP5zv/0i/gZvwvHv8L5OPfRf6uf//ns/5zk//a3/jl/N2PfDfb1+bnwIub13PlT//R97+jfsj853/8l/I9P/Cz+fwXfilf/uo/f+frtfGpT30Tq18+z8/b/5loaDx/+wBoMQgRBsAE+P16XYxyjB86l1w9M1Io9ozuLfNn5sU8xc7u3GIWoBWzMLyjQXDl9gY+ojdx/PRKDj7ZRjIVQRsPZ/rkwDSeDnKkFYd6PdepoMur0beamBdxKyQZT/M5vkeW95y/yiX7kF9bKdmnXKgCNjSzi9BhNixuSllizWp0cODydD7znnczL9WP/vgvx8vmno+4dF/+yj/7upft+7//9/GUhkcmHcscCAxY6JJDfunhtPRwItVLg4Yoc3iTtmlyezg9VcERC/TEilCyDhxkxptQYgs+yByvHmCus7d4nYvKZTw9C300cCMnY0B0xMETZoFSG97qB750ZE25zm41ap25XQ29MXX4Q0eTwt9pgt5qX/x//0r8ac/58Q3n9bHMKXZgw037ou3RwKAl+YqdAozNa1SDv++Sebm+9NV/tjuYmxXMaF/+MpftK+9+Z/OnUJ1hXh8TzBPBvhYR2Sb8815Zu/MabUxBQKecBdMy+GM+EoJtVNAgTN34hc06SzbxpHtOzLQNOCRXkblo5d7BEEFNuNghxdMXqFCF0ZN2czhAoracsZRtn3RguMt6gLnhozEmxJy5cKBzO4AWNYDe13s386IhCwNxTuBkc30kodF4iGshHe+dA7oqYNrGY9TFb3Mvmv3GjxDXc47vbGpO+Ub6FL9Wk+wJ5rKl1OuNL2UcToMxHe/kiYTVNYUdTBBTDjcZ7i4ECDBbVnjxZGSEZg8wmWJC9pSLxu3zRnv7zOvc4bmR3tARSJxbvty2F69tEdUAABAASURBVDtLM3No7I7tPTBdekR2EcEY+KFzh3qzvngklOrE+3rsfY7vmX2W7/iz7a3mJVPHsIOkx6z8OqO+EFoIVjeUz3vpV3+zUoaYxeJ3+hl7q8272Ujdh+Tg+8eNBb6z0Xmr+Y6mI/OhPJ49DE88u67PxYwAmwbZlU+YJWoe7srZpBD9pd3MYxSMM7oZ8o155ND3PEL2QWKQXDTKuXsEjMtYPHgT4yTDVAbYSSdvb5lwCo83aZsmt+c6krCmtoOMGZT79CBz22AhwhrLHyu9+xffXjJdZZjSFx9UMgtJeXn6pAY3aZvGs1GkW5WOf3Zp/Tb312alcfsBZQ4aEzUffvjr4xBvtU995neEl0Fv9YCU8PBurQgvPQ1owjv9EEXy4wmM3nj6WnVKWtsJQ6IeZnOkCdrFhCMmYfzJAHdvbh0V5k31ZsJanYRA80qTaXhJt7diMI3p89VBxobb7MRxCaQ0Z8c8zx3DT2OFQKlHS4KWJP6597yToeLXZf/CRwCZO0vmWVP6Gm1RYLap1tiNQW1DvC+U9hRkjbYlm6ze66s7EuZYzmsDYABKAKKv8Ou1o3oj+Y62P4X6RPVMbhrtfG5uYs+83mEJaKAHOGlPrTJ52zShQRd5N+TokniaLIkt6IaCAcJhgiENOMZShtc+4ILObbzuXtPtkItbmFcnksnphHNxZhxhS6YTVM3jLC5scHI3GyGSDRNpbQawDgyIx/z1382+9j/sd/Azk33EJmGhKedc2Nz4kRfRCiJt4zE+vO/9adNff11aZ29nF5Y+ffiu9r7L9qnP+H217ETJeva0FNOxFocqMUwLUL1QEKR47gwXXTKwa1cFUzzr6Jo9wEjWkOs0ltNLhP59NG/odSPB3NK5idxQb/6W8OeGXhwTmXPr1Dw7XXrElYIx8EPjc3YfyzGaa2C161vcOl7Q13s3++J//yvs4Tlor2c4v84rmsVmHA1PWD1gNcyORgLNYCVbTzkh/Hnmu//g3p82lfEIXsbOAlw0/vwssb0vfemf8cB32/f+wO9D5utD40ofgF/zsJiNecSEq2dGuvOoNDkIzBkddkQzOoGnTV8tioPV2YZR7yepUyKggR3s3AflvpWZkoNPPldxsex6OMORM9okkcGbtE2T23MdSVhT27R4SrcT8yJuhSTjaYIXN3/im7/h6/7aLHOc6AMB286WIQnFA2PuldXo8dRwu9XtaBjJ/n2yW/IGUKauhumKCiqMns1NAFre96728U98Yz6BKw+nBeFEqpcGDVF24k3apsnt4fRUBUcs0BMrQsk6cJAZb0KJHRCP2Lw+lc/Ez58M2Jjrt9dwbqgQDpuuWffWHmJkBG8syYG5zJdE2eAzp0btiLYYCYNr6vBtCWhvIRj/1m9+9zsIgnzxvz/fk2JuniU5GEB+vJ55JIGv13kUgebM3Do55jTg0oCpydj7frfpT5us5BkM7LAAisGpeTbMCKBoDPwy72offvhr03lb8KfQa/L+sbyE1x6W+Qj7fk43Pjo5JFRnwCRh1sGbeBLDg+UE4wRsXjC85rNYuHbNmPF5R9tO79t5I25itxnghSJXYmHapsXByZUzpxMDqzfIQkyIoWgbj/F2gHRTW7h5/XP8CcBn/9C7f6f5Jb45G+ZW3UENh4WTYY6ZjvfOAV0VMG3jMT5ctKx/50z0Nn/j+2LswtKnjwzOfSoiYPnql84XjNxr/jHe0T7xid/6xDoRtiWGx3MoabWVjqcGvAVhQAywBs5oS2ySkRHE4QCTKSZkD1jDrU1tR2bUzzsaV5Nbt7f83OChFr+4rUen9qXzCHrEc8mdpYK7dN54sVlfjPx+lngK2pthsMWffc/vNP1Wxk/6bvb0PPdTnvl9LSw+n46v76Xbe7hj+1x3uMvBzcu/729pzDsSsse+l8+69rjbveoWr+5rX/tVvt3x7ne1T/Ibg3vGBQxjvmjo3eEz5CarmR8CXhSkHEL0Q5KdWQyiRRydLbG9xTS3cCfrpiYjV0Cp/qV/wMVLJpBSg/HJ4eJ55NJ1pBWFaj3XqaDLj0ZcyA4HGOvEDNc2WIjJxCbkcNrmc//2u9/JkPBT5q+gdiaTGUnbNK+fDmccb9I2jWejSLcqnVK0xDECHFQ+/cl3/02NL99/rFTURd7JvdACOAiN/RiocCWHd7V3/8bg47yrfeqz1/fWlJe5K4ObuI+UdSPca9UpE8AoGOrBj0y3CTR2QDxi8/pUzutQVd2Ekm/YchO5gtxEzCtpHbEB99ZOojH966ZCUmOIwZdJIJUcaHCHWb91KNTh0vsVRnMLwfr0X3393wAw97yDch/AvI9n2RoNDGob4uvzn7wqZjEkfLYASIXUoxFDvcvmp0016udJgKlnG/usmSbRphbTUwMBGu5rf/fX8hV+vUb3reZF+7h/QdI99xzT7MFmx7SY3h8bq+MmZrRxwlJnnhkX3HsEK6ADwObHgmqMPsbI2+fvP+v01rVNZyp3Fi1XOFyNHg+16XgnF11wcjcbIZINE2ltBrAODIjHvN7Dfu7ffvc7hxNf4tsZqyUWhoUm0NiFzT07zTnYDEzbeIwPB2HJBFJBzWf8i4l5+/mQ7/KHXVhWvTFTJaR4eoK6QHb8oJLxr3zxn+Z951Ofu97VmjrfpG2aPXeGiz407NpVpfMRIo6u2QOMZA25TmM5vUSYtvjBAWdPTYTzJwPeQhiu5N58MDfc6K1dTs2z00VP5BYTwRj4oXme3XV8GWgvfIvrGSxwGWn3fJZf/H/2fb8245I5+3DH3WnGeTDV2Wfavep9fbQlccxPAPGkCRd3kxKMvJq/1+/2t/mH/o6ReZ/x7D4Pmh1MATDwvh7A7JXbGTWwEF/4q79I8XbzNwYf+/g3KGS9u4TkI3cXDez6HGiwk4fBLVatbnwoGOSrAai30ClnkLy02mfn+ei2t1j9B1w+bZxnkLl+ght1EBc2rThpMp7rSMCY2qbFU7qdmBdxKyQZTxO8uDkccSdfgfwO89sZZXZ2EfqkW1y6wU9Wo8dTw+1Wt6NhCLuZg0PmG7Uf43/cvP185Dsaqo5nousAWuZAYMBLlek1uXM47f456Edfe/dvDD71ud8ZZHhTZ46TqJcxhkobTzYxWFCTiZvBBxSWKnu66cSpnNfhihZIzHg4cqGad7RwrhvoHaQkeUtBNOamc4O9mZT04C8b3mL1atXdbosbPna07lgdzS0E62jskfJZfgPwWd7RaLzVvsi72TTYgfG6mMJ8lrzQ12GPVwdFnIIOZg9yjXpa97AErSHB8EzzjPCbgN9O4+3m985WxwyzRIREjBUzD0FGJQcJosaosW3DL4Bhz/t+Cp13NX5z4OeD+oyBMLZSA7DBZp39m3g6+wfLCcYJ2MzAa+5n2do1Y9aHdRfgnhNT0P+gYM2baA63T65XptFahXNlIFZcM1d9Y0yIcQYPBzq3A6Q7DM3JBRSk587vu2T+ukxvOCyczOQx0/HeOaCrAqZtPMaHi5YdhKbxIxObr/9u1lspYuBYzQTsSZHFkDwKnDm9kSXvaryjve9d7Xt/+PcrvKfa5i4ETyUQpjiS6h0cjmNblOoysIbLmNqOzLhuJ4EmhFMcg2i7f9b54rZy++Yd5Y0cLje3k3TpkQx36b3xYrO+eCSUe9uXE7NrF1yCF5r3XTJfwk/+d//81gPYAes+nM1Q+wyfdzkkOrpo/Bzk5aacYPvq7/z0EU+bgL3312df40K4V59Z1jHuYsuT2Q05kb7addtDoL0ynxdaiHzlJ7/ebwx+J7JrbrOjkC6eZNjnEvlkMCgxz5mRCVs4TImAeezgfa3OPPuO2Fs94l1O+Sq8o52bl0cumEuYVpQ0Gc91JGBMbQcZMyj36UHmtsFChDV2MiGtODEKdX/azDvOvJMhbjszL2XLGcebtE3j2SjSrUqnFC1xjAAHRRSH/HD/n0l5x/kK3ztzoky4jqStGgIDT5ec6TU5uZsV4c0e4AK6vqO976fQT/I70I9/q39iUNSONQNIgk2F0pdpQo0f0K2yp5tOnIoX1PaoyE0oQ4qng5pN5Oz5gHu66Nw+rh/mTYWGw24JDNibKlrN3OC5+fK4LR14zZpHB8fy3eGMDje9p5mv9272xXk3Y2AWm/HL4DA2s9g4iYDFxq3bl8FnAYC0d2nEUsyPwWMw2A3Arxtz0yY799J5kjwkaJ5pOXr27I8BnSEv1sbxM/eVv/1PIN5t/sZgl6thD8YoBbuJ/vj7LGl1Ps6+nFluXME0HcLBGC32AGxTuM6kAMfoDalADKVx0TqXL0RRS9TjAZuOd3JRBid3sxEi2TCR1mYA68CAeMzrPeyVKedPAL7eu1lYWFcdv7C58SMvohVE2sZjfDgISyaQCtKBg8zr4Xz6U+/+nt6Xv/zPmUBUHJtE2MeWnp7NJevZ01JMx1ocqsQwLUD1Jl/4kfd8u+NbvzEf810NHRaPuc5SVGehGRhgQlFDrtNYTi8Rpi1+cMDZU9MjhBY+xMBwrPhd595Qb6G3+uEorht5bueUhEtzzZjvrwa0SJYaPIF6817/xbsHhgEMkPnXfnjyW83/N5O/NmPZaK9nso3az8NkXn/oUGg+ZPLqnPc1rO5uQDu/GkcgkLx6/+82/duxiDEGefkADOwudrMExB4p0FLUAIT362AUxRotBBjgaEj58O/+avxpdKRvCfOutiPs4Vk+m0GfAT37KOlhAh0N1RiCyUszD5jZybbkeDDLQBCCy+XBJtxdzp53tIYLi5Np9jhpTSLlI2nJeprgRnM4iwOLUyADAyZuDqdTh4j3eDLfzvAfUQG+1T78e/9iZq4mo8AOZ6wVD207XE7MOSXfjsZ2DccX51QnN5GoIe8+tUXARAn7sSSdj3D6mlOmhcWJli8cmrqZjyZt02T8Kz/57p9CP3be1dRmZpomeHEz3sSihvFwij9sKud16KIDEjMejlysmsRmEmE4m0vd3O9oXNa5mZvzOF5OnRtvz9vpLb19lAg0HNlpUWCnQDUFz8BYZAU5bWf09/2U6ffM/LWZunGGZ4e7APOVRePx+hTQII0BkaqYch4Mt3nA9oCrWxkkLxgM+d6fNv0zSWZHj9y9jIB8ZTQ0HUaTNasbn4KAZnegGGzW7ZFJ2oc/zbsafxYqfpt/77/3+88aZnghrvITAZpwGAzAOAB7+TqYoyPnj6lttff8boeiI0lSOyMTmJdDB8oHLTdOtzl3b8CETgys3iALMSGGom08xtsB0k1t4eb1wnWYggBYce2zf/j9fzvjS/wGIE4xgImO9872r0qybTzGh4uWHYSmiAvVOweUF+fTn/4dL+rn4iv+tAlR3EFzAaVeb3jMOpwGYzreyRMJo035SAYTmj13hvvKF9/9rqb6U9/28k8M5BhLZsmE7AFruLWp7ciM63YSaEI4xTGIdvHGxNw2WIjj8/fR5sZ6+7yZ4Rw8JWH7kAfPzb3xdam5tyOZgGQzXWzx7rFcLZe4wUeZAAAQAElEQVT91r3v3cxvZ6hF7PCB7njpV3+z0uu5F77yxZ/MRj4dxty3n7+vFdqB9SnovctYgI3WWTYxQZxHmHXbQ8yzCBobL06o7tl3BobmYod8xke8q331Pb8L/SQXzdek1swLYnSfxSb2iU2bV8OTADvzjNUxRY8lgtvVzm4k9qxf9w9K8/KEQpOYe5i0zfVBBc59epC5bbAQYY2dTEgrTozC8ewp7Gf/8Lt/J6dqfsoUjDsRpsx4wSwkJbB5OnKFK1xLHCPAQRHFIT+5FN5hwxE1n/7Ue/7YyZ822Y/desBsKGC9WyvCC68BTXinH6JIfjyB0RtPX6v8tdpH7/kp9FN//HftRJnGuxWFVsPtU/HQtkdFbkIZUjwd1Gwi5xwgRlFaejaXjPOOFm4mftncWAtvMy3rua7eetyWDnzZkqAhqTujS09tz32bbY0T3vtu9t/9CkPMXMYujCkWGycRML+aLhmtKfksdn6HwAjFCK+eJSwMJhgnYAxk/nYErbfZ/H0xdSzxq1j92/bePXTzwq5lM2vhFAV9tSRX+WnYZGR7Q8pSDia/73tr/sZg5C5EOzNDsFaONAbGaPM6ACNFB4RTsQwvhBqMDbalI8RAzN9z4NGFX6PRuqwDyk0MTu5mI0SyYSKtzYC98YB4zOtFUaieTNJgsOZgQN5+vvb3fj2+m10Kc5kq8pKDNx6i5guhBNLJcSpsqyRoGj+Adxbn5VHXjOLxry/mzaMGzyhLPKhkPXtaCrrGkMMZTJgWXPUmbdPsuTNc9KFh16b6Gb/d8Y53tY/d31NjYNQGsIafitXFkyZ48cyp8REyjfM6hk4O1UgTM94EoMXDtzdIc+uu22de98b61WXmkq4QrTf34UvcuhE+uNW574wPIYZD+4lvfvf/q+nDn/kXClGZ0M9a8/r9us7OfQ3nC+4FN4NXg33OW26GYJWafQ6FTZL9V/HfKaPzVvvyF//p6BhgjXoQz8YAmADf18YzZgtZwxnGAEdDmhpmlUsMR2Ah9HBARWCeKom/3T7GtztQ02QAu/YwCs00wNe3DuVnMrrFq4dARwsSvNsG79ybe9Tac34vmrePu2dqmxZPWdWJeRG3QpLxNMGLm8MRd3IyGgosnqYmvKCSv54VXfCTWdguls3TKfh2NAxhN3NwyE9+2h02HFHv/L53s+e/d8bAbNjJDA6nhcGJVC8NGqJo8SZt0+T2cHqqgiMW6IkVoeTmfb9O++inr7/L1jyfqXymTqNuaojr4fSqamHIMOFcVWE6dcOqdWqtbST4Plq4lTjXby7sBjhurdgWvTGoK+9NpalGt6GjsUeiuWukbx9Aixl1/vRI9Vb7+Lfwbjc69szcftX4FbLl9RTGgUihAZQvNDamQ0/T1ehgWjwAgAEoAc7g7/v12Ydf839AXhMTY465E5clsU4Sn4Ig1Blg/fT9cbgxEk0OCVAxjiEeu/ileBLD+66F/K2GEpsfk6vPjJSczxrMYuh9BnikEuNUZhO+cwNQ+hrA9LG3zs87Gncu9aMxJsRwE1uIJMbbAdIdNpzeXrhOVRAAKz4GcC50gBM7Me88/inBt37LN6IK3mTiZmBaseyzLyczCE3jxzJNqNbzfNTRLN3LA57/80fefvyNQGkVHeMZp9bkzOudNJGwuqawgwliyuHMLQwmjiwYi8dcuFIo83eWH/PXYtRvN5Qabt/Ulg3BzXrmQAcy2UCiNyQwe0pqGyzE9UoGnHMkhE3b8I7mbVzfW8rd9lri3nSuqzSuhssKT4Ed3UMw3LZXu62jo7H77D178r53tU//kd929j72QPBCMHaKN9l/cMvL8SxeyKUxU9K2Fwdu9/U9O6L4r/igesP8/wXMZtewdOdcNQSjV3b0Cd9apgfvDJXCp7md8bVCKppkULvPAyHz+2Uz/Jbw1f/2/zez7uFxgx+zzl/OMLvs+YxnX45RJPLWbzrNt83zYLUftNy4rGdy7tODzG2DhQhr7GRCWnFiFI5nT5elEJUcmGY0hCb5kt/CIL/N/E/cfOvH9l0tUZ37WBWuMC1xjAAHRRSH/ORSeIcNR9Q73wjwaf9PunTeZh99bf+1ImSzqcQibIk4kSoBZk/TASXjTeyRsm6Ee606ZQIYBUM92Oy7Wd5zvvq3uGinX7PzOrjuaUIZUjwd1Gwi5xwgRlFaejaXrGdPa4FPudnI99G4htw6b+r4CAjQ+xXATR5skDfjzuhAbywdhBi19O0XYNnopu9XERNwtr/2M7/+3nc1/7M46t72+tjEQx+7RuNeKYd4niUJBhOMEzCHdwcFQiIUEfuY/59JRt5m/voMuU+Y9nxuVJNtDEtgD/SA6zlqRkIPo+cjQRfpACXsmKVgKbagG0zj676bzXaCMw5NZgfU7BzOQkCmvz+GB5PG4DGgs7hyCMw1wxPANIa0Ei/1QbmX0RvOBKuHQ80lHQbJZEhyxzOx4RAwAAbABvQoJleqMORjJb/vXe3j3/IN8d+5aFVmZkXrRGzZJGgaP4B3FuflUdeMoidulVDG866fNu199OGvhRWpBd6KeurCZDFwWlTVCw9BiufOcNElA7t2VTDFs46uST7Jd/1J77Sf8qdNumrbBkun7mTg5t4oQyhMBoZju22wENe7DRMIu1CnD0EmNnFu39G8gVw+7t4aeKjJE7jkm0fAVxIEtjf7xVfoDAYZvRc6anrY9GyNE9icr330a/Gdjcm3mv9tJZ+DnOe6ginNha85T7o1tnZGMasljrvv2R2ypf7T5x8oZuIN8zcBl875nXM/Pmqyhj/31Do3eXTX58Er3gYEjcHAa54XNDPwS73KJ7/td+G/E/HbzV+bIZ8l9ywE5jZfFj2NjUNemX3WJlzhy/l9rSOZkQksOnm3U2NQ1zzvaG7j2pE2JmZv4fhWxGHj6VM1mgYmc/oC9eZW17trowYYc8lffs+v1ZT6b8UWMD4Lw9RUySCx6Mml8NLPHFFBhdGzuQlAyxzrAW+Gj/i2xtVuQTjxDSE0XNmJN2mbJreH01MVHLFAT6wIJevAQebkY//mN+ZdZ/6wnV+bVYHP1MF1viGuh9OrqoUhw4RzVYXp1A2r1qm1thkiHjBpIsFW0vmYb29w99bmBgLJ3mIu51UEsH56JGpYwbPPEC2u/OwA7q0GwE1bvSV+9fg64R3t1/Klv/PPZd/qn/6j38T4Gb53UYunI97XBNodgnECRneUPlcfCka7ncn992IBb7H59dmIafpsHTjmQp3+0AR/HEg82s9yVDwaEQYJAcAGm3Xn6SDErjkyPd/N3vftjI/4IymG2MACHjzPB7qf0kTFIqK6dYppXu19lhVoJIaRzOgE2uQhmfeJV3llOXwuWtHcDvAmlnsIjfX2wnWqggBY8TGAc6EDnNiJSWg0HuJaSMd754C+9Hf4Q/S8+/zg9/ze09y5zO7FxtK9HPgwdTSax0cG5z4VEd73u01/2lwZQsH44okEHjWbgRlMECu9M1yuQgDGlFjhxZOREZo9wHzyj7//p8yf8t0MoTPN9fGYh6IojqnDQUsD7LQNFuJ6t2ECYRdq2lInRuF4svWJfB/tupTc27mFE7i8m+lii+crY+BquagPHbfaPgRm//Kc+SvDDySfGed0Bkf7+b/+yyjebv7G4OP3nxjMi+FluMvRzRCssscOnkFhk2T/4RAavdUpHYJRmLebIvx6vUB0DAAwdj32q5Gb7ItSuQSP2Zmhb24EUM87XGltj98AfNu7LxmKfPVv/uPZPc9k7yPTnUdOcOntanioNpz1m/6OeabUkniu477W45D29Mc7GtcQ4/7Nvbwz69PKJUbhePZ0WQpRyYFpRkNoXj9NoUocb9I2jWejvynwP1co8zb/zB/bvxvWp6kiHJ8QOoJwevDmXswCehCar0EOxn+6U+ptPu9oak8TeCEmhSXjTeyRsm6Ee606ZQIYBUM9+JHpNoHm3ex35V1nfpfpvI6oDAAzTq3JJR2LjZxzqKTn46CS9expLfApN08k2MqZFnewTPyTAW+h7k2PYJ3bCFjjK4POwSCkV3tICzTeXKFXm5IWQmw4xsZoYFDbEKuHWP2I+CbuT73712r+F3p1vm6YwViF7aRgnIDRZTXAB+EgKCJGA8zYYLbRf/9Pm+dvaqBjam1mhcy7kJ4/DqTZbXu6EroafRoEzFKx0B+LmXeIxjUi56/Nhn5H+Mrf5JuzDKidXU/z7p8x+j6DFtSqLt4WJC0+F1sQmBQSCRN5SDDKMSh3Ls0s5GCCLZUfzM1rzt0Lh4IqeMcvlFBqmdMcXPKTNwlLTTnnwubGj7yIVhBpG4/Rf8zuve9qn/zt6XyEuJ7nwy6M3qqMmSohxdMT1AWyeN57LkVHNZHwPC9ue2/qKJMWpMcjTojxmEtVinF0ZsoAE4pP/fHfzbvZu3/a9N1MLVLkxTOnxkfINBQmA8Ox3TZYiOvdhgmEXajThyATmzzmwOn5SABaPPf30fwq8Fbu9eUeehvHYU7moiKxx6jcEsM5v749W+MEJlyCzsSNdxZf4qF3HvnS9L/0nv+epe9oH+PXashYigl80GQA2X3P7mJoxPvMxft6JEF570+b9//Fzf1MkHYngGX7LHk38QxM7lnj52dtZkQxED2FWjbRtnbYNgy9T77vNwD84v8r96/NmHEjYyQMwDxLaYjdSzbh8vNcNC8zTWRyam6fjTZ2XV69ouXrxe2xBwL0cv4D71+4d1cOR9zJydxWCiyepia8oJIzeXUF5z4d1OGMpW6bFgcnzfOxuh2N72i+sz1rnvFnPsW72hnovUtUZIXRs7kJQMsc9mPAwpWcfPoz7/l/On3xzX9g5Xle3JZduT2ciyk4dgR6YkUoWQcOMuNNKLHyTva7877DY9EFbx44c2DITbDYTCIMZ3Op9WwuWc+e1gKfcvNEgq3sVMSNH5nTiUkLwvldp7cTnztI1riVpHiAXF2QhD46a27wNC+8edqj27G53dSoFVBa6JZk0hjQdauD2SL+l+Go3mq+q+n7TBcoc4N4nQhJxFZHqbEfA1164G/Erj0sm3mCX/UkPmF37RI5JBRnwCRh1sGbmGF4sJxgnICxlIv27p8y/UPzeTfjSWpZZaKaYTBZksQPPjyUEQ4D8fz7ueDRQcMZZ2aFlDQ13PbSWwwmWCHch1jAuYPfdXLjsl5yUfVkIMiIN+FiJikfG0UNh8ZkOwBMdLx3DuiqgGkbj/HhomVFH/7Mv8j73tW+/3vP99XYhaVPHxmc+1REwKazOYsp3vvTJu9oSBTn8RxKyLZp9twZ7iYFNLARmQtXqpERxJQBJlNM4JK9+3eZ4cz3zBhYdfL6vES73Y1oEuji5JD1JiQteyRETVtBjMJxmKmXjVwgMDoYYDjI4lw0rt25dX4VLnzcbDlvpH5h88NZOkNXZnYgGf6hezzHXfsVsRwyKDFfCFsIDvcqn//xX2Lju+3T/sMrzD0/S8wCjV3MTv/KvDZewEQfbxPCqAAAEABJREFUS+99f5N2/qkoNBi7mAK4f10Kjn1s53mz8M5Do4fQFEOhh3sxzxgNNAB6CFnHn2m+5w/OfTfbHe4b+WPeHcfVzG4kCDD1rzvNefQEHn8yg/f8UE9zp2ffR1HObsLMy4vNXLSkbTxG4bgE3nRiyMXDMY+GUOqX1lEZx5u0TePZKNKtSqcULXGMAAdFFIec+B/wyjvOp/m1WlB1/KAmlFrmsB8DFq7kkB/+8Rf/JZK85XT0IYrcNZ7A6I2nr1WnpLWdMCTqYTZHmqBdDN+c5Xeaec+ZdzPkrAxplB3UbCLnHCBGUVp6NpesZ09rgU+5eSLBVnYq4sYPmfVw2iZ6kvIx1lzfR+OqhkO6bqa3c3zCNryZW3qr1cub8ctYgCGjZ5xEwLzdl4zWlGzi9sPuEBihWGpES33pK+/+jzsgzfd/Hz+FMsf0DgheOE+iT5OtNDTdYfy9P23Ov7TIPJO+aH8czioZptkqoQ9zLT6ZhGI69/xOWdoiI8KmQOlfAfp6v9NUez+SmV3JEkkSS4eaAIcB9/MYTMBcMzwBzOCQVuKl3LX0a/PKdJvI1Y3DqfRl2eJ3neHSFU8MzTkAjKLQT16pwpGP9SmXTqlLDt54iNp900PnchvhkIlB0/iRiU3I6z/6hXf/FOrfHfPv96ufgWQTBCsTqo4fVLKe5OP+Q8Pkt9n8TQ10mdnGXS05e7oJHgS/pTghxmMuVSnG0ZkpA0woash1/J3m77qKN7LvZD/1t/7x8DU+QmbNLF0Yju22wUJc7zZMIOxCnT4EmdjkMQdOz0cC0DJnRcDtA+5eKfg+mjeWuze3cbG30FsJKwEEcS0fX8mIsdERhIg0/KGlYJ6nMIuMEqU2bqA9PfHD2YB2e7av2Q8//PXobHyr+TcuLr0LFrNtAWvEb+5936/P5m9qOI9fn//k8wrE+6zr9dNAy8OkfRjQ5x6ftlhg+8yhohp73zsZU/nwp/+ns1v5mZ9nilGITbhCX+ObTlO52mfndfDqXOwomYr+zNsD22CUHnZqucvVIp0SBdB/w5brdl9Gnt2ne3hh82gIRXPZ4s6EsTTapsXBSfN8rG5HY7uG44tzqpObSNQQfq32np9CfVe7/3+Y7MeYcLLkzAbR5eG0fe87mv9AcZlEllaUNBkPp4M2RtzEFI4wM9N06p6cQBO0JtHD2exPmxRvNd/Nfuanf/VpwhkcyyxNhOFsLrWezSXr2dNa4FNunkiwlZ2KuPEjczoxaUF6Oh+ETJmkCd7xeUfjynnx1qcIeG8y6GDQ3F4zju2tVodk5sSwtw6Mbgy4NEBiCwapxcwzTY1JqdHBtG7yww9/7f3vatf/oYSdGBvcyhJNh9FkzX7Z+e/0D34t+NOmfb9CZxeBFaOSm9c1ARbjRY4NRViKJ91zYsZtwCG5ijPH7zL/t+/+KRNx/J7ZYw5m9rAQ87XCnPZ5FhVoW2ixfRa82mkMacUSDbe99BaDCVYq7a/DwLvU58hNiYgO64kQ82s0uHDtsjfRG2hp1hMbjYe4FtLx3jmgqwKmbTzGh4uWHYSm8SMTm5DX83zU0Szdr/wG3tXUlfn1hvF1OA3GFP/DXQPeErxoO3epk2ZPC8JOFRtXaS5Ek4yMIA4HmEwxIXvA2Mf/17/1vd83893sjXmIlmEWbcysbxssxPUmAC17JERNW0GMwnGYqZeNXCAwOhhgOMjiMMRF8oUYn6LXf2dgb91+lXIvuYHi9XBZ7V+Z/kDyC52aZ3dsa2TnZj84COz0+TK4NGbK05sHOTS+r+dV/Lf8P+TXa3Tfap/8zP5NXF4hexwFsRijPs/cIu96N3Pxl//2P0F/5n1RkL4GySsPfXaZpgdpf/3M81ho2oAVuo2mSS75lvf+n4Bz/10zljDnzLrPmd2ssmf9ptMc+YTHPINqSY7i148VGdLe+pvz8gxg7nTl6zPUfq4472hz79jSuZGAyXsR5WSefTnjeJO2aTwbRbpV6ZSiJY4R4KCI4pCfXArvsOGIeucLXf/KIo03zN99+jvJ1WY2ldgkLREnxqPO/DZHFnXrxlCbSw6nGUASbCqUvkwTavyAbpU93UT8JD9lfr1fmyFj2hkciy9QUqfGQM1+ZHPJeva0FviUmycSbGWnIm78kFkPp22iJykfY80cUyHGm7RNs4dv2HIbublcSwyMATSvKI5QjjTG7cSYkPTGwi6BVo5aAy4tgNhiNWIptowdCRR9IvW8ADMCngJPwRxxsL8bfN9l+74f+H2PFa5klkE4NgzOe3/a9DcBPFpjmkln9GHcIX0yCcV0eAA2rxgB2R6+X/1Qo5IAzz4y9vUu2Vf/5j9ChTkzfjBpDA4D8kyeMZiA+dKGJ4B9Nr4FNWaJEIN9bR5mzObR8QkuxXPG5AGm/TzZscUl5Ru23Lk+e9nxdBOp6BozucQmL6IVRFo7U0W0TsSWTYKm8QN4Z3FeHnXNKHriVgllPPO3XQXv8E9/9ndc0rRF1ZSYE9/3bvbR1341ntUnLUiPR5wQ4zGXqhTj6MyUASYUNeQ6jeX0woXn3Yz0TvvqfM+MGRWmMyh8UI00MeOlg2MA7UKd/s00ecyB0/ORALTMWRFw+4C7V4pS1Ty6UlEQLUXztze8hQ+Hvm8v+NxUr+bQhEsrR3u/JLjB8tZIaEnYMr907jv97andGTU8T+K4+57dIVvqL/y+y+Z3+/XVuh/nETx53s3e9+uzD88/bOfzX8y7whdBNk2PF6RunP2T6UPTBqzQjo/G5TZ97Fu/MX/svX+eyTdmnb/dNcybcB6A8SNKf597YZrI5BDsw9AAeFk2QCR7asbtqMFtUdIGUQN2AKx2ezwDwBNpE++e+PTgaPqOFu9dvHntdRMzpyeaGz+SlqzHU8PtVrejCUUNxxfnVCc3kaghHlEBhdGzuQlAyxz2Y/nyl/5ZPnrPbwz8XaX/gYdPnO/++y72fT/0v5iLNnveEr7KHzn5OFudJ26MuIkpHGF4EY0fmdiEjB/QrbKnm078+L/5W/Pn/+IfONXb00/517Pd4SjPUiV85NLVs7lkPXtaC3zKzRMJtrJTETd+ZE4nJi1IT+eDkCmTNMG7DtmKMwe4mW5wfo2WualePG8eF5QaDuMug/d2EofZ20wHGz3sGLU7Vgezxcyvzo6iQyEZg9pnAjC6lICZd0aVNZlEE4DRxwRctjf/UiKN23xX+14u14/93/+NmN/3TubQV37S323yMMzXM35h8sc+8Vvjv+P/+f/6D+ZHcfPn/5tvyef/mz+Im5/8/wEe/1g+b/5/ko9/z1/4X/m4d/pP/a39/wDMjx/PnYxaKL4+f3+UBhOwebknkFAPyaA/eFd5ZTl8JARohcyRLOB8loTPQbrl3UaE5roXVHQwge4Azu86Ibl+JYWbd8x0vHe2f1WSbeMxPly07CA0RVyo3jmgvDzqGvjevlXuUxEBO5qQk4++9uv5iG/k5l/B+SrvZq5peQomjk8BY/nUt/3OuWQf+1b/K3L5TTvz/zb/m/y0yetofXJ8FfFYtQ0W4nrp4BhAu1DTViJG4TjM1MtGLhAYHQwwHGRxGOIi+UKMT1F6EMSadICtDFe/jxYuK1cPm6vq7RyK+yt+8qu/2TGGpn/hK1/8ydxoZIy50+WXTrwampj9h0NoiFfnDgm/elC51RJ/lR99z393nOnfkH30tV/Ll//2vov4DBbfzxb7/Pf96z2/oYf8BkU/5SXzgfi+ln0p+zr47A9vb53F80M5AfHJ/iihJTmKP81C7qzczE+fMPP2xJul0L2YoZ7dZufN57nwO7fcvqOheVi5g8HNeMFcTVICm6cjV7jCtcQxAhwUURzyk0vhHTYcUe98owW3KuzHbh0gR3Jy84W/9g+l/6XcS/aF//IfsGu3GkOljROWy2/68afMj/iD8+wnvM/j4Ri42Y9sLlnPntYCn3LzRIKt7FTEjR8y6+G0TfQk5WOsmWMqxHiTtmn2AGNRw/HF0A2/GeACcklzn7mJ3N4hvY104LZUTK0BlxZAbMGgNS7lEPBqQdEnwvmVYluCp5AgERIPvnRXRkWfJmOoNJ225lfPh3xL4nu/8/+bj3hnkvuNuvof+y9+gdUsZDtgbMJFwQs/8t+1+I0u/pfQ/cR/9nP56v+L75n5sPl8zxIwRsGPg6/l9IcTw9HkJVMMaSVeyh/vpV+bV6bbRK5uHE6la22xRUgLEQQGhYIS6eARTIBE4P8m9oBcNFBxzVxuYylKDt54iNpc28A+u41wyMSgafzIxCbk9TwfdTRL9/IMTkjx9ASkoELr2Vyynj0txXSSH/ur/zD+9Wsv0HbfHu1/4Uf+QeadzHl9pOxau6rZDJWv/rfv/+8vzcC/RPjop381XrKPnt7JfJ6rzG2DhbheOjgG0C7U6d9Mk8ccOD0fCUDLnBUBtw+4e6UoVc2jKxUF0VKUG9urZdqmyTi/6+RWhsO1w+bGehN1Cm7q9ryoy0HNhTWIzS/djY9dzl868UPrvmf3ec75rAdm2yFB0jiINeqch8AgRsczaPid/R/7kV/M9/25/2EunfXlc7nOBfuQ/4EZxs48iYIXjB3M06CI7P+Qd7Qv/Oe/MBfOd7c3nH0f/QbdnyJ/nHewH/xTX8tP/Kc/m4/+P/8TD+X18xwinwUR7Of4pttDTp8XJ1hnilcKxq7Xj2bm7YHV26IEgg5HMUNqt8czAO6Tu1ydI4iBZ16dJK6OcnpinYvWuXHGsrdtWhycNM/H6nY0tms4vjinOrmJRA3xiAoojJ7NTQBa5rAfAxau5JBfejgtPZxI9dKgIRp/J/nVL/6TfPWL/xT8T+bf5P/ofEO2Z2tRRizQEytCyTpwUOJ/qeSr/Kbhx7lwP/5XfiHrP/+UxT+fn/grPw/385N/4i//XH7iL4Mng7lgX+UX/P7dMjezmtR4NmYoY0FNJvo5jWdPS0efEkyeSFi6j7lBCLDiWgvSpydOpkxChXcdshVnDnAz3Ryfbqn0ZFmEbfmpc27g3M1MpOYqCnFuK+wYcFpeVYktViOG9+aT7nFl1usukFElXidCErHVUWrsxECX/jwKRpM178McngoReUoUZpb4FUWiB0dbk5vnTUCIIRgbirAUMwwPlhOME7B7QIzO3eNgKV+fzxr8PA++dGqmZEZutGCMkufTHEzA9pFwNMGoh7QSL2V7aTiKwQQrlVAIQXDzfAifRNrSln76fg423ph3QEe3GtYiwpgWg+jxu87uzbtjEeAaN5HidHIyjXg6ddCICtX4EeJ6no+6hl5v3yr3qYiAHU3IesP4evbALJjYRwSONuUjGUxo9twZLlchAGMjMheuVCMjiCkDDK1kQ/bQ1XBrU9tRGNftJNCEcIpjEO3ijYm5bbAQ15sAtOyREDVtBTEKx2GmXjZygcDoYIDhIIvDEBfJF2J8itKDINakA2xlOAsQBAgGnD091Qfewsu9sQ9/3EYuJLQ388FBYHJ+HeADN+8Fl2NsIH0AABAASURBVOBhO+zg6K9nmSE0eqtTKmGPTawhuobGcAdfms3MorzxrXVWZ72cGqTuWe0sozkkebXTQ6tu3dbVW6wG5OBJ7gKS7O2cM89+5LwWNc+unkdqs8T6TX/HPFNqSWfl0zMh7a2/OS/v0GYfzeyLGerzek0PLZ8oxM5dmjN/eHvj7KOTD7iEaffeJc3zsSpcIVviGAEOiigO+cml8A4bjqh3vtGCWxX2Y7cOkCM5uZsV4VWAA4lapx+iSH48gdEbT1+rTklrO2FI1MNsjjRBu5hwxKRjUzmvwzV8NKEMKZ4OajaRcw4QoygtPZtL1rOntcCn3DyRYCs7FXHjh8x6OG0TPUn5GGvmmAox3qRtmj3AWNRwfDF0M8fUNi2ey6f1lj8ZWN5L6AXmPnp7IbmpFPDWuJQE8GpB0SfCneEpuPPwkAiJB08LfGVU9CVApscKJVfPrMONuVDntewckT62O0YElNDRUY1NcFaH33TmZ04MoOFXJ5ItBHDzAmHG2I2x0hl8yZWodUZO0TiFmTQGxoDO4s5AYOyEfn1eSg7das4MHOg8VzRCdigEK9aFaMdoIRAyR0Efg2KeEungEUyARDA/JjSBRCTE4SCYtGLfJN7RyOH2jXETw+kLp8KiIBw0BVd454Dy8qhDVDqXZ3BCiqcnIAUVWs/mkvXsaSmmYy0OVWKYFqB6oSBI8dwZLrpkYNeuCqZ41tE1e4CRrCHXaSynlwjTFj844Oyp6RFCCx9iYDhWbYOFuN5tmEDYhTp9CDKxyWMOnJ6PBKBlzoqA2wfcvVKUqubRlYqCaCnKje3VMm3T5DVfxmgHiSkf7AX1hnIjuYnWkyZcnP2He1unTcAQYQIkc4UHoyJ7wy+3B4U4uP0rzyCPXm719pTRY0jOeVhMzp7QGevNSJGBaWnX3JV5yA7uyGDV9scZmkxfrZiFsuvDEzTcnpqHs5JBXwcJQ2Rxu2vgTPib87watJiLaKO1uHw2ytEmIcDOjL2js0U5PcKKr97kpYjIzjy8n4d6IK9ueevRSeKrgTnYWlfHxJkz8SrQyPN9NIhj3sLbvYoUfbqvi/PEgJtI1BCPqIDC6NncBKBlDvsxYOFKDvmlh9PSw4lULw0aoszhTdqmye3h9FQFRyzQEytCyTpwkBlvQokdEI/YvD6Vz9ShqrohrofTq6qFIcOEc1WF6dQNq9aptbYZIh4waSLBVtL96IUyh3KzIn1VIWXKJGpKHIdsRZkD3Ew/x6dbKj1ZFmFbcJMTw4EKZTKhvKPlnOvycb+HmZsImkwTfm4rcChaY9S0vN54EorV0UBIREbEaB2NFCo5SNDwyKnoYcOR/WoYF+vO6CjV2/Or6cZqcDkkovUzM9zBm3gSw4NpAue1uJdyZwUKpgmlgaXU+azB6KB3HqzM/jrVNK+2z4VDBxqJYSSzbAJi8pCrRa4MHqNFpMEGMToTxEWTYeABjIpBJDUmX7sL72xDn+YTYMdqdt72dOFnnu10xi44ecKrvWh7F3PunlU4nTpcTVEPszkx5/mogyydyzM496mIgE1ncxZTsAKcOb2RZQ3LAFfXqQcTOooMF04LgwExwBo4oymxSUZGEIcDTKaYkD1gDbc2tR2Zcd1OAk0IpzgG0S7emJjbBgtxvQlAyx4JUdNWEKNwHGbqZSMXCIwOBhgOsjgMcZF8IcanKD0IYk06wFaGswBBgGDA2dNTdUoiNhS1MBTF+TXafjU8bit39bqlV+ZWLlwtJZdYXbjB5nV3PDsiDc3q3CExGpawzRIH0cQGE7BrJ7NoITR2XdorS4HVIHW3wisPvYulFUOhh1NDgrd22PY+F3QIkxwMCTGm/nVXpwbRLrUY9zk8VLtruZf+jnmm1JF4ruNPz4W0t/7mvLxDm8/si5ndtRrnH/Vy+7m8mOdzs76dfWxWTlr9FEcnVvuBt608oyWOEbiBUERxyE8uhXfYcES9840W3KqwH7t1gBzJyd2sCK8CHEjUOv0QRfLjCYzeePpadUpa2wlDoh5mc6QJ2sWEIyYdm8p5Ha7howllSPF0ULOJnHOAGEVp6dlcsp49rQU+5eaJBFvZqYgbP2TWw2mb6EnKx1gzx1SI8SZt0+wBxqKG44uhmzmmtmnxXD6tQCUNx4BDtOTDfMAd5iZScQP3cp5bKSVxShIMJhgnYA7vDgp2EKGImOMPR0WfJhRNTWel5q2fnhpdUkfDAIh5AT21JOUy9IAS+jAMYbBjQxGWYg+6wXIC3J2U7KIQTDJAac5YTmaHHDrKfQZ4KUSSJL+ahzPAYSBn8dMfTvz6PMpZTG81ZwYdaFfToD3KISzgBsOqQ37KqcCI0GCsh6NEOlgtgBISwfyYWFGSpj2cva3YR8f+7QeowemOct7RHjcRmltYiArvHFBeHnWISufyDE5I8fQEpKBC69lcsp49LcV0rMWhSgzTAlQvFAQpnjvDRZcM7NpVwRTPOrpmDzCSNeQ6jeX0EmHa4gcHnD01PUJo4UMMDMeqbbAQ17sNEwi7UKcPQSY2ecyB0/ORALTMWRFw+4C7V4pS1Ty6UlEQLUW5sb1apm2avObLGO0gMcUzHESZKETvHP9kgBvozXty7vncVG+vtFdSbD4NLj4qmvKX24Oix1MAGHh1AMbFIBoYABPg7nCeSczXZE/ojPVmpMjAtLRr7so8ZAd3ZLBq++MMTaavVsxC2fXhCRpuT83DWcmgr4OEIbK43TVwJvzNeV4NWsxFtNFaXD4b5WiTEGBnxt7R2aKcHmHFV29yhqaB7MzD+3nYAPLqlrcenSS+GpiDrXV1TJw5E68Cjbx+a+DoPD2Xi1ZuXY4vvqqTm9iuIR5RAYXRs7kJQMscb7YcTHG5Ep6dMi0MTrR84dDUzXw0aZsmt4fTUxUcsUBPrAgl68BBZrwJJXZAPGLz+lQ+U4eq6oa4Hk6vqhaGDBPOVRWmUzesWqfW2maIeMCkiQRbSfejF8ocys2K9FWFlCmTqClxHLIVZQ5wM/0cn26p9GRZhG3BTU4MByqUyQR6EC05uZiTG+n5Xae3kfs7N5AwpTcyHsE4AXvoKM7NlXPu9pnj7iOxBzK92Dvc0U1DrDujs4z1zKEEYIvV4PvVA0BHY2yCszr8pjOvVE5ynIDNDLz28nUw5wwP9lkjRUS5I2DbMyOpwAwplN/SPbgkBPb2eeamge7STJ7nPM2PjoBuW4ARopG4yktiD/dzmNcEP4ZO+TxTcGtg6GErg985GRxTfvsF0M0zmBrJBHbZp/eB1407lyIosZMDysvDtcTge3sG5T4VEbDpbM5iip3PnMIOmNBHBK6uoxhM6CgyXDgtDAbEAGvgjKbEJhkZQRwOMJliQvaANdza1HZkxnU7CTQhnOIYRLt4Y2JuGyzE9SYALXskRE1bQYzCcZipl41cIDA6GGA4yOIwxEXyhRifovQgiDXpAFsZzgIEAYIBZ09P1SmJ2FDUwlB0HPQ83wQaWzDvaPO1wA30RnL5vIZcYomAzevbR41IjEhDszpo8Olzk0HTV7tu+7HL5sygvTHEapkevDNUPGTxanePo9Y6cgVQqCl2j5Q14zMyYQuUmrNmRhRTqn/dd8SdCEZ35eWgkMhZv+k059ETEJ/Mg9WSHMWfngtpb/3NeXkGMHe5ktkXM9R+Ujim4GjVP/esbS/n3tvZR4c5n6/u4WpsmGGBL+chMDo8/APGuXV5+F5AakE4PXhzL2YBPQjN2ywHU3woQm/vsG0TnBgP0IQ3JYZYvSA9odIpErC55HCaASTBpkLpyzShxg/oVtnTTSdOxQtqe1TkJpQhxdNBzSZyzgFiFKWlZ3PJeva0FviUmycSbGWnIm78kFkPp22iJykfY80cUyHGm7RNswcYixqOL4Zu5pjapsVz+bQClTQcAw7Rkh/MtEu8POImJP4Iai/c3Nt4qKfgFk4mcE+51DTgiAcjtrgdFX2aM0HgNluhw7z1VpNHB6ld8wywAQmRPgYeU8UuhBeJVpsu9IWFCDF27BQYQGOfCx6xBHj2kTUwxkpmAShgxZOuMP1ZqgAdjTUwBmaGZwwmYMxAwxl31mErsobLjxbdbLg4ZbrNw6kdCu2YPMC0nycbtrikvAYIDMAogH2jtaIksYEuvENsgCLae+GnQHfPq7zo3UK8CZYmfB8tXrjxPB9uKwbf2zMoIcXTE9QFsuMHlaxnT0tx+iGHIyOcFqB6k7Zp9twZLvrQsGtXlc5HiDi6Zg8wkjXkOo3l9BJh2uIHB5w9NT1CaOFDDAzHqm2wENe7DRMIu1CnD0EmNnnMgdPzkQC0zFkRcPuAu1eKUtU8ulJREC1FubG9WqZtmrzmyxjtIDHFMxxEmShE75xAE7QmKR/hFF/7gIs5N25u51xCgiQu9+xz02lzXZm5brt5SZASHLTUYAIGwc59rHhWTPAZCLCds0YxUjENDMZ5EgVz2MFMQRHpLyVmnOKaR30Ikw0YEoOY+svh+QRZJRCREUqMn3kTzjB2zT5nmjM24bFjNsotxTB25uzNM6gZp5weYcVXb/JSRGTo4fxcdfWUbFjeenSS+GpgDrbW1TFx5ky+zoffGuds+2TxSCawYjMtHzAqAu9oXjq85w6GvNioH6YJLS1zuMIYsHAlh/zSw2np4USqlwYNUebwJm3T5PZweqqCIxboiRWhZB04yIw3ocQOiEdsXp/KZ+pQVd0Q18PpVdXCkGHCuarCdOqGVevUWtsMEQ+YNJFgK+l+9EKZQ7lZkb6qkDJlEjUljkO2oswBbqaf49MtlZ4si7AtuMmJ4UCFMplAD6IlJxdzciNNTEJsQsrjSCT8rhOKS8hV5AaCuYpzq7mpcramIbh8ZKisbz0qaszusAtg3DUFgZLmgKEJfqWQZoFtmjwahEFSArDBZp0lm3wdOCr7u4dCsAILW/gQ1Bj9qcw6FAKeC9gGAKM3pJxYCpfb0mfjpz+cmNenTN34Fj5iSzSjHd3T/OgI9LcFGCEaiau8JPZwfwxdTJsOJtCd0W8NLwEeQyQG2VOjU5puv4Aa3KGRTGDePryvzspyXKFOz9c1v+u0DtcSS58+Mjj3qYiATWdzFlPsfOYUdsCEPiJwdR3FYEJHkeHCaWEwIAZYA2c0JTbJyAjicIDJFBOyB6zh1qa2IzOu20mgCeEUxyDaxRsTc9tgIa43AWjZIyFq2gpiFI7DTL1s5AKB0cEAw0EWhyEuki/E+BSlB0GsSQfYynAWIAgQDDh7eqpOScSGohaGouOg5/km0NgC4xB5nOGcwXlH42qeW+dXxbN7E23NDQVgXFruLoAITZxxs257CEav7IOfsLMvfGeYVvg0tzM8RME4Y5shH69Timnk0Pc8LPsgMcgp73nAY95Z/cjprZ7Bg9XObiT2rN90mjMygeedzKBakqO4zzoOaW/9zXl5BjB3uZK5FzPU92t0/lEzhDl3cWcevXtvZx8dtDs/AI1ZzZXZBNxd8joERgc9xhrw7Nu2+iAtAAAF3klEQVRdzxre0fp0K8WZmksoAGcPBAZWU3KmJ1rv1orwZg9wAd3lOkh+nG7HjZneFc0QkwyjYKiQTSZuBh9QWKrs6aYTp3JehytaYMapNbmkY7GRcw6V9HwcVLKePa0FPuXmiQRbOdPiDpZZD6dtoicpH2PNHFMhxpu0TbMHGIsaji+GbuaY2qbFc/m0ApU0HAMO0ZIfzLRLvDziJiQteyRETVtBjFw0bqm30CuJeye5ptzOJBa3o6NvA2S6ZW6b28ueyaOTxa95e7hDakZCD0PEOgldjT4NAmaJ4iSejk56OAHuziNgn4SJTBpzxnIyO5b05biGyqaJjGZ2maHGwBjQWRyZw8OJZ8A2xZBguauEwyCZhR9MoA2HgaF5LTBiKYm7dM72EiMhYCgxAS0UFiT0clbDQw2mgKdr5adwGlOC6c9zUaCDtbp1sFDGSzcl4ZgzOlPX/yaWfB+t3Lj1DEpI8fSEvZiF1rO5ZD17WorpWItDlRimBaheKAhSPHeGiy4Z2LWrgimedXTNHmAka8h1GsvpJcK0xQ8OOHtqeoTQwocYGI5V22AhrncbJhB2oU4fgkxs8pgDp+cjAWiZsyLg9gF3rxSlqnl0paIgWopyY3u1TNs0ec2XMdpBYopnOIgyUYjeOYEmaE1SPsIp/rCpELadvhEYne+jcTu5cnv7xAzOZb2weQjv6NxstYxw+zEBLjdNxmHXZuzMHw0JGdzokC0xHAGCxnDAa94nw/mMpZgfoAZAD+TgSXJAkjtnDs1m+Nl3yRHRU7cuD2fC5XbOZz47TWT21NzubnyMvryacUjzcIxTAhG9eP4+Y3srghmdszqFRpPONcvAo+dOPk96cpc7xMSZM6l7uLrROGf77KSEXt2loeUDRkUYfPXM18yFeUdLrpsH0DKHa4gBp0vO9JrcOZwWBidSvTRoiKLHm7RNk9vD6akKjligJ1aEknXgIDPehBI7IB6xeX0qn6lDVXVDXA+nV1ULQ4YJ56oK06kbVq1Ta20zRDxg0kSCraT70QtlDuVmRfqqQsqUSdSUOA7ZijIHuJl+jk+3VHqyLMK24CYnhgMVymQCPYiWnFzMyY00MQmxCSmPI0GFqF1sFI4jtv6Ae4qKiO0tpdS4khjIrwGamg6jyZq56nObBxvQzB7CzBOuW63W9sjgkQBlcIxFYxe/FE9CO5gG8GhgsFOcNAQ7MYRTmXUoRUAT1XTBZEmSr48GTyHCYQJqX4PwymBY48yskJIlGm576S0GE6wQ8lyiBdzsYIDtw0vRhaG0wP0xVOeIvW2KZPBb4wxuC3/MoXHoJOH4BMhr3hkdyta+JuNNPJaj8zVdvs/aNusGOCmYdzT1ujePC6jhxXMcXLCePZ3O4hzccAjPN3kwAZpmjpIMdxcCBFg85sKVYmQEMWWAyRQTsges4damtiMzrttJoAnhFMcg2sUbE3PbYCGuNwFo2SMhatoKYhSOw0y9bOQCgdHBAMNBFochLpIvxPgUpQdBrEkH2MpwFiAIEAw4e3qqTknEhqIWhqLjoOf5JtDYAuMQeZzhnNGhq7ohrofTU/FrNKq5rNy9O4O5zhPhvKnr3E2vKm7tTQbuAgCmAJpJCjWkrdmHcDABm8XoYVcEt5hpMBEeO9ge8iW2cCdrENDADt7nXvNX3hF7q0d8di3HPBJ71m86zRmZgPhkXoNakqP49TwypL31N+flGcDc5crXZ6j3NaJx/lFLvHUevfztvAY2Kyf5nIersWGGBe7+qdkDgdEBYzs/+16+ltXbvubBo9uaJXm8o3krywJuYPFFGdSEXJyMLnizB7gg5UNoxpvYI2XdCPdadcoEMAqGevAj020CjR0Qj9i8PpXzOlRVN6EMKZ4OajaRcw4QoygtPZtL1rOntcCn3DyRYCs7FXHjh8x6OG0TPUn5GGvmmAox3qRtmj3AWNRwfDF0M8fUNi2ey6cVqKThGHCIlvxgpl3i5RE3IWnZIyFq2gpiFI5TTU0OLk6S/z8AAAD//zTCpeMAAAAGSURBVAMAylgkpZpprMUAAAAASUVORK5CYII= + 0 + 1033 + Store Policy Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json b/submissions/blastbox-demo-agents/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json new file mode 100644 index 0000000..82b01ee --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the Store Policy Agent \u2014 the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns \u0026 Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won\u0027t power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns \u0026 Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn\u0027t specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.\n" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/Default_draft_IsBewO/bot.xml b/submissions/blastbox-demo-agents/solution/bots/Default_draft_IsBewO/bot.xml new file mode 100644 index 0000000..47cd693 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/Default_draft_IsBewO/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Returns & Service Assistant + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/Default_draft_IsBewO/configuration.json b/submissions/blastbox-demo-agents/solution/bots/Default_draft_IsBewO/configuration.json new file mode 100644 index 0000000..b1d9e93 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/Default_draft_IsBewO/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the BlastPass Assistant, a friendly self-serve helper that BlastBox customers talk to directly. You help members with their BlastPass membership and their membership card.\n\nTone: warm, upbeat, and concise. Speak directly to the customer like a helpful concierge - one friendly idea per turn, never a wall of text. A single light emoji is fine; don\u0027t overdo it. Ask for one thing at a time, and follow the steps in your skills when one applies.\n\nA couple of house rules: always verify identity before making any change to a membership or card, and never invent membership details, serials, or numbers - look them up with your tools or ask the customer.\n" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml b/submissions/blastbox-demo-agents/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml new file mode 100644 index 0000000..3828bb5 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Store Associate Assistant + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json b/submissions/blastbox-demo-agents/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json new file mode 100644 index 0000000..bc76c7f --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json @@ -0,0 +1,32 @@ +{ + "$kind": "BotConfiguration", + "channels": [ + { + "$kind": "ChannelDefinition", + "id": "MsTeams", + "channelId": "MsTeams" + } + ], + "publishOnCreate": false, + "publishOnImport": true, + "isLightweightBot": false, + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer\u0027s whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer." + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/customizations.xml b/submissions/blastbox-demo-agents/solution/customizations.xml new file mode 100644 index 0000000..9c34175 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/customizations.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + 2449e305-4c62-f111-ab0b-0022480a52c7 + + Policy RAG MCP + #007ee5 + new_policy-20rag-20mcp + 1 + [] + /Connector/new_policy-20rag-20mcp_openapidefinition.json + /Connector/new_policy-20rag-20mcp_connectionparameters.json + /Connector/new_policy-20rag-20mcp_policytemplateinstances.json + /Connector/new_policy-20rag-20mcp_customcodeblobcontent.csx + + + 5d6180ed-0d6f-418c-8ccb-1360b7c33f88 + BlastPass console membership lookup, cancellation, and card reissue. Tools: get_membership, cancel_membership, reissue_card. Runs entirely inside the connector — no external MCP server needed. + Membership MCP v2 + #007ee5 + cat_membership-20mcp-20v2 + 1 + [] + /Connector/cat_membership-20mcp-20v2_openapidefinition.json + /Connector/cat_membership-20mcp-20v2_connectionparameters.json + /Connector/cat_membership-20mcp-20v2_policytemplateinstances.json + /Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx + /Connector/cat_membership-20mcp-20v2_iconblob.Png + + + e4e7e713-5c3f-f111-bec6-70a8a59a4421 + + Order Management MCP + #007ee5 + new_order-20management-20mcp + 1 + [] + /Connector/new_order-20management-20mcp_openapidefinition.json + /Connector/new_order-20management-20mcp_connectionparameters.json + /Connector/new_order-20management-20mcp_policytemplateinstances.json + /Connector/new_order-20management-20mcp_customcodeblobcontent.csx + + + 357a348b-d53f-f111-bec6-70a8a59a4421 + Warehouse inventory and fulfillment with tools for checking stock, tracking pipeline stages, finding alternatives, and restock dates. Runs entirely inside the connector — no external MCP server needed. + Warehouse MCP + #007ee5 + new_warehouse-20mcp + 1 + [] + /Connector/new_warehouse-20mcp_openapidefinition.json + /Connector/new_warehouse-20mcp_connectionparameters.json + /Connector/new_warehouse-20mcp_policytemplateinstances.json + /Connector/new_warehouse-20mcp_customcodeblobcontent.csx + /Connector/new_warehouse-20mcp_iconblob.Png + + + + 1033 + + \ No newline at end of file diff --git a/submissions/blastbox-demo-agents/solution/solution.xml b/submissions/blastbox-demo-agents/solution/solution.xml new file mode 100644 index 0000000..114bf63 --- /dev/null +++ b/submissions/blastbox-demo-agents/solution/solution.xml @@ -0,0 +1,87 @@ + + + BlastBoxDemo + + + + + 1.0.0.1 + 0 + + CAT + + + + + + + cat + 76486 + +
+ 1 + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + + +
+
+ 2 + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + + +
+
+
+ + + + + + + +
+
\ No newline at end of file diff --git a/submissions/blastbox-deploy/README.md b/submissions/blastbox-deploy/README.md new file mode 100644 index 0000000..1166ece --- /dev/null +++ b/submissions/blastbox-deploy/README.md @@ -0,0 +1,18 @@ +# BlastBox Deploy + +BlastBox Deploy packages a coordinated retail-agent team for import into another +Power Platform environment. It combines conversational agents with MCP-backed +store operations and reusable file-based skills. + +## Agents + +The store assistant works with inventory and policy specialists and includes +capabilities for membership, orders, warehouse inventory, refunds, points, and +customer documents. + +## Import + +Import the rebuilt ZIP through Power Platform, then review all connection +references and MCP endpoints before publishing. File-based skills are included +in the solution package and should be reviewed with the same care as other +submitted source files. diff --git a/submissions/blastbox-deploy/metadata.json b/submissions/blastbox-deploy/metadata.json new file mode 100644 index 0000000..02f61aa --- /dev/null +++ b/submissions/blastbox-deploy/metadata.json @@ -0,0 +1,13 @@ +{ + "name": "BlastBox Deploy", + "description": "A packaged multi-agent retail assistant with connected specialists, operational MCP tools, and portable skill files.", + "tags": [ + "retail", + "multi-agent", + "mcp", + "skills", + "deployment" + ], + "author": "BlastBox team", + "featured": false +} diff --git a/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_connectionparameters.json b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx new file mode 100644 index 0000000..7960808 --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx @@ -0,0 +1,1195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Membership MCP Connector (inline) ║ +// ║ ║ +// ║ BlastPass membership lookup + cancellation for the BlastBox Omega console.║ +// ║ Give it a mega-blast-customer-id and it returns the membership tier and ║ +// ║ how many whole months are left on the term — the number the Returns & ║ +// ║ Service Assistant feeds into its prorated-refund Python calculation. ║ +// ║ ║ +// ║ 2 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "blastpass-membership", + Version = "1.0.0", + Title = "BlastPass Membership", + Description = "Look up a BlastPass console membership by its mega-blast-customer-id, cancel memberships, and reissue lost/stolen/damaged membership cards. Returns the membership tier, activation date, hours streamed, current blast_points balance, the card_serial on file, and how many whole months remain on the 12-month term — the inputs needed to calculate a prorated cancellation refund, reconcile loyalty points, or print a replacement card." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use get_membership with a mega-blast-customer-id (e.g. 'MEGA-BLAST-1024') to retrieve the member's BlastPass tier, annual price, activation date, hours streamed, card_serial, current blast_points balance, and months_remaining on the term. Pass months_remaining and the price to the prorated-refund calculation, and the returned blast_points to the points-reconciliation calculation — never ask the associate for the points balance, it is on the membership record. Use cancel_membership once a refund figure is agreed to close the membership and get a confirmation number. Use reissue_card for a self-serve lost/stolen/damaged card replacement: it deactivates the old card_serial and issues a new one while leaving the membership active — feed the returned new_card_serial into the membership-card-png skill to give the member a digital card to save while the physical one ships." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + // + // months_remaining is the authoritative count of WHOLE unused months left + // on the 12-month term, pre-computed so the demo is deterministic. The + // prorated refund = months_remaining x (annual_price / 12) - cancellation_fee + // - nonrefundable_credit, unless the cooling-off rule applies (activated <= 14 + // days ago AND hours_streamed < 2 -> full refund of annual_price). + + private static readonly JArray Memberships = JArray.Parse(@"[ + { + ""customer_id"":""MEGA-BLAST-1024"", + ""member_name"":""Jordan Pixel"", + ""tier"":""BlastPass Plus Extra"", + ""tier_code"":""extra"", + ""status"":""active"", + ""annual_price"":129.99, + ""cancellation_fee"":10.00, + ""nonrefundable_credit"":0.00, + ""activation_date"":""2026-02-26"", + ""term_end_date"":""2027-02-26"", + ""months_remaining"":8, + ""hours_streamed"":47.5, + ""blast_points"":4200, + ""console_serial"":""OMEGA-7F3A-1024"", + ""card_serial"":""BLAST-7F3A-1024"" + }, + { + ""customer_id"":""MEGA-BLAST-2048"", + ""member_name"":""Sam Sparkle"", + ""tier"":""BlastPass Plus Extra MEGA!!!"", + ""tier_code"":""mega"", + ""status"":""active"", + ""annual_price"":199.99, + ""cancellation_fee"":25.00, + ""nonrefundable_credit"":20.00, + ""activation_date"":""2026-05-30"", + ""term_end_date"":""2027-05-30"", + ""months_remaining"":11, + ""hours_streamed"":1.2, + ""blast_points"":15600, + ""console_serial"":""OMEGA-9B1C-2048"", + ""card_serial"":""BLAST-9B1C-2048"" + }, + { + ""customer_id"":""MEGA-BLAST-4096"", + ""member_name"":""Riley Retro"", + ""tier"":""BlastPass Plus"", + ""tier_code"":""plus"", + ""status"":""active"", + ""annual_price"":79.99, + ""cancellation_fee"":0.00, + ""nonrefundable_credit"":0.00, + ""activation_date"":""2025-12-10"", + ""term_end_date"":""2026-12-10"", + ""months_remaining"":6, + ""hours_streamed"":88.0, + ""blast_points"":9050, + ""console_serial"":""OMEGA-2D4E-4096"", + ""card_serial"":""BLAST-2D4E-4096"" + }, + { + ""customer_id"":""MEGA-BLAST-8192"", + ""member_name"":""Casey Combo"", + ""tier"":""BlastPass Plus Extra MEGA!!!"", + ""tier_code"":""mega"", + ""status"":""active"", + ""annual_price"":199.99, + ""cancellation_fee"":25.00, + ""nonrefundable_credit"":20.00, + ""activation_date"":""2026-01-05"", + ""term_end_date"":""2027-01-05"", + ""months_remaining"":5, + ""hours_streamed"":210.0, + ""blast_points"":33700, + ""console_serial"":""OMEGA-5A6F-8192"", + ""card_serial"":""BLAST-5A6F-8192"" + } + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. get_membership + handler.AddTool("get_membership", + "Look up a BlastPass membership by its mega-blast-customer-id. Returns the membership tier, annual price, activation date, hours streamed, cancellation fee, any non-refundable credit, months_remaining (the whole unused months left on the 12-month term), and the member's current blast_points balance. Feed months_remaining and annual_price into the prorated-refund calculation, and blast_points into the points-reconciliation calculation.", + schemaConfig: s => s.String("customer_id", "The mega-blast-customer-id (e.g. 'MEGA-BLAST-1024'). Case-insensitive.", required: true), + handler: async (args, ct) => + { + var id = (args.Value("customer_id") ?? "").Trim(); + foreach (var m in Memberships) + { + if (string.Equals(m["customer_id"]?.ToString(), id, StringComparison.OrdinalIgnoreCase)) + { + var activation = DateTime.Parse(m["activation_date"].ToString()); + var daysSince = (int)(DateTime.UtcNow.Date - activation.Date).TotalDays; + var hours = m["hours_streamed"]?.Value() ?? 0; + var coolingOff = daysSince <= 14 && daysSince >= 0 && hours < 2; + return new JObject + { + ["customer_id"] = m["customer_id"], + ["member_name"] = m["member_name"], + ["tier"] = m["tier"], + ["tier_code"] = m["tier_code"], + ["status"] = m["status"], + ["annual_price"] = m["annual_price"], + ["cancellation_fee"] = m["cancellation_fee"], + ["nonrefundable_credit"] = m["nonrefundable_credit"], + ["activation_date"] = m["activation_date"], + ["term_end_date"] = m["term_end_date"], + ["days_since_activation"] = daysSince, + ["months_remaining"] = m["months_remaining"], + ["hours_streamed"] = m["hours_streamed"], + ["blast_points"] = m["blast_points"], + ["within_cooling_off"] = coolingOff, + ["console_serial"] = m["console_serial"], + ["card_serial"] = m["card_serial"] + }; + } + } + throw new ArgumentException($"No BlastPass membership found for customer id \"{id}\". Check the mega-blast-customer-id (format: MEGA-BLAST-####)."); + }); + + // 2. cancel_membership + handler.AddTool("cancel_membership", + "Cancel a BlastPass membership after a refund amount has been agreed. Records the cancellation and returns a confirmation number, the effective date, and the refund amount on file. Only call this once the associate confirms the customer wants to proceed.", + schemaConfig: s => s + .String("customer_id", "The mega-blast-customer-id of the membership to cancel (e.g. 'MEGA-BLAST-1024').", required: true) + .Number("refund_amount", "The agreed prorated refund amount in dollars, as calculated by the refund step.", required: true), + handler: async (args, ct) => + { + var id = (args.Value("customer_id") ?? "").Trim(); + var refund = args.Value("refund_amount") ?? 0.0; + foreach (var m in Memberships) + { + if (string.Equals(m["customer_id"]?.ToString(), id, StringComparison.OrdinalIgnoreCase)) + { + var confirmation = "BPX-CXL-" + id.Replace("MEGA-BLAST-", "") + "-" + DateTime.UtcNow.ToString("yyyyMMdd"); + return new JObject + { + ["customer_id"] = m["customer_id"], + ["member_name"] = m["member_name"], + ["tier"] = m["tier"], + ["previous_status"] = m["status"], + ["new_status"] = "cancelled", + ["cancellation_confirmation"] = confirmation, + ["effective_date"] = DateTime.UtcNow.ToString("yyyy-MM-dd"), + ["refund_amount"] = Math.Round(refund, 2), + ["message"] = $"BlastPass {m["tier"]} membership for {m["member_name"]} is cancelled. A refund of ${Math.Round(refund, 2):0.00} will be issued to the original payment method." + }; + } + } + throw new ArgumentException($"No BlastPass membership found for customer id \"{id}\". Cannot cancel."); + }); + + // 3. reissue_card + handler.AddTool("reissue_card", + "Deactivate a member's current physical BlastPass card and issue a replacement, WITHOUT cancelling or changing the membership itself. Use this for a self-serve lost / stolen / damaged card request: it invalidates the old card_serial, mints a brand-new card_serial, keeps the membership active, and queues the new physical card for mailing. Returns the previous (now void) card_serial, the new card_serial to print on a digital card, a reissue confirmation number, and the mailing ETA for the physical card. The membership tier, term, and BlastPoints are untouched.", + schemaConfig: s => s + .String("customer_id", "The mega-blast-customer-id whose card is being replaced (e.g. 'MEGA-BLAST-1024').", required: true) + .String("reason", "Why the card is being reissued: 'lost', 'stolen', or 'damaged'. Defaults to 'lost'.", required: false), + handler: async (args, ct) => + { + var id = (args.Value("customer_id") ?? "").Trim(); + var reason = (args.Value("reason") ?? "lost").Trim().ToLowerInvariant(); + foreach (var m in Memberships) + { + if (string.Equals(m["customer_id"]?.ToString(), id, StringComparison.OrdinalIgnoreCase)) + { + if (!string.Equals(m["status"]?.ToString(), "active", StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException($"BlastPass membership for {m["member_name"]} is {m["status"]}, so a card cannot be reissued."); + + var prevCard = m["card_serial"]?.ToString() ?? ""; + // Mint a new card_serial: keep the trailing member segment, refresh the middle block. + var tail = id.Replace("MEGA-BLAST-", ""); + var seed = (prevCard + DateTime.UtcNow.Ticks).GetHashCode(); + var block = ((seed & 0x7FFFFFFF) % 0x10000).ToString("X4"); + var newCard = $"BLAST-{block}-{tail}"; + // Persist so a follow-up get_membership reflects the new card. + m["card_serial"] = newCard; + + var confirmation = "BPX-CARD-" + tail + "-" + DateTime.UtcNow.ToString("yyyyMMdd"); + var shipDays = 7; + var eta = DateTime.UtcNow.Date.AddDays(shipDays); + var last4 = prevCard.Length >= 4 ? prevCard.Substring(prevCard.Length - 4) : prevCard; + return new JObject + { + ["customer_id"] = m["customer_id"], + ["member_name"] = m["member_name"], + ["tier"] = m["tier"], + ["tier_code"] = m["tier_code"], + ["reason"] = reason, + ["previous_card_serial"] = prevCard, + ["previous_card_status"] = "deactivated", + ["new_card_serial"] = newCard, + ["reissue_confirmation"] = confirmation, + ["membership_status"] = m["status"], + ["digital_card_ready"] = true, + ["ship_method"] = "Standard mail", + ["ship_eta_days"] = shipDays, + ["estimated_delivery"] = eta.ToString("yyyy-MM-dd"), + ["message"] = $"Your old card ending {last4} is now deactivated and can no longer be used. A new {m["tier"]} card ({newCard}) has been issued — a printed copy is on its way by standard mail (arriving ~{eta:yyyy-MM-dd}). You can save the digital card now and use it in-store and at checkout right away." + }; + } + } + throw new ArgumentException($"No BlastPass membership found for customer id \"{id}\". Cannot reissue a card."); + }); + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_iconblob.Png b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_iconblob.Png new file mode 100644 index 0000000..f417fb8 Binary files /dev/null and b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_iconblob.Png differ diff --git a/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_openapidefinition.json b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_openapidefinition.json new file mode 100644 index 0000000..0a178ad --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_openapidefinition.json @@ -0,0 +1,41 @@ +{ + "swagger": "2.0", + "info": { + "title": "Membership MCP v2", + "description": "BlastPass console membership lookup, cancellation, and card reissue. Tools: get_membership, cancel_membership, reissue_card. Runs entirely inside the connector \u2014 no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Membership MCP v2 Server", + "description": "MCP server for BlastPass memberships. Tools: get_membership, cancel_membership, reissue_card.", + "operationId": "InvokeMembershipMCPv2", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "definitions": {}, + "parameters": {}, + "responses": {}, + "security": [], + "tags": [], + "securityDefinitions": {} +} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_policytemplateinstances.json b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/cat_membership-20mcp-20v2_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_connectionparameters.json b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx new file mode 100644 index 0000000..fc9a695 --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx @@ -0,0 +1,1169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Order Management MCP Connector (inline) ║ +// ║ ║ +// ║ 5 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + private const string APP_INSIGHTS_CONNECTION_STRING = ""; + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "order-management", + Version = "1.0.0", + Title = "Order Management", + Description = "E-commerce order management with tools for searching orders, viewing details, tracking shipments, and managing returns." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use search_orders first to find orders by customer name, email, or order number. Then use the order_id from results with get_order, get_shipment, request_return, or get_return_status." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + + private static readonly JArray Orders = JArray.Parse(@"[ + {""id"":""ORD-10421"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-04-10"",""status"":""shipped"",""items"":[{""sku"":""SKU-WH1000"",""name"":""Sony WH-1000XM5 Headphones"",""quantity"":1,""unitPrice"":349.99},{""sku"":""SKU-USBC3"",""name"":""USB-C Charging Cable (3-pack)"",""quantity"":1,""unitPrice"":14.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":364.98}, + {""id"":""ORD-10422"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-04-15"",""status"":""processing"",""items"":[{""sku"":""SKU-KINDLE"",""name"":""Kindle Paperwhite (16GB)"",""quantity"":1,""unitPrice"":149.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":149.99}, + {""id"":""ORD-10318"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-03-22"",""status"":""delivered"",""items"":[{""sku"":""SKU-AIRPOD"",""name"":""AirPods Pro (2nd Gen)"",""quantity"":1,""unitPrice"":249.00},{""sku"":""SKU-CASE01"",""name"":""Silicone AirPods Case - Midnight"",""quantity"":1,""unitPrice"":19.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":268.99}, + {""id"":""ORD-10455"",""customer"":{""id"":""C-002"",""name"":""James Rivera"",""email"":""j.rivera@example.com""},""date"":""2026-04-12"",""status"":""shipped"",""items"":[{""sku"":""SKU-HOODIE"",""name"":""Nike Tech Fleece Hoodie - Black (L)"",""quantity"":1,""unitPrice"":120.00},{""sku"":""SKU-JOGGER"",""name"":""Nike Sportswear Joggers - Grey (L)"",""quantity"":1,""unitPrice"":85.00}],""shippingAddress"":""88 Sunset Blvd, Apt 4B, Los Angeles, CA 90028"",""paymentMethod"":""Mastercard ending in 8811"",""total"":205.00}, + {""id"":""ORD-10460"",""customer"":{""id"":""C-002"",""name"":""James Rivera"",""email"":""j.rivera@example.com""},""date"":""2026-04-18"",""status"":""processing"",""items"":[{""sku"":""SKU-SWITCH"",""name"":""Nintendo Switch OLED"",""quantity"":1,""unitPrice"":349.99},{""sku"":""SKU-ZELDA"",""name"":""The Legend of Zelda: Tears of the Kingdom"",""quantity"":1,""unitPrice"":59.99}],""shippingAddress"":""88 Sunset Blvd, Apt 4B, Los Angeles, CA 90028"",""paymentMethod"":""Mastercard ending in 8811"",""total"":409.98}, + {""id"":""ORD-10389"",""customer"":{""id"":""C-003"",""name"":""Emily Chen"",""email"":""emily.chen@example.com""},""date"":""2026-03-28"",""status"":""delivered"",""items"":[{""sku"":""SKU-DUNE2"",""name"":""Dune: Part Two (4K Blu-ray)"",""quantity"":1,""unitPrice"":29.99},{""sku"":""SKU-FOUNDATION"",""name"":""Foundation (Isaac Asimov) - Paperback"",""quantity"":1,""unitPrice"":12.99},{""sku"":""SKU-3BODY"",""name"":""The Three-Body Problem - Paperback"",""quantity"":1,""unitPrice"":14.99}],""shippingAddress"":""1200 Main Street, Unit 7C, Seattle, WA 98101"",""paymentMethod"":""Amex ending in 3003"",""total"":57.97}, + {""id"":""ORD-10478"",""customer"":{""id"":""C-003"",""name"":""Emily Chen"",""email"":""emily.chen@example.com""},""date"":""2026-04-14"",""status"":""shipped"",""items"":[{""sku"":""SKU-ERGOCHAIR"",""name"":""ErgoMax Office Chair - Mesh Back"",""quantity"":1,""unitPrice"":299.00}],""shippingAddress"":""1200 Main Street, Unit 7C, Seattle, WA 98101"",""paymentMethod"":""Amex ending in 3003"",""total"":299.00}, + {""id"":""ORD-10502"",""customer"":{""id"":""C-004"",""name"":""Jordan Pixel"",""email"":""jordan.pixel@example.com""},""date"":""2026-06-09"",""status"":""delivered"",""items"":[{""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""quantity"":1,""unitPrice"":399.99}],""shippingAddress"":""15 Arcade Lane, Pixel Heights, WA 98052"",""paymentMethod"":""Visa ending in 1024"",""total"":399.99} + ]"); + + private static readonly JObject Shipments = JObject.Parse(@"{ + ""ORD-10421"":{""order_id"":""ORD-10421"",""carrier"":""FedEx"",""tracking_number"":""FX-794644790132"",""tracking_url"":""https://www.fedex.com/fedextrack/?trknbr=FX-794644790132"",""estimated_delivery"":""2026-04-22"",""last_update"":""2026-04-20T14:30:00Z"",""status"":""out_for_delivery""}, + ""ORD-10455"":{""order_id"":""ORD-10455"",""carrier"":""UPS"",""tracking_number"":""1Z999AA10123456784"",""tracking_url"":""https://www.ups.com/track?tracknum=1Z999AA10123456784"",""estimated_delivery"":""2026-04-21"",""last_update"":""2026-04-19T09:15:00Z"",""status"":""in_transit""}, + ""ORD-10318"":{""order_id"":""ORD-10318"",""carrier"":""USPS"",""tracking_number"":""9400111899223100287654"",""tracking_url"":""https://tools.usps.com/go/TrackConfirmAction?tLabels=9400111899223100287654"",""estimated_delivery"":""2026-03-28"",""last_update"":""2026-03-28T10:15:00Z"",""status"":""delivered""}, + ""ORD-10478"":{""order_id"":""ORD-10478"",""carrier"":""FedEx"",""tracking_number"":""FX-794644790199"",""tracking_url"":""https://www.fedex.com/fedextrack/?trknbr=FX-794644790199"",""estimated_delivery"":""2026-04-24"",""last_update"":""2026-04-21T10:00:00Z"",""status"":""in_transit""} + }"); + + private static readonly JObject Returns = JObject.Parse(@"{ + ""RA-50012"":{""return_id"":""RA-50012"",""order_id"":""ORD-10318"",""status"":""refund_processed"",""items_returned"":[{""sku"":""SKU-CASE01"",""name"":""Silicone AirPods Case - Midnight""}],""reason"":""Wrong color received"",""refund_amount"":""$19.99"",""refund_method"":""Visa ending in 4242""}, + ""RA-50018"":{""return_id"":""RA-50018"",""order_id"":""ORD-10389"",""status"":""received"",""items_returned"":[{""sku"":""SKU-DUNE2"",""name"":""Dune: Part Two (4K Blu-ray)""}],""reason"":""Disc scratched on arrival"",""refund_amount"":""$29.99"",""refund_method"":""Amex ending in 3003""} + }"); + + private static int _returnCounter = 50019; + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. search_orders + handler.AddTool("search_orders", + "Search for orders by customer name, email, or order number. Returns a summary list. This is the entry point — use it first to find order IDs.", + schemaConfig: s => s.String("query", "Customer name, email address, or order number to search for", required: true), + handler: async (args, ct) => + { + var query = (args.Value("query") ?? "").ToLower(); + var matches = new JArray(); + foreach (var order in Orders) + { + var name = order["customer"]?["name"]?.ToString().ToLower() ?? ""; + var email = order["customer"]?["email"]?.ToString().ToLower() ?? ""; + var id = order["id"]?.ToString().ToLower() ?? ""; + if (name.Contains(query) || email.Contains(query) || id.Contains(query)) + { + matches.Add(new JObject + { + ["order_id"] = order["id"], + ["date"] = order["date"], + ["status"] = order["status"], + ["total"] = "$" + order["total"], + ["customer"] = order["customer"]?["name"], + ["item_count"] = (order["items"] as JArray)?.Count ?? 0 + }); + } + } + if (matches.Count == 0) + return new JObject { ["message"] = $"No orders found matching \"{query}\"." }; + return new JObject { ["orders"] = matches }; + }); + + // 2. get_order + handler.AddTool("get_order", + "Get full details for a specific order including line items, shipping address, and payment info. Use an order_id from search_orders results.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. ORD-10421) from search_orders results", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + foreach (var order in Orders) + { + if (order["id"]?.ToString() == orderId) + return JObject.Parse(order.ToString()); + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 3. get_shipment + handler.AddTool("get_shipment", + "Get shipment tracking info for an order. Only available for shipped/delivered orders. Returns carrier, tracking number, URL, and delivery estimate.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. ORD-10421)", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + if (Shipments[orderId] != null) + return JObject.Parse(Shipments[orderId].ToString()); + + // Check if order exists but isn't shipped + foreach (var order in Orders) + { + if (order["id"]?.ToString() == orderId) + throw new ArgumentException($"Order \"{orderId}\" has status \"{order["status"]}\" — shipment tracking is only available for shipped or delivered orders."); + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 4. request_return + handler.AddTool("request_return", + "Initiate a return for specific items in an order. Returns a return authorization with ID, label, refund estimate, and instructions.", + schemaConfig: s => s + .String("order_id", "Order ID to return items from", required: true) + .Array("item_skus", "SKUs of items to return (from get_order line items)", itemSchema: new JObject { ["type"] = "string" }, required: true) + .String("reason", "Reason for return (e.g. 'Defective', 'Wrong item')", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + var reason = args.Value("reason") ?? "Not specified"; + var skuArray = args["item_skus"] as JArray ?? new JArray(); + var skus = skuArray.Select(s => s.ToString()).ToList(); + + foreach (var order in Orders) + { + if (order["id"]?.ToString() != orderId) continue; + + var items = new JArray(); + double refundTotal = 0; + foreach (var item in order["items"] as JArray ?? new JArray()) + { + if (skus.Contains(item["sku"]?.ToString())) + { + items.Add(new JObject { ["sku"] = item["sku"], ["name"] = item["name"] }); + refundTotal += item["unitPrice"]?.Value() ?? 0; + } + } + + var returnId = $"RA-{System.Threading.Interlocked.Increment(ref _returnCounter):D5}"; + return new JObject + { + ["return_id"] = returnId, + ["order_id"] = orderId, + ["status"] = "requested", + ["items_returned"] = items, + ["reason"] = reason, + ["refund_amount"] = $"${refundTotal:F2}", + ["refund_method"] = order["paymentMethod"], + ["return_label_url"] = $"https://returns.example.com/labels/{returnId}.pdf", + ["instructions"] = "Print the return label, attach it to the package, and drop it off at any carrier location within 14 days." + }; + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 5. get_return_status + handler.AddTool("get_return_status", + "Check the status of a return request. Returns current stage, refund status, and timeline.", + schemaConfig: s => s.String("return_id", "Return ID (e.g. RA-50019) from request_return result", required: true), + handler: async (args, ct) => + { + var returnId = args.Value("return_id") ?? ""; + if (Returns[returnId] != null) + return JObject.Parse(Returns[returnId].ToString()); + + // For newly created returns, return a generic status + return new JObject + { + ["return_id"] = returnId, + ["status"] = "requested", + ["refund_status"] = "pending", + ["timeline"] = new JArray { new JObject { ["stage"] = "Return requested", ["date"] = DateTime.UtcNow.ToString("yyyy-MM-dd") } } + }; + }); + } + + // ── App Insights (optional, from Power MCP Template) ──────────────── + + private async Task LogToAppInsights(string eventName, JObject data, string correlationId) + { + if (string.IsNullOrEmpty(APP_INSIGHTS_CONNECTION_STRING)) return; + try + { + var iKey = APP_INSIGHTS_CONNECTION_STRING.Split(';').FirstOrDefault(s => s.StartsWith("InstrumentationKey="))?.Split('=')[1] ?? ""; + var endpoint = APP_INSIGHTS_CONNECTION_STRING.Split(';').FirstOrDefault(s => s.StartsWith("IngestionEndpoint="))?.Split('=')[1]?.TrimEnd('/') ?? "https://dc.services.visualstudio.com"; + var envelope = new JObject + { + ["name"] = "Microsoft.ApplicationInsights.Event", + ["time"] = DateTime.UtcNow.ToString("o"), + ["iKey"] = iKey, + ["data"] = new JObject + { + ["baseType"] = "EventData", + ["baseData"] = new JObject { ["ver"] = 2, ["name"] = eventName, ["properties"] = new JObject { ["correlationId"] = correlationId, ["details"] = data?.ToString(Newtonsoft.Json.Formatting.None) ?? "{}" } } + } + }; + var request = new HttpRequestMessage(HttpMethod.Post, $"{endpoint}/v2/track") { Content = new StringContent($"[{envelope}]", Encoding.UTF8, "application/json") }; + await this.Context.SendAsync(request, this.CancellationToken).ConfigureAwait(false); + } + catch { } + } +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_openapidefinition.json b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_openapidefinition.json new file mode 100644 index 0000000..98302dc --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Order Management MCP", + "description": "E-commerce order management with tools for searching orders, viewing details, tracking shipments, and managing returns. Runs entirely inside the connector \u2014 no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx new file mode 100644 index 0000000..379d78e --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx @@ -0,0 +1,1214 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Policy RAG MCP Connector (inline, MOCK) ║ +// ║ ║ +// ║ A pretend "knowledge base" for the BlastBox Omega Store Policy Agent. ║ +// ║ Modern Copilot Studio agents can't take uploaded files, so the tier ║ +// ║ policy booklets live here as static passages and a mock keyword search. ║ +// ║ ║ +// ║ No embeddings, no external store — just canned text + naive scoring. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "blastpass-policy-rag", + Version = "1.0.0", + Title = "BlastPass Policy Knowledge", + Description = "Mock policy knowledge base for BlastBox Omega. Search the BlastPass membership, returns, and loyalty policies, or fetch the exact refund rule for a tier. Use this to confirm a member's tier and whether a prorated refund is allowed before running the refund math." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Call search_policy with the associate's question (and optionally a tier_code of plus/extra/mega) to retrieve the most relevant policy passages. Call get_tier_refund_policy with a tier_code to get the structured refund rule (cooling-off window, proration method, cancellation fee, non-refundable credit) needed to calculate a prorated refund. Call get_markdown_policy with a markdown_type ('promo' or 'clearance') to get the store's pricing markdown guardrails (max discount, margin floor, clearance/manager-flag rules) for merchandising markdown decisions. This is a mock knowledge base with static text." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static "Knowledge Base" ───────────────────────────────────────── + // + // Each passage is a mock policy chunk. tier is plus/extra/mega or "all". + // Naive search scores passages by how many query words appear in the + // keywords + text. This is intentionally NOT real RAG — just a demo prop. + + private static readonly JArray Passages = JArray.Parse(@"[ + { + ""id"":""mem-plus-refund"", + ""doc"":""BlastPass Plus Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""plus"", + ""keywords"":""plus tier1 refund cancel cancellation prorate prorated proration cooling-off cooling off window months remaining price 79.99 fee"", + ""text"":""BlastPass Plus ($79.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 79.99/12 = $6.6658/mo). Plus has NO cancellation fee and no non-refundable credit. Prorated refunds ARE allowed for Plus."" + }, + { + ""id"":""mem-extra-refund"", + ""doc"":""BlastPass Plus Extra Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""extra"", + ""keywords"":""extra plus extra tier2 refund cancel cancellation prorate prorated proration cooling-off window months remaining price 129.99 fee 10 vault"", + ""text"":""BlastPass Plus Extra ($129.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 129.99/12 = $10.8325/mo), MINUS a $10 cancellation fee (never below $0). No non-refundable credit. Prorated refunds ARE allowed for Plus Extra."" + }, + { + ""id"":""mem-mega-refund"", + ""doc"":""BlastPass Plus Extra MEGA!!! Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""mega"", + ""keywords"":""mega plus extra mega tier3 refund cancel cancellation prorate prorated proration cooling-off window months remaining price 199.99 fee 25 welcome credit 20 non-refundable loot crate"", + ""text"":""BlastPass Plus Extra MEGA!!! ($199.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 199.99/12 = $16.6658/mo), MINUS a $25 cancellation fee AND MINUS the one-time $20 MEGA Welcome Credit, which is non-refundable (never below $0). Prorated refunds ARE allowed for MEGA, but remember to subtract BOTH the $25 fee and the $20 credit."" + }, + { + ""id"":""mem-perks"", + ""doc"":""BlastPass Membership Terms"", + ""section"":""Tiers & Perks"", + ""tier"":""all"", + ""keywords"":""tier tiers perks benefits vault mystery cartridge cloud save blastbuddies co-op which tier difference compare"", + ""text"":""Tiers: Plus ($79.99) — 4-player co-op, 1 Mystery Cartridge/mo, 25GB cloud saves. Plus Extra ($129.99) — Extra Vault (200+ titles), 100GB saves, 2 cartridges/mo, early demos. Plus Extra MEGA!!! ($199.99) — MEGA Vault (600+ titles incl. day-one exclusives), unlimited saves, 4 cartridges/mo + quarterly Loot Crate, MEGA Lounge, plus a one-time $20 Welcome Credit."" + }, + { + ""id"":""returns-windows"", + ""doc"":""Store Returns & Exchanges Policy"", + ""section"":""Return Windows"", + ""tier"":""all"", + ""keywords"":""return returns exchange window days console accessory game sealed opened receipt eligible eligibility"", + ""text"":""Return windows (with receipt): Consoles & hardware — 30 days, like-new. Accessories (controllers, headsets) — 30 days. Sealed physical games — 30 days. Opened physical games — 14 days, exchange only for the same title if defective. Digital/redeemed codes — non-returnable once revealed. Day-one MEGA exclusives — NON-RETURNABLE, all sales final."" + }, + { + ""id"":""returns-restocking"", + ""doc"":""Store Returns & Exchanges Policy"", + ""section"":""Restocking Fees & Store Credit"", + ""tier"":""all"", + ""keywords"":""restocking fee store credit refund opened bundle settlement defective warranty percent bonus"", + ""text"":""Restocking fee: 15% on opened consoles/hardware returned in like-new condition; waived if the item is defective. Defective hardware within 30 days is a free warranty swap, not a return. Refunds go to the original tender; choosing STORE CREDIT instead adds a 10% goodwill bonus on the eligible merchandise total (the bonus does NOT apply to membership-proration refunds or to fees)."" + }, + { + ""id"":""loyalty-blastpoints"", + ""doc"":""BlastPoints Loyalty Program Terms"", + ""section"":""Earning, Promos & Expiry"", + ""tier"":""all"", + ""keywords"":""blastpoints loyalty points earn redeem expiry expire promo multiplier triple weekend tier bonus reconcile balance"", + ""text"":""BlastPoints: earn 10 points per $1 on merchandise. Tier bonus: Extra members +20%, MEGA members +50% on earned points. Promo events (e.g. Triple BLAST Weekend) multiply BASE earn by 3x before the tier bonus. Points from returned items are clawed back. Points expire 90 days after they are earned if the account is inactive. Redemption: 1,000 points = $5 store credit."" + } + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. search_policy — mock keyword search over the static passages + handler.AddTool("search_policy", + "Search the BlastBox Omega policy knowledge base (membership, returns, and loyalty terms) and return the most relevant policy passages. Use this to confirm a member's tier, check whether a prorated refund is allowed, look up return windows, restocking fees, or loyalty rules.", + schemaConfig: s => s + .String("query", "What you want to know, in natural language (e.g. 'is a prorated refund allowed for Plus Extra?' or 'return window for an opened console').", required: true) + .String("tier_code", "Optional tier filter: 'plus', 'extra', or 'mega'. Omit to search all policies.", required: false), + handler: async (args, ct) => + { + var query = (args.Value("query") ?? "").Trim(); + var tier = (args.Value("tier_code") ?? "").Trim().ToLowerInvariant(); + var terms = Tokenize(query); + + var scored = new List(); + foreach (var p in Passages) + { + var pTier = p["tier"]?.ToString() ?? "all"; + if (tier.Length > 0 && pTier != "all" && pTier != tier) + continue; + + var hay = ((p["keywords"]?.ToString() ?? "") + " " + (p["text"]?.ToString() ?? "")).ToLowerInvariant(); + int score = 0; + foreach (var t in terms) + if (hay.Contains(t)) score++; + if (tier.Length > 0 && pTier == tier) score += 2; // tier match boost + + if (score > 0) + { + scored.Add(new JObject + { + ["id"] = p["id"], + ["document"] = p["doc"], + ["section"] = p["section"], + ["tier"] = pTier, + ["text"] = p["text"], + ["score"] = score + }); + } + } + + var top = scored.OrderByDescending(x => (int)x["score"]).Take(3).ToList(); + if (top.Count == 0) + { + return new JObject + { + ["query"] = query, + ["matches"] = new JArray(), + ["note"] = "No matching policy passage. Try mentioning the tier (Plus, Plus Extra, MEGA) or a topic like 'refund', 'return window', or 'BlastPoints'." + }; + } + + var results = new JArray(); + foreach (var m in top) results.Add(m); + return new JObject { ["query"] = query, ["matches"] = results }; + }); + + // 2. get_tier_refund_policy — structured refund rule for a tier + handler.AddTool("get_tier_refund_policy", + "Return the exact, structured membership refund rule for a BlastPass tier: the cooling-off window, the proration method, the cancellation fee, and any non-refundable credit. Use this once the tier is known so the refund can be calculated precisely.", + schemaConfig: s => s + .String("tier_code", "The tier: 'plus' (BlastPass Plus), 'extra' (BlastPass Plus Extra), or 'mega' (BlastPass Plus Extra MEGA!!!).", required: true), + handler: async (args, ct) => + { + var tier = (args.Value("tier_code") ?? "").Trim().ToLowerInvariant(); + switch (tier) + { + case "plus": + return RefundRule("BlastPass Plus", 79.99, 0.00, 0.00); + case "extra": + return RefundRule("BlastPass Plus Extra", 129.99, 10.00, 0.00); + case "mega": + return RefundRule("BlastPass Plus Extra MEGA!!!", 199.99, 25.00, 20.00); + default: + throw new ArgumentException("Unknown tier_code \"" + tier + "\". Use 'plus', 'extra', or 'mega'."); + } + }); + + // 3. get_markdown_policy + handler.AddTool("get_markdown_policy", + "Return the store's pricing markdown guardrails. Pass markdown_type='ask' FIRST to get the clarifying question the manager must answer (promo vs clearance). Once known, pass markdown_type='promo' (temporary promotion: capped discount, margin floor, no clearance) or 'clearance' (discontinuation: no cap/floor, requires a manager flag). Pass the returned guardrails into the markdown-optimizer; never hardcode them.", + schemaConfig: s => s + .String("markdown_type", "The markdown type: 'promo' (temporary promotion), 'clearance' (discontinuation), or 'ask' to receive the clarifying question to put to the manager.", required: true), + handler: async (args, ct) => + { + var type = (args.Value("markdown_type") ?? "").Trim().ToLowerInvariant(); + switch (type) + { + case "": + case "ask": + return new JObject + { + ["needs_clarification"] = true, + ["question"] = "Are these markdowns a temporary end-of-quarter PROMOTION, or a CLEARANCE because we're discontinuing the items? Promotions are capped and must stay above the margin floor; clearance has no floor but needs manager approval.", + ["options"] = new JArray("promo", "clearance"), + ["rule"] = "The markdown type determines the discount cap and margin floor, so confirm it with the manager before recommending any discounts." + }; + case "promo": + return new JObject + { + ["markdown_type"] = "promo", + ["max_discount_pct"] = 30, + ["margin_floor_pct"] = 15, + ["min_useful_promo_discount_pct"] = 10, + ["clearance_allowed"] = false, + ["requires_manager_flag"] = false, + ["rule"] = "Temporary promotion: discount each item by up to 30% off its current price, but never below a 15% gross margin. If the deepest discount allowed by the margin floor is under min_useful_promo_discount_pct (10%), the item cannot be meaningfully promoted and should be flagged for clearance instead." + }; + case "clearance": + return new JObject + { + ["markdown_type"] = "clearance", + ["max_discount_pct"] = null, + ["margin_floor_pct"] = null, + ["min_useful_promo_discount_pct"] = null, + ["clearance_allowed"] = true, + ["requires_manager_flag"] = true, + ["rule"] = "Clearance / discontinuation: no discount cap and no margin floor (may sell below cost to clear), but every clearance markdown requires explicit manager approval (manager flag)." + }; + default: + return new JObject + { + ["needs_clarification"] = true, + ["question"] = "I didn't recognize that markdown type. Is this a temporary PROMOTION or a CLEARANCE (discontinuation)?", + ["options"] = new JArray("promo", "clearance") + }; + } + }); + } + + private static JObject RefundRule(string name, double annual, double fee, double credit) + { + return new JObject + { + ["tier"] = name, + ["annual_price"] = annual, + ["monthly_value"] = Math.Round(annual / 12.0, 4), + ["term_months"] = 12, + ["cooling_off_days"] = 14, + ["cooling_off_max_hours_streamed"] = 2, + ["cooling_off_rule"] = "If cancelled within 14 days of activation AND under 2 hours streamed, issue a FULL refund of the annual price (no fee).", + ["proration_rule"] = "Otherwise refund = whole UNUSED months remaining x (annual_price / 12), then subtract the cancellation fee and any non-refundable credit; never below $0.", + ["cancellation_fee"] = fee, + ["nonrefundable_credit"] = credit, + ["prorated_refund_allowed"] = true + }; + } + + // Naive tokenizer: lowercase, split on non-alphanumeric, drop short/stop words. + private static List Tokenize(string s) + { + var stop = new HashSet { "the","a","an","is","are","for","to","of","and","or","my","i","do","does","can","what","whats","how","on","in","it","be","with","please" }; + var sb = new StringBuilder(); + foreach (var ch in (s ?? "").ToLowerInvariant()) + sb.Append(char.IsLetterOrDigit(ch) ? ch : ' '); + var tokens = new List(); + foreach (var w in sb.ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) + if (w.Length >= 3 && !stop.Contains(w) && !tokens.Contains(w)) + tokens.Add(w); + return tokens; + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json new file mode 100644 index 0000000..289294e --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Policy RAG MCP v2", + "description": "BlastPass Policy Knowledge (v2) for BlastBox Omega. Tools: search_policy, get_tier_refund_policy, get_markdown_policy. Runs entirely inside the connector.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Policy RAG MCP v2 Server", + "description": "MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy, get_markdown_policy.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_connectionparameters.json b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx new file mode 100644 index 0000000..18dbcb2 --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx @@ -0,0 +1,1209 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Warehouse MCP Connector (inline) ║ +// ║ ║ +// ║ 7 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "warehouse-fulfillment", + Version = "1.0.0", + Title = "Warehouse & Fulfillment", + Description = "Warehouse inventory and fulfillment tools for checking stock, tracking fulfillment pipeline stages, finding product alternatives, and looking up restock dates." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use check_stock to look up inventory for a product SKU. If out of stock, use get_restock_date for restock info and find_alternatives for similar products. Use get_fulfillment_status with an order_id to check where an order is in the warehouse pipeline. Use check_game_compatibility to find which BlastBox Omega model a game requires. Use get_inventory_aging to see how long a product's stock has been sitting and whether more is inbound, to inform markdown/clearance decisions. Use get_console_exclusives to list the MEGA-only AAA titles (with an associate upsell pitch) when helping a customer decide between the base console and the MEGA Edition." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + + private static readonly JArray Stock = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""category"":""console"",""price"":399.99,""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-20"",""availableForShipping"":false}, + {""sku"":""SKU-OMEGA-MEGA"",""name"":""BlastBox Omega MEGA Edition"",""category"":""console"",""price"":499.99,""quantity"":5,""warehouse"":""Seattle-WA1"",""aisle"":""E-21"",""availableForShipping"":true}, + {""sku"":""SKU-APEX5"",""name"":""Apex NoiseGuard 5 Headphones"",""category"":""electronics"",""quantity"":3,""warehouse"":""Seattle-WA1"",""aisle"":""E-14"",""availableForShipping"":true}, + {""sku"":""SKU-USBC3"",""name"":""USB-C Charging Cable (3-pack)"",""category"":""electronics"",""quantity"":142,""warehouse"":""Seattle-WA1"",""aisle"":""A-02"",""availableForShipping"":true}, + {""sku"":""SKU-LUMI"",""name"":""LumiRead E-Reader (16GB)"",""category"":""electronics"",""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-08"",""availableForShipping"":false}, + {""sku"":""SKU-PULSE"",""name"":""PulseWave Pro Earbuds"",""category"":""electronics"",""quantity"":17,""warehouse"":""Seattle-WA1"",""aisle"":""E-12"",""availableForShipping"":true}, + {""sku"":""SKU-VORTEX"",""name"":""Vortex Gaming Console OLED"",""category"":""electronics"",""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-22"",""availableForShipping"":false}, + {""sku"":""SKU-REALM"",""name"":""Realm of Legends: The Lost Crown"",""category"":""electronics"",""quantity"":24,""warehouse"":""Chicago-IL1"",""aisle"":""G-05"",""availableForShipping"":true}, + {""sku"":""SKU-APEX4"",""name"":""Apex NoiseGuard 4 Headphones (Previous Gen)"",""category"":""electronics"",""quantity"":8,""warehouse"":""Seattle-WA1"",""aisle"":""E-14"",""availableForShipping"":true}, + {""sku"":""SKU-CLAR7"",""name"":""Clarity NC700 Headphones"",""category"":""electronics"",""quantity"":6,""warehouse"":""Seattle-WA1"",""aisle"":""E-15"",""availableForShipping"":true}, + {""sku"":""SKU-PWMAX"",""name"":""PulseWave Max Headphones - Space Gray"",""category"":""electronics"",""quantity"":2,""warehouse"":""Seattle-WA1"",""aisle"":""E-13"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE"",""name"":""TrailMark Fleece Hoodie - Black (L)"",""category"":""clothing"",""quantity"":4,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE-XL"",""name"":""TrailMark Fleece Hoodie - Black (XL)"",""category"":""clothing"",""quantity"":7,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE-GRY"",""name"":""TrailMark Fleece Hoodie - Grey (L)"",""category"":""clothing"",""quantity"":2,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-JOGGER"",""name"":""TrailMark Sport Joggers - Grey (L)"",""category"":""clothing"",""quantity"":11,""warehouse"":""Dallas-TX1"",""aisle"":""C-09"",""availableForShipping"":true}, + {""sku"":""SKU-STAR2"",""name"":""Starfall: Part Two (4K Blu-ray)"",""category"":""media"",""quantity"":9,""warehouse"":""Chicago-IL1"",""aisle"":""M-03"",""availableForShipping"":true}, + {""sku"":""SKU-COMFT"",""name"":""ComfortEdge Pro - Matte Black"",""category"":""furniture"",""quantity"":1,""warehouse"":""Seattle-WA1"",""aisle"":""F-01"",""availableForShipping"":true}, + {""sku"":""SKU-COMFT-W"",""name"":""ComfortEdge Pro - White"",""category"":""furniture"",""quantity"":3,""warehouse"":""Seattle-WA1"",""aisle"":""F-01"",""availableForShipping"":true}, + {""sku"":""SKU-DESKMAT"",""name"":""Felt Desk Mat - Dark Grey (90x40cm)"",""category"":""furniture"",""quantity"":30,""warehouse"":""Seattle-WA1"",""aisle"":""F-04"",""availableForShipping"":true} + ]"); + + private static readonly JObject Fulfillment = JObject.Parse(@"{ + ""ORD-10422"":{""order_id"":""ORD-10422"",""warehouse"":""Chicago-IL1"",""assigned_worker"":""Mike Torres"",""current_stage"":""received"",""estimated_ship_date"":""2026-04-23"",""notes"":""LumiRead E-Reader — awaiting restock. Item reserved from incoming shipment."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-15T09:30:00Z""}]}, + ""ORD-10460"":{""order_id"":""ORD-10460"",""warehouse"":""Chicago-IL1"",""assigned_worker"":""Lisa Park"",""current_stage"":""picked"",""estimated_ship_date"":""2026-04-21"",""notes"":""Vortex Console on backorder, Realm of Legends picked, holding for bundle ship."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-18T10:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-19T14:20:00Z""}]}, + ""ORD-10421"":{""order_id"":""ORD-10421"",""warehouse"":""Seattle-WA1"",""assigned_worker"":""Carlos Mendez"",""current_stage"":""handed_to_carrier"",""estimated_ship_date"":""2026-04-13"",""notes"":""Shipped via SwiftShip. Two items in single box."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-11T08:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-11T11:30:00Z""},{""stage"":""packed"",""completed_at"":""2026-04-12T09:15:00Z""},{""stage"":""labeled"",""completed_at"":""2026-04-12T10:00:00Z""},{""stage"":""handed_to_carrier"",""completed_at"":""2026-04-13T08:45:00Z""}]}, + ""ORD-10455"":{""order_id"":""ORD-10455"",""warehouse"":""Dallas-TX1"",""assigned_worker"":""Rachel Kim"",""current_stage"":""handed_to_carrier"",""estimated_ship_date"":""2026-04-14"",""notes"":""Shipped via PrimeFreight. Hoodie + joggers in one package."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-12T11:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-13T09:00:00Z""},{""stage"":""packed"",""completed_at"":""2026-04-13T14:30:00Z""},{""stage"":""labeled"",""completed_at"":""2026-04-14T08:00:00Z""},{""stage"":""handed_to_carrier"",""completed_at"":""2026-04-14T15:00:00Z""}]} + }"); + + private static readonly JArray Restock = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""current_quantity"":0,""next_shipment_date"":""2026-06-11"",""expected_quantity"":40,""supplier"":""BlastBox Manufacturing"",""notes"":""Standard replenishment — next batch arrives in about 4 days. MEGA Edition is in stock now as an upgrade alternative.""}, + {""sku"":""SKU-LUMI"",""name"":""LumiRead E-Reader (16GB)"",""current_quantity"":0,""next_shipment_date"":""2026-04-22"",""expected_quantity"":50,""supplier"":""LumiRead Distribution Co."",""notes"":""Delayed from original April 18 date. Supplier confirmed new ETA.""}, + {""sku"":""SKU-VORTEX"",""name"":""Vortex Gaming Console OLED"",""current_quantity"":0,""next_shipment_date"":""2026-04-28"",""expected_quantity"":30,""supplier"":""Vortex Games Inc."",""notes"":""High demand — limited allocation. Next batch after this is mid-May.""}, + {""sku"":""SKU-APEX5"",""name"":""Apex NoiseGuard 5 Headphones"",""current_quantity"":3,""next_shipment_date"":""2026-05-05"",""expected_quantity"":20,""supplier"":""Apex Audio Corp."",""notes"":""Regular replenishment cycle. Current stock sufficient for 1-2 weeks.""}, + {""sku"":""SKU-COMFT"",""name"":""ComfortEdge Pro - Matte Black"",""current_quantity"":1,""next_shipment_date"":""2026-04-30"",""expected_quantity"":10,""supplier"":""ComfortEdge Furniture Co."",""notes"":""Low stock alert triggered. Express shipment arranged.""} + ]"); + + private static readonly JArray Games = JArray.Parse(@"[ + {""title"":""MEGA Lizards from Outer Space"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA title — requires the MEGA Edition GPU. Will NOT run on the base BlastBox Omega.""}, + {""title"":""Galactic Tax Evader VII: Audit Protocol"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA open-world heist sim — the real-time audit physics only run on the MEGA Edition co-processor.""}, + {""title"":""Mecha-Granny: Knitpocalypse"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA action roguelite — the 4K yarn engine requires the MEGA Edition. Will NOT run on the base console.""}, + {""title"":""Realm of Legends: The Lost Crown"",""required_model"":""BlastBox Omega Console"",""required_sku"":""SKU-OMEGA"",""runs_on_base"":true,""note"":""Runs on any BlastBox Omega model, including the base console.""}, + {""title"":""Galaxy Smash"",""required_model"":""BlastBox Omega Console"",""required_sku"":""SKU-OMEGA"",""runs_on_base"":true,""note"":""Runs on any BlastBox Omega model.""} + ]"); + + // Exclusive AAA titles per console model, with a ready-to-say associate upsell pitch. + private static readonly JObject Exclusives = JObject.Parse(@"{ + ""SKU-OMEGA-MEGA"":{ + ""model"":""BlastBox Omega MEGA Edition"", + ""sku"":""SKU-OMEGA-MEGA"", + ""tagline"":""Three AAA exclusives the base console literally can't load."", + ""titles"":[ + {""title"":""MEGA Lizards from Outer Space"",""genre"":""Co-op chaos shooter"",""pitch"":""Picture two hundred neon space-lizards on screen at once and a co-op buddy screaming next to you. It's the #1 couch-chaos shooter of the year — and it physically will not run on the base console.""}, + {""title"":""Galactic Tax Evader VII: Audit Protocol"",""genre"":""Open-world heist sim"",""pitch"":""You're an interstellar accountant on the run, dodging audits in real time across a living open galaxy. Critics call it 'Grand Theft Spreadsheet.' MEGA-exclusive, because the audit physics melt anything smaller.""}, + {""title"":""Mecha-Granny: Knitpocalypse"",""genre"":""Action roguelite"",""pitch"":""A sweet old lady in a battle-mech knits the apocalypse back together, one 4K scarf at a time. It's hilarious, it's brutal, and the yarn engine only spins up on MEGA.""} + ] + }, + ""SKU-OMEGA"":{ + ""model"":""BlastBox Omega Console"", + ""sku"":""SKU-OMEGA"", + ""tagline"":""Plays the full shared BlastBox library, but none of the MEGA-only AAA exclusives."", + ""titles"":[] + } + }"); + + // Inventory aging / inbound status for merchandising markdown decisions. + private static readonly JArray Aging = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA-MEGA"",""name"":""BlastBox Omega MEGA Edition"",""weeks_in_stock"":3,""received_date"":""2026-05-11"",""inbound_po"":true,""next_inbound_date"":""2026-06-15"",""aging_bucket"":""fresh"",""note"":""Hot seller. Replenishment PO inbound.""}, + {""sku"":""SKU-MEGALIZARDS"",""name"":""MEGA Lizards from Outer Space"",""weeks_in_stock"":4,""received_date"":""2026-05-04"",""inbound_po"":true,""next_inbound_date"":""2026-06-12"",""aging_bucket"":""fresh"",""note"":""AAA title driving MEGA Edition attach. Reorder inbound.""}, + {""sku"":""SKU-PULSE-CTRL"",""name"":""PulseGrip Pro Controller"",""weeks_in_stock"":5,""received_date"":""2026-04-27"",""inbound_po"":true,""next_inbound_date"":""2026-06-20"",""aging_bucket"":""fresh"",""note"":""Steady accessory attach.""}, + {""sku"":""SKU-OMEGA-CORE"",""name"":""BlastBox Omega Core (1st-gen)"",""weeks_in_stock"":16,""received_date"":""2026-02-16"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""aging"",""note"":""Being phased out by the MEGA Edition. No further inbound — candidate for discontinuation/clearance.""}, + {""sku"":""SKU-RETRO-CADET"",""name"":""BlastBox Cadet Bundle"",""weeks_in_stock"":22,""received_date"":""2026-01-05"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""stale"",""note"":""Last-gen bundle. No inbound. Strong markdown candidate.""}, + {""sku"":""SKU-GALAXY-SMASH"",""name"":""Galaxy Smash"",""weeks_in_stock"":14,""received_date"":""2026-03-02"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""aging"",""note"":""Overstocked at launch. No inbound. Markdown candidate.""}, + {""sku"":""SKU-VR-GOGGLES"",""name"":""OmegaVision VR Headset"",""weeks_in_stock"":18,""received_date"":""2026-02-02"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""stale"",""note"":""Slow launch. No inbound. Strong markdown/clearance candidate.""} + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. check_stock + handler.AddTool("check_stock", + "Check warehouse inventory for a product by SKU. Returns quantity on hand, warehouse location, aisle, and whether the item is available for shipping. If quantity is 0, use get_restock_date for restock info.", + schemaConfig: s => s.String("sku", "Product SKU (e.g. 'SKU-APEX5'). Use SKUs from order line items.", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + foreach (var item in Stock) + { + if (item["sku"]?.ToString() == sku) + { + var qty = item["quantity"]?.Value() ?? 0; + return new JObject + { + ["sku"] = item["sku"], + ["name"] = item["name"], + ["category"] = item["category"], + ["price"] = item["price"], + ["quantity_on_hand"] = qty, + ["warehouse"] = item["warehouse"], + ["aisle"] = item["aisle"], + ["available_for_shipping"] = item["availableForShipping"], + ["status"] = qty == 0 ? "OUT_OF_STOCK" : qty <= 3 ? "LOW_STOCK" : "IN_STOCK" + }; + } + } + throw new ArgumentException($"SKU \"{sku}\" not found in warehouse inventory."); + }); + + // 2. get_fulfillment_status + handler.AddTool("get_fulfillment_status", + "Get the fulfillment pipeline status for an order. Shows which warehouse stage the order is at (received → picked → packed → labeled → handed_to_carrier), assigned worker, and estimated ship date.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. 'ORD-10422') from the order management system", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + if (Fulfillment[orderId] != null) + return JObject.Parse(Fulfillment[orderId].ToString()); + throw new ArgumentException($"No fulfillment record found for order \"{orderId}\". The order may not have entered the warehouse yet."); + }); + + // 3. find_alternatives + handler.AddTool("find_alternatives", + "Find alternative products similar to a given SKU. Returns items in the same category that are in stock. Useful when a customer wants a different size, color, or comparable product, or when an item is out of stock.", + schemaConfig: s => s.String("sku", "Product SKU to find alternatives for (e.g. 'SKU-HOODIE')", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + JToken original = null; + foreach (var item in Stock) + { + if (item["sku"]?.ToString() == sku) { original = item; break; } + } + if (original == null) + throw new ArgumentException($"SKU \"{sku}\" not found in inventory."); + + var category = original["category"]?.ToString(); + var alternatives = new JArray(); + foreach (var item in Stock) + { + if (item["category"]?.ToString() == category && item["sku"]?.ToString() != sku && (item["quantity"]?.Value() ?? 0) > 0) + { + alternatives.Add(new JObject + { + ["sku"] = item["sku"], + ["name"] = item["name"], + ["price"] = item["price"], + ["quantity_available"] = item["quantity"], + ["warehouse"] = item["warehouse"], + ["available_for_shipping"] = item["availableForShipping"] + }); + } + } + + return new JObject + { + ["original"] = new JObject { ["sku"] = original["sku"], ["name"] = original["name"], ["category"] = category }, + ["alternatives"] = alternatives + }; + }); + + // 4. get_restock_date + handler.AddTool("get_restock_date", + "Get the next restock date and supplier info for a product. Use this when check_stock shows an item is out of stock or low stock. Returns expected delivery date, quantity, and supplier details.", + schemaConfig: s => s.String("sku", "Product SKU (e.g. 'SKU-LUMI'). Typically used after check_stock shows low/no stock.", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + foreach (var item in Restock) + { + if (item["sku"]?.ToString() == sku) + return JObject.Parse(item.ToString()); + } + return new JObject { ["message"] = $"No restock schedule found for SKU \"{sku}\". The item may be regularly stocked or discontinued." }; + }); + + // 5. check_game_compatibility + handler.AddTool("check_game_compatibility", + "Check which BlastBox Omega console model a game requires. Use when a customer asks whether a game will run on their console, or when deciding between the base console and the MEGA Edition. Returns the required model/SKU and whether it runs on the base console.", + schemaConfig: s => s.String("game_title", "The game title (e.g. 'MEGA Lizards from Outer Space'). Case-insensitive, partial match allowed.", required: true), + handler: async (args, ct) => + { + var q = (args.Value("game_title") ?? "").Trim().ToLowerInvariant(); + foreach (var g in Games) + { + var title = (g["title"]?.ToString() ?? "").ToLowerInvariant(); + if (title == q || (q.Length > 0 && title.Contains(q))) + return JObject.Parse(g.ToString()); + } + return new JObject { ["message"] = $"No compatibility record found for game \"{args.Value("game_title")}\"." }; + }); + + // 6. get_inventory_aging + handler.AddTool("get_inventory_aging", + "Return inventory aging and inbound-replenishment status for a product SKU: how many weeks the stock has been sitting, its received date, whether there is an inbound purchase order, and an aging bucket (fresh / aging / stale). Use this to judge whether a slow-selling item is a markdown or clearance candidate. Omit sku to return aging for all tracked products.", + schemaConfig: s => s.String("sku", "Optional. Product SKU (e.g. 'SKU-VR-GOGGLES'). Omit to return all tracked products."), + handler: async (args, ct) => + { + var sku = (args.Value("sku") ?? "").Trim(); + if (sku.Length == 0) + return new JObject { ["count"] = Aging.Count, ["items"] = JArray.Parse(Aging.ToString()) }; + foreach (var item in Aging) + { + if (string.Equals(item["sku"]?.ToString(), sku, StringComparison.OrdinalIgnoreCase)) + return JObject.Parse(item.ToString()); + } + return new JObject { ["message"] = $"No aging record found for SKU \"{sku}\"." }; + }); + + // 7. get_console_exclusives + handler.AddTool("get_console_exclusives", + "Return the AAA game titles that run ONLY on a given BlastBox Omega console model, each with a ready-to-say associate upsell pitch. Use this to help an associate upsell a customer to the MEGA Edition: it surfaces the marquee titles the base console cannot run plus a short, attractive blurb to read to the customer. Accepts a SKU (e.g. 'SKU-OMEGA-MEGA') or a model name (e.g. 'mega', 'base').", + schemaConfig: s => s.String("model", "Console SKU or model name. Use 'SKU-OMEGA-MEGA' or 'mega' for the MEGA Edition; 'SKU-OMEGA' or 'base' for the base console.", required: true), + handler: async (args, ct) => + { + var q = (args.Value("model") ?? "").Trim().ToLowerInvariant(); + string key = null; + if (q.Contains("mega")) key = "SKU-OMEGA-MEGA"; + else if (q == "base" || q == "sku-omega" || q.Contains("base")) key = "SKU-OMEGA"; + else if (q == "sku-omega-mega") key = "SKU-OMEGA-MEGA"; + if (key != null && Exclusives[key] != null) + return JObject.Parse(Exclusives[key].ToString()); + return new JObject { ["message"] = $"No console-exclusive list found for model \"{args.Value("model")}\". Try 'mega' or 'base'." }; + }); + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_iconblob.Png b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_iconblob.Png new file mode 100644 index 0000000..f417fb8 Binary files /dev/null and b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_iconblob.Png differ diff --git a/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_openapidefinition.json b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_openapidefinition.json new file mode 100644 index 0000000..d624a9f --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Warehouse MCP", + "description": "Warehouse inventory and fulfillment with tools for checking stock, tracking pipeline stages, finding alternatives, and restock dates. Runs entirely inside the connector \u2014 no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Warehouse MCP Server", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-deploy/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/[Content_Types].xml b/submissions/blastbox-deploy/solution/[Content_Types].xml new file mode 100644 index 0000000..7ea4763 --- /dev/null +++ b/submissions/blastbox-deploy/solution/[Content_Types].xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml new file mode 100644 index 0000000..f85df1a --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/botcomponent.xml @@ -0,0 +1,15 @@ + + 14 + This knowledge source searches information contained in SKILL.md + SKILL.md + 0 + SKILL.md + + Default_draft_IsBewO.skill.membership-card-png + + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md new file mode 100644 index 0000000..3dba17b --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.SKILL.md/filedata/SKILL.md @@ -0,0 +1,95 @@ +--- +name: membership-card-png +description: Generate a digital BlastPass membership card as a PNG image by running the bundled card_png.py script. Use when a member self-serves a lost/stolen/damaged card replacement, or any time a card needs to be shown on screen (vs. printed). The skill takes the member details returned by the Membership MCP (get_membership) plus the new_card_serial returned by reissue_card and passes them as command-line flags to a bundled Python (matplotlib) script that renders a CR80-sized membership card to a real PNG. It only renders the card from already-retrieved values — it never looks anything up or invents data. +--- + +# BlastPass Membership Card PNG Generator + +This skill produces a **digital BlastPass membership card** as a shareable image by +running the **bundled `card_png.py` script**. It is the self-serve companion to +`membership-card-pdf`: when a member reissues a lost, stolen, or damaged card, you +mint a new serial via the Membership MCP and hand the member a PNG to save to their +phone **while the physical card ships**. + +You do **not** write any Python. You take the member record you already pulled from +the **Membership MCP** (`get_membership`) and the **new serial** returned by +`reissue_card`, then **run the bundled script** with those values as CLI flags. + +Never invent a member, tier, or serial number — every value on the card comes from +the MCP results. The script only *draws* the card; it does not decide anything. + +## When to use + +Use this skill in the **self-serve card reissue** scenario, after: + +1. `get_membership(customer_id)` confirmed the member and returned their details. +2. The member verified their identity (last 4 of the console serial). +3. `reissue_card(customer_id, reason)` deactivated the old card and returned a + `new_card_serial`. + +Use the PDF skill instead when an in-store associate needs a **printable** card. +Use this PNG skill when the member needs an **on-screen / saveable** card. + +## Inputs (map MCP results → CLI flags) + +| Script flag | Source | +| --- | --- | +| `--member-name` | `get_membership.member_name` | +| `--member-id` | `get_membership.customer_id` | +| `--tier` | `get_membership.tier` (e.g. `"BlastPass Plus Extra"`) | +| `--tier-code` | `get_membership.tier_code` (`plus` \| `extra` \| `mega`) — drives accent color & perks | +| `--card-serial` | **`reissue_card.new_card_serial`** (the freshly minted card; falls back to `get_membership.card_serial` if not a reissue) | +| `--console-serial` | `get_membership.console_serial` | +| `--member-since` | `get_membership.activation_date` | +| `--valid-through` | `get_membership.term_end_date` | +| `--out` | optional output path (default `blastpass_card.png`) | + +The `--tier-code` selects the card's accent color, header badge, and the perks line +printed beneath the card: + +- `plus` → blue, "PLUS", 5% off + 10 BlastPoints/$1 +- `extra` → purple, "PLUS EXTRA", 10% off + 20% BlastPoints bonus +- `mega` → orange, "MEGA!!!", 15% off + 50% BlastPoints + day-one exclusives + +## Workflow + +### 1. Gather the confirmed values + +From `get_membership` (tier, dates, console serial, name, id) and from +`reissue_card` (`new_card_serial`). Do **not** pass any value the MCP didn't return. + +### 2. Run the bundled script + +Pass the confirmed values as flags. The script writes `blastpass_card.png` to the +working directory (or `--out`) and prints a one-line confirmation: + +```bash +python3 card_png.py \ + --member-name "Jordan Pixel" \ + --member-id MEGA-BLAST-1024 \ + --tier "BlastPass Plus Extra" \ + --tier-code extra \ + --card-serial BLAST-A1B2-1024 \ + --console-serial OMEGA-7F3A-1024 \ + --member-since 2026-02-26 \ + --valid-through 2027-02-26 +``` + +`--card-serial` must be the **new** serial from `reissue_card` — never the old +(now-deactivated) one. + +### 3. Hand back the image + +Tell the member their new digital card is ready (`blastpass_card.png`), read back +the **new card serial** and tier so they can confirm it, and remind them the old +card is now deactivated and the physical replacement ships in ~7 days. + +## Notes + +- The bundled script is `card_png.py`, validated against the four mock members in + the Membership MCP. It depends only on `matplotlib` (available in the runtime). +- The card serial shown is the **new** one from `reissue_card`; the old serial is + already deactivated server-side, so it must never appear on the card. +- The barcode is decorative (deterministic from the card serial) — it is not a real + scannable symbology. +- All data is mock; the card is for demo purposes only. diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml new file mode 100644 index 0000000..5e2c1fb --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/botcomponent.xml @@ -0,0 +1,15 @@ + + 14 + This knowledge source searches information contained in card_png.py + card_png.py + 0 + card_png.py + + Default_draft_IsBewO.skill.membership-card-png + + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py new file mode 100644 index 0000000..9673be9 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.file.card_png.py/filedata/card_png.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +"""Render a BlastPass membership card to a PNG. + +Bundled skill script: the agent runs this with the member's confirmed values +(from the Membership MCP get_membership + reissue_card results) as CLI flags. The +script only *draws* the card — it never looks anything up or invents data. + +Usage: + python3 card_png.py \ + --member-name "Jordan Pixel" \ + --member-id MEGA-BLAST-1024 \ + --tier "BlastPass Plus Extra" \ + --tier-code extra \ + --card-serial BLAST-A1B2-1024 \ + --console-serial OMEGA-7F3A-1024 \ + --member-since 2026-02-26 \ + --valid-through 2027-02-26 \ + [--out blastpass_card.png] + +--card-serial must be the NEW serial from reissue_card (new_card_serial) on a +reissue, otherwise the card_serial on file from get_membership. +""" + +import argparse +import hashlib +import sys + +import matplotlib +matplotlib.use("Agg") # headless: render straight to a file, no display needed +import matplotlib.pyplot as plt +from matplotlib.patches import FancyBboxPatch, Rectangle + +# Tier styling: accent color, header badge text, perks blurb (printed under card) +TIERS = { + "plus": ("#3aa0ff", "PLUS", + "Free 2-day shipping | 5% off accessories | 10 BlastPoints / $1"), + "extra": ("#9b51e0", "PLUS EXTRA", + "Free next-day shipping | 10% off accessories | +20% BlastPoints bonus"), + "mega": ("#ff7a18", "MEGA!!!", + "Same-day delivery | 15% off everything | +50% BlastPoints | Day-one exclusives"), +} +INK = "#1b1030" +MUTED = "#9d90bd" + + +def parse_args(argv): + p = argparse.ArgumentParser(description="Render a BlastPass membership card PNG.") + p.add_argument("--member-name", required=True) + p.add_argument("--member-id", required=True) + p.add_argument("--tier", required=True, help='e.g. "BlastPass Plus Extra"') + p.add_argument("--tier-code", required=True, choices=["plus", "extra", "mega"]) + p.add_argument("--card-serial", required=True, + help="NEW serial from reissue_card, else card_serial on file") + p.add_argument("--console-serial", required=True) + p.add_argument("--member-since", required=True, help="activation_date (YYYY-MM-DD)") + p.add_argument("--valid-through", required=True, help="term_end_date (YYYY-MM-DD)") + p.add_argument("--out", default="blastpass_card.png") + return p.parse_args(argv) + + +def render(card, out_path): + accent, tier_badge, perks = TIERS.get(card["tier_code"], TIERS["plus"]) + + # Card geometry in inches (CR80 ratio 3.375 x 2.125), drawn on a small canvas. + CARD_W, CARD_H = 3.375, 2.125 + fig_w, fig_h = CARD_W + 1.4, CARD_H + 1.0 + fig = plt.figure(figsize=(fig_w, fig_h), dpi=300) + ax = fig.add_axes([0, 0, 1, 1]) + ax.set_xlim(0, fig_w) + ax.set_ylim(0, fig_h) + ax.axis("off") + + x0 = (fig_w - CARD_W) / 2 + y0 = (fig_h - CARD_H) / 2 + 0.18 # nudge up to leave room for perks footer + + # Card body (rounded) + ax.add_patch(FancyBboxPatch( + (x0, y0), CARD_W, CARD_H, + boxstyle="round,pad=0,rounding_size=0.12", + linewidth=0, facecolor=INK, zorder=1)) + + # Accent header band (rounded top, squared bottom via an overlapping rectangle) + band_h = 0.55 + ax.add_patch(FancyBboxPatch( + (x0, y0 + CARD_H - band_h), CARD_W, band_h, + boxstyle="round,pad=0,rounding_size=0.12", + linewidth=0, facecolor=accent, zorder=2)) + ax.add_patch(Rectangle( + (x0, y0 + CARD_H - band_h), CARD_W, band_h * 0.45, + linewidth=0, facecolor=accent, zorder=2)) + + pad = 0.22 + # Brand + ax.text(x0 + pad, y0 + CARD_H - 0.34, "BlastBox Omega", + color="white", fontsize=12, fontweight="bold", va="center", zorder=3) + # Tier badge pill (right side of band) + badge_w = 0.16 + 0.085 * len(tier_badge) + bx = x0 + CARD_W - pad - badge_w + ax.add_patch(FancyBboxPatch( + (bx, y0 + CARD_H - 0.45), badge_w, 0.26, + boxstyle="round,pad=0,rounding_size=0.06", + linewidth=0, facecolor="white", alpha=0.22, zorder=3)) + ax.text(bx + badge_w / 2, y0 + CARD_H - 0.32, tier_badge, + color="white", fontsize=7.5, fontweight="bold", + ha="center", va="center", zorder=4) + + # Member name + ax.text(x0 + pad, y0 + CARD_H - 0.80, card["member_name"], + color="white", fontsize=13.5, fontweight="bold", va="center", zorder=3) + # Card serial (mono, accent) + ax.text(x0 + pad, y0 + CARD_H - 1.06, card["card_serial"], + color=accent, fontsize=12.5, fontweight="bold", + family="monospace", va="center", zorder=3) + + # Detail rows + details = [ + ("MEMBER ID", card["member_id"]), + ("MEMBER SINCE", card["member_since"]), + ("VALID THROUGH", card["valid_through"]), + ("CONSOLE S/N", card["console_serial"]), + ] + dy = y0 + CARD_H - 1.30 + for label, val in details: + ax.text(x0 + pad, dy, label, color=MUTED, fontsize=6, va="center", zorder=3) + ax.text(x0 + pad + 0.95, dy, str(val), color="white", fontsize=8, + fontweight="bold", va="center", zorder=3) + dy -= 0.165 + + # Faux barcode strip (deterministic from the card serial) + seed = int(hashlib.md5(card["card_serial"].encode()).hexdigest(), 16) + bx2 = x0 + pad + by2 = y0 + 0.14 + bar_h = 0.14 + w = 0.0 + i = 0 + max_w = CARD_W - 2 * pad + while w < max_w: + bar = 0.012 + ((seed >> (i % 32)) & 0x3) * 0.006 + if i % 2 == 0: + ax.add_patch(Rectangle((bx2 + w, by2), bar, bar_h, + linewidth=0, facecolor="white", zorder=3)) + w += bar + i += 1 + + # Perks footer (under the card) — shrink font if the perks line is long + perks_fs = 6.5 if len(perks) <= 80 else 5.6 + ax.text(fig_w / 2, y0 - 0.16, perks, color="#666666", fontsize=perks_fs, + style="italic", ha="center", va="center") + ax.text(fig_w / 2, y0 - 0.34, + "Present this card in-store or scan at checkout. " + "A printed copy is on its way by mail.", + color="#888888", fontsize=6.5, ha="center", va="center") + + fig.savefig(out_path, dpi=300, facecolor="white") + plt.close(fig) + + +def main(argv): + args = parse_args(argv) + card = { + "member_name": args.member_name, + "member_id": args.member_id, + "tier": args.tier, + "tier_code": args.tier_code, + "card_serial": args.card_serial, + "console_serial": args.console_serial, + "member_since": args.member_since, + "valid_through": args.valid_through, + } + render(card, args.out) + print(f"WROTE {args.out}") + print(f" {card['member_name']} · {card['tier']} · card {card['card_serial']}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml new file mode 100644 index 0000000..ad792ee --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Replace (reissue) a member's BlastPass card when it is lost, stolen, or damaged. Use whenever the person wants a new/replacement BlastPass card. The skill verifies identity (name + last 4 of the console serial) before deactivating the old card, mints a new one with reissue_card, and hands over a digital card. Do not use this to cancel a membership. + 0 + card-reissue + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/data b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/data new file mode 100644 index 0000000..5f7c834 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.card-reissue/data @@ -0,0 +1,54 @@ +kind: InlineAgentSkill +content: | + --- + name: card-reissue + description: Replace (reissue) a member's BlastPass card when it is lost, stolen, or damaged. Use whenever the person wants a new/replacement BlastPass card. The skill verifies identity (name + last 4 of the console serial) before deactivating the old card, mints a new one with reissue_card, and hands over a digital card. Do not use this to cancel a membership. + --- + + # BlastPass Card Reissue + + Use this when someone wants to replace a **lost, stolen, or damaged** BlastPass + card. It is a card swap only — the membership stays active. Never use + `cancel_membership` here. + + Follow these steps in order. Do not skip the identity check, and do not change + anything until identity is verified. + + ## 1. Look up the membership + + Ask for the membership ID if you don't have it, then call **`get_membership`** + with it. This is read-only — nothing changes yet. Keep the details you get back + (name, console serial, tier, dates) to yourself for now. + + ## 2. Verify identity (two factors) + + Before deactivating any card, confirm it's really the account holder. Ask for + **two things**: + + 1. The **name** on the membership, and + 2. The **last 4 digits of the console serial** (printed on the bottom of the + BlastBox Omega). + + **Do not reveal** the name, serial, or any on-file detail while asking — make + them tell you. Continue **only if both match** the record (`member_name`, and the + last 4 of `console_serial`). If either one doesn't match, politely stop and do + **not** reissue. + + ## 3. Reissue the card + + Once both factors match, call **`reissue_card(membership_id, reason)`** (reason = + lost / stolen / damaged). This deactivates the old card, mints a **new serial**, + keeps the membership active, and queues the physical card for standard mail + (~7 days). + + ## 4. Hand over the digital card + + Run the **`membership-card-png`** skill with the member's details and the + **new** serial from `reissue_card` to generate `blastpass_card.png`. Never put + the old (now-deactivated) serial on the card. + + ## 5. Confirm + + In one warm, short message: the new card is ready (the PNG), the new serial, that + the old card is now deactivated, the mail ETA, and the reissue confirmation + number. Note their membership, tier, and points are unchanged. diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml new file mode 100644 index 0000000..feffbee --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Generate a digital BlastPass membership card as a PNG image by running the bundled card_png.py script. Use when a member reissues a lost, stolen, or damaged card, or any time a BlastPass card needs to be shown on screen. Pass the member details (name, id, tier, tier code, card serial, console serial, dates) as command-line flags to the bundled Python (matplotlib) script, which renders a CR80-sized card to a real PNG. It only draws the card from values you already have. + 0 + membership-card-png + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/data b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/data new file mode 100644 index 0000000..ba412cd --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/Default_draft_IsBewO.skill.membership-card-png/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml new file mode 100644 index 0000000..4be0d43 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Reconcile a customer's BlastPoints loyalty balance after a complex visit (purchases, returns, promo multipliers, expiry) and convert the net balance to store credit. Use when an associate asks about a member's points, or after a return that should claw back previously earned points. The skill runs a bundled Python script that applies the base earn rate, promo multipliers, tier bonus, clawbacks, and expiry, then prints a points ledger and the redeemable store-credit value. + 0 + points-reconciliation + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data new file mode 100644 index 0000000..62e182d --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml new file mode 100644 index 0000000..1132c0b --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Calculate the exact prorated refund when a customer cancels a BlastPass membership, and the net settlement when a defective unit is traded up to a pricier model. Use after the Store Policy Agent confirms the refund rule and the Membership MCP has returned the membership details. The skill runs a bundled Python script so the arithmetic is exact. + 0 + prorated-refund-calculator + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data new file mode 100644 index 0000000..b6fc35e --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml new file mode 100644 index 0000000..e363b80 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill runs a bundled Python script that renders the confirmed slip values into a real PDF file. + 0 + slip-pdf-generator + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data new file mode 100644 index 0000000..5bf2619 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml new file mode 100644 index 0000000..f30b9d9 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Delegate to the Inventory & Fulfillment Agent for anything about stock and consoles - whether a SKU is in stock locally, restock/ETA dates, in-stock alternatives or upgrades, which BlastBox Omega model a game requires, and the MEGA-only exclusive titles used to upsell a customer choosing between the base console and the MEGA Edition. + 0 + Inventory & Fullfilment Agent + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data new file mode 100644 index 0000000..e1c3b0a --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_InventoryFullfilmentAgent_X-w2GP +historyType: + kind: ConversationHistory diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json new file mode 100644 index 0000000..52b1d1d --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_InventoryFullfilmentAgent_X-w2GP"}] \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml new file mode 100644 index 0000000..6a2d654 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Delegate to the Store Policy Agent for any policy ruling - returns and warranty eligibility, defective-vs-accidental damage, return windows and restocking fees, and BlastPass membership refund/proration rules. Ask it before ruling on a return; it establishes the defect cause first and may return a clarifying question to relay back to the associate. + 0 + Store Policy Agent + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data new file mode 100644 index 0000000..6f26d5d --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_StorePolicyAgent_s9s-u8 +historyType: + kind: ConversationHistory diff --git a/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json new file mode 100644 index 0000000..789e87d --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_StorePolicyAgent_s9s-u8"}] \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml new file mode 100644 index 0000000..a9e80db --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + blastpass_refund.py + 0 + blastpass_refund.py + + cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py new file mode 100644 index 0000000..858173c --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""BlastPass membership refund + upgrade-settlement calculator. + +Two subcommands, both driven by values that come straight from the Membership MCP +(get_membership), the Store Policy Agent, and the Warehouse MCP. The arithmetic +must be exact, so always run this script rather than computing in your head. + + refund Prorated (or cooling-off) refund for cancelling a BlastPass membership. + settle Net due / refund when a defective unit is credited toward a pricier + model and the membership refund is applied as store credit. + +Refund rules (BlastPass Membership Refund Policy): + 1. Cooling-off: if --within-cooling-off is set, refund the full annual price, + no fee, no proration. + 2. Otherwise: refund the value of the whole unused months remaining in the + 12-month term, minus the tier cancellation fee. + 3. Subtract any non-refundable welcome credit (MEGA tier only). + 4. The refund is never negative. + +Usage: + python3 blastpass_refund.py refund \\ + --annual-price 129.99 --months-remaining 8 --cancellation-fee 10 \\ + [--nonrefundable-credit 0] [--within-cooling-off] + + python3 blastpass_refund.py settle \\ + --defective-credit 399.99 --upgrade-price 499.99 --membership-credit 76.66 +""" + +import argparse +import sys + +TERM_MONTHS = 12 + + +def money(value): + cents = int(value * 100 + (0.5 if value >= 0 else -0.5)) + return cents / 100.0 + + +def compute_refund(annual_price, months_remaining, cancellation_fee, + nonrefundable_credit=0.0, within_cooling_off=False): + lines = [f"Annual price: ${annual_price:.2f}"] + if within_cooling_off: + refund = money(annual_price) + lines.append("Within cooling-off period -> full refund, no fee.") + return {"refund": refund, "basis": "cooling_off", "lines": lines} + + monthly = annual_price / TERM_MONTHS + prorated = money(months_remaining * monthly) + lines.append(f"Monthly value: ${annual_price:.2f} / {TERM_MONTHS} = ${monthly:.4f}") + lines.append(f"Prorated: {months_remaining} unused months x ${monthly:.4f} = ${prorated:.2f}") + refund = prorated + if cancellation_fee > 0: + lines.append(f"Cancellation fee: -${cancellation_fee:.2f}") + refund = money(refund - cancellation_fee) + if nonrefundable_credit > 0: + lines.append(f"Non-refundable welcome credit: -${nonrefundable_credit:.2f}") + refund = money(refund - nonrefundable_credit) + refund = max(refund, 0.0) + return {"refund": refund, "basis": "prorated", "lines": lines} + + +def compute_settlement(defective_credit, upgrade_price, membership_credit): + upgrade_difference = money(upgrade_price - defective_credit) + net = money(upgrade_difference - membership_credit) + lines = [ + f"Defective unit warranty credit: ${defective_credit:.2f}", + f"Upgrade model price: ${upgrade_price:.2f}", + f"Upgrade difference: ${upgrade_price:.2f} - ${defective_credit:.2f} = ${upgrade_difference:.2f}", + f"BlastPass refund applied as store credit: -${membership_credit:.2f}", + ] + return {"net": net, "lines": lines} + + +def _print_lines(lines): + print("\n".join(lines)) + print("-" * 36) + + +def main(argv): + parser = argparse.ArgumentParser(description="BlastPass refund + settlement calculator.") + sub = parser.add_subparsers(dest="command", required=True) + + r = sub.add_parser("refund", help="Prorated / cooling-off membership refund.") + r.add_argument("--annual-price", type=float, required=True) + r.add_argument("--months-remaining", type=int, required=True) + r.add_argument("--cancellation-fee", type=float, default=0.0) + r.add_argument("--nonrefundable-credit", type=float, default=0.0) + r.add_argument("--within-cooling-off", action="store_true") + + s = sub.add_parser("settle", help="Upgrade settlement net due / refund.") + s.add_argument("--defective-credit", type=float, required=True) + s.add_argument("--upgrade-price", type=float, required=True) + s.add_argument("--membership-credit", type=float, default=0.0) + + args = parser.parse_args(argv[1:]) + + if args.command == "refund": + result = compute_refund( + annual_price=args.annual_price, + months_remaining=args.months_remaining, + cancellation_fee=args.cancellation_fee, + nonrefundable_credit=args.nonrefundable_credit, + within_cooling_off=args.within_cooling_off, + ) + _print_lines(result["lines"]) + print(f"REFUND DUE: ${result['refund']:.2f}") + return 0 + + result = compute_settlement( + defective_credit=args.defective_credit, + upgrade_price=args.upgrade_price, + membership_credit=args.membership_credit, + ) + _print_lines(result["lines"]) + net = result["net"] + if net >= 0: + print(f"NET DUE FROM CUSTOMER: ${net:.2f}") + else: + print(f"REFUND TO CUSTOMER: ${-net:.2f}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml new file mode 100644 index 0000000..2d7ed71 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + points_reconciliation.py + 0 + points_reconciliation.py + + cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py new file mode 100644 index 0000000..95b08ea --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +"""BlastBox Omega — BlastPoints Loyalty Reconciliation calculator (reference logic). + +Reconciles a member's BlastPoints after a complex visit (purchases, returns, +promos, expiry) and converts the net balance to store credit. + +Rules (mirror the BlastPoints Loyalty Program Terms): + * Base earn: 10 points per $1 of merchandise. + * Promo events (e.g. Triple BLAST Weekend) multiply the BASE earn by the promo + multiplier BEFORE the tier bonus is applied. + * Tier bonus on earned points: Plus +0%, Plus Extra +20%, MEGA +50%. + * Returned items are clawed back: points are removed using the SAME formula + (promo multiplier + tier bonus that applied to the original purchase). + * Expired points (older than 90 days while the account was inactive) are + removed as provided. + * Redemption: 1,000 points = $5.00 store credit (whole 1,000-point blocks). +""" +import argparse, json + +TIER_BONUS = {"plus": 0.0, "extra": 0.20, "mega": 0.50} + + +def earn(amount, promo_multiplier, tier_bonus): + base = 10.0 * amount + return int(round(base * promo_multiplier * (1.0 + tier_bonus))) + + +def reconcile(tier, prior_balance, purchases, returns, expired_points): + bonus = TIER_BONUS[tier] + lines = [] + earned = 0 + for p in purchases: + pts = earn(p["amount"], p.get("promo_multiplier", 1.0), bonus) + earned += pts + lines.append({"type": "earn", "amount": p["amount"], + "promo_multiplier": p.get("promo_multiplier", 1.0), + "points": pts}) + clawback = 0 + for r in returns: + pts = earn(r["amount"], r.get("promo_multiplier", 1.0), bonus) + clawback += pts + lines.append({"type": "clawback", "amount": r["amount"], + "promo_multiplier": r.get("promo_multiplier", 1.0), + "points": -pts}) + if expired_points: + lines.append({"type": "expired", "points": -int(expired_points)}) + + net_balance = int(prior_balance + earned - clawback - int(expired_points)) + if net_balance < 0: + net_balance = 0 + blocks = net_balance // 1000 + store_credit = money(blocks * 5.0) + return { + "tier": tier, + "tier_bonus_pct": int(bonus * 100), + "prior_balance": int(prior_balance), + "earned": earned, + "clawback": clawback, + "expired": int(expired_points), + "ledger": lines, + "net_balance": net_balance, + "redeemable_blocks": int(blocks), + "store_credit_value": store_credit, + } + + +def money(v): + cents = int(v * 100 + 0.5) + return cents / 100.0 + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--tier", choices=["plus", "extra", "mega"], required=True) + ap.add_argument("--prior-balance", type=float, default=0) + ap.add_argument("--purchases", default="[]", help="JSON list {amount, promo_multiplier}") + ap.add_argument("--returns", default="[]", help="JSON list {amount, promo_multiplier}") + ap.add_argument("--expired-points", type=float, default=0) + a = ap.parse_args() + print(json.dumps(reconcile(a.tier, a.prior_balance, json.loads(a.purchases), + json.loads(a.returns), a.expired_points), indent=2)) + + +if __name__ == "__main__": + main() diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml new file mode 100644 index 0000000..8d0f9cc --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + SKILL.md + 0 + SKILL.md + + cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md new file mode 100644 index 0000000..6179946 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md @@ -0,0 +1,83 @@ +--- +name: prorated-refund-calculator +description: Calculate the exact prorated refund when a customer cancels a BlastPass membership, and the net settlement when a defective unit is traded up to a pricier model. Use after the Store Policy Agent confirms the refund rule and the Membership MCP has returned the membership details. The skill runs a bundled Python script so the arithmetic is exact. +--- + +# Prorated Refund Calculator (BlastPass) + +This skill computes the **exact** refund owed when a customer cancels their +BlastPass membership, and the **net settlement** when a defective unit is credited +toward a pricier model. The math must be exact and consistent, so you **run the +bundled `blastpass_refund.py` script** — never compute the refund in your head, and +never write your own code. + +## When to use + +Use this skill in a BlastPass cancellation or trade-up, once you have: + +1. The **Store Policy Agent** has confirmed the member's tier and that a + **prorated refund** (or cooling-off full refund) applies. +2. The **Membership MCP** (`get_membership`) has returned the membership record, + including `annual_price`, `months_remaining`, `cancellation_fee`, + `nonrefundable_credit`, and `within_cooling_off`. + +Do **not** invent any of these numbers. They come from the Membership MCP and the +Store Policy Agent. + +## The refund rules (single source of truth) + +1. **Cooling-off (full refund).** If `within_cooling_off` is true, refund the full + `annual_price`. No fee, no proration. +2. **Prorated cancellation (the usual case).** Otherwise refund the value of the + whole unused months left on the 12-month term, then subtract the tier + cancellation fee. +3. **Non-refundable credit.** Subtract `nonrefundable_credit` (only the MEGA tier + has one). For other tiers it is 0. +4. The refund is **never negative**. + +## Workflow + +### 1. Compute the membership refund + +Take `annual_price`, `months_remaining`, `cancellation_fee`, +`nonrefundable_credit`, and `within_cooling_off` from the `get_membership` result, +and run: + +```bash +python3 blastpass_refund.py refund \ + --annual-price 129.99 --months-remaining 8 --cancellation-fee 10 +``` + +Add `--nonrefundable-credit ` for MEGA members, or `--within-cooling-off` when +the membership is still within the cooling-off window. The script prints the +breakdown and a `REFUND DUE: $...` line. + +Worked example: BlastPass Plus Extra, `annual_price 129.99`, `months_remaining 8`, +`cancellation_fee 10` -> monthly $10.8325, prorated $86.66, minus $10 = **$76.66**. + +### 2. (Trade-up only) Compute the upgrade settlement + +When the cancellation happens alongside a console upgrade — the defective unit is +credited under warranty and the customer puts the value toward a pricier model, +applying the membership refund as store credit — run: + +```bash +python3 blastpass_refund.py settle \ + --defective-credit 399.99 --upgrade-price 499.99 --membership-credit 76.66 +``` + +Use after (a) the Store Policy Agent confirms the defective unit is credited at its +purchase price with no restocking fee, (b) the Warehouse MCP returns the upgrade +model's price, and (c) the prorated refund above is known. The script prints the +breakdown and a `NET DUE FROM CUSTOMER: $...` (or `REFUND TO CUSTOMER: $...`) line. + +Worked example: defective base console credited $399.99, MEGA edition $499.99, +membership refund $76.66 applied as credit -> upgrade difference $100.00, net +**$23.34 due from the customer**. + +### 3. Report the result + +- State the **refund due** and (if a trade-up) the **net due / refund** clearly. +- Show the breakdown lines so the associate can verify how it was derived. +- Offer to cancel the membership via the Membership MCP (`cancel_membership`) and + to hand the final numbers to the `slip-pdf-generator` skill to print the slip. diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml new file mode 100644 index 0000000..5fe9147 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + SKILL.md + 0 + SKILL.md + + cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md new file mode 100644 index 0000000..a73618c --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md @@ -0,0 +1,80 @@ +--- +name: points-reconciliation +description: Reconcile a customer's BlastPoints loyalty balance after a complex visit (purchases, returns, promo multipliers, expiry) and convert the net balance to store credit. Use when an associate asks about a member's points, or after a return that should claw back previously earned points. The skill runs a bundled Python script that applies the base earn rate, promo multipliers, tier bonus, clawbacks, and expiry, then prints a points ledger and the redeemable store-credit value. +--- + +# BlastPoints Loyalty Reconciliation (BlastBox Omega) + +This skill reconciles a member's **BlastPoints** after a visit that involves any +mix of earning, returns (clawback), promo events, and expiry, and converts the +net balance to store credit. The points math must be exact, so you **run the +bundled `points_reconciliation.py` script** — never estimate points by hand, and +never write your own code. + +## When to use + +Use this skill when an associate asks things like: +- "How many BlastPoints does this member have after today?" +- "They returned a console — claw back the points they earned on it." +- "What's that worth in store credit?" + +Confirm the member's **tier** with the Membership MCP / Store Policy Agent first, +and confirm any **promo multiplier** in effect with the Store Policy Agent +(Policy RAG MCP, `search_policy "BlastPoints promo"`). + +## The points rules (single source of truth) + +These mirror the BlastPoints Loyalty Program Terms held by the Store Policy Agent: + +1. **Base earn:** 10 points per $1 of merchandise. +2. **Promo multiplier:** promo events (e.g. **Triple BLAST Weekend** = 3x) + multiply the **base** earn **before** the tier bonus is applied. +3. **Tier bonus on earned points:** Plus **+0%**, Plus Extra **+20%**, MEGA + **+50%**. +4. **Clawback:** returned items remove points using the **same** formula (the + promo multiplier and tier bonus that applied to the original purchase). +5. **Expiry:** points older than 90 days while the account was inactive are + removed (provided as a number). +6. **Redemption:** **1,000 points = $5.00** store credit, in whole 1,000-point + blocks. The net balance never goes below 0. + +## Workflow + +### 1. Gather the inputs + +Get the **tier** (`plus` / `extra` / `mega`), the **prior balance**, the list of +**purchases** (each `amount` plus its `promo_multiplier`), the list of **returns** +to claw back (same shape), and any **expired points**. + +### 2. Run the bundled script + +Pass the inputs to `points_reconciliation.py`. Purchases and returns are JSON +lists of `{"amount": , "promo_multiplier": }`: + +```bash +python3 points_reconciliation.py \ + --tier extra \ + --prior-balance 4200 \ + --purchases '[{"amount": 499.99, "promo_multiplier": 3.0}]' \ + --expired-points 800 +``` + +Add `--returns '[{"amount": 79.99, "promo_multiplier": 3.0}]'` when items are +being returned and their points clawed back. The script prints a JSON object with +the ledger, `net_balance`, `redeemable_blocks`, and `store_credit_value`. + +### 3. Report the result + +- State the **net BlastPoints balance** and the **store-credit value**. +- Show the ledger lines (earn / clawback / expired) so the associate can verify. +- Offer to apply the redeemable store credit to the current transaction. + +## Worked example + +Plus Extra member, prior balance 4,200, a Triple BLAST Weekend console purchase of +$499.99 (3x), and 800 expired points: + +- Earn: $499.99 x 3 x 1.2 (Plus Extra +20%) = **18,000** +- Expired: **-800** +- Net = 4,200 + 18,000 - 800 = **21,400 BlastPoints** +- Redeemable: 21 x 1,000 = **$105.00** store credit diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml new file mode 100644 index 0000000..64c60d0 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + SKILL.md + 0 + SKILL.md + + cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4 + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md new file mode 100644 index 0000000..175c092 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md @@ -0,0 +1,104 @@ +--- +name: slip-pdf-generator +description: Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill runs a bundled Python script that renders the confirmed slip values into a real PDF file. +--- + +# Return / RMA Slip PDF Generator + +This skill produces the **printable slip** that closes out a transaction at the +returns desk. You collect the final, already-confirmed values, then **run the +bundled `slip_pdf.py` script** to render the slip to a **PDF** using `reportlab`. +Do not hand-draw the PDF, write your own rendering code, or invent amounts — every +value comes from earlier steps (Policy Agent ruling, Membership MCP, Warehouse MCP, +and the `prorated-refund-calculator` skill). + +## When to use + +Use this skill **last**, after every amount is final: + +- the eligibility / warranty ruling is decided (Store Policy Agent), +- any membership refund is computed (`prorated-refund-calculator`), +- any upgrade settlement / net-due is computed (`prorated-refund-calculator`), +- the customer has agreed to the outcome. + +Do **not** use it to *decide* anything — it only renders the agreed result. + +## Slip types + +Set `slip_type` to the right label: + +- `WARRANTY SWAP` — defective item replaced under warranty. +- `RETURN / REFUND` — item returned for a refund. +- `EXCHANGE / UPGRADE` — item swapped for a different model with a price difference. +- `MEMBERSHIP CANCELLATION` — BlastPass cancelled (often combined with the above). + +## Workflow + +### 1. Assemble the confirmed values + +Build a JSON object with the final values. Every field is optional except those +that apply to this transaction. Keep amounts formatted to 2 decimals with a `$` +prefix. + +| Field | Meaning | +| --- | --- | +| `slip_type` | one of the labels above | +| `rma_ref` | RMA / reference number | +| `date` | `YYYY-MM-DD` | +| `store` / `associate` | store name/number and associate/desk | +| `customer_name` | customer name | +| `member_id` / `member_tier` | BlastPass id and tier | +| `items` | list of `[item, sku, disposition, amount]` rows | +| `settlement` | list of `[label, amount]` rows (upgrade difference, refund applied as credit, etc.) | +| `net_label` / `net_amount` | e.g. `NET DUE FROM CUSTOMER` / `$23.34`, or `REFUND TO CUSTOMER` / `$76.66` | +| `notes` | free-text notes | + +### 2. Run the bundled script + +Pass the JSON to `slip_pdf.py`: + +```bash +python3 slip_pdf.py --slip-json '' --output blastbox_slip.pdf +``` + +For the upgrade-settlement example (defective base console → MEGA edition, with a +BlastPass refund applied as store credit): + +```bash +python3 slip_pdf.py --output blastbox_slip.pdf --slip-json '{ + "slip_type": "EXCHANGE / UPGRADE", + "rma_ref": "RMA-OMEGA-1024", + "date": "2026-06-07", + "store": "BlastBox #214 - Seattle", + "associate": "Associate Desk", + "customer_name": "Jordan Pixel", + "member_id": "MEGA-BLAST-1024", + "member_tier": "BlastPass Plus Extra", + "items": [ + ["BlastBox Omega (defective)", "SKU-OMEGA", "Warranty credit", "$399.99"], + ["BlastBox Omega MEGA Edition", "SKU-OMEGA-MEGA", "Upgrade (new)", "$499.99"] + ], + "settlement": [ + ["Upgrade difference ($499.99 - $399.99)", "$100.00"], + ["BlastPass Plus Extra refund applied as store credit", "-$76.66"] + ], + "net_label": "NET DUE FROM CUSTOMER", + "net_amount": "$23.34", + "notes": "Manufacturing defect confirmed within 30 days - no restocking fee. BlastPass Plus Extra (MEGA-BLAST-1024) cancelled; 8 unused months prorated." +}' +``` + +The script writes the PDF and prints `WROTE blastbox_slip.pdf`. If the JSON is +long, you may write it to a file and pass `--slip-file slip.json` instead. + +### 3. Report + +- Confirm the PDF was written and give its filename. +- Restate the **net amount** (due or refunded) and the slip type. +- Offer to email or reprint the slip if needed. + +## Notes + +- The script uses `reportlab` (the available PDF library). No network calls. +- The layout fits a single page for a normal transaction. +- Pass plain text in the values; the script escapes `&` for you. diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml new file mode 100644 index 0000000..062babf --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + slip_pdf.py + 0 + slip_pdf.py + + cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4 + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py new file mode 100644 index 0000000..8888d10 --- /dev/null +++ b/submissions/blastbox-deploy/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""BlastBox Returns & Service slip -> PDF renderer. + +Renders the printable return / RMA / exchange slip that closes out a transaction +at the returns desk. All amounts and labels are passed in as a single JSON blob +of already-confirmed values; this script only lays them out and writes the PDF. + +Usage: + python3 slip_pdf.py --slip-json '' [--output blastbox_slip.pdf] + python3 slip_pdf.py --slip-file slip.json [--output blastbox_slip.pdf] + +The JSON object accepts these fields (omit any that do not apply): + slip_type str e.g. "EXCHANGE / UPGRADE", "RETURN / REFUND", + "WARRANTY SWAP", "MEMBERSHIP CANCELLATION" + rma_ref str RMA / reference number + date str YYYY-MM-DD + store str store name / number + associate str associate name or desk + customer_name str + member_id str BlastPass member id + member_tier str BlastPass tier + items list of [item, sku, disposition, amount] rows + settlement list of [label, amount] rows + net_label str e.g. "NET DUE FROM CUSTOMER" / "REFUND TO CUSTOMER" + net_amount str e.g. "$23.34" + notes str free-text notes + +Every value comes from earlier steps (Store Policy Agent ruling, Membership MCP, +Warehouse MCP, and the prorated-refund-calculator skill). This script never +decides or invents amounts. +""" + +import argparse +import json +import sys + +ACCENT = "#6b2fb5" +ROW_ALT = "#f3eefb" + + +def _esc(text): + """Escape ampersands for reportlab Paragraph markup.""" + return str(text).replace("&", "&") + + +def build_pdf(slip, output): + from reportlab.lib.pagesizes import LETTER + from reportlab.lib.units import inch + from reportlab.lib import colors + from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table, + TableStyle, HRFlowable) + from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle + + styles = getSampleStyleSheet() + h1 = ParagraphStyle("h1", parent=styles["Title"], fontSize=18, spaceAfter=6) + h2 = ParagraphStyle("h2", parent=styles["Heading2"], fontSize=12, + spaceBefore=10, spaceAfter=4) + body = styles["BodyText"] + + doc = SimpleDocTemplate(output, pagesize=LETTER, + topMargin=0.7 * inch, bottomMargin=0.7 * inch) + flow = [] + flow.append(Paragraph("BlastBox - Returns & Service Slip", h1)) + flow.append(HRFlowable(width="100%", color=colors.HexColor(ACCENT))) + + meta = (f"Slip type: {_esc(slip.get('slip_type', ''))}    " + f"RMA / Ref #: {_esc(slip.get('rma_ref', ''))}
" + f"Date: {_esc(slip.get('date', ''))}    " + f"Store / Associate: {_esc(slip.get('store', ''))} / " + f"{_esc(slip.get('associate', ''))}") + flow.append(Paragraph(meta, body)) + + if slip.get("customer_name"): + flow.append(Paragraph("Customer", h2)) + flow.append(Paragraph( + f"{_esc(slip.get('customer_name', ''))}  |  " + f"BlastPass {_esc(slip.get('member_id', ''))} " + f"({_esc(slip.get('member_tier', ''))})", body)) + + items = slip.get("items") or [] + if items: + flow.append(Paragraph("Items", h2)) + item_data = [["Item", "SKU", "Disposition", "Amount"]] + [list(r) for r in items] + t = Table(item_data, colWidths=[2.6 * inch, 1.3 * inch, 1.5 * inch, 1.0 * inch]) + t.setStyle(TableStyle([ + ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor(ACCENT)), + ("TEXTCOLOR", (0, 0), (-1, 0), colors.white), + ("FONTSIZE", (0, 0), (-1, -1), 9), + ("GRID", (0, 0), (-1, -1), 0.5, colors.grey), + ("ALIGN", (-1, 0), (-1, -1), "RIGHT"), + ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.HexColor(ROW_ALT)]), + ])) + flow.append(t) + + settlement = slip.get("settlement") or [] + if settlement: + flow.append(Paragraph("Settlement", h2)) + for label, amount in settlement: + flow.append(Paragraph(f"{_esc(label)}: {_esc(amount)}", body)) + + if slip.get("net_label") or slip.get("net_amount"): + flow.append(Spacer(1, 6)) + flow.append(Paragraph( + f"{_esc(slip.get('net_label', ''))}: " + f"{_esc(slip.get('net_amount', ''))}", h2)) + + if slip.get("notes"): + flow.append(Paragraph("Notes", h2)) + flow.append(Paragraph(_esc(slip["notes"]), body)) + + flow.append(Spacer(1, 10)) + flow.append(Paragraph("Keep this slip for your records. Warranty swaps carry " + "the remaining original warranty.", body)) + + doc.build(flow) + + +def main(argv): + parser = argparse.ArgumentParser(description="Render a BlastBox return/RMA slip PDF.") + src = parser.add_mutually_exclusive_group(required=True) + src.add_argument("--slip-json", help="Slip data as a JSON string.") + src.add_argument("--slip-file", help="Path to a JSON file with the slip data.") + parser.add_argument("--output", default="blastbox_slip.pdf", + help="Output PDF path (default: blastbox_slip.pdf).") + args = parser.parse_args(argv[1:]) + + try: + if args.slip_json: + slip = json.loads(args.slip_json) + else: + with open(args.slip_file, "r", encoding="utf-8") as fh: + slip = json.load(fh) + except (ValueError, OSError) as exc: + print(f"Error: could not read slip JSON ({exc}).") + return 1 + + try: + build_pdf(slip, args.output) + except ImportError: + print("Error: reportlab is required to render the slip PDF.") + return 1 + + print(f"WROTE {args.output}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/submissions/blastbox-deploy/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml b/submissions/blastbox-deploy/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml new file mode 100644 index 0000000..5c9f448 --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Inventory & Fullfilment Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json b/submissions/blastbox-deploy/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json new file mode 100644 index 0000000..ea735b3 --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the Inventory \u0026 Fulfillment Agent for BlastBox Omega stores. You support the Returns \u0026 Service Assistant with stock, alternatives, restock, and game compatibility.\u200B\u200C\n\nTools:\u200B\u200C\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.\u200B\u200C\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.\u200B\u200C\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.\u200B\u200C\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.\u200B\u200C\nRules:\u200B\u200C\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.\u200B\u200C\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).\u200B\u200C\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.\u200B\u200C\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.\u200B\u200C\nBe concise. Always include SKUs, prices, and quantities you looked up.\u200B\u200C\n\n\nInventory aging (merchandising reviews):\u200B\u200C\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.\u200B\u200C\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.\u200B\u200C" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml b/submissions/blastbox-deploy/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml new file mode 100644 index 0000000..b58beff --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Store Policy Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json b/submissions/blastbox-deploy/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json new file mode 100644 index 0000000..82b01ee --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the Store Policy Agent \u2014 the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns \u0026 Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won\u0027t power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns \u0026 Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn\u0027t specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.\n" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/bots/Default_draft_IsBewO/bot.xml b/submissions/blastbox-deploy/solution/bots/Default_draft_IsBewO/bot.xml new file mode 100644 index 0000000..47cd693 --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/Default_draft_IsBewO/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Returns & Service Assistant + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/bots/Default_draft_IsBewO/configuration.json b/submissions/blastbox-deploy/solution/bots/Default_draft_IsBewO/configuration.json new file mode 100644 index 0000000..b1d9e93 --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/Default_draft_IsBewO/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the BlastPass Assistant, a friendly self-serve helper that BlastBox customers talk to directly. You help members with their BlastPass membership and their membership card.\n\nTone: warm, upbeat, and concise. Speak directly to the customer like a helpful concierge - one friendly idea per turn, never a wall of text. A single light emoji is fine; don\u0027t overdo it. Ask for one thing at a time, and follow the steps in your skills when one applies.\n\nA couple of house rules: always verify identity before making any change to a membership or card, and never invent membership details, serials, or numbers - look them up with your tools or ask the customer.\n" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml b/submissions/blastbox-deploy/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml new file mode 100644 index 0000000..3828bb5 --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Store Associate Assistant + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json b/submissions/blastbox-deploy/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json new file mode 100644 index 0000000..bd8bb1a --- /dev/null +++ b/submissions/blastbox-deploy/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json @@ -0,0 +1 @@ +{"categories":[],"channels":[{"id":"MsTeams","channelId":"MsTeams","channelSpecifier":null,"displayName":null}],"settings":{},"publishOnCreate":false,"publishOnImport":true,"isLightweightBot":false,"$kind":"BotConfiguration","recognizer":{"$kind":"CLICopilotRecognizer"},"agentSettings":{"$kind":"AgentSettings","model":{"$kind":"ModelConfig","series":"Sonnet46"},"instructions":{"$kind":"Instructions","segments":[{"$kind":"StaticSegment","value":"You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer."}]}}} \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/customizations.xml b/submissions/blastbox-deploy/solution/customizations.xml new file mode 100644 index 0000000..283f679 --- /dev/null +++ b/submissions/blastbox-deploy/solution/customizations.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + 32399478-9062-f111-ab0b-0022480a52c7 + + Policy RAG MCP v2 + #007ee5 + new_policy-20rag-20mcp-20v2 + 1 + [] + /Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json + /Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json + /Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json + /Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx + + + 5d6180ed-0d6f-418c-8ccb-1360b7c33f88 + BlastPass console membership lookup, cancellation, and card reissue. Tools: get_membership, cancel_membership, reissue_card. Runs entirely inside the connector — no external MCP server needed. + Membership MCP v2 + #007ee5 + cat_membership-20mcp-20v2 + 1 + [] + /Connector/cat_membership-20mcp-20v2_openapidefinition.json + /Connector/cat_membership-20mcp-20v2_connectionparameters.json + /Connector/cat_membership-20mcp-20v2_policytemplateinstances.json + /Connector/cat_membership-20mcp-20v2_customcodeblobcontent.csx + /Connector/cat_membership-20mcp-20v2_iconblob.Png + + + e4e7e713-5c3f-f111-bec6-70a8a59a4421 + + Order Management MCP + #007ee5 + new_order-20management-20mcp + 1 + [] + /Connector/new_order-20management-20mcp_openapidefinition.json + /Connector/new_order-20management-20mcp_connectionparameters.json + /Connector/new_order-20management-20mcp_policytemplateinstances.json + /Connector/new_order-20management-20mcp_customcodeblobcontent.csx + + + 357a348b-d53f-f111-bec6-70a8a59a4421 + Warehouse inventory and fulfillment with tools for checking stock, tracking pipeline stages, finding alternatives, and restock dates. Runs entirely inside the connector — no external MCP server needed. + Warehouse MCP + #007ee5 + new_warehouse-20mcp + 1 + [] + /Connector/new_warehouse-20mcp_openapidefinition.json + /Connector/new_warehouse-20mcp_connectionparameters.json + /Connector/new_warehouse-20mcp_policytemplateinstances.json + /Connector/new_warehouse-20mcp_customcodeblobcontent.csx + /Connector/new_warehouse-20mcp_iconblob.Png + + + + 1033 + + \ No newline at end of file diff --git a/submissions/blastbox-deploy/solution/solution.xml b/submissions/blastbox-deploy/solution/solution.xml new file mode 100644 index 0000000..a448362 --- /dev/null +++ b/submissions/blastbox-deploy/solution/solution.xml @@ -0,0 +1,87 @@ + + + BlastBoxDeploy + + + + + 1.0.0.5 + 0 + + Cr5a062 + + + + + + + cr26e + 80871 + +
+ 1 + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ 2 + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + +
+
\ No newline at end of file diff --git a/submissions/blastbox-omega/README.md b/submissions/blastbox-omega/README.md new file mode 100644 index 0000000..b74f825 --- /dev/null +++ b/submissions/blastbox-omega/README.md @@ -0,0 +1,45 @@ +# BlastBox Omega Store Operations + +BlastBox Omega is a retro-gaming store solution built around one store-associate +assistant and two specialist agents. Together they handle returns, warranty +swaps, membership changes, stock questions, fulfillment alternatives, policy +checks, and settlement. + +The solution includes two modern workflows that reuse the same agent and MCP +data layer. They show how classification, loops, conditions, human review, and +agent nodes combine into practical store operations. + +## Agents + +- **Store Associate Assistant** coordinates the customer's complete visit and + delegates stock and policy questions to its specialist agents. +- **Inventory & Fulfillment Agent** checks stock, alternatives, restock timing, + compatibility, and inventory aging. +- **Store Policy Agent** grounds returns, warranty, membership, and markdown + decisions in store policy. + +The primary assistant also includes inline skills for prorated refunds, points +reconciliation, and PDF slip generation, plus MCP-backed order and membership +tools. + +## BlastBox Post-Sales Service + +An email with the subject **BlastBox Customer Service** starts the workflow. A +classification step separates shipping issues, defective products, and general +post-sale questions. Each branch calls the appropriate agents, checks order, +stock, weather, and policy context, and asks for human input only when the +customer's choice or an exception requires it. + +## BlastBox Weekly Inventory Health + +This workflow reviews active SKUs with the inventory agent, loops through stock +aging results, and branches between price adjustment, reorder, and keep-price +paths. Markdown policy and human review protect consequential discount +decisions. The run ends with an HTML inventory report sent by email. + +## Import + +Download the rebuilt solution ZIP from this page and import it through Power +Platform. The package contains the original exploded solution files, including +its agents, workflows, connectors, skills, and connection references. Review and +replace environment-specific connections during import. diff --git a/submissions/blastbox-omega/metadata.json b/submissions/blastbox-omega/metadata.json new file mode 100644 index 0000000..f64a8d5 --- /dev/null +++ b/submissions/blastbox-omega/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "BlastBox Omega Store Operations", + "description": "A multi-agent retail solution pairing customer service and inventory agents with two human-aware operational workflows.", + "tags": [ + "retail", + "customer-service", + "inventory", + "multi-agent", + "human-in-the-loop" + ], + "author": "Adriana Trujillo", + "createdAt": "2026-08-11", + "updatedAt": "2026-08-11", + "featured": true +} diff --git a/submissions/blastbox-omega/solution/Assets/botcomponent_connectionreferenceset.xml b/submissions/blastbox-omega/solution/Assets/botcomponent_connectionreferenceset.xml new file mode 100644 index 0000000..6335409 --- /dev/null +++ b/submissions/blastbox-omega/solution/Assets/botcomponent_connectionreferenceset.xml @@ -0,0 +1,14 @@ + + + 1 + + + 1 + + + 1 + + + 1 + + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_connectionparameters.json b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx new file mode 100644 index 0000000..fc9a695 --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_customcodeblobcontent.csx @@ -0,0 +1,1169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Order Management MCP Connector (inline) ║ +// ║ ║ +// ║ 5 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + private const string APP_INSIGHTS_CONNECTION_STRING = ""; + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "order-management", + Version = "1.0.0", + Title = "Order Management", + Description = "E-commerce order management with tools for searching orders, viewing details, tracking shipments, and managing returns." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use search_orders first to find orders by customer name, email, or order number. Then use the order_id from results with get_order, get_shipment, request_return, or get_return_status." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + + private static readonly JArray Orders = JArray.Parse(@"[ + {""id"":""ORD-10421"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-04-10"",""status"":""shipped"",""items"":[{""sku"":""SKU-WH1000"",""name"":""Sony WH-1000XM5 Headphones"",""quantity"":1,""unitPrice"":349.99},{""sku"":""SKU-USBC3"",""name"":""USB-C Charging Cable (3-pack)"",""quantity"":1,""unitPrice"":14.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":364.98}, + {""id"":""ORD-10422"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-04-15"",""status"":""processing"",""items"":[{""sku"":""SKU-KINDLE"",""name"":""Kindle Paperwhite (16GB)"",""quantity"":1,""unitPrice"":149.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":149.99}, + {""id"":""ORD-10318"",""customer"":{""id"":""C-001"",""name"":""Sarah Mitchell"",""email"":""sarah.mitchell@example.com""},""date"":""2026-03-22"",""status"":""delivered"",""items"":[{""sku"":""SKU-AIRPOD"",""name"":""AirPods Pro (2nd Gen)"",""quantity"":1,""unitPrice"":249.00},{""sku"":""SKU-CASE01"",""name"":""Silicone AirPods Case - Midnight"",""quantity"":1,""unitPrice"":19.99}],""shippingAddress"":""742 Evergreen Terrace, Springfield, IL 62704"",""paymentMethod"":""Visa ending in 4242"",""total"":268.99}, + {""id"":""ORD-10455"",""customer"":{""id"":""C-002"",""name"":""James Rivera"",""email"":""j.rivera@example.com""},""date"":""2026-04-12"",""status"":""shipped"",""items"":[{""sku"":""SKU-HOODIE"",""name"":""Nike Tech Fleece Hoodie - Black (L)"",""quantity"":1,""unitPrice"":120.00},{""sku"":""SKU-JOGGER"",""name"":""Nike Sportswear Joggers - Grey (L)"",""quantity"":1,""unitPrice"":85.00}],""shippingAddress"":""88 Sunset Blvd, Apt 4B, Los Angeles, CA 90028"",""paymentMethod"":""Mastercard ending in 8811"",""total"":205.00}, + {""id"":""ORD-10460"",""customer"":{""id"":""C-002"",""name"":""James Rivera"",""email"":""j.rivera@example.com""},""date"":""2026-04-18"",""status"":""processing"",""items"":[{""sku"":""SKU-SWITCH"",""name"":""Nintendo Switch OLED"",""quantity"":1,""unitPrice"":349.99},{""sku"":""SKU-ZELDA"",""name"":""The Legend of Zelda: Tears of the Kingdom"",""quantity"":1,""unitPrice"":59.99}],""shippingAddress"":""88 Sunset Blvd, Apt 4B, Los Angeles, CA 90028"",""paymentMethod"":""Mastercard ending in 8811"",""total"":409.98}, + {""id"":""ORD-10389"",""customer"":{""id"":""C-003"",""name"":""Emily Chen"",""email"":""emily.chen@example.com""},""date"":""2026-03-28"",""status"":""delivered"",""items"":[{""sku"":""SKU-DUNE2"",""name"":""Dune: Part Two (4K Blu-ray)"",""quantity"":1,""unitPrice"":29.99},{""sku"":""SKU-FOUNDATION"",""name"":""Foundation (Isaac Asimov) - Paperback"",""quantity"":1,""unitPrice"":12.99},{""sku"":""SKU-3BODY"",""name"":""The Three-Body Problem - Paperback"",""quantity"":1,""unitPrice"":14.99}],""shippingAddress"":""1200 Main Street, Unit 7C, Seattle, WA 98101"",""paymentMethod"":""Amex ending in 3003"",""total"":57.97}, + {""id"":""ORD-10478"",""customer"":{""id"":""C-003"",""name"":""Emily Chen"",""email"":""emily.chen@example.com""},""date"":""2026-04-14"",""status"":""shipped"",""items"":[{""sku"":""SKU-ERGOCHAIR"",""name"":""ErgoMax Office Chair - Mesh Back"",""quantity"":1,""unitPrice"":299.00}],""shippingAddress"":""1200 Main Street, Unit 7C, Seattle, WA 98101"",""paymentMethod"":""Amex ending in 3003"",""total"":299.00}, + {""id"":""ORD-10502"",""customer"":{""id"":""C-004"",""name"":""Jordan Pixel"",""email"":""jordan.pixel@example.com""},""date"":""2026-06-09"",""status"":""delivered"",""items"":[{""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""quantity"":1,""unitPrice"":399.99}],""shippingAddress"":""15 Arcade Lane, Pixel Heights, WA 98052"",""paymentMethod"":""Visa ending in 1024"",""total"":399.99} + ]"); + + private static readonly JObject Shipments = JObject.Parse(@"{ + ""ORD-10421"":{""order_id"":""ORD-10421"",""carrier"":""FedEx"",""tracking_number"":""FX-794644790132"",""tracking_url"":""https://www.fedex.com/fedextrack/?trknbr=FX-794644790132"",""estimated_delivery"":""2026-04-22"",""last_update"":""2026-04-20T14:30:00Z"",""status"":""out_for_delivery""}, + ""ORD-10455"":{""order_id"":""ORD-10455"",""carrier"":""UPS"",""tracking_number"":""1Z999AA10123456784"",""tracking_url"":""https://www.ups.com/track?tracknum=1Z999AA10123456784"",""estimated_delivery"":""2026-04-21"",""last_update"":""2026-04-19T09:15:00Z"",""status"":""in_transit""}, + ""ORD-10318"":{""order_id"":""ORD-10318"",""carrier"":""USPS"",""tracking_number"":""9400111899223100287654"",""tracking_url"":""https://tools.usps.com/go/TrackConfirmAction?tLabels=9400111899223100287654"",""estimated_delivery"":""2026-03-28"",""last_update"":""2026-03-28T10:15:00Z"",""status"":""delivered""}, + ""ORD-10478"":{""order_id"":""ORD-10478"",""carrier"":""FedEx"",""tracking_number"":""FX-794644790199"",""tracking_url"":""https://www.fedex.com/fedextrack/?trknbr=FX-794644790199"",""estimated_delivery"":""2026-04-24"",""last_update"":""2026-04-21T10:00:00Z"",""status"":""in_transit""} + }"); + + private static readonly JObject Returns = JObject.Parse(@"{ + ""RA-50012"":{""return_id"":""RA-50012"",""order_id"":""ORD-10318"",""status"":""refund_processed"",""items_returned"":[{""sku"":""SKU-CASE01"",""name"":""Silicone AirPods Case - Midnight""}],""reason"":""Wrong color received"",""refund_amount"":""$19.99"",""refund_method"":""Visa ending in 4242""}, + ""RA-50018"":{""return_id"":""RA-50018"",""order_id"":""ORD-10389"",""status"":""received"",""items_returned"":[{""sku"":""SKU-DUNE2"",""name"":""Dune: Part Two (4K Blu-ray)""}],""reason"":""Disc scratched on arrival"",""refund_amount"":""$29.99"",""refund_method"":""Amex ending in 3003""} + }"); + + private static int _returnCounter = 50019; + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. search_orders + handler.AddTool("search_orders", + "Search for orders by customer name, email, or order number. Returns a summary list. This is the entry point — use it first to find order IDs.", + schemaConfig: s => s.String("query", "Customer name, email address, or order number to search for", required: true), + handler: async (args, ct) => + { + var query = (args.Value("query") ?? "").ToLower(); + var matches = new JArray(); + foreach (var order in Orders) + { + var name = order["customer"]?["name"]?.ToString().ToLower() ?? ""; + var email = order["customer"]?["email"]?.ToString().ToLower() ?? ""; + var id = order["id"]?.ToString().ToLower() ?? ""; + if (name.Contains(query) || email.Contains(query) || id.Contains(query)) + { + matches.Add(new JObject + { + ["order_id"] = order["id"], + ["date"] = order["date"], + ["status"] = order["status"], + ["total"] = "$" + order["total"], + ["customer"] = order["customer"]?["name"], + ["item_count"] = (order["items"] as JArray)?.Count ?? 0 + }); + } + } + if (matches.Count == 0) + return new JObject { ["message"] = $"No orders found matching \"{query}\"." }; + return new JObject { ["orders"] = matches }; + }); + + // 2. get_order + handler.AddTool("get_order", + "Get full details for a specific order including line items, shipping address, and payment info. Use an order_id from search_orders results.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. ORD-10421) from search_orders results", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + foreach (var order in Orders) + { + if (order["id"]?.ToString() == orderId) + return JObject.Parse(order.ToString()); + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 3. get_shipment + handler.AddTool("get_shipment", + "Get shipment tracking info for an order. Only available for shipped/delivered orders. Returns carrier, tracking number, URL, and delivery estimate.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. ORD-10421)", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + if (Shipments[orderId] != null) + return JObject.Parse(Shipments[orderId].ToString()); + + // Check if order exists but isn't shipped + foreach (var order in Orders) + { + if (order["id"]?.ToString() == orderId) + throw new ArgumentException($"Order \"{orderId}\" has status \"{order["status"]}\" — shipment tracking is only available for shipped or delivered orders."); + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 4. request_return + handler.AddTool("request_return", + "Initiate a return for specific items in an order. Returns a return authorization with ID, label, refund estimate, and instructions.", + schemaConfig: s => s + .String("order_id", "Order ID to return items from", required: true) + .Array("item_skus", "SKUs of items to return (from get_order line items)", itemSchema: new JObject { ["type"] = "string" }, required: true) + .String("reason", "Reason for return (e.g. 'Defective', 'Wrong item')", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + var reason = args.Value("reason") ?? "Not specified"; + var skuArray = args["item_skus"] as JArray ?? new JArray(); + var skus = skuArray.Select(s => s.ToString()).ToList(); + + foreach (var order in Orders) + { + if (order["id"]?.ToString() != orderId) continue; + + var items = new JArray(); + double refundTotal = 0; + foreach (var item in order["items"] as JArray ?? new JArray()) + { + if (skus.Contains(item["sku"]?.ToString())) + { + items.Add(new JObject { ["sku"] = item["sku"], ["name"] = item["name"] }); + refundTotal += item["unitPrice"]?.Value() ?? 0; + } + } + + var returnId = $"RA-{System.Threading.Interlocked.Increment(ref _returnCounter):D5}"; + return new JObject + { + ["return_id"] = returnId, + ["order_id"] = orderId, + ["status"] = "requested", + ["items_returned"] = items, + ["reason"] = reason, + ["refund_amount"] = $"${refundTotal:F2}", + ["refund_method"] = order["paymentMethod"], + ["return_label_url"] = $"https://returns.example.com/labels/{returnId}.pdf", + ["instructions"] = "Print the return label, attach it to the package, and drop it off at any carrier location within 14 days." + }; + } + throw new ArgumentException($"Order \"{orderId}\" not found."); + }); + + // 5. get_return_status + handler.AddTool("get_return_status", + "Check the status of a return request. Returns current stage, refund status, and timeline.", + schemaConfig: s => s.String("return_id", "Return ID (e.g. RA-50019) from request_return result", required: true), + handler: async (args, ct) => + { + var returnId = args.Value("return_id") ?? ""; + if (Returns[returnId] != null) + return JObject.Parse(Returns[returnId].ToString()); + + // For newly created returns, return a generic status + return new JObject + { + ["return_id"] = returnId, + ["status"] = "requested", + ["refund_status"] = "pending", + ["timeline"] = new JArray { new JObject { ["stage"] = "Return requested", ["date"] = DateTime.UtcNow.ToString("yyyy-MM-dd") } } + }; + }); + } + + // ── App Insights (optional, from Power MCP Template) ──────────────── + + private async Task LogToAppInsights(string eventName, JObject data, string correlationId) + { + if (string.IsNullOrEmpty(APP_INSIGHTS_CONNECTION_STRING)) return; + try + { + var iKey = APP_INSIGHTS_CONNECTION_STRING.Split(';').FirstOrDefault(s => s.StartsWith("InstrumentationKey="))?.Split('=')[1] ?? ""; + var endpoint = APP_INSIGHTS_CONNECTION_STRING.Split(';').FirstOrDefault(s => s.StartsWith("IngestionEndpoint="))?.Split('=')[1]?.TrimEnd('/') ?? "https://dc.services.visualstudio.com"; + var envelope = new JObject + { + ["name"] = "Microsoft.ApplicationInsights.Event", + ["time"] = DateTime.UtcNow.ToString("o"), + ["iKey"] = iKey, + ["data"] = new JObject + { + ["baseType"] = "EventData", + ["baseData"] = new JObject { ["ver"] = 2, ["name"] = eventName, ["properties"] = new JObject { ["correlationId"] = correlationId, ["details"] = data?.ToString(Newtonsoft.Json.Formatting.None) ?? "{}" } } + } + }; + var request = new HttpRequestMessage(HttpMethod.Post, $"{endpoint}/v2/track") { Content = new StringContent($"[{envelope}]", Encoding.UTF8, "application/json") }; + await this.Context.SendAsync(request, this.CancellationToken).ConfigureAwait(false); + } + catch { } + } +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_openapidefinition.json b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_openapidefinition.json new file mode 100644 index 0000000..98302dc --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Order Management MCP", + "description": "E-commerce order management with tools for searching orders, viewing details, tracking shipments, and managing returns. Runs entirely inside the connector \u2014 no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_order-20management-20mcp_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx new file mode 100644 index 0000000..379d78e --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx @@ -0,0 +1,1214 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Policy RAG MCP Connector (inline, MOCK) ║ +// ║ ║ +// ║ A pretend "knowledge base" for the BlastBox Omega Store Policy Agent. ║ +// ║ Modern Copilot Studio agents can't take uploaded files, so the tier ║ +// ║ policy booklets live here as static passages and a mock keyword search. ║ +// ║ ║ +// ║ No embeddings, no external store — just canned text + naive scoring. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "blastpass-policy-rag", + Version = "1.0.0", + Title = "BlastPass Policy Knowledge", + Description = "Mock policy knowledge base for BlastBox Omega. Search the BlastPass membership, returns, and loyalty policies, or fetch the exact refund rule for a tier. Use this to confirm a member's tier and whether a prorated refund is allowed before running the refund math." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Call search_policy with the associate's question (and optionally a tier_code of plus/extra/mega) to retrieve the most relevant policy passages. Call get_tier_refund_policy with a tier_code to get the structured refund rule (cooling-off window, proration method, cancellation fee, non-refundable credit) needed to calculate a prorated refund. Call get_markdown_policy with a markdown_type ('promo' or 'clearance') to get the store's pricing markdown guardrails (max discount, margin floor, clearance/manager-flag rules) for merchandising markdown decisions. This is a mock knowledge base with static text." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static "Knowledge Base" ───────────────────────────────────────── + // + // Each passage is a mock policy chunk. tier is plus/extra/mega or "all". + // Naive search scores passages by how many query words appear in the + // keywords + text. This is intentionally NOT real RAG — just a demo prop. + + private static readonly JArray Passages = JArray.Parse(@"[ + { + ""id"":""mem-plus-refund"", + ""doc"":""BlastPass Plus Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""plus"", + ""keywords"":""plus tier1 refund cancel cancellation prorate prorated proration cooling-off cooling off window months remaining price 79.99 fee"", + ""text"":""BlastPass Plus ($79.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 79.99/12 = $6.6658/mo). Plus has NO cancellation fee and no non-refundable credit. Prorated refunds ARE allowed for Plus."" + }, + { + ""id"":""mem-extra-refund"", + ""doc"":""BlastPass Plus Extra Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""extra"", + ""keywords"":""extra plus extra tier2 refund cancel cancellation prorate prorated proration cooling-off window months remaining price 129.99 fee 10 vault"", + ""text"":""BlastPass Plus Extra ($129.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 129.99/12 = $10.8325/mo), MINUS a $10 cancellation fee (never below $0). No non-refundable credit. Prorated refunds ARE allowed for Plus Extra."" + }, + { + ""id"":""mem-mega-refund"", + ""doc"":""BlastPass Plus Extra MEGA!!! Membership Terms"", + ""section"":""Cancellations & Refunds"", + ""tier"":""mega"", + ""keywords"":""mega plus extra mega tier3 refund cancel cancellation prorate prorated proration cooling-off window months remaining price 199.99 fee 25 welcome credit 20 non-refundable loot crate"", + ""text"":""BlastPass Plus Extra MEGA!!! ($199.99/yr). Cooling-off: cancel within 14 days of activation with under 2 hours streamed for a FULL refund. Otherwise a prorated refund is available for the whole UNUSED months left on the 12-month term (monthly value = 199.99/12 = $16.6658/mo), MINUS a $25 cancellation fee AND MINUS the one-time $20 MEGA Welcome Credit, which is non-refundable (never below $0). Prorated refunds ARE allowed for MEGA, but remember to subtract BOTH the $25 fee and the $20 credit."" + }, + { + ""id"":""mem-perks"", + ""doc"":""BlastPass Membership Terms"", + ""section"":""Tiers & Perks"", + ""tier"":""all"", + ""keywords"":""tier tiers perks benefits vault mystery cartridge cloud save blastbuddies co-op which tier difference compare"", + ""text"":""Tiers: Plus ($79.99) — 4-player co-op, 1 Mystery Cartridge/mo, 25GB cloud saves. Plus Extra ($129.99) — Extra Vault (200+ titles), 100GB saves, 2 cartridges/mo, early demos. Plus Extra MEGA!!! ($199.99) — MEGA Vault (600+ titles incl. day-one exclusives), unlimited saves, 4 cartridges/mo + quarterly Loot Crate, MEGA Lounge, plus a one-time $20 Welcome Credit."" + }, + { + ""id"":""returns-windows"", + ""doc"":""Store Returns & Exchanges Policy"", + ""section"":""Return Windows"", + ""tier"":""all"", + ""keywords"":""return returns exchange window days console accessory game sealed opened receipt eligible eligibility"", + ""text"":""Return windows (with receipt): Consoles & hardware — 30 days, like-new. Accessories (controllers, headsets) — 30 days. Sealed physical games — 30 days. Opened physical games — 14 days, exchange only for the same title if defective. Digital/redeemed codes — non-returnable once revealed. Day-one MEGA exclusives — NON-RETURNABLE, all sales final."" + }, + { + ""id"":""returns-restocking"", + ""doc"":""Store Returns & Exchanges Policy"", + ""section"":""Restocking Fees & Store Credit"", + ""tier"":""all"", + ""keywords"":""restocking fee store credit refund opened bundle settlement defective warranty percent bonus"", + ""text"":""Restocking fee: 15% on opened consoles/hardware returned in like-new condition; waived if the item is defective. Defective hardware within 30 days is a free warranty swap, not a return. Refunds go to the original tender; choosing STORE CREDIT instead adds a 10% goodwill bonus on the eligible merchandise total (the bonus does NOT apply to membership-proration refunds or to fees)."" + }, + { + ""id"":""loyalty-blastpoints"", + ""doc"":""BlastPoints Loyalty Program Terms"", + ""section"":""Earning, Promos & Expiry"", + ""tier"":""all"", + ""keywords"":""blastpoints loyalty points earn redeem expiry expire promo multiplier triple weekend tier bonus reconcile balance"", + ""text"":""BlastPoints: earn 10 points per $1 on merchandise. Tier bonus: Extra members +20%, MEGA members +50% on earned points. Promo events (e.g. Triple BLAST Weekend) multiply BASE earn by 3x before the tier bonus. Points from returned items are clawed back. Points expire 90 days after they are earned if the account is inactive. Redemption: 1,000 points = $5 store credit."" + } + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. search_policy — mock keyword search over the static passages + handler.AddTool("search_policy", + "Search the BlastBox Omega policy knowledge base (membership, returns, and loyalty terms) and return the most relevant policy passages. Use this to confirm a member's tier, check whether a prorated refund is allowed, look up return windows, restocking fees, or loyalty rules.", + schemaConfig: s => s + .String("query", "What you want to know, in natural language (e.g. 'is a prorated refund allowed for Plus Extra?' or 'return window for an opened console').", required: true) + .String("tier_code", "Optional tier filter: 'plus', 'extra', or 'mega'. Omit to search all policies.", required: false), + handler: async (args, ct) => + { + var query = (args.Value("query") ?? "").Trim(); + var tier = (args.Value("tier_code") ?? "").Trim().ToLowerInvariant(); + var terms = Tokenize(query); + + var scored = new List(); + foreach (var p in Passages) + { + var pTier = p["tier"]?.ToString() ?? "all"; + if (tier.Length > 0 && pTier != "all" && pTier != tier) + continue; + + var hay = ((p["keywords"]?.ToString() ?? "") + " " + (p["text"]?.ToString() ?? "")).ToLowerInvariant(); + int score = 0; + foreach (var t in terms) + if (hay.Contains(t)) score++; + if (tier.Length > 0 && pTier == tier) score += 2; // tier match boost + + if (score > 0) + { + scored.Add(new JObject + { + ["id"] = p["id"], + ["document"] = p["doc"], + ["section"] = p["section"], + ["tier"] = pTier, + ["text"] = p["text"], + ["score"] = score + }); + } + } + + var top = scored.OrderByDescending(x => (int)x["score"]).Take(3).ToList(); + if (top.Count == 0) + { + return new JObject + { + ["query"] = query, + ["matches"] = new JArray(), + ["note"] = "No matching policy passage. Try mentioning the tier (Plus, Plus Extra, MEGA) or a topic like 'refund', 'return window', or 'BlastPoints'." + }; + } + + var results = new JArray(); + foreach (var m in top) results.Add(m); + return new JObject { ["query"] = query, ["matches"] = results }; + }); + + // 2. get_tier_refund_policy — structured refund rule for a tier + handler.AddTool("get_tier_refund_policy", + "Return the exact, structured membership refund rule for a BlastPass tier: the cooling-off window, the proration method, the cancellation fee, and any non-refundable credit. Use this once the tier is known so the refund can be calculated precisely.", + schemaConfig: s => s + .String("tier_code", "The tier: 'plus' (BlastPass Plus), 'extra' (BlastPass Plus Extra), or 'mega' (BlastPass Plus Extra MEGA!!!).", required: true), + handler: async (args, ct) => + { + var tier = (args.Value("tier_code") ?? "").Trim().ToLowerInvariant(); + switch (tier) + { + case "plus": + return RefundRule("BlastPass Plus", 79.99, 0.00, 0.00); + case "extra": + return RefundRule("BlastPass Plus Extra", 129.99, 10.00, 0.00); + case "mega": + return RefundRule("BlastPass Plus Extra MEGA!!!", 199.99, 25.00, 20.00); + default: + throw new ArgumentException("Unknown tier_code \"" + tier + "\". Use 'plus', 'extra', or 'mega'."); + } + }); + + // 3. get_markdown_policy + handler.AddTool("get_markdown_policy", + "Return the store's pricing markdown guardrails. Pass markdown_type='ask' FIRST to get the clarifying question the manager must answer (promo vs clearance). Once known, pass markdown_type='promo' (temporary promotion: capped discount, margin floor, no clearance) or 'clearance' (discontinuation: no cap/floor, requires a manager flag). Pass the returned guardrails into the markdown-optimizer; never hardcode them.", + schemaConfig: s => s + .String("markdown_type", "The markdown type: 'promo' (temporary promotion), 'clearance' (discontinuation), or 'ask' to receive the clarifying question to put to the manager.", required: true), + handler: async (args, ct) => + { + var type = (args.Value("markdown_type") ?? "").Trim().ToLowerInvariant(); + switch (type) + { + case "": + case "ask": + return new JObject + { + ["needs_clarification"] = true, + ["question"] = "Are these markdowns a temporary end-of-quarter PROMOTION, or a CLEARANCE because we're discontinuing the items? Promotions are capped and must stay above the margin floor; clearance has no floor but needs manager approval.", + ["options"] = new JArray("promo", "clearance"), + ["rule"] = "The markdown type determines the discount cap and margin floor, so confirm it with the manager before recommending any discounts." + }; + case "promo": + return new JObject + { + ["markdown_type"] = "promo", + ["max_discount_pct"] = 30, + ["margin_floor_pct"] = 15, + ["min_useful_promo_discount_pct"] = 10, + ["clearance_allowed"] = false, + ["requires_manager_flag"] = false, + ["rule"] = "Temporary promotion: discount each item by up to 30% off its current price, but never below a 15% gross margin. If the deepest discount allowed by the margin floor is under min_useful_promo_discount_pct (10%), the item cannot be meaningfully promoted and should be flagged for clearance instead." + }; + case "clearance": + return new JObject + { + ["markdown_type"] = "clearance", + ["max_discount_pct"] = null, + ["margin_floor_pct"] = null, + ["min_useful_promo_discount_pct"] = null, + ["clearance_allowed"] = true, + ["requires_manager_flag"] = true, + ["rule"] = "Clearance / discontinuation: no discount cap and no margin floor (may sell below cost to clear), but every clearance markdown requires explicit manager approval (manager flag)." + }; + default: + return new JObject + { + ["needs_clarification"] = true, + ["question"] = "I didn't recognize that markdown type. Is this a temporary PROMOTION or a CLEARANCE (discontinuation)?", + ["options"] = new JArray("promo", "clearance") + }; + } + }); + } + + private static JObject RefundRule(string name, double annual, double fee, double credit) + { + return new JObject + { + ["tier"] = name, + ["annual_price"] = annual, + ["monthly_value"] = Math.Round(annual / 12.0, 4), + ["term_months"] = 12, + ["cooling_off_days"] = 14, + ["cooling_off_max_hours_streamed"] = 2, + ["cooling_off_rule"] = "If cancelled within 14 days of activation AND under 2 hours streamed, issue a FULL refund of the annual price (no fee).", + ["proration_rule"] = "Otherwise refund = whole UNUSED months remaining x (annual_price / 12), then subtract the cancellation fee and any non-refundable credit; never below $0.", + ["cancellation_fee"] = fee, + ["nonrefundable_credit"] = credit, + ["prorated_refund_allowed"] = true + }; + } + + // Naive tokenizer: lowercase, split on non-alphanumeric, drop short/stop words. + private static List Tokenize(string s) + { + var stop = new HashSet { "the","a","an","is","are","for","to","of","and","or","my","i","do","does","can","what","whats","how","on","in","it","be","with","please" }; + var sb = new StringBuilder(); + foreach (var ch in (s ?? "").ToLowerInvariant()) + sb.Append(char.IsLetterOrDigit(ch) ? ch : ' '); + var tokens = new List(); + foreach (var w in sb.ToString().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) + if (w.Length >= 3 && !stop.Contains(w) && !tokens.Contains(w)) + tokens.Add(w); + return tokens; + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json new file mode 100644 index 0000000..289294e --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Policy RAG MCP v2", + "description": "BlastPass Policy Knowledge (v2) for BlastBox Omega. Tools: search_policy, get_tier_refund_policy, get_markdown_policy. Runs entirely inside the connector.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Policy RAG MCP v2 Server", + "description": "MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy, get_markdown_policy.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_connectionparameters.json b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_connectionparameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_connectionparameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx new file mode 100644 index 0000000..18dbcb2 --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_customcodeblobcontent.csx @@ -0,0 +1,1209 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +// ╔══════════════════════════════════════════════════════════════════════════════╗ +// ║ Warehouse MCP Connector (inline) ║ +// ║ ║ +// ║ 7 tools with static mock data — no external server needed. ║ +// ║ Based on Power MCP Template v2.1 by Troy Taylor. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +public class Script : ScriptBase +{ + + + private static readonly McpServerOptions Options = new McpServerOptions + { + ServerInfo = new McpServerInfo + { + Name = "warehouse-fulfillment", + Version = "1.0.0", + Title = "Warehouse & Fulfillment", + Description = "Warehouse inventory and fulfillment tools for checking stock, tracking fulfillment pipeline stages, finding product alternatives, and looking up restock dates." + }, + ProtocolVersion = "2025-11-25", + Capabilities = new McpCapabilities { Tools = true }, + Instructions = "Use check_stock to look up inventory for a product SKU. If out of stock, use get_restock_date for restock info and find_alternatives for similar products. Use get_fulfillment_status with an order_id to check where an order is in the warehouse pipeline. Use check_game_compatibility to find which BlastBox Omega model a game requires. Use get_inventory_aging to see how long a product's stock has been sitting and whether more is inbound, to inform markdown/clearance decisions. Use get_console_exclusives to list the MEGA-only AAA titles (with an associate upsell pitch) when helping a customer decide between the base console and the MEGA Edition." + }; + + public override async Task ExecuteAsync() + { + var handler = new McpRequestHandler(Options); + RegisterCapabilities(handler); + + var body = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = await handler.HandleAsync(body, this.CancellationToken).ConfigureAwait(false); + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "application/json") + }; + } + + // ── Static Data ───────────────────────────────────────────────────── + + private static readonly JArray Stock = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""category"":""console"",""price"":399.99,""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-20"",""availableForShipping"":false}, + {""sku"":""SKU-OMEGA-MEGA"",""name"":""BlastBox Omega MEGA Edition"",""category"":""console"",""price"":499.99,""quantity"":5,""warehouse"":""Seattle-WA1"",""aisle"":""E-21"",""availableForShipping"":true}, + {""sku"":""SKU-APEX5"",""name"":""Apex NoiseGuard 5 Headphones"",""category"":""electronics"",""quantity"":3,""warehouse"":""Seattle-WA1"",""aisle"":""E-14"",""availableForShipping"":true}, + {""sku"":""SKU-USBC3"",""name"":""USB-C Charging Cable (3-pack)"",""category"":""electronics"",""quantity"":142,""warehouse"":""Seattle-WA1"",""aisle"":""A-02"",""availableForShipping"":true}, + {""sku"":""SKU-LUMI"",""name"":""LumiRead E-Reader (16GB)"",""category"":""electronics"",""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-08"",""availableForShipping"":false}, + {""sku"":""SKU-PULSE"",""name"":""PulseWave Pro Earbuds"",""category"":""electronics"",""quantity"":17,""warehouse"":""Seattle-WA1"",""aisle"":""E-12"",""availableForShipping"":true}, + {""sku"":""SKU-VORTEX"",""name"":""Vortex Gaming Console OLED"",""category"":""electronics"",""quantity"":0,""warehouse"":""Chicago-IL1"",""aisle"":""E-22"",""availableForShipping"":false}, + {""sku"":""SKU-REALM"",""name"":""Realm of Legends: The Lost Crown"",""category"":""electronics"",""quantity"":24,""warehouse"":""Chicago-IL1"",""aisle"":""G-05"",""availableForShipping"":true}, + {""sku"":""SKU-APEX4"",""name"":""Apex NoiseGuard 4 Headphones (Previous Gen)"",""category"":""electronics"",""quantity"":8,""warehouse"":""Seattle-WA1"",""aisle"":""E-14"",""availableForShipping"":true}, + {""sku"":""SKU-CLAR7"",""name"":""Clarity NC700 Headphones"",""category"":""electronics"",""quantity"":6,""warehouse"":""Seattle-WA1"",""aisle"":""E-15"",""availableForShipping"":true}, + {""sku"":""SKU-PWMAX"",""name"":""PulseWave Max Headphones - Space Gray"",""category"":""electronics"",""quantity"":2,""warehouse"":""Seattle-WA1"",""aisle"":""E-13"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE"",""name"":""TrailMark Fleece Hoodie - Black (L)"",""category"":""clothing"",""quantity"":4,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE-XL"",""name"":""TrailMark Fleece Hoodie - Black (XL)"",""category"":""clothing"",""quantity"":7,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-HOODIE-GRY"",""name"":""TrailMark Fleece Hoodie - Grey (L)"",""category"":""clothing"",""quantity"":2,""warehouse"":""Dallas-TX1"",""aisle"":""C-07"",""availableForShipping"":true}, + {""sku"":""SKU-JOGGER"",""name"":""TrailMark Sport Joggers - Grey (L)"",""category"":""clothing"",""quantity"":11,""warehouse"":""Dallas-TX1"",""aisle"":""C-09"",""availableForShipping"":true}, + {""sku"":""SKU-STAR2"",""name"":""Starfall: Part Two (4K Blu-ray)"",""category"":""media"",""quantity"":9,""warehouse"":""Chicago-IL1"",""aisle"":""M-03"",""availableForShipping"":true}, + {""sku"":""SKU-COMFT"",""name"":""ComfortEdge Pro - Matte Black"",""category"":""furniture"",""quantity"":1,""warehouse"":""Seattle-WA1"",""aisle"":""F-01"",""availableForShipping"":true}, + {""sku"":""SKU-COMFT-W"",""name"":""ComfortEdge Pro - White"",""category"":""furniture"",""quantity"":3,""warehouse"":""Seattle-WA1"",""aisle"":""F-01"",""availableForShipping"":true}, + {""sku"":""SKU-DESKMAT"",""name"":""Felt Desk Mat - Dark Grey (90x40cm)"",""category"":""furniture"",""quantity"":30,""warehouse"":""Seattle-WA1"",""aisle"":""F-04"",""availableForShipping"":true} + ]"); + + private static readonly JObject Fulfillment = JObject.Parse(@"{ + ""ORD-10422"":{""order_id"":""ORD-10422"",""warehouse"":""Chicago-IL1"",""assigned_worker"":""Mike Torres"",""current_stage"":""received"",""estimated_ship_date"":""2026-04-23"",""notes"":""LumiRead E-Reader — awaiting restock. Item reserved from incoming shipment."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-15T09:30:00Z""}]}, + ""ORD-10460"":{""order_id"":""ORD-10460"",""warehouse"":""Chicago-IL1"",""assigned_worker"":""Lisa Park"",""current_stage"":""picked"",""estimated_ship_date"":""2026-04-21"",""notes"":""Vortex Console on backorder, Realm of Legends picked, holding for bundle ship."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-18T10:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-19T14:20:00Z""}]}, + ""ORD-10421"":{""order_id"":""ORD-10421"",""warehouse"":""Seattle-WA1"",""assigned_worker"":""Carlos Mendez"",""current_stage"":""handed_to_carrier"",""estimated_ship_date"":""2026-04-13"",""notes"":""Shipped via SwiftShip. Two items in single box."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-11T08:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-11T11:30:00Z""},{""stage"":""packed"",""completed_at"":""2026-04-12T09:15:00Z""},{""stage"":""labeled"",""completed_at"":""2026-04-12T10:00:00Z""},{""stage"":""handed_to_carrier"",""completed_at"":""2026-04-13T08:45:00Z""}]}, + ""ORD-10455"":{""order_id"":""ORD-10455"",""warehouse"":""Dallas-TX1"",""assigned_worker"":""Rachel Kim"",""current_stage"":""handed_to_carrier"",""estimated_ship_date"":""2026-04-14"",""notes"":""Shipped via PrimeFreight. Hoodie + joggers in one package."",""pipeline"":[{""stage"":""received"",""completed_at"":""2026-04-12T11:00:00Z""},{""stage"":""picked"",""completed_at"":""2026-04-13T09:00:00Z""},{""stage"":""packed"",""completed_at"":""2026-04-13T14:30:00Z""},{""stage"":""labeled"",""completed_at"":""2026-04-14T08:00:00Z""},{""stage"":""handed_to_carrier"",""completed_at"":""2026-04-14T15:00:00Z""}]} + }"); + + private static readonly JArray Restock = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA"",""name"":""BlastBox Omega Console"",""current_quantity"":0,""next_shipment_date"":""2026-06-11"",""expected_quantity"":40,""supplier"":""BlastBox Manufacturing"",""notes"":""Standard replenishment — next batch arrives in about 4 days. MEGA Edition is in stock now as an upgrade alternative.""}, + {""sku"":""SKU-LUMI"",""name"":""LumiRead E-Reader (16GB)"",""current_quantity"":0,""next_shipment_date"":""2026-04-22"",""expected_quantity"":50,""supplier"":""LumiRead Distribution Co."",""notes"":""Delayed from original April 18 date. Supplier confirmed new ETA.""}, + {""sku"":""SKU-VORTEX"",""name"":""Vortex Gaming Console OLED"",""current_quantity"":0,""next_shipment_date"":""2026-04-28"",""expected_quantity"":30,""supplier"":""Vortex Games Inc."",""notes"":""High demand — limited allocation. Next batch after this is mid-May.""}, + {""sku"":""SKU-APEX5"",""name"":""Apex NoiseGuard 5 Headphones"",""current_quantity"":3,""next_shipment_date"":""2026-05-05"",""expected_quantity"":20,""supplier"":""Apex Audio Corp."",""notes"":""Regular replenishment cycle. Current stock sufficient for 1-2 weeks.""}, + {""sku"":""SKU-COMFT"",""name"":""ComfortEdge Pro - Matte Black"",""current_quantity"":1,""next_shipment_date"":""2026-04-30"",""expected_quantity"":10,""supplier"":""ComfortEdge Furniture Co."",""notes"":""Low stock alert triggered. Express shipment arranged.""} + ]"); + + private static readonly JArray Games = JArray.Parse(@"[ + {""title"":""MEGA Lizards from Outer Space"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA title — requires the MEGA Edition GPU. Will NOT run on the base BlastBox Omega.""}, + {""title"":""Galactic Tax Evader VII: Audit Protocol"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA open-world heist sim — the real-time audit physics only run on the MEGA Edition co-processor.""}, + {""title"":""Mecha-Granny: Knitpocalypse"",""required_model"":""BlastBox Omega MEGA Edition"",""required_sku"":""SKU-OMEGA-MEGA"",""runs_on_base"":false,""note"":""AAA action roguelite — the 4K yarn engine requires the MEGA Edition. Will NOT run on the base console.""}, + {""title"":""Realm of Legends: The Lost Crown"",""required_model"":""BlastBox Omega Console"",""required_sku"":""SKU-OMEGA"",""runs_on_base"":true,""note"":""Runs on any BlastBox Omega model, including the base console.""}, + {""title"":""Galaxy Smash"",""required_model"":""BlastBox Omega Console"",""required_sku"":""SKU-OMEGA"",""runs_on_base"":true,""note"":""Runs on any BlastBox Omega model.""} + ]"); + + // Exclusive AAA titles per console model, with a ready-to-say associate upsell pitch. + private static readonly JObject Exclusives = JObject.Parse(@"{ + ""SKU-OMEGA-MEGA"":{ + ""model"":""BlastBox Omega MEGA Edition"", + ""sku"":""SKU-OMEGA-MEGA"", + ""tagline"":""Three AAA exclusives the base console literally can't load."", + ""titles"":[ + {""title"":""MEGA Lizards from Outer Space"",""genre"":""Co-op chaos shooter"",""pitch"":""Picture two hundred neon space-lizards on screen at once and a co-op buddy screaming next to you. It's the #1 couch-chaos shooter of the year — and it physically will not run on the base console.""}, + {""title"":""Galactic Tax Evader VII: Audit Protocol"",""genre"":""Open-world heist sim"",""pitch"":""You're an interstellar accountant on the run, dodging audits in real time across a living open galaxy. Critics call it 'Grand Theft Spreadsheet.' MEGA-exclusive, because the audit physics melt anything smaller.""}, + {""title"":""Mecha-Granny: Knitpocalypse"",""genre"":""Action roguelite"",""pitch"":""A sweet old lady in a battle-mech knits the apocalypse back together, one 4K scarf at a time. It's hilarious, it's brutal, and the yarn engine only spins up on MEGA.""} + ] + }, + ""SKU-OMEGA"":{ + ""model"":""BlastBox Omega Console"", + ""sku"":""SKU-OMEGA"", + ""tagline"":""Plays the full shared BlastBox library, but none of the MEGA-only AAA exclusives."", + ""titles"":[] + } + }"); + + // Inventory aging / inbound status for merchandising markdown decisions. + private static readonly JArray Aging = JArray.Parse(@"[ + {""sku"":""SKU-OMEGA-MEGA"",""name"":""BlastBox Omega MEGA Edition"",""weeks_in_stock"":3,""received_date"":""2026-05-11"",""inbound_po"":true,""next_inbound_date"":""2026-06-15"",""aging_bucket"":""fresh"",""note"":""Hot seller. Replenishment PO inbound.""}, + {""sku"":""SKU-MEGALIZARDS"",""name"":""MEGA Lizards from Outer Space"",""weeks_in_stock"":4,""received_date"":""2026-05-04"",""inbound_po"":true,""next_inbound_date"":""2026-06-12"",""aging_bucket"":""fresh"",""note"":""AAA title driving MEGA Edition attach. Reorder inbound.""}, + {""sku"":""SKU-PULSE-CTRL"",""name"":""PulseGrip Pro Controller"",""weeks_in_stock"":5,""received_date"":""2026-04-27"",""inbound_po"":true,""next_inbound_date"":""2026-06-20"",""aging_bucket"":""fresh"",""note"":""Steady accessory attach.""}, + {""sku"":""SKU-OMEGA-CORE"",""name"":""BlastBox Omega Core (1st-gen)"",""weeks_in_stock"":16,""received_date"":""2026-02-16"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""aging"",""note"":""Being phased out by the MEGA Edition. No further inbound — candidate for discontinuation/clearance.""}, + {""sku"":""SKU-RETRO-CADET"",""name"":""BlastBox Cadet Bundle"",""weeks_in_stock"":22,""received_date"":""2026-01-05"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""stale"",""note"":""Last-gen bundle. No inbound. Strong markdown candidate.""}, + {""sku"":""SKU-GALAXY-SMASH"",""name"":""Galaxy Smash"",""weeks_in_stock"":14,""received_date"":""2026-03-02"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""aging"",""note"":""Overstocked at launch. No inbound. Markdown candidate.""}, + {""sku"":""SKU-VR-GOGGLES"",""name"":""OmegaVision VR Headset"",""weeks_in_stock"":18,""received_date"":""2026-02-02"",""inbound_po"":false,""next_inbound_date"":null,""aging_bucket"":""stale"",""note"":""Slow launch. No inbound. Strong markdown/clearance candidate.""} + ]"); + + // ── Tool Registration ─────────────────────────────────────────────── + + private void RegisterCapabilities(McpRequestHandler handler) + { + // 1. check_stock + handler.AddTool("check_stock", + "Check warehouse inventory for a product by SKU. Returns quantity on hand, warehouse location, aisle, and whether the item is available for shipping. If quantity is 0, use get_restock_date for restock info.", + schemaConfig: s => s.String("sku", "Product SKU (e.g. 'SKU-APEX5'). Use SKUs from order line items.", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + foreach (var item in Stock) + { + if (item["sku"]?.ToString() == sku) + { + var qty = item["quantity"]?.Value() ?? 0; + return new JObject + { + ["sku"] = item["sku"], + ["name"] = item["name"], + ["category"] = item["category"], + ["price"] = item["price"], + ["quantity_on_hand"] = qty, + ["warehouse"] = item["warehouse"], + ["aisle"] = item["aisle"], + ["available_for_shipping"] = item["availableForShipping"], + ["status"] = qty == 0 ? "OUT_OF_STOCK" : qty <= 3 ? "LOW_STOCK" : "IN_STOCK" + }; + } + } + throw new ArgumentException($"SKU \"{sku}\" not found in warehouse inventory."); + }); + + // 2. get_fulfillment_status + handler.AddTool("get_fulfillment_status", + "Get the fulfillment pipeline status for an order. Shows which warehouse stage the order is at (received → picked → packed → labeled → handed_to_carrier), assigned worker, and estimated ship date.", + schemaConfig: s => s.String("order_id", "Order ID (e.g. 'ORD-10422') from the order management system", required: true), + handler: async (args, ct) => + { + var orderId = args.Value("order_id") ?? ""; + if (Fulfillment[orderId] != null) + return JObject.Parse(Fulfillment[orderId].ToString()); + throw new ArgumentException($"No fulfillment record found for order \"{orderId}\". The order may not have entered the warehouse yet."); + }); + + // 3. find_alternatives + handler.AddTool("find_alternatives", + "Find alternative products similar to a given SKU. Returns items in the same category that are in stock. Useful when a customer wants a different size, color, or comparable product, or when an item is out of stock.", + schemaConfig: s => s.String("sku", "Product SKU to find alternatives for (e.g. 'SKU-HOODIE')", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + JToken original = null; + foreach (var item in Stock) + { + if (item["sku"]?.ToString() == sku) { original = item; break; } + } + if (original == null) + throw new ArgumentException($"SKU \"{sku}\" not found in inventory."); + + var category = original["category"]?.ToString(); + var alternatives = new JArray(); + foreach (var item in Stock) + { + if (item["category"]?.ToString() == category && item["sku"]?.ToString() != sku && (item["quantity"]?.Value() ?? 0) > 0) + { + alternatives.Add(new JObject + { + ["sku"] = item["sku"], + ["name"] = item["name"], + ["price"] = item["price"], + ["quantity_available"] = item["quantity"], + ["warehouse"] = item["warehouse"], + ["available_for_shipping"] = item["availableForShipping"] + }); + } + } + + return new JObject + { + ["original"] = new JObject { ["sku"] = original["sku"], ["name"] = original["name"], ["category"] = category }, + ["alternatives"] = alternatives + }; + }); + + // 4. get_restock_date + handler.AddTool("get_restock_date", + "Get the next restock date and supplier info for a product. Use this when check_stock shows an item is out of stock or low stock. Returns expected delivery date, quantity, and supplier details.", + schemaConfig: s => s.String("sku", "Product SKU (e.g. 'SKU-LUMI'). Typically used after check_stock shows low/no stock.", required: true), + handler: async (args, ct) => + { + var sku = args.Value("sku") ?? ""; + foreach (var item in Restock) + { + if (item["sku"]?.ToString() == sku) + return JObject.Parse(item.ToString()); + } + return new JObject { ["message"] = $"No restock schedule found for SKU \"{sku}\". The item may be regularly stocked or discontinued." }; + }); + + // 5. check_game_compatibility + handler.AddTool("check_game_compatibility", + "Check which BlastBox Omega console model a game requires. Use when a customer asks whether a game will run on their console, or when deciding between the base console and the MEGA Edition. Returns the required model/SKU and whether it runs on the base console.", + schemaConfig: s => s.String("game_title", "The game title (e.g. 'MEGA Lizards from Outer Space'). Case-insensitive, partial match allowed.", required: true), + handler: async (args, ct) => + { + var q = (args.Value("game_title") ?? "").Trim().ToLowerInvariant(); + foreach (var g in Games) + { + var title = (g["title"]?.ToString() ?? "").ToLowerInvariant(); + if (title == q || (q.Length > 0 && title.Contains(q))) + return JObject.Parse(g.ToString()); + } + return new JObject { ["message"] = $"No compatibility record found for game \"{args.Value("game_title")}\"." }; + }); + + // 6. get_inventory_aging + handler.AddTool("get_inventory_aging", + "Return inventory aging and inbound-replenishment status for a product SKU: how many weeks the stock has been sitting, its received date, whether there is an inbound purchase order, and an aging bucket (fresh / aging / stale). Use this to judge whether a slow-selling item is a markdown or clearance candidate. Omit sku to return aging for all tracked products.", + schemaConfig: s => s.String("sku", "Optional. Product SKU (e.g. 'SKU-VR-GOGGLES'). Omit to return all tracked products."), + handler: async (args, ct) => + { + var sku = (args.Value("sku") ?? "").Trim(); + if (sku.Length == 0) + return new JObject { ["count"] = Aging.Count, ["items"] = JArray.Parse(Aging.ToString()) }; + foreach (var item in Aging) + { + if (string.Equals(item["sku"]?.ToString(), sku, StringComparison.OrdinalIgnoreCase)) + return JObject.Parse(item.ToString()); + } + return new JObject { ["message"] = $"No aging record found for SKU \"{sku}\"." }; + }); + + // 7. get_console_exclusives + handler.AddTool("get_console_exclusives", + "Return the AAA game titles that run ONLY on a given BlastBox Omega console model, each with a ready-to-say associate upsell pitch. Use this to help an associate upsell a customer to the MEGA Edition: it surfaces the marquee titles the base console cannot run plus a short, attractive blurb to read to the customer. Accepts a SKU (e.g. 'SKU-OMEGA-MEGA') or a model name (e.g. 'mega', 'base').", + schemaConfig: s => s.String("model", "Console SKU or model name. Use 'SKU-OMEGA-MEGA' or 'mega' for the MEGA Edition; 'SKU-OMEGA' or 'base' for the base console.", required: true), + handler: async (args, ct) => + { + var q = (args.Value("model") ?? "").Trim().ToLowerInvariant(); + string key = null; + if (q.Contains("mega")) key = "SKU-OMEGA-MEGA"; + else if (q == "base" || q == "sku-omega" || q.Contains("base")) key = "SKU-OMEGA"; + else if (q == "sku-omega-mega") key = "SKU-OMEGA-MEGA"; + if (key != null && Exclusives[key] != null) + return JObject.Parse(Exclusives[key].ToString()); + return new JObject { ["message"] = $"No console-exclusive list found for model \"{args.Value("model")}\". Try 'mega' or 'base'." }; + }); + } + +} + +// ║ SECTION 2: MCP FRAMEWORK ║ +// ║ ║ +// ║ Built-in McpRequestHandler that brings MCP C# SDK patterns to Power ║ +// ║ Platform. If Microsoft enables the official SDK namespaces, this section ║ +// ║ becomes a using statement instead of inline code. ║ +// ║ ║ +// ║ Spec coverage: MCP 2025-11-25 ║ +// ║ Handles: initialize, ping, tools/*, resources/*, prompts/*, ║ +// ║ completion/complete, logging/setLevel, all notifications ║ +// ║ ║ +// ║ Stateless limitations (Power Platform cannot send async notifications): ║ +// ║ - Tasks (experimental, requires persistent state between requests) ║ +// ║ - Server→client requests (sampling, elicitation, roots/list) ║ +// ║ - Server→client notifications (progress, logging/message, list_changed) ║ +// ║ ║ +// ║ Do not modify unless extending the framework itself. ║ +// ╚══════════════════════════════════════════════════════════════════════════════╝ + +// ── Configuration Types ────────────────────────────────────────────────────── + +/// Server identity reported in initialize response. +public class McpServerInfo +{ + public string Name { get; set; } = "mcp-server"; + public string Version { get; set; } = "1.0.0"; + public string Title { get; set; } + public string Description { get; set; } +} + +/// Capabilities declared during initialization. +public class McpCapabilities +{ + public bool Tools { get; set; } = true; + public bool Resources { get; set; } + public bool Prompts { get; set; } + public bool Logging { get; set; } + public bool Completions { get; set; } +} + +/// Top-level configuration for the MCP handler. +public class McpServerOptions +{ + public McpServerInfo ServerInfo { get; set; } = new McpServerInfo(); + public string ProtocolVersion { get; set; } = "2025-11-25"; + public McpCapabilities Capabilities { get; set; } = new McpCapabilities(); + public string Instructions { get; set; } +} + +// ── Error Handling ─────────────────────────────────────────────────────────── + +/// Standard JSON-RPC 2.0 error codes used by MCP. +public enum McpErrorCode +{ + RequestTimeout = -32000, + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 +} + +/// +/// Throw from tool methods to surface a structured MCP error. +/// Mirrors ModelContextProtocol.McpException from the official SDK. +/// +public class McpException : Exception +{ + public McpErrorCode Code { get; } + public McpException(McpErrorCode code, string message) : base(message) => Code = code; +} + +// ── Schema Builder (Fluent API) ────────────────────────────────────────────── + +/// Fluent builder for JSON Schema objects used in tool inputSchema. +public class McpSchemaBuilder +{ + private readonly JObject _properties = new JObject(); + private readonly JArray _required = new JArray(); + + public McpSchemaBuilder String(string name, string description, bool required = false, string format = null, string[] enumValues = null) + { + var prop = new JObject { ["type"] = "string", ["description"] = description }; + if (format != null) prop["format"] = format; + if (enumValues != null) prop["enum"] = new JArray(enumValues); + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Integer(string name, string description, bool required = false, int? defaultValue = null) + { + var prop = new JObject { ["type"] = "integer", ["description"] = description }; + if (defaultValue.HasValue) prop["default"] = defaultValue.Value; + _properties[name] = prop; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Number(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "number", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Boolean(string name, string description, bool required = false) + { + _properties[name] = new JObject { ["type"] = "boolean", ["description"] = description }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Array(string name, string description, JObject itemSchema, bool required = false) + { + _properties[name] = new JObject + { + ["type"] = "array", + ["description"] = description, + ["items"] = itemSchema + }; + if (required) _required.Add(name); + return this; + } + + public McpSchemaBuilder Object(string name, string description, Action nestedConfig, bool required = false) + { + var nested = new McpSchemaBuilder(); + nestedConfig?.Invoke(nested); + var obj = nested.Build(); + obj["description"] = description; + _properties[name] = obj; + if (required) _required.Add(name); + return this; + } + + public JObject Build() + { + var schema = new JObject + { + ["type"] = "object", + ["properties"] = _properties + }; + if (_required.Count > 0) schema["required"] = _required; + return schema; + } +} + +// ── Internal Tool Registration ─────────────────────────────────────────────── + +internal class McpToolDefinition +{ + public string Name { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public JObject InputSchema { get; set; } + public JObject OutputSchema { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Resource Registration ─────────────────────────────────────────── + +internal class McpResourceDefinition +{ + public string Uri { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +internal class McpResourceTemplateDefinition +{ + public string UriTemplate { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string MimeType { get; set; } + public JObject Annotations { get; set; } + public Func> Handler { get; set; } +} + +// ── Internal Prompt Registration ───────────────────────────────────────────── + +/// Describes a single prompt argument. +public class McpPromptArgument +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Required { get; set; } +} + +internal class McpPromptDefinition +{ + public string Name { get; set; } + public string Description { get; set; } + public List Arguments { get; set; } = new List(); + public Func> Handler { get; set; } +} + +// ── McpRequestHandler ──────────────────────────────────────────────────────── +// +// The core bridge class. Stateless, no DI, no ASP.NET Core. +// Takes a JSON-RPC string in → returns a JSON-RPC string out. +// This is the class that does not exist in the official SDK today. +// + +/// +/// Stateless MCP request handler that bridges the official SDK's patterns +/// to Power Platform's ScriptBase.ExecuteAsync() model. +/// +/// Handles all JSON-RPC 2.0 routing, protocol negotiation, tool discovery, +/// parameter binding, and response formatting internally. +/// +public class McpRequestHandler +{ + private readonly McpServerOptions _options; + private readonly Dictionary _tools; + private readonly Dictionary _resources; + private readonly List _resourceTemplates; + private readonly Dictionary _prompts; + + /// + /// Optional logging callback. Wire this up to Application Insights, + /// Context.Logger, or any other telemetry sink. + /// + public Action OnLog { get; set; } + + public McpRequestHandler(McpServerOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resources = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resourceTemplates = new List(); + _prompts = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + // ── Tool Registration ──────────────────────────────────────────────── + + /// + /// Register a tool using the fluent API. + /// Define the schema with McpSchemaBuilder, provide a handler, and optionally set annotations. + /// + public McpRequestHandler AddTool( + string name, + string description, + Action schemaConfig, + Func> handler, + Action annotationsConfig = null, + string title = null, + Action outputSchemaConfig = null) + { + var builder = new McpSchemaBuilder(); + schemaConfig?.Invoke(builder); + + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + JObject outputSchema = null; + if (outputSchemaConfig != null) + { + var outBuilder = new McpSchemaBuilder(); + outputSchemaConfig(outBuilder); + outputSchema = outBuilder.Build(); + } + + _tools[name] = new McpToolDefinition + { + Name = name, + Title = title, + Description = description, + InputSchema = builder.Build(), + OutputSchema = outputSchema, + Annotations = annotations, + Handler = async (args, ct) => await handler(args, ct).ConfigureAwait(false) + }; + + return this; + } + + // ── Resource Registration ───────────────────────────────────────────── + + /// + /// Register a static resource. The handler returns the resource contents + /// as a JArray of {uri, text, mimeType} or {uri, blob, mimeType} objects. + /// + public McpRequestHandler AddResource( + string uri, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resources[uri] = new McpResourceDefinition + { + Uri = uri, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }; + + return this; + } + + /// + /// Register a resource template. The handler receives the resolved URI + /// and returns the resource contents as a JArray. + /// + public McpRequestHandler AddResourceTemplate( + string uriTemplate, + string name, + string description, + Func> handler, + string mimeType = "application/json", + Action annotationsConfig = null) + { + JObject annotations = null; + if (annotationsConfig != null) + { + annotations = new JObject(); + annotationsConfig(annotations); + } + + _resourceTemplates.Add(new McpResourceTemplateDefinition + { + UriTemplate = uriTemplate, + Name = name, + Description = description, + MimeType = mimeType, + Annotations = annotations, + Handler = handler + }); + + return this; + } + + // ── Prompt Registration ────────────────────────────────────────────── + + /// + /// Register a prompt. The handler receives the argument values as a JObject + /// and returns a JArray of message objects ({role, content: {type, text}}). + /// + public McpRequestHandler AddPrompt( + string name, + string description, + List arguments, + Func> handler) + { + _prompts[name] = new McpPromptDefinition + { + Name = name, + Description = description, + Arguments = arguments ?? new List(), + Handler = handler + }; + + return this; + } + + // ── Main Handler ───────────────────────────────────────────────────── + + /// + /// Process a raw JSON-RPC 2.0 request string and return a JSON-RPC response string. + /// This is the single method that bridges the gap. + /// + public async Task HandleAsync(string body, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(body)) + return SerializeError(null, McpErrorCode.InvalidRequest, "Empty request body"); + + JObject request; + try + { + request = JObject.Parse(body); + } + catch (JsonException) + { + return SerializeError(null, McpErrorCode.ParseError, "Invalid JSON"); + } + + var method = request.Value("method") ?? string.Empty; + var id = request["id"]; + + Log("McpRequestReceived", new { Method = method, HasId = id != null }); + + try + { + switch (method) + { + // Core initialization + case "initialize": + return HandleInitialize(id, request); + + // Notifications — Copilot Studio requires valid JSON-RPC for ALL requests + case "initialized": + case "notifications/initialized": + case "notifications/cancelled": + case "notifications/roots/list_changed": + return SerializeSuccess(id, new JObject()); + + // Health check + case "ping": + return SerializeSuccess(id, new JObject()); + + // Tools + case "tools/list": + return HandleToolsList(id); + + case "tools/call": + return await HandleToolsCallAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Resources + case "resources/list": + return HandleResourcesList(id); + + case "resources/templates/list": + return HandleResourceTemplatesList(id); + + case "resources/read": + return await HandleResourcesReadAsync(id, request, cancellationToken).ConfigureAwait(false); + + case "resources/subscribe": + case "resources/unsubscribe": + return SerializeSuccess(id, new JObject()); + + // Prompts + case "prompts/list": + return HandlePromptsList(id); + + case "prompts/get": + return await HandlePromptsGetAsync(id, request, cancellationToken).ConfigureAwait(false); + + // Completions + case "completion/complete": + return SerializeSuccess(id, new JObject + { + ["completion"] = new JObject + { + ["values"] = new JArray(), + ["total"] = 0, + ["hasMore"] = false + } + }); + + // Logging level + case "logging/setLevel": + return SerializeSuccess(id, new JObject()); + + default: + Log("McpMethodNotFound", new { Method = method }); + return SerializeError(id, McpErrorCode.MethodNotFound, "Method not found", method); + } + } + catch (McpException ex) + { + Log("McpError", new { Method = method, Code = (int)ex.Code, Message = ex.Message }); + return SerializeError(id, ex.Code, ex.Message); + } + catch (Exception ex) + { + Log("McpError", new { Method = method, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // ── Protocol Handlers ──────────────────────────────────────────────── + + private string HandleInitialize(JToken id, JObject request) + { + var clientProtocolVersion = request["params"]?["protocolVersion"]?.ToString() + ?? _options.ProtocolVersion; + + var capabilities = new JObject(); + if (_options.Capabilities.Tools) + capabilities["tools"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Resources) + capabilities["resources"] = new JObject { ["subscribe"] = false, ["listChanged"] = false }; + if (_options.Capabilities.Prompts) + capabilities["prompts"] = new JObject { ["listChanged"] = false }; + if (_options.Capabilities.Logging) + capabilities["logging"] = new JObject(); + if (_options.Capabilities.Completions) + capabilities["completions"] = new JObject(); + + var serverInfo = new JObject + { + ["name"] = _options.ServerInfo.Name, + ["version"] = _options.ServerInfo.Version + }; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Title)) + serverInfo["title"] = _options.ServerInfo.Title; + if (!string.IsNullOrWhiteSpace(_options.ServerInfo.Description)) + serverInfo["description"] = _options.ServerInfo.Description; + + var result = new JObject + { + ["protocolVersion"] = clientProtocolVersion, + ["capabilities"] = capabilities, + ["serverInfo"] = serverInfo + }; + + if (!string.IsNullOrWhiteSpace(_options.Instructions)) + result["instructions"] = _options.Instructions; + + Log("McpInitialized", new + { + Server = _options.ServerInfo.Name, + Version = _options.ServerInfo.Version, + ProtocolVersion = clientProtocolVersion + }); + + return SerializeSuccess(id, result); + } + + private string HandleToolsList(JToken id) + { + var toolsArray = new JArray(); + foreach (var tool in _tools.Values) + { + var toolObj = new JObject + { + ["name"] = tool.Name, + ["description"] = tool.Description, + ["inputSchema"] = tool.InputSchema + }; + if (!string.IsNullOrWhiteSpace(tool.Title)) + toolObj["title"] = tool.Title; + if (tool.OutputSchema != null) + toolObj["outputSchema"] = tool.OutputSchema; + if (tool.Annotations != null && tool.Annotations.Count > 0) + toolObj["annotations"] = tool.Annotations; + toolsArray.Add(toolObj); + } + + Log("McpToolsListed", new { Count = _tools.Count }); + return SerializeSuccess(id, new JObject { ["tools"] = toolsArray }); + } + + private string HandleResourcesList(JToken id) + { + var resourcesArray = new JArray(); + foreach (var res in _resources.Values) + { + var obj = new JObject + { + ["uri"] = res.Uri, + ["name"] = res.Name + }; + if (!string.IsNullOrWhiteSpace(res.Description)) + obj["description"] = res.Description; + if (!string.IsNullOrWhiteSpace(res.MimeType)) + obj["mimeType"] = res.MimeType; + if (res.Annotations != null && res.Annotations.Count > 0) + obj["annotations"] = res.Annotations; + resourcesArray.Add(obj); + } + + Log("McpResourcesListed", new { Count = _resources.Count }); + return SerializeSuccess(id, new JObject { ["resources"] = resourcesArray }); + } + + private string HandleResourceTemplatesList(JToken id) + { + var templatesArray = new JArray(); + foreach (var tmpl in _resourceTemplates) + { + var obj = new JObject + { + ["uriTemplate"] = tmpl.UriTemplate, + ["name"] = tmpl.Name + }; + if (!string.IsNullOrWhiteSpace(tmpl.Description)) + obj["description"] = tmpl.Description; + if (!string.IsNullOrWhiteSpace(tmpl.MimeType)) + obj["mimeType"] = tmpl.MimeType; + if (tmpl.Annotations != null && tmpl.Annotations.Count > 0) + obj["annotations"] = tmpl.Annotations; + templatesArray.Add(obj); + } + + Log("McpResourceTemplatesListed", new { Count = _resourceTemplates.Count }); + return SerializeSuccess(id, new JObject { ["resourceTemplates"] = templatesArray }); + } + + private async Task HandleResourcesReadAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var uri = paramsObj?.Value("uri"); + + if (string.IsNullOrWhiteSpace(uri)) + return SerializeError(id, McpErrorCode.InvalidParams, "Resource URI is required"); + + // 1. Try exact match on registered static resources + if (_resources.TryGetValue(uri, out var resource)) + { + Log("McpResourceReadStarted", new { Uri = uri }); + try + { + var contents = await resource.Handler(ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + // 2. Try matching against registered resource templates + foreach (var tmpl in _resourceTemplates) + { + if (MatchesUriTemplate(tmpl.UriTemplate, uri)) + { + Log("McpResourceReadStarted", new { Uri = uri, Template = tmpl.UriTemplate }); + try + { + var contents = await tmpl.Handler(uri, ct).ConfigureAwait(false); + Log("McpResourceReadCompleted", new { Uri = uri }); + return SerializeSuccess(id, new JObject { ["contents"] = contents }); + } + catch (Exception ex) + { + Log("McpResourceReadError", new { Uri = uri, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + } + + return SerializeError(id, McpErrorCode.InvalidParams, $"Resource not found: {uri}"); + } + + /// + /// Simple URI template matcher. Checks if a concrete URI matches a template + /// with {param} placeholders (e.g., "data://records/{id}" matches "data://records/123"). + /// + private static bool MatchesUriTemplate(string template, string uri) + { + // Split both on '/' and compare segments + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return false; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) continue; // wildcard + if (!string.Equals(seg, uriParts[i], StringComparison.OrdinalIgnoreCase)) return false; + } + return true; + } + + /// + /// Extract named parameters from a URI given a template pattern. + /// E.g., template "data://records/{id}" with uri "data://records/123" returns { "id": "123" }. + /// + public static Dictionary ExtractUriParameters(string template, string uri) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var templateParts = template.Split('/'); + var uriParts = uri.Split('/'); + + if (templateParts.Length != uriParts.Length) return result; + + for (int i = 0; i < templateParts.Length; i++) + { + var seg = templateParts[i]; + if (seg.StartsWith("{") && seg.EndsWith("}")) + { + var paramName = seg.Substring(1, seg.Length - 2); + result[paramName] = uriParts[i]; + } + } + return result; + } + + private string HandlePromptsList(JToken id) + { + var promptsArray = new JArray(); + foreach (var prompt in _prompts.Values) + { + var obj = new JObject + { + ["name"] = prompt.Name + }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + obj["description"] = prompt.Description; + + if (prompt.Arguments.Count > 0) + { + var argsArray = new JArray(); + foreach (var arg in prompt.Arguments) + { + var argObj = new JObject { ["name"] = arg.Name }; + if (!string.IsNullOrWhiteSpace(arg.Description)) + argObj["description"] = arg.Description; + if (arg.Required) + argObj["required"] = true; + argsArray.Add(argObj); + } + obj["arguments"] = argsArray; + } + + promptsArray.Add(obj); + } + + Log("McpPromptsListed", new { Count = _prompts.Count }); + return SerializeSuccess(id, new JObject { ["prompts"] = promptsArray }); + } + + private async Task HandlePromptsGetAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var promptName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(promptName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Prompt name is required"); + + if (!_prompts.TryGetValue(promptName, out var prompt)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Prompt not found: {promptName}"); + + Log("McpPromptGetStarted", new { Prompt = promptName }); + + try + { + var messages = await prompt.Handler(arguments, ct).ConfigureAwait(false); + Log("McpPromptGetCompleted", new { Prompt = promptName, MessageCount = messages.Count }); + + var result = new JObject { ["messages"] = messages }; + if (!string.IsNullOrWhiteSpace(prompt.Description)) + result["description"] = prompt.Description; + + return SerializeSuccess(id, result); + } + catch (Exception ex) + { + Log("McpPromptGetError", new { Prompt = promptName, Error = ex.Message }); + return SerializeError(id, McpErrorCode.InternalError, ex.Message); + } + } + + private async Task HandleToolsCallAsync(JToken id, JObject request, CancellationToken ct) + { + var paramsObj = request["params"] as JObject; + var toolName = paramsObj?.Value("name"); + var arguments = paramsObj?["arguments"] as JObject ?? new JObject(); + + if (string.IsNullOrWhiteSpace(toolName)) + return SerializeError(id, McpErrorCode.InvalidParams, "Tool name is required"); + + if (!_tools.TryGetValue(toolName, out var tool)) + return SerializeError(id, McpErrorCode.InvalidParams, $"Unknown tool: {toolName}"); + + Log("McpToolCallStarted", new { Tool = toolName }); + + try + { + var result = await tool.Handler(arguments, ct).ConfigureAwait(false); + + JObject callResult; + + // Support pre-formatted MCP tool results with rich content types + // (image, audio, resource, or mixed content arrays). + // If the handler returns { "content": [ { "type": "..." } ], ... }, + // pass it through directly instead of wrapping in text. + if (result is JObject jobj && jobj["content"] is JArray contentArray + && contentArray.Count > 0 && contentArray[0]?["type"] != null) + { + callResult = new JObject + { + ["content"] = contentArray, + ["isError"] = jobj.Value("isError") ?? false + }; + if (jobj["structuredContent"] is JObject structured) + callResult["structuredContent"] = structured; + } + else + { + string text; + if (result is JObject plainObj) + text = plainObj.ToString(Newtonsoft.Json.Formatting.Indented); + else if (result is string s) + text = s; + else if (result == null) + text = "{}"; + else + text = JsonConvert.SerializeObject(result, Newtonsoft.Json.Formatting.Indented); + + callResult = new JObject + { + ["content"] = new JArray { new JObject { ["type"] = "text", ["text"] = text } }, + ["isError"] = false + }; + } + + Log("McpToolCallCompleted", new { Tool = toolName, IsError = callResult.Value("isError") }); + return SerializeSuccess(id, callResult); + } + catch (ArgumentException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Invalid arguments: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (McpException ex) + { + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool error: {ex.Message}" } + }, + ["isError"] = true + }); + } + catch (Exception ex) + { + Log("McpToolCallError", new { Tool = toolName, Error = ex.Message }); + + return SerializeSuccess(id, new JObject + { + ["content"] = new JArray + { + new JObject { ["type"] = "text", ["text"] = $"Tool execution failed: {ex.Message}" } + }, + ["isError"] = true + }); + } + } + + // ── Content Helpers ──────────────────────────────────────────────── + // + // Use these to build rich tool results with image, audio, or resource + // content. Return McpRequestHandler.ToolResult(...) from your handler + // to bypass automatic text wrapping. + // + + /// Create a text content item. + public static JObject TextContent(string text) => + new JObject { ["type"] = "text", ["text"] = text }; + + /// Create an image content item (base64-encoded). + public static JObject ImageContent(string base64Data, string mimeType) => + new JObject { ["type"] = "image", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an audio content item (base64-encoded). + public static JObject AudioContent(string base64Data, string mimeType) => + new JObject { ["type"] = "audio", ["data"] = base64Data, ["mimeType"] = mimeType }; + + /// Create an embedded resource content item. + public static JObject ResourceContent(string uri, string text, string mimeType = "text/plain") => + new JObject + { + ["type"] = "resource", + ["resource"] = new JObject { ["uri"] = uri, ["text"] = text, ["mimeType"] = mimeType } + }; + + /// + /// Build a pre-formatted tool result with mixed content types. + /// Return this from a tool handler to bypass automatic text wrapping. + /// + public static JObject ToolResult(JArray content, JObject structuredContent = null, bool isError = false) + { + var result = new JObject { ["content"] = content, ["isError"] = isError }; + if (structuredContent != null) result["structuredContent"] = structuredContent; + return result; + } + + // ── JSON-RPC Serialization ─────────────────────────────────────────── + + private string SerializeSuccess(JToken id, JObject result) + { + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private string SerializeError(JToken id, McpErrorCode code, string message, string data = null) + { + return SerializeError(id, (int)code, message, data); + } + + private string SerializeError(JToken id, int code, string message, string data = null) + { + var error = new JObject + { + ["code"] = code, + ["message"] = message + }; + if (!string.IsNullOrWhiteSpace(data)) + error["data"] = data; + + return new JObject + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = error + }.ToString(Newtonsoft.Json.Formatting.None); + } + + private void Log(string eventName, object data) + { + OnLog?.Invoke(eventName, data); + } +} diff --git a/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_iconblob.Png b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_iconblob.Png new file mode 100644 index 0000000..f417fb8 Binary files /dev/null and b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_iconblob.Png differ diff --git a/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_openapidefinition.json b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_openapidefinition.json new file mode 100644 index 0000000..d624a9f --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_openapidefinition.json @@ -0,0 +1,37 @@ +{ + "swagger": "2.0", + "info": { + "title": "Warehouse MCP", + "description": "Warehouse inventory and fulfillment with tools for checking stock, tracking pipeline stages, finding alternatives, and restock dates. Runs entirely inside the connector \u2014 no external MCP server needed.", + "version": "1.0.0" + }, + "host": "placeholder.azure-apim.net", + "basePath": "/mcp", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/": { + "post": { + "summary": "Warehouse MCP Server", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date.", + "operationId": "InvokeMCP", + "x-ms-agentic-protocol": "mcp-streamable-1.0", + "parameters": [], + "responses": { + "200": { + "description": "MCP response" + } + } + } + } + }, + "securityDefinitions": {}, + "security": [] +} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/submissions/blastbox-omega/solution/Connector/new_warehouse-20mcp_policytemplateinstances.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Workflows/BlastBoxPostSalesService-62D468F3-4A55-1BA5-2EC3-AE1A4B3A4B35.json b/submissions/blastbox-omega/solution/Workflows/BlastBoxPostSalesService-62D468F3-4A55-1BA5-2EC3-AE1A4B3A4B35.json new file mode 100644 index 0000000..f19a8a8 --- /dev/null +++ b/submissions/blastbox-omega/solution/Workflows/BlastBoxPostSalesService-62D468F3-4A55-1BA5-2EC3-AE1A4B3A4B35.json @@ -0,0 +1,3744 @@ +{ + "properties": { + "connectionReferences": { + "shared_office365-1": { + "api": { + "name": "shared_office365" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedoffice365_37c07" + }, + "runtimeSource": "embedded", + "connectionName": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190" + }, + "shared_agentnode": { + "api": { + "name": "shared_agentnode" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedagentnode_36bfd7b0" + }, + "runtimeSource": "embedded", + "connectionName": "shared-agentnode-0e6dddd0-d4fc-43e9-a95e-2406784e675f" + }, + "shared_advancedapprovals": { + "api": { + "name": "shared_advancedapprovals" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedadvancedapprovals_00cb31fc" + }, + "runtimeSource": "embedded", + "connectionName": "be923ac0d38a430a90ba2395510118d3" + } + }, + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$authentication": { + "defaultValue": {}, + "type": "SecureObject" + }, + "$connections": { + "defaultValue": {}, + "type": "Object" + } + }, + "triggers": { + "When_a_new_email_arrives": { + "type": "OpenApiConnectionNotification", + "inputs": { + "parameters": { + "folderPath": "Id::AAMkAGJhNzZhMjFhLWIwMmQtNDllOC1hNjYwLTQ4NzI4Mjg4NDcyOQAuAAAAAADUlIVU6wZISLwUfI-WrtyAAQAEl1rPER--S6jrwcM0kjSGAAAAAAEMAAA=", + "importance": "Any", + "subjectFilter": "BlastBox Customer Service" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "operationId": "OnNewEmailV3", + "connectionName": "shared_office365-1" + } + }, + "splitOn": "@triggerOutputs()?['body/value']", + "metadata": { + "associatedData": { + "graph": { + "name": "BlastBox Post Sales Service", + "nodes": [ + { + "id": "start-ee6d2ba4-6711-46df-873b-8ff4e5362436", + "name": "When a new email arrives", + "type": "start", + "version": 1, + "position": { + "x": -173.36318146106169, + "y": 560.35515758051918 + }, + "data": { + "config": { + "triggerType": "connector", + "connector": { + "apiName": "shared_office365", + "operationName": "OnNewEmailV3", + "connectionName": "shared_office365-1", + "inputs": { + "folderPath": "Id::AAMkAGJhNzZhMjFhLWIwMmQtNDllOC1hNjYwLTQ4NzI4Mjg4NDcyOQAuAAAAAADUlIVU6wZISLwUfI-WrtyAAQAEl1rPER--S6jrwcM0kjSGAAAAAAEMAAA=", + "importance": "Any", + "subjectFilter": "BlastBox Customer Service" + }, + "displayName": "When a new email arrives", + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "operationType": "OpenApiConnectionNotification", + "parametersSchema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "title": "To", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Recipient email addresses separated by semicolons (If any match, the trigger will run).", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "to" + }, + "cc": { + "type": "string", + "title": "CC", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "CC recipient email addresses separated by semicolons (If any match, the trigger will run).", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "cc" + }, + "toOrCc": { + "type": "string", + "title": "To or CC", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "To or CC recipient email addresses separated by semicolons (If any match, the trigger will run).", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "toOrCc" + }, + "from": { + "type": "string", + "title": "From", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Sender email addresses separated by semicolons (If any match, the trigger will run).", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "from" + }, + "includeAttachments": { + "type": "boolean", + "title": "Include Attachments", + "description": "Should the response of the trigger include the attachments content.", + "default": false, + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "includeAttachments" + }, + "subjectFilter": { + "type": "string", + "title": "Subject Filter", + "description": "String to look for in the subject line.", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "subjectFilter" + }, + "folderPath": { + "type": "string", + "title": "Folder", + "x-ms-dynamic-tree": { + "settings": { + "canSelectParentNodes": true, + "canSelectLeafNodes": false + }, + "open": { + "operationId": "OnFilePickerOpen", + "parameters": { + "operation": { + "value": "MailFolders" + } + }, + "itemsPath": "value", + "itemValuePath": "Id", + "itemTitlePath": "DisplayName", + "itemFullTitlePath": "Path", + "itemIsParent": "(IsFolder eq true)" + }, + "browse": { + "operationId": "OnFilePickerBrowse", + "parameters": { + "id": { + "selectedItemValuePath": "Id" + }, + "operation": { + "value": "MailFolders" + } + }, + "itemsPath": "value", + "itemValuePath": "Id", + "itemTitlePath": "DisplayName", + "itemFullTitlePath": "Path", + "itemIsParent": "(IsFolder eq true)" + } + }, + "description": "Mail folder to check for new emails.", + "default": "Inbox", + "x-ms-property-name-alias": "folderPath" + }, + "importance": { + "type": "string", + "title": "Importance", + "description": "Importance of the email (Any, High, Normal, Low).", + "default": "Any", + "enum": [ + "Any", + "Low", + "Normal", + "High" + ], + "x-ms-visibility": "advanced", + "x-ms-enum-values": [ + { + "displayName": "Any", + "value": "Any" + }, + { + "displayName": "Low", + "value": "Low" + }, + { + "displayName": "Normal", + "value": "Normal" + }, + { + "displayName": "High", + "value": "High" + } + ], + "x-ms-property-name-alias": "importance" + }, + "fetchOnlyWithAttachment": { + "type": "boolean", + "title": "Only with Attachments", + "description": "If set to true, only emails with an attachment will be retrieved. Emails without any attachments will be skipped. If set to false, all emails will be retrieved.", + "default": false, + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "fetchOnlyWithAttachment" + } + }, + "required": [] + }, + "triggerBatchMode": "Batch", + "outputSchema": { + "200": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "object", + "properties": { + "from": { + "type": "string", + "title": "From", + "format": "email", + "description": "The mailbox owner and sender of the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "from" + }, + "toRecipients": { + "type": "string", + "title": "To", + "format": "email", + "description": "The recipients for the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "toRecipients" + }, + "ccRecipients": { + "type": "string", + "title": "CC", + "format": "email", + "description": "The Cc recipients for the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "ccRecipients" + }, + "bccRecipients": { + "type": "string", + "title": "BCC", + "format": "email", + "description": "The Bcc recipients for the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "bccRecipients" + }, + "replyTo": { + "type": "string", + "title": "Reply To", + "format": "email", + "description": "The email addresses to use when replying", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyTo" + }, + "subject": { + "type": "string", + "title": "Subject", + "description": "The subject of the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "subject" + }, + "body": { + "type": "string", + "title": "Body", + "description": "The body of the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "body" + }, + "importance": { + "type": "string", + "title": "Importance", + "description": "The importance of the message (low, normal, high)", + "enum": [ + "low", + "normal", + "high" + ], + "x-ms-property-name-alias": "importance" + }, + "bodyPreview": { + "type": "string", + "title": "Body Preview", + "description": "The preview of the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "bodyPreview" + }, + "hasAttachments": { + "type": "boolean", + "title": "Has Attachment", + "description": "Indicates whether the message has attachments", + "x-ms-property-name-alias": "hasAttachments" + }, + "id": { + "type": "string", + "title": "Message Id", + "description": "The unique identifier of the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "id" + }, + "internetMessageId": { + "type": "string", + "title": "Internet Message Id", + "description": "The message ID in the format specified by RFC2822", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "internetMessageId" + }, + "conversationId": { + "type": "string", + "title": "Conversation Id", + "description": "The Id of the conversation the email belongs to", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "conversationId" + }, + "receivedDateTime": { + "type": "string", + "title": "Received Time", + "format": "date-time", + "description": "The date and time the message was received", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "receivedDateTime" + }, + "isRead": { + "type": "boolean", + "title": "Is Read", + "description": "Indicates whether the message has been read", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "isRead" + }, + "attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "title": "Attachment Id", + "description": "Attachment Id", + "x-ms-property-name-alias": "id" + }, + "name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "name" + }, + "contentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "contentBytes" + }, + "contentType": { + "type": "string", + "title": "Content-Type", + "description": "Attachment content type", + "x-ms-property-name-alias": "contentType" + }, + "size": { + "type": "integer", + "title": "Size", + "format": "int64", + "description": "The size in bytes of the attachment", + "x-ms-property-name-alias": "size" + }, + "isInline": { + "type": "boolean", + "title": "Is Inline", + "description": "Set to true if this is an inline attachment", + "x-ms-property-name-alias": "isInline" + }, + "lastModifiedDateTime": { + "type": "string", + "title": "Last Modified DateTime", + "format": "date-time", + "description": "The date and time when the attachment was last modified", + "x-ms-property-name-alias": "lastModifiedDateTime" + }, + "contentId": { + "type": "string", + "title": "Content Id", + "description": "Content Id", + "x-ms-property-name-alias": "contentId" + } + }, + "required": [], + "description": "File Attachment" + }, + "description": "The file attachments for the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "attachments" + }, + "isHtml": { + "type": "boolean", + "title": "Is HTML", + "description": "Is Html?", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "isHtml" + }, + "sensitivityLabelInfo": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sensitivityLabelId": { + "type": "string", + "title": "sensitivityLabelId", + "description": "SensitivityLabel Id.", + "x-ms-property-name-alias": "sensitivityLabelId" + }, + "name": { + "type": "string", + "title": "name", + "description": "SensitivityLabel name.", + "x-ms-property-name-alias": "name" + }, + "displayName": { + "type": "string", + "title": "displayName", + "description": "SensitivityLabel displayName info", + "x-ms-property-name-alias": "displayName" + }, + "tooltip": { + "type": "string", + "title": "tooltip", + "description": "SensitivityLabel details on tooltip.", + "x-ms-property-name-alias": "tooltip" + }, + "priority": { + "type": "integer", + "title": "priority", + "format": "int32", + "description": "SensitivityLabel priority.", + "x-ms-property-name-alias": "priority" + }, + "color": { + "type": "string", + "title": "color", + "description": "SensitivityLabel color.", + "x-ms-property-name-alias": "color" + }, + "isEncrypted": { + "type": "boolean", + "title": "isEncrypted", + "description": " is SensitivityLabel Encrypted.", + "x-ms-property-name-alias": "isEncrypted" + }, + "isEnabled": { + "type": "boolean", + "title": "isEnabled", + "description": "Whether SensitivityLabel is Enabled.", + "x-ms-property-name-alias": "isEnabled" + }, + "isParent": { + "type": "boolean", + "title": "isParent", + "description": "Whether SensitivityLabel is Parent.", + "x-ms-property-name-alias": "isParent" + }, + "parentSensitivityLabelId": { + "type": "string", + "title": "parentSensitivityLabelId", + "description": "Parent SensitivityLabel Id.", + "x-ms-property-name-alias": "parentSensitivityLabelId" + } + }, + "required": [] + }, + "x-ms-property-name-alias": "sensitivityLabelInfo" + } + }, + "required": [], + "description": "Graph Client Receive Email Message" + }, + "description": "A list of the response objects", + "x-ms-property-name-alias": "body/value" + } + }, + "required": [], + "description": "Represents a wrapper object for batch trigger response", + "x-ms-property-name-alias": "body" + } + } + } + } + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "title": "From", + "format": "email", + "description": "The mailbox owner and sender of the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "body/from" + }, + "toRecipients": { + "type": "string", + "title": "To", + "format": "email", + "description": "The recipients for the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "body/toRecipients" + }, + "ccRecipients": { + "type": "string", + "title": "CC", + "format": "email", + "description": "The Cc recipients for the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/ccRecipients" + }, + "bccRecipients": { + "type": "string", + "title": "BCC", + "format": "email", + "description": "The Bcc recipients for the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/bccRecipients" + }, + "replyTo": { + "type": "string", + "title": "Reply To", + "format": "email", + "description": "The email addresses to use when replying", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/replyTo" + }, + "subject": { + "type": "string", + "title": "Subject", + "description": "The subject of the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "body/subject" + }, + "body": { + "type": "string", + "title": "Body", + "description": "The body of the message", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "body/body" + }, + "importance": { + "type": "string", + "title": "Importance", + "description": "The importance of the message (low, normal, high)", + "enum": [ + "low", + "normal", + "high" + ], + "x-ms-property-name-alias": "body/importance" + }, + "bodyPreview": { + "type": "string", + "title": "Body Preview", + "description": "The preview of the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/bodyPreview" + }, + "hasAttachments": { + "type": "boolean", + "title": "Has Attachment", + "description": "Indicates whether the message has attachments", + "x-ms-property-name-alias": "body/hasAttachments" + }, + "id": { + "type": "string", + "title": "Message Id", + "description": "The unique identifier of the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/id" + }, + "internetMessageId": { + "type": "string", + "title": "Internet Message Id", + "description": "The message ID in the format specified by RFC2822", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/internetMessageId" + }, + "conversationId": { + "type": "string", + "title": "Conversation Id", + "description": "The Id of the conversation the email belongs to", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/conversationId" + }, + "receivedDateTime": { + "type": "string", + "title": "Received Time", + "format": "date-time", + "description": "The date and time the message was received", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/receivedDateTime" + }, + "isRead": { + "type": "boolean", + "title": "Is Read", + "description": "Indicates whether the message has been read", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/isRead" + }, + "attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "title": "Attachment Id", + "description": "Attachment Id", + "x-ms-property-name-alias": "id" + }, + "name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "name" + }, + "contentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "contentBytes" + }, + "contentType": { + "type": "string", + "title": "Content-Type", + "description": "Attachment content type", + "x-ms-property-name-alias": "contentType" + }, + "size": { + "type": "integer", + "title": "Size", + "format": "int64", + "description": "The size in bytes of the attachment", + "x-ms-property-name-alias": "size" + }, + "isInline": { + "type": "boolean", + "title": "Is Inline", + "description": "Set to true if this is an inline attachment", + "x-ms-property-name-alias": "isInline" + }, + "lastModifiedDateTime": { + "type": "string", + "title": "Last Modified DateTime", + "format": "date-time", + "description": "The date and time when the attachment was last modified", + "x-ms-property-name-alias": "lastModifiedDateTime" + }, + "contentId": { + "type": "string", + "title": "Content Id", + "description": "Content Id", + "x-ms-property-name-alias": "contentId" + } + }, + "required": [], + "description": "File Attachment" + }, + "description": "The file attachments for the message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/attachments" + }, + "isHtml": { + "type": "boolean", + "title": "Is HTML", + "description": "Is Html?", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "body/isHtml" + }, + "sensitivityLabelInfo": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sensitivityLabelId": { + "type": "string", + "title": "sensitivityLabelId", + "description": "SensitivityLabel Id.", + "x-ms-property-name-alias": "sensitivityLabelId" + }, + "name": { + "type": "string", + "title": "name", + "description": "SensitivityLabel name.", + "x-ms-property-name-alias": "name" + }, + "displayName": { + "type": "string", + "title": "displayName", + "description": "SensitivityLabel displayName info", + "x-ms-property-name-alias": "displayName" + }, + "tooltip": { + "type": "string", + "title": "tooltip", + "description": "SensitivityLabel details on tooltip.", + "x-ms-property-name-alias": "tooltip" + }, + "priority": { + "type": "integer", + "title": "priority", + "format": "int32", + "description": "SensitivityLabel priority.", + "x-ms-property-name-alias": "priority" + }, + "color": { + "type": "string", + "title": "color", + "description": "SensitivityLabel color.", + "x-ms-property-name-alias": "color" + }, + "isEncrypted": { + "type": "boolean", + "title": "isEncrypted", + "description": " is SensitivityLabel Encrypted.", + "x-ms-property-name-alias": "isEncrypted" + }, + "isEnabled": { + "type": "boolean", + "title": "isEnabled", + "description": "Whether SensitivityLabel is Enabled.", + "x-ms-property-name-alias": "isEnabled" + }, + "isParent": { + "type": "boolean", + "title": "isParent", + "description": "Whether SensitivityLabel is Parent.", + "x-ms-property-name-alias": "isParent" + }, + "parentSensitivityLabelId": { + "type": "string", + "title": "parentSensitivityLabelId", + "description": "Parent SensitivityLabel Id.", + "x-ms-property-name-alias": "parentSensitivityLabelId" + } + }, + "required": [] + }, + "x-ms-property-name-alias": "body/sensitivityLabelInfo" + } + }, + "required": [], + "description": "Graph Client Receive Email Message" + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + } + }, + { + "id": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "name": "Classify", + "type": "classifyOnInlineAgent", + "version": 1, + "position": { + "x": 239.63619816708766, + "y": 478.71031516103835 + }, + "data": { + "config": { + "input": "Mail input:\n@{triggerOutputs()?['body/body']}", + "categories": [ + { + "id": "f1efd1a5-76cc-4a05-a432-8d5b2fcc0839", + "name": "Order Shipment Issue", + "description": "The email refers to an issue with the process of delivery or shipment of an order" + }, + { + "id": "1e11370e-7602-466a-904f-0242d972063c", + "name": "Order Defect Issue", + "description": "The email refers to an issue with a received product that contains a defect or does not meet expectations" + } + ], + "examples": [ + { + "id": "8414fb0e-fdf8-490c-8673-049b9fe33019", + "input": "My order delivery is late, it was supposed to arrive a week ago", + "label": "Order Shipment Issue" + }, + { + "id": "d852f490-49b3-441c-b8e1-31e2312e1260", + "input": "My BlastBox OMEGA is not turning on. It is brand new, I just received it yesterday", + "label": "Order Defect Issue" + } + ], + "model": "GPT55Chat", + "connectionName": "shared_agentnode", + "inputRich": { + "segments": [ + { + "type": "static", + "value": "Mail input:\n" + }, + { + "type": "token", + "value": "Body", + "tokenRef": { + "type": "step", + "stepId": "start-ee6d2ba4-6711-46df-873b-8ff4e5362436", + "stepName": "When a new email arrives", + "outputName": "body/body", + "outputDisplayName": "Body", + "outputType": "string", + "outputAlias": "body/body" + } + } + ] + } + }, + "outcomes": [ + { + "id": "category:f1efd1a5-76cc-4a05-a432-8d5b2fcc0839", + "label": "Order Shipment Issue" + }, + { + "id": "category:1e11370e-7602-466a-904f-0242d972063c", + "label": "Order Defect Issue" + }, + { + "id": "default-category", + "label": "Other" + } + ] + }, + "measured": { + "width": 240, + "height": 213 + } + }, + { + "id": "canvasNote-a592e707-ed7b-4419-b23f-4a1265cb05c5", + "name": "Note", + "type": "canvasNote", + "version": 1, + "position": { + "x": -184.00976257403167, + "y": 269.83199601990958 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Customer email arrives in Inbox with enquiry. Subject filter is \"BlastBox Customer Service\", a Post-Sales entry point", + "color": "yellow" + }, + "outcomes": [] + }, + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "name": "Review Order, Stock & Weather", + "type": "agent", + "version": 1, + "position": { + "x": 667.68656913042832, + "y": 229.55219617704242 + }, + "data": { + "config": { + "mode": "inline", + "instructions": "", + "botSchemaName": "", + "isHitlEscalationEnabled": false, + "webSearchEnabled": false, + "inlineInstructions": "Explore the possible issues with the shipment of the order, such as lack of stock, as per the customer enquiry:\n\nEnquiry: {triggerOutputs()?['body/body']}\n\nEstimate a new ETA considering the weather conditions of the place of the destination address, and indicate if a substitute product exists that the customer can order instead. If no substitute exists, propose a refund of 100% of the original amount.\n\nReturn a summary of the findings and options ready for customer review with the information available. No greeting or commentary, only the findings and options.", + "outputMode": "structured-simple", + "inlineModel": "Sonnet5", + "connectionName": "shared_agentnode", + "inlineTools": [ + { + "id": "8cdff204-61cf-46a2-847c-99f8eb3515b6", + "connectorId": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "operationId": "InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "connectionReferenceName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "authMode": "invoker", + "isMcp": true, + "iconUri": "https://defaulticons.powerapps.com/defaulticons/api-dedicated.png", + "brandColor": "#007ee5", + "mcpToolMode": "all", + "mcpEnabledTools": [] + }, + { + "id": "7a9bcac8-c35f-40b1-923f-7013f6a3e059", + "connectorId": "shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261", + "operationId": "InvokeMCP", + "displayName": "Warehouse MCP — Warehouse MCP Server", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date.", + "connectionReferenceName": "05a0340ff51b4b32b71abf433dd1e354", + "connectionId": "05a0340ff51b4b32b71abf433dd1e354", + "authMode": "invoker", + "isMcp": true, + "iconUri": "https://pafeblobprodby.blob.core.windows.net/20260805t000000z2117f79ad6a84d449d3e463b5e8dfcaf/new_warehouse-20mcp_iconblob.Png?skoid=1b4c3c9e-b403-4e0f-9933-e50756645683&sktid=975f013f-7f24-47e8-a7d3-abc4752bf346&skt=2026-08-05T20%3A00%3A00Z&ske=2026-08-12T20%3A00%3A00Z&sks=b&skv=2025-07-05&sv=2025-07-05&spr=https&st=2026-08-05T20%3A38%3A11Z&se=2026-08-10T12%3A00%3A00Z&sr=c&sp=rl&scid=61bec03c-bd92-4b66-8adf-84cf37369ffd&sig=BpVxeZetxR6YWQ1ZuKA8TiVyTSt4uQHRlcPOAxhU6LA%3D", + "brandColor": "#007ee5", + "mcpToolMode": "all", + "mcpEnabledTools": [] + }, + { + "id": "ca023e2a-2af4-4f7b-8229-67f6b1113336", + "connectorId": "shared_msnweather", + "operationId": "CurrentWeather", + "displayName": "Get current weather", + "description": "Get the current weather for a location.", + "connectionReferenceName": "b1072dd0220d4fbd8a2db07ef073e482", + "authMode": "invoker", + "isMcp": false, + "iconUri": "https://conn-afd-prod-endpoint-bmc9bqahasf3grgk.b01.azurefd.net/releases/v1.0.1802/1.0.1802.4640/msnweather/icon.png", + "brandColor": "#0078D4", + "connectionId": "b1072dd0220d4fbd8a2db07ef073e482" + } + ], + "simpleProperties": [ + { + "id": "4c494ef5-5ac7-4950-836f-b58ddbc95282", + "name": "Summary", + "type": "string", + "description": "" + } + ], + "jsonSchema": { + "type": "object", + "properties": { + "Summary": { + "type": "string" + } + }, + "required": [ + "Summary" + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "title": "Agent Response", + "description": "Aggregated message text emitted before the structured output (may be null)" + }, + "activities": { + "type": "array", + "description": "Bot response activities (typing, message, and event types)" + }, + "structuredOutput": { + "type": "object", + "properties": { + "Summary": { + "type": "string" + } + }, + "required": [ + "Summary" + ], + "title": "" + } + }, + "required": [ + "activities", + "structuredOutput" + ] + } + } + ] + }, + "measured": { + "width": 275, + "height": 66 + } + }, + { + "id": "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "name": "Review Order & Policy", + "type": "agent", + "version": 1, + "position": { + "x": 671.7037587803834, + "y": 561.14075175607672 + }, + "data": { + "config": { + "mode": "inline", + "instructions": "\nee", + "botSchemaName": "", + "isHitlEscalationEnabled": true, + "webSearchEnabled": false, + "inlineInstructions": "Explore the complain of the customer and the options to help them according to our policy:\n\nComplain: {triggerOutputs()?['body/body']}\n\nOutput the following:\nReturn: 'applicable' if policy makes a return, refund or exchange applicable straightaway, 'exception' if the conditions are not fully covered by policy but the case is extreme, shows enough ambiguity or has grey areas not covered explicitly in the policy, 'other' otherwise (e.g. clearly not covered by policy or not justified an exception)\nSummary: A summary of the findings that explain the rationale behind return. No introduction, greeting, or commentary, only findings. ", + "outputMode": "structured-simple", + "inlineModel": "Sonnet5", + "connectionName": "shared_agentnode", + "simpleProperties": [ + { + "id": "d290ecea-95d1-4343-b5fc-886b5237df61", + "name": "Return", + "type": "enum", + "description": "", + "enumValues": [ + "applicable", + "exception", + "other" + ] + }, + { + "id": "72af3677-c728-4ec5-94ad-cdc68bdaaf09", + "name": "Summary", + "type": "string", + "description": "" + } + ], + "instructionsRich": { + "segments": [ + { + "type": "static", + "value": "\nee" + } + ] + }, + "inlineTools": [ + { + "id": "8f97e212-39b0-4217-9482-fb79fb752c61", + "connectorId": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "operationId": "InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "connectionReferenceName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "authMode": "invoker", + "isMcp": true, + "iconUri": "https://defaulticons.powerapps.com/defaulticons/api-dedicated.png", + "brandColor": "#007ee5", + "mcpToolMode": "all", + "mcpEnabledTools": [] + }, + { + "id": "79e1b978-ca64-4b83-ba17-02319d98b9e2", + "connectorId": "shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261", + "operationId": "InvokeMCP", + "displayName": "Warehouse MCP — Warehouse MCP Server", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date.", + "connectionReferenceName": "05a0340ff51b4b32b71abf433dd1e354", + "connectionId": "05a0340ff51b4b32b71abf433dd1e354", + "authMode": "invoker", + "isMcp": true, + "iconUri": "https://pafeblobprodby.blob.core.windows.net/20260805t000000z2117f79ad6a84d449d3e463b5e8dfcaf/new_warehouse-20mcp_iconblob.Png?skoid=1b4c3c9e-b403-4e0f-9933-e50756645683&sktid=975f013f-7f24-47e8-a7d3-abc4752bf346&skt=2026-08-05T20%3A00%3A00Z&ske=2026-08-12T20%3A00%3A00Z&sks=b&skv=2025-07-05&sv=2025-07-05&spr=https&st=2026-08-05T20%3A38%3A11Z&se=2026-08-10T12%3A00%3A00Z&sr=c&sp=rl&scid=61bec03c-bd92-4b66-8adf-84cf37369ffd&sig=BpVxeZetxR6YWQ1ZuKA8TiVyTSt4uQHRlcPOAxhU6LA%3D", + "brandColor": "#007ee5", + "mcpToolMode": "all", + "mcpEnabledTools": [] + }, + { + "id": "f5f5a22e-bea5-4ecd-a5c1-7c653e4e948e", + "connectorId": "shared_advancedapprovals", + "operationId": "RequestForInformation", + "displayName": "Advanced Approvals — Request for information", + "description": "Ask a human for additional information.", + "connectionReferenceName": "new_sharedadvancedapprovals_00cb31fc", + "connectionId": "be923ac0d38a430a90ba2395510118d3", + "parameters": { + "channel": "Outlook", + "assignedTo": "3f1848f3-2d5c-4958-9033-c9a92b6aed59" + } + } + ], + "jsonSchema": { + "type": "object", + "properties": { + "Return": { + "type": "string", + "enum": [ + "applicable", + "exception", + "other" + ] + }, + "Summary": { + "type": "string" + } + }, + "required": [ + "Return", + "Summary" + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "title": "Agent Response", + "description": "Aggregated message text emitted before the structured output (may be null)" + }, + "activities": { + "type": "array", + "description": "Bot response activities (typing, message, and event types)" + }, + "structuredOutput": { + "type": "object", + "properties": { + "Return": { + "type": "string", + "enum": [ + "applicable", + "exception", + "other" + ] + }, + "Summary": { + "type": "string" + } + }, + "required": [ + "Return", + "Summary" + ], + "title": "" + } + }, + "required": [ + "activities", + "structuredOutput" + ] + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + } + }, + { + "id": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "name": "Policy Condition", + "type": "ifElse", + "version": 1, + "position": { + "x": 1041.2437396594803, + "y": 490 + }, + "data": { + "config": { + "conditions": [ + { + "id": "6f435d40-5285-416a-8ed1-0ad82cc517f4", + "name": "If return applicable", + "expression": "{\n \"and\": [\n {\n \"equals\": [\n \"@body('agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b')?['structuredOutput/Return']\",\n \"applicable\"\n ]\n }\n ]\n}" + }, + { + "id": "dcc6151b-4a09-43d6-b94a-d904a273f861", + "name": "If exception applicable", + "expression": "{\n \"and\": [\n {\n \"equals\": [\n \"@body('agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b')?['structuredOutput/Return']\",\n \"exception\"\n ]\n }\n ]\n}" + } + ] + }, + "outcomes": [ + { + "id": "condition:6f435d40-5285-416a-8ed1-0ad82cc517f4", + "label": "If return applicable" + }, + { + "id": "condition:dcc6151b-4a09-43d6-b94a-d904a273f861", + "label": "If exception applicable" + }, + { + "id": "else", + "label": "Else" + } + ] + }, + "measured": { + "width": 240, + "height": 213 + }, + "selected": false + }, + { + "id": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "name": "Trigger Order Return/Exchange", + "type": "agent", + "version": 1, + "position": { + "x": 1966.9371349815933, + "y": 459.85290511248394 + }, + "data": { + "config": { + "mode": "inline", + "instructions": "", + "botSchemaName": "", + "isHitlEscalationEnabled": false, + "webSearchEnabled": false, + "inlineInstructions": "Initiate a return request for the item(s) in the customer order below that have issues with shipment or that present defects:\n\n{body('connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e')}\n\nSend an email to the customer address indicated below (never other email) indicating that the return request has been initiated:\n\nCustomer email: {triggerOutputs()?['body/from']}\nSubject: 'BlastBox Service - Your enquiry'", + "outputMode": "text", + "inlineModel": "Sonnet5", + "connectionName": "shared_agentnode", + "inlineTools": [ + { + "id": "811e4135-4dea-4e07-8bf0-a5067da10f5a", + "connectorId": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "operationId": "InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "connectionReferenceName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "authMode": "invoker", + "isMcp": true, + "iconUri": "https://defaulticons.powerapps.com/defaulticons/api-dedicated.png", + "brandColor": "#007ee5", + "mcpToolMode": "all", + "mcpEnabledTools": [] + }, + { + "id": "bd68b02a-817d-4c70-b0a3-4c30a986b6ab", + "connectorId": "shared_office365", + "operationId": "SendEmailV2", + "displayName": "Send an email", + "description": "This operation sends an email message.", + "connectionReferenceName": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190", + "authMode": "invoker", + "isMcp": false, + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "connectionId": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190" + } + ] + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "title": "Agent Response", + "description": "The agent response text" + }, + "activities": { + "type": "array", + "description": "Bot response activities (typing, message, and event types)" + } + }, + "required": [ + "activities" + ] + } + } + ] + }, + "measured": { + "width": 276, + "height": 66 + } + }, + { + "id": "connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "name": "Human Review: Exception", + "type": "connector", + "version": 1, + "position": { + "x": 1342.2819165478081, + "y": 592.58129114894189 + }, + "data": { + "config": { + "apiName": "shared_advancedapprovals", + "operationName": "RequestForInformation", + "displayName": "Human review", + "paletteIconHint": "humanReview", + "connectionName": "shared_advancedapprovals", + "operationType": "OpenApiConnectionWebhook", + "iconUri": "https://static.powerapps.com/resource/ppcr/releases/v1.0.1819/1.0.1819.4798/advancedapprovals/icon.png", + "parameters": { + "RequestForInformationInput/channel": "Outlook", + "RequestForInformationInput/title": "Action needed: Review Return Policy Exception", + "RequestForInformationInput/message": "Dear manager,\n\nPlease review the following case where return policy has been flagged as applicable.\n\nAssessment Summary:\n@{body('agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b')?['structuredOutput/Summary']}\n\nCustomer Email:\n@{triggerOutputs()?['body/body']}", + "RequestForInformationInput/assignedTo": "@{triggerOutputs()?['body/from']}", + "RequestForInformationInput/input": { + "type": "object", + "properties": { + "boolean": { + "title": "Approve", + "type": "boolean", + "x-ms-content-hint": "BOOLEAN", + "x-ms-dynamically-added": true, + "description": "Please select yes or no" + } + }, + "required": [ + "boolean" + ] + } + }, + "parametersSchema": { + "type": "object", + "properties": { + "RequestForInformationInput": { + "type": "object", + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Specify the title of the request.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/title" + }, + "message": { + "type": "string", + "title": "Message", + "description": "Specify the message for the request.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/message" + }, + "assignedTo": { + "type": "string", + "title": "Assigned to (first to respond)", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail", + "itemTitlePath": "displayName" + }, + "format": "email", + "description": "Recipient email addresses, UPNs, or Microsoft Entra user ids separated by semicolons. The response from the first user to respond to the request is used.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/assignedTo" + }, + "channel": { + "type": "string", + "title": "Channel", + "description": "Set this to the notification channel for the request based on user query. Choose one of: Outlook or Teams. Defaults to Outlook if not specified. Use Teams if the user mentions Teams, flow bot, or teams bot.", + "default": "Outlook", + "enum": [ + "Outlook", + "Teams" + ], + "x-ms-visibility": "important", + "x-ms-enum-values": [ + { + "displayName": "Outlook", + "value": "Outlook" + }, + { + "displayName": "Teams", + "value": "Teams" + } + ], + "x-ms-property-name-alias": "RequestForInformationInput/channel" + }, + "input": { + "type": "object", + "title": "Input", + "properties": {}, + "required": [], + "description": "Input definition.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/input" + } + }, + "required": [ + "assignedTo", + "channel", + "input", + "message", + "title" + ], + "description": "Request information", + "x-ms-property-name-alias": "RequestForInformationInput" + } + }, + "required": [ + "RequestForInformationInput" + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "boolean": { + "title": "Approve", + "type": "boolean", + "x-ms-content-hint": "BOOLEAN", + "x-ms-dynamically-added": true, + "description": "Please select yes or no" + } + }, + "required": [ + "boolean" + ] + } + } + ] + }, + "measured": { + "width": 242, + "height": 66 + }, + "selected": false + }, + { + "id": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "name": "Approval Condition", + "type": "ifElse", + "version": 1, + "position": { + "x": 1689.5534197048107, + "y": 562.72203527805493 + }, + "data": { + "config": { + "conditions": [ + { + "id": "a1be36bb-5825-4ddd-88cd-142f82444df3", + "name": "If exception approved", + "expression": "{\n \"and\": [\n {\n \"equals\": [\n \"@body('connector-7492932a-4693-4b1e-91dc-313e11fd221b')?['boolean']\",\n \"Yes\"\n ]\n }\n ]\n}" + } + ] + }, + "outcomes": [ + { + "id": "condition:a1be36bb-5825-4ddd-88cd-142f82444df3", + "label": "If exception approved" + }, + { + "id": "else", + "label": "Else" + } + ] + }, + "measured": { + "width": 240, + "height": 167 + } + }, + { + "id": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "name": "Send reply: No refund", + "type": "connector", + "version": 1, + "position": { + "x": 1974.0114277344587, + "y": 777.97966288727457 + }, + "data": { + "config": { + "apiName": "shared_office365", + "displayName": "Office 365 Outlook", + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "operationName": "ReplyToV3", + "parametersSchema": { + "type": "object", + "properties": { + "messageId": { + "type": "string", + "title": "Message Id", + "description": "Id of the email to reply to.", + "minLength": 1, + "x-ms-property-name-alias": "messageId" + }, + "replyParameters": { + "type": "object", + "title": "Details of the email reply.", + "properties": { + "To": { + "type": "string", + "title": "To", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/To" + }, + "Cc": { + "type": "string", + "title": "CC", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Cc" + }, + "Bcc": { + "type": "string", + "title": "BCC", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Bcc" + }, + "Subject": { + "type": "string", + "title": "Subject", + "description": "Email subject (if empty, the original subject used).", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Subject" + }, + "Body": { + "type": "string", + "title": "Body", + "format": "html", + "description": "Content of the email.", + "x-ms-property-name-alias": "replyParameters/Body" + }, + "ReplyAll": { + "type": "boolean", + "title": "Reply All", + "description": "True to reply to all recipients. (default: False)", + "x-ms-property-name-alias": "replyParameters/ReplyAll" + }, + "Importance": { + "type": "string", + "title": "Importance", + "description": "Pick an importance. (default: Low)", + "enum": [ + "Low", + "Normal", + "High" + ], + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Importance" + }, + "Attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "Name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "Name" + }, + "ContentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "ContentBytes" + } + }, + "required": [ + "ContentBytes", + "Name" + ], + "description": "Attachment" + }, + "description": "Details of attachments to be sent along with the reply.", + "x-ms-property-name-alias": "replyParameters/Attachments" + } + }, + "required": [], + "description": "Details of the email reply.", + "x-ms-property-name-alias": "replyParameters" + }, + "mailboxAddress": { + "type": "string", + "title": "Original Mailbox Address", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "description": "Address of the shared mailbox to reply from.", + "x-ms-property-name-alias": "mailboxAddress" + } + }, + "required": [ + "messageId", + "replyParameters" + ] + }, + "parameters": { + "replyParameters/Importance": "Low", + "messageId": "@{triggerOutputs()?['body/id']}", + "replyParameters/Body": "
Dear customer,

We have assessed your enquiry and have concluded that your case does not apply for a refund or exchange under our current policy.

Findings summary:@{body('agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b')?['structuredOutput/Summary']}​

We apologize for any inconvenience this may cause to you. If you need further information about our policies, please visit our site.

Thank you
", + "replyParameters/Subject": "BlastBox Service - Your enquiry", + "replyParameters/To": "@{triggerOutputs()?['body/from']}" + }, + "operationType": "OpenApiConnection", + "connectionName": "shared_office365-1" + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {} + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + } + }, + { + "id": "connector-5f867ed3-6c32-4c2d-b747-251877850d50", + "name": "Send reply", + "type": "connector", + "version": 1, + "position": { + "x": 1036.526545065823, + "y": 896.99961682905746 + }, + "data": { + "config": { + "apiName": "shared_office365", + "displayName": "Office 365 Outlook", + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "operationName": "ReplyToV3", + "operationType": "OpenApiConnection", + "parameters": { + "replyParameters/Importance": "Low", + "replyParameters/Body": "
Dear customer,

Thank you for reaching out. We have assessed your enquiry. Here is some useful information for you:

@{body('agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13')?['result']}

Further questions? Please reach out to us or visit our site. 

Thank you​
", + "messageId": "@{triggerOutputs()?['body/id']}", + "replyParameters/To": "@{triggerOutputs()?['body/from']}", + "replyParameters/Subject": "BlastBox Service - Your enquiry" + }, + "parametersSchema": { + "type": "object", + "properties": { + "messageId": { + "type": "string", + "title": "Message Id", + "description": "Id of the email to reply to.", + "minLength": 1, + "x-ms-property-name-alias": "messageId" + }, + "replyParameters": { + "type": "object", + "title": "Details of the email reply.", + "properties": { + "To": { + "type": "string", + "title": "To", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/To" + }, + "Cc": { + "type": "string", + "title": "CC", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Cc" + }, + "Bcc": { + "type": "string", + "title": "BCC", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Bcc" + }, + "Subject": { + "type": "string", + "title": "Subject", + "description": "Email subject (if empty, the original subject used).", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Subject" + }, + "Body": { + "type": "string", + "title": "Body", + "format": "html", + "description": "Content of the email.", + "x-ms-property-name-alias": "replyParameters/Body" + }, + "ReplyAll": { + "type": "boolean", + "title": "Reply All", + "description": "True to reply to all recipients. (default: False)", + "x-ms-property-name-alias": "replyParameters/ReplyAll" + }, + "Importance": { + "type": "string", + "title": "Importance", + "description": "Pick an importance. (default: Low)", + "enum": [ + "Low", + "Normal", + "High" + ], + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Importance" + }, + "Attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "Name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "Name" + }, + "ContentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "ContentBytes" + } + }, + "required": [ + "ContentBytes", + "Name" + ], + "description": "Attachment" + }, + "description": "Details of attachments to be sent along with the reply.", + "x-ms-property-name-alias": "replyParameters/Attachments" + } + }, + "required": [], + "description": "Details of the email reply.", + "x-ms-property-name-alias": "replyParameters" + }, + "mailboxAddress": { + "type": "string", + "title": "Original Mailbox Address", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "description": "Address of the shared mailbox to reply from.", + "x-ms-property-name-alias": "mailboxAddress" + } + }, + "required": [ + "messageId", + "replyParameters" + ] + }, + "connectionName": "shared_office365-1" + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {} + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + } + }, + { + "id": "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e", + "name": "Send context and options", + "type": "connector", + "version": 1, + "position": { + "x": 1043.9965195642978, + "y": 228.67374640118007 + }, + "data": { + "config": { + "apiName": "shared_office365", + "displayName": "Office 365 Outlook", + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "operationName": "SendMailWithOptions", + "connectionName": "shared_office365-1", + "operationType": "OpenApiConnectionWebhook", + "parameters": { + "optionsEmailSubscription/Message/Importance": "Normal", + "optionsEmailSubscription/Message/Subject": "Action Needed: Your Delivery Enquiry", + "optionsEmailSubscription/Message/Options": "Wait, Return", + "optionsEmailSubscription/Message/To": "@{triggerOutputs()?['body/from']}", + "optionsEmailSubscription/Message/Body": "Dear customer,\n\nPlease find below the findings about your recent enquiry:\n@{body('agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c')?['structuredOutput/Summary']} \n\nIndicate to us if you would be happy to wait for your order under its new ETA, or if you would prefer a refund (or exchange, if applicable).\n\nThank you\n" + }, + "parametersSchema": { + "type": "object", + "properties": { + "optionsEmailSubscription": { + "type": "object", + "title": "Subscription request for options email", + "properties": { + "Message": { + "type": "object", + "properties": { + "To": { + "type": "string", + "title": "To", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Specify email addresses separated by semicolons like someone@contoso.com", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/To" + }, + "Subject": { + "type": "string", + "title": "Subject", + "description": "Subject of the email", + "default": "Your input is required", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/Subject" + }, + "Options": { + "type": "string", + "title": "User Options", + "description": "List of comma separated options for the email response", + "default": "Choice1, Choice2, Choice3", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/Options" + }, + "HeaderText": { + "type": "string", + "title": "Header Text", + "description": "Header text for email body", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/HeaderText" + }, + "SelectionText": { + "type": "string", + "title": "Selection Text", + "description": "Header text for users options selection", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/SelectionText" + }, + "Body": { + "type": "string", + "title": "Body", + "description": "Body of the email", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/Body" + }, + "Importance": { + "type": "string", + "title": "Importance", + "description": "Importance", + "default": "Normal", + "enum": [ + "Low", + "Normal", + "High" + ], + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/Importance" + }, + "Attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "Name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "Name" + }, + "ContentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "ContentBytes" + } + }, + "required": [ + "ContentBytes", + "Name" + ], + "description": "Attachment" + }, + "description": "Attachments", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/Attachments" + }, + "UseOnlyHTMLMessage": { + "type": "boolean", + "title": "Use only HTML message", + "description": "Use only HTML message", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/UseOnlyHTMLMessage" + }, + "HideHTMLMessage": { + "type": "boolean", + "title": "Hide HTML message", + "description": "If set to Yes, then the email body is hidden and only message card is displayed. Email clients which do not support actionable messages will display HTML message regardless of the parameter value.", + "default": false, + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/HideHTMLMessage" + }, + "ShowHTMLConfirmationDialog": { + "type": "boolean", + "title": "Show HTML confirmation dialog", + "description": "If set to Yes then a dialog wil be shown to confirm selected option of HTML message", + "default": false, + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/ShowHTMLConfirmationDialog" + }, + "HideMicrosoftFooter": { + "type": "boolean", + "title": "Hide Microsoft footer", + "description": "If set to Yes, then the Microsoft footer is hidden in the email body.", + "default": false, + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "optionsEmailSubscription/Message/HideMicrosoftFooter" + } + }, + "required": [ + "To" + ], + "description": "User Options Email Message. This is the message expected as part of user input", + "x-ms-property-name-alias": "optionsEmailSubscription/Message" + } + }, + "required": [ + "Message" + ], + "description": "Subscription request for options email.", + "x-ms-property-name-alias": "optionsEmailSubscription" + } + }, + "required": [ + "optionsEmailSubscription" + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "SelectedOption": { + "type": "string", + "description": "User response", + "x-ms-property-name-alias": "body/SelectedOption" + }, + "UserEmailAddress": { + "type": "string", + "description": "User email address. The value is the user's email address for individual users and user ID for the members in Distribution Group or Mail Enabled Security Group.", + "x-ms-property-name-alias": "body/UserEmailAddress" + }, + "UserTenantId": { + "type": "string", + "description": "User tenant ID. The value is the tenant id of the user for both individual users and the members in Distribution Group or Mail Enabled Security Group.", + "x-ms-property-name-alias": "body/UserTenantId" + }, + "UserId": { + "type": "string", + "description": "User ID. The value is the user id for both individual users and the members in Distribution Group or Mail Enabled Security Group.", + "x-ms-property-name-alias": "body/UserId" + } + }, + "required": [], + "description": "Approval Email Response", + "x-ms-property-name-alias": "body" + } + } + } + } + ] + }, + "measured": { + "width": 241, + "height": 66 + } + }, + { + "id": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "name": "Choice Condition", + "type": "ifElse", + "version": 1, + "position": { + "x": 1342.5961834690788, + "y": 224.47134893916609 + }, + "data": { + "config": { + "conditions": [ + { + "id": "6c4633ad-a5c4-4c49-91ac-5a7d4fa0ee07", + "name": "If wait until new ETA", + "expression": "{\n \"and\": [\n {\n \"equals\": [\n \"@outputs('connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e')?['body/SelectedOption']\",\n \"Wait\"\n ]\n }\n ]\n}" + } + ] + }, + "outcomes": [ + { + "id": "condition:6c4633ad-a5c4-4c49-91ac-5a7d4fa0ee07", + "label": "If wait until new ETA" + }, + { + "id": "else", + "label": "Else" + } + ] + }, + "measured": { + "width": 240, + "height": 167 + } + }, + { + "id": "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "name": "Call Store Policy Agent", + "type": "agent", + "version": 1, + "position": { + "x": 665.6063840283349, + "y": 898.31899062875834 + }, + "data": { + "config": { + "mode": "invoke", + "instructions": "Review the customer query and craft a paragraph that summarizes the answer to the question, no greeting or comments, only the answer to the enquiry:\n\nEnquiry: @{triggerOutputs()?['body/body']} \n", + "botSchemaName": "Default_StorePolicyAgent_s9s-u8", + "isHitlEscalationEnabled": false, + "webSearchEnabled": false, + "inlineInstructions": "", + "outputMode": "text", + "inlineModel": "claude-opus-5", + "connectionName": "shared_agentnode", + "instructionsRich": { + "segments": [ + { + "type": "static", + "value": "Review the customer query and craft a paragraph that summarizes the answer to the question, no greeting or comments, only the answer to the enquiry:\n\nEnquiry: " + }, + { + "type": "token", + "value": "Body", + "tokenRef": { + "type": "step", + "stepId": "start-ee6d2ba4-6711-46df-873b-8ff4e5362436", + "stepName": "When a new email arrives", + "outputName": "body/body", + "outputDisplayName": "Body", + "outputType": "string", + "outputAlias": "body/body" + } + }, + { + "type": "static", + "value": " \n" + } + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "result": { + "type": "string", + "description": "The agent response text" + }, + "status": { + "type": "string", + "description": "The completion status (e.g., Completed)" + }, + "conversationId": { + "type": "string", + "description": "Unique identifier for the conversation" + } + }, + "required": [ + "result", + "conversationId" + ] + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + } + }, + { + "id": "canvasNote-55701c44-ed9e-47e5-9956-8e4262503f3b", + "name": "Note 2", + "type": "canvasNote", + "version": 1, + "position": { + "x": 232.61791522643375, + "y": 263.65601320945666 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Email is classified as Order Shipment Issue, Order Defect Issue, or Other if this is a post-sale generic enquiry, not tied to a specific order", + "color": "yellow" + }, + "outcomes": [] + }, + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "canvasNote-3eb5c945-0bd1-4cde-9a53-b43376917c23", + "name": "Note 4", + "type": "canvasNote", + "version": 1, + "position": { + "x": 2312.1449513388206, + "y": 227 + }, + "width": 254, + "height": 200, + "style": { + "width": 254, + "height": 200 + }, + "data": { + "config": { + "markdown": "Agent builds a contextualized answer, specifying if problem comes from delivery disruptions or stock unavailability and gets new ETA. Next steps options always include \"wait\" and \"cancel/return\", and may include \"exchange\" if substitute exists.", + "color": "blue" + }, + "outcomes": [] + }, + "measured": { + "width": 254, + "height": 200 + }, + "resizing": false + }, + { + "id": "canvasNote-c1d662dc-14df-43dc-a503-84fa55f357a4", + "name": "Note 5", + "type": "canvasNote", + "version": 1, + "position": { + "x": 1714.3555743703328, + "y": 884.16960964728685 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "General enquiries are checked by calling existing Store Policy Agent, followed by an email action to send a structured reply to the customer", + "color": "green" + }, + "outcomes": [] + }, + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "name": "Send reply: Thanks for waiting", + "type": "connector", + "version": 1, + "position": { + "x": 1965.0142830129066, + "y": 134.99388135688142 + }, + "data": { + "config": { + "apiName": "shared_office365", + "displayName": "Office 365 Outlook", + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "operationName": "ReplyToV3", + "operationType": "OpenApiConnection", + "parameters": { + "replyParameters/Importance": "Low", + "replyParameters/Body": "
Dear customer,

Thanks for your patience. 

You can keep track of your order in the separate email sent to you. If experiencing issues with your new ETA, please reach out to us.

Thank you
", + "messageId": "@{triggerOutputs()?['body/id']}", + "replyParameters/To": "@{triggerOutputs()?['body/from']}", + "replyParameters/Subject": "BlastBox Service - Your enquiry" + }, + "parametersSchema": { + "type": "object", + "properties": { + "messageId": { + "type": "string", + "title": "Message Id", + "description": "Id of the email to reply to.", + "minLength": 1, + "x-ms-property-name-alias": "messageId" + }, + "replyParameters": { + "type": "object", + "title": "Details of the email reply.", + "properties": { + "To": { + "type": "string", + "title": "To", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/To" + }, + "Cc": { + "type": "string", + "title": "CC", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Cc" + }, + "Bcc": { + "type": "string", + "title": "BCC", + "format": "email", + "description": "Example: recipient1@domain.com; recipient2@domain.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Bcc" + }, + "Subject": { + "type": "string", + "title": "Subject", + "description": "Email subject (if empty, the original subject used).", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Subject" + }, + "Body": { + "type": "string", + "title": "Body", + "format": "html", + "description": "Content of the email.", + "x-ms-property-name-alias": "replyParameters/Body" + }, + "ReplyAll": { + "type": "boolean", + "title": "Reply All", + "description": "True to reply to all recipients. (default: False)", + "x-ms-property-name-alias": "replyParameters/ReplyAll" + }, + "Importance": { + "type": "string", + "title": "Importance", + "description": "Pick an importance. (default: Low)", + "enum": [ + "Low", + "Normal", + "High" + ], + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "replyParameters/Importance" + }, + "Attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "Name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "Name" + }, + "ContentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "ContentBytes" + } + }, + "required": [ + "ContentBytes", + "Name" + ], + "description": "Attachment" + }, + "description": "Details of attachments to be sent along with the reply.", + "x-ms-property-name-alias": "replyParameters/Attachments" + } + }, + "required": [], + "description": "Details of the email reply.", + "x-ms-property-name-alias": "replyParameters" + }, + "mailboxAddress": { + "type": "string", + "title": "Original Mailbox Address", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "description": "Address of the shared mailbox to reply from.", + "x-ms-property-name-alias": "mailboxAddress" + } + }, + "required": [ + "messageId", + "replyParameters" + ] + }, + "connectionName": "shared_office365-1" + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {} + } + } + ] + }, + "measured": { + "width": 270, + "height": 66 + } + }, + { + "id": "canvasNote-258c5821-c40f-4722-8aaf-5467ce81b20f", + "name": "Note 4 2", + "type": "canvasNote", + "version": 1, + "position": { + "x": 2310.9852399919387, + "y": 534 + }, + "width": 254, + "height": 224, + "style": { + "width": 254, + "height": 224 + }, + "data": { + "config": { + "markdown": "Agent assesses if case qualifies for an automatic return/replacement, if the conditions may still apply due to exceptional circumstances, or if these options are unapplicable. When exception is considered, a human approves/disapproves. ", + "color": "red" + }, + "outcomes": [] + }, + "measured": { + "width": 254, + "height": 224 + }, + "resizing": false, + "selected": false + }, + { + "id": "end-2120af3d-79db-4382-8e87-bc95af0281b7", + "name": "End 2", + "type": "end", + "version": 1, + "position": { + "x": 2319, + "y": 448 + }, + "data": { + "config": { + "runStatus": "Succeeded" + }, + "outcomes": [] + }, + "measured": { + "width": 240, + "height": 66 + }, + "selected": true + }, + { + "id": "end-59c4f94f-70bb-4161-ada6-11e1284ea27d", + "name": "End 1", + "type": "end", + "version": 1, + "position": { + "x": 2311, + "y": 135 + }, + "data": { + "config": { + "runStatus": "Succeeded" + }, + "outcomes": [] + }, + "measured": { + "width": 240, + "height": 66 + }, + "selected": false + }, + { + "id": "end-120d8b37-4a78-416c-a48b-63bd0c976443", + "name": "End 3", + "type": "end", + "version": 1, + "position": { + "x": 2290, + "y": 778 + }, + "data": { + "config": { + "runStatus": "Succeeded" + }, + "outcomes": [] + }, + "measured": { + "width": 240, + "height": 66 + } + }, + { + "id": "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b", + "name": "End 4", + "type": "end", + "version": 1, + "position": { + "x": 1353, + "y": 897 + }, + "data": { + "config": { + "runStatus": "Succeeded" + }, + "outcomes": [] + }, + "measured": { + "width": 240, + "height": 66 + } + } + ], + "edges": [ + { + "id": "edge-start-ee6d2ba4-6711-46df-873b-8ff4e5362436-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "source": "start-ee6d2ba4-6711-46df-873b-8ff4e5362436", + "target": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b" + }, + { + "id": "edge-agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "source": "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "target": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4" + }, + { + "id": "edge-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b-category:1e11370e-7602-466a-904f-0242d972063c-agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "source": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "target": "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b", + "sourceHandle": "category:1e11370e-7602-466a-904f-0242d972063c" + }, + { + "id": "edge-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4-condition:6f435d40-5285-416a-8ed1-0ad82cc517f4-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "source": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "target": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "sourceHandle": "condition:6f435d40-5285-416a-8ed1-0ad82cc517f4" + }, + { + "id": "edge-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4-condition:dcc6151b-4a09-43d6-b94a-d904a273f861-connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "source": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "target": "connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "sourceHandle": "condition:dcc6151b-4a09-43d6-b94a-d904a273f861" + }, + { + "id": "edge-connector-7492932a-4693-4b1e-91dc-313e11fd221b-ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "source": "connector-7492932a-4693-4b1e-91dc-313e11fd221b", + "target": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339" + }, + { + "id": "edge-ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339-condition:a1be36bb-5825-4ddd-88cd-142f82444df3-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa-input", + "source": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "target": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "sourceHandle": "condition:a1be36bb-5825-4ddd-88cd-142f82444df3", + "targetHandle": "input" + }, + { + "id": "edge-ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4-else-connector-9ccb141c-3277-4402-b045-e4569768d047", + "source": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4", + "target": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "sourceHandle": "else" + }, + { + "id": "edge-ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339-else-connector-9ccb141c-3277-4402-b045-e4569768d047-input", + "source": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339", + "target": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "sourceHandle": "else", + "targetHandle": "input" + }, + { + "id": "edge-agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c-connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e", + "source": "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "target": "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e" + }, + { + "id": "edge-connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e-ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "source": "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e", + "target": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c" + }, + { + "id": "edge-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b-default-category-agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "source": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "target": "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "sourceHandle": "default-category" + }, + { + "id": "edge-agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13-connector-5f867ed3-6c32-4c2d-b747-251877850d50", + "source": "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13", + "target": "connector-5f867ed3-6c32-4c2d-b747-251877850d50" + }, + { + "id": "edge-classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b-category:f1efd1a5-76cc-4a05-a432-8d5b2fcc0839-agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "source": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b", + "target": "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c", + "sourceHandle": "category:f1efd1a5-76cc-4a05-a432-8d5b2fcc0839" + }, + { + "id": "edge-canvasNote-a592e707-ed7b-4419-b23f-4a1265cb05c5-canvasNote-55701c44-ed9e-47e5-9956-8e4262503f3b-input", + "source": "canvasNote-a592e707-ed7b-4419-b23f-4a1265cb05c5", + "target": "canvasNote-55701c44-ed9e-47e5-9956-8e4262503f3b", + "targetHandle": "input" + }, + { + "id": "edge-ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c-condition:6c4633ad-a5c4-4c49-91ac-5a7d4fa0ee07-connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "source": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "target": "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "sourceHandle": "condition:6c4633ad-a5c4-4c49-91ac-5a7d4fa0ee07" + }, + { + "id": "edge-ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c-else-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa-input", + "source": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c", + "target": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "sourceHandle": "else", + "targetHandle": "input" + }, + { + "id": "edge-agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa-end-2120af3d-79db-4382-8e87-bc95af0281b7", + "source": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa", + "target": "end-2120af3d-79db-4382-8e87-bc95af0281b7" + }, + { + "id": "edge-connector-ef8cfaa6-a984-4216-b10a-3408c1c15675-end-59c4f94f-70bb-4161-ada6-11e1284ea27d", + "source": "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675", + "target": "end-59c4f94f-70bb-4161-ada6-11e1284ea27d" + }, + { + "id": "edge-connector-9ccb141c-3277-4402-b045-e4569768d047-end-120d8b37-4a78-416c-a48b-63bd0c976443", + "source": "connector-9ccb141c-3277-4402-b045-e4569768d047", + "target": "end-120d8b37-4a78-416c-a48b-63bd0c976443" + }, + { + "id": "edge-connector-5f867ed3-6c32-4c2d-b747-251877850d50-end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b", + "source": "connector-5f867ed3-6c32-4c2d-b747-251877850d50", + "target": "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b" + }, + { + "id": "edge-end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b-canvasNote-c1d662dc-14df-43dc-a503-84fa55f357a4", + "source": "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b", + "target": "canvasNote-c1d662dc-14df-43dc-a503-84fa55f357a4" + } + ], + "connectionReferences": { + "shared_office365-1": { + "api": { + "name": "shared_office365" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedoffice365_37c07" + }, + "runtimeSource": "embedded", + "connectionName": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190" + }, + "shared_agentnode": { + "api": { + "name": "shared_agentnode" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedagentnode_36bfd7b0" + }, + "runtimeSource": "embedded", + "connectionName": "shared-agentnode-0e6dddd0-d4fc-43e9-a95e-2406784e675f" + }, + "shared_advancedapprovals": { + "api": { + "name": "shared_advancedapprovals" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedadvancedapprovals_00cb31fc" + }, + "runtimeSource": "embedded", + "connectionName": "be923ac0d38a430a90ba2395510118d3" + } + } + }, + "nodeActionMapping": { + "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c": [ + "Review_Order__Stock___Weather" + ], + "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e": [ + "Send_context_and_options" + ], + "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675": [ + "Send_reply__Thanks_for_waiting" + ], + "end-59c4f94f-70bb-4161-ada6-11e1284ea27d": [ + "end-59c4f94f-70bb-4161-ada6-11e1284ea27d" + ], + "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c": [ + "Choice_Condition" + ], + "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b": [ + "Review_Order___Policy" + ], + "connector-7492932a-4693-4b1e-91dc-313e11fd221b": [ + "Human_Review__Exception" + ], + "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339": [ + "Approval_Condition" + ], + "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4": [ + "Policy_Condition" + ], + "connector-9ccb141c-3277-4402-b045-e4569768d047": [ + "Send_reply__No_refund" + ], + "end-120d8b37-4a78-416c-a48b-63bd0c976443": [ + "end-120d8b37-4a78-416c-a48b-63bd0c976443" + ], + "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13": [ + "Call_Store_Policy_Agent" + ], + "connector-5f867ed3-6c32-4c2d-b747-251877850d50": [ + "Send_reply" + ], + "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b": [ + "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b" + ], + "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b": [ + "Classify", + "Classify_Switch" + ], + "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa": [ + "Trigger_Order_Return_Exchange" + ], + "end-2120af3d-79db-4382-8e87-bc95af0281b7": [ + "end-2120af3d-79db-4382-8e87-bc95af0281b7" + ] + } + } + } + } + }, + "actions": { + "Classify": { + "type": "OpenApiConnection", + "inputs": { + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_agentnode", + "connectionName": "shared_agentnode", + "operationId": "InvokeDefinition" + }, + "parameters": { + "body/definition": { + "components": [], + "environmentVariables": [], + "flows": [], + "dataverseTableSearchs": [], + "dataverseTableSearchGlossaryConfigurations": [], + "dataverseTableSearchEntityConfigurations": [], + "dataverseTableSearchEntityColumnSynonyms": [], + "connectionReferences": [], + "connectorDefinitions": [], + "aIModelDefinitions": [], + "aIPluginOperations": [], + "connectedAgentDefinitions": [], + "componentCollections": [], + "connectedBots": [], + "diagnostics": [], + "entity": { + "authorizedSecurityGroupIds": [], + "supportedLanguages": [], + "diagnostics": [], + "schemaName": "workflow_classifyoninlineagent", + "language": 1033, + "accessControlPolicy": "Any", + "authenticationMode": "Integrated", + "authenticationTrigger": "Always", + "synchronizationStatus": { + "graphDriveItems": [], + "diagnostics": [], + "lastPublishedDetails": { + "diagnosticDetails": [], + "diagnostics": [], + "authenticationMode": "Integrated", + "$kind": "SuccessfulPublishResultDetails" + }, + "$kind": "BotSynchronizationDetails" + }, + "configuration": { + "categories": [], + "channels": [], + "settings": {}, + "memoryParameters": [], + "diagnostics": [], + "recognizer": { + "diagnostics": [], + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "conversationStarters": [], + "diagnostics": [], + "model": { + "diagnostics": [], + "series": "GPT55Chat", + "$kind": "ModelConfig" + }, + "instructions": { + "segments": [], + "diagnostics": [], + "$kind": "Instructions" + }, + "output": { + "diagnostics": [], + "schema": "{\"type\":\"object\",\"properties\":{\"predictedCategory\":{\"type\":\"string\",\"enum\":[\"Order Shipment Issue\",\"Order Defect Issue\",\"Other\"]}},\"required\":[\"predictedCategory\"]}", + "$kind": "StructuredAgentOutput" + }, + "$kind": "AgentSettings" + }, + "$kind": "BotConfiguration" + }, + "$kind": "BotEntity" + }, + "$kind": "BotDefinition" + }, + "body/message": "Classify the following input into one of these categories: Order Shipment Issue, Order Defect Issue, Other\n\nCategory definitions:\n- Order Shipment Issue: The email refers to an issue with the process of delivery or shipment of an order\n- Order Defect Issue: The email refers to an issue with a received product that contains a defect or does not meet expectations\n\nExamples:\nInput: \"My order delivery is late, it was supposed to arrive a week ago\" -> Category: \"Order Shipment Issue\"\nInput: \"My BlastBox OMEGA is not turning on. It is brand new, I just received it yesterday\" -> Category: \"Order Defect Issue\"\n\nInput to classify: Mail input:\n@{triggerOutputs()?['body/body']}\n\nRespond with only the category name. If the input does not clearly match any of the categories, respond with \"Other\"." + } + }, + "metadata": { + "description": "Classify", + "nodeId": "classifyOnInlineAgent-cf2f15e8-2660-4732-b934-7431240f433b" + } + }, + "Classify_Switch": { + "type": "Switch", + "expression": "@outputs('Classify')?['body']?['structuredOutput']?['predictedCategory']", + "cases": { + "Order Shipment Issue": { + "case": "Order Shipment Issue", + "actions": { + "Review_Order__Stock___Weather": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "body/definition": { + "components": [ + { + "diagnostics": [], + "schemaName": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261_InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "dialog": { + "toolInputs": [], + "allowedTools": [], + "subToolSettings": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "authMode": "Maker", + "connectionReference": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "operationId": "InvokeMCP", + "$kind": "McpTool" + }, + "$kind": "DialogComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261_InvokeMCP", + "displayName": "Warehouse MCP — Warehouse MCP Server", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date.", + "dialog": { + "toolInputs": [], + "allowedTools": [], + "subToolSettings": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261", + "authMode": "Maker", + "connectionReference": "05a0340ff51b4b32b71abf433dd1e354", + "operationId": "InvokeMCP", + "$kind": "McpTool" + }, + "$kind": "DialogComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_msnweather_CurrentWeather", + "displayName": "Get current weather", + "description": "Get the current weather for a location.", + "dialog": { + "toolInputs": [], + "toolOutputs": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_msnweather", + "authMode": "Maker", + "connectionReference": "b1072dd0220d4fbd8a2db07ef073e482", + "operationId": "CurrentWeather", + "$kind": "ConnectorTool" + }, + "$kind": "DialogComponent" + } + ], + "environmentVariables": [], + "flows": [], + "dataverseTableSearchs": [], + "dataverseTableSearchGlossaryConfigurations": [], + "dataverseTableSearchEntityConfigurations": [], + "dataverseTableSearchEntityColumnSynonyms": [], + "connectionReferences": [ + { + "diagnostics": [], + "connectionReferenceLogicalName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "$kind": "ConnectionReference" + }, + { + "diagnostics": [], + "connectionReferenceLogicalName": "05a0340ff51b4b32b71abf433dd1e354", + "connectionId": "05a0340ff51b4b32b71abf433dd1e354", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261", + "$kind": "ConnectionReference" + }, + { + "diagnostics": [], + "connectionReferenceLogicalName": "b1072dd0220d4fbd8a2db07ef073e482", + "connectionId": "b1072dd0220d4fbd8a2db07ef073e482", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_msnweather", + "$kind": "ConnectionReference" + } + ], + "connectorDefinitions": [], + "aIModelDefinitions": [], + "aIPluginOperations": [], + "connectedAgentDefinitions": [], + "componentCollections": [], + "connectedBots": [], + "diagnostics": [], + "entity": { + "authorizedSecurityGroupIds": [], + "supportedLanguages": [], + "diagnostics": [], + "schemaName": "workflow_agentnode", + "language": 1033, + "accessControlPolicy": "Any", + "authenticationMode": "Integrated", + "authenticationTrigger": "Always", + "synchronizationStatus": { + "graphDriveItems": [], + "diagnostics": [], + "lastPublishedDetails": { + "diagnosticDetails": [], + "diagnostics": [], + "authenticationMode": "Integrated", + "$kind": "SuccessfulPublishResultDetails" + }, + "$kind": "BotSynchronizationDetails" + }, + "configuration": { + "categories": [], + "channels": [], + "settings": {}, + "memoryParameters": [], + "diagnostics": [], + "recognizer": { + "diagnostics": [], + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "conversationStarters": [], + "diagnostics": [], + "model": { + "diagnostics": [], + "series": "Sonnet5", + "$kind": "ModelConfig" + }, + "instructions": { + "segments": [], + "diagnostics": [], + "$kind": "Instructions" + }, + "output": { + "diagnostics": [], + "schema": "{\"type\":\"object\",\"properties\":{\"Summary\":{\"type\":\"string\"}},\"required\":[\"Summary\"]}", + "$kind": "StructuredAgentOutput" + }, + "$kind": "AgentSettings" + }, + "$kind": "BotConfiguration" + }, + "$kind": "BotEntity" + }, + "$kind": "BotDefinition" + }, + "body/message": "Explore the possible issues with the shipment of the order, such as lack of stock, as per the customer enquiry:\n\nEnquiry: @{triggerOutputs()?['body/body']}\n\nEstimate a new ETA considering the weather conditions of the place of the destination address, and indicate if a substitute product exists that the customer can order instead. If no substitute exists, propose a refund of 100% of the original amount.\n\nReturn a summary of the findings and options ready for customer review with the information available. No greeting or commentary, only the findings and options." + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_agentnode", + "connectionName": "shared_agentnode", + "operationId": "InvokeDefinition" + } + }, + "metadata": { + "description": "Review Order, Stock & Weather", + "nodeId": "agent-c795e6ad-eef4-47c5-b492-88ca35dbef9c" + } + }, + "Send_context_and_options": { + "type": "OpenApiConnectionWebhook", + "inputs": { + "parameters": { + "optionsEmailSubscription/Message/Importance": "Normal", + "optionsEmailSubscription/Message/Subject": "Action Needed: Your Delivery Enquiry", + "optionsEmailSubscription/Message/Options": "Wait, Return", + "optionsEmailSubscription/Message/To": "@{triggerOutputs()?['body/from']}", + "optionsEmailSubscription/Message/Body": "Dear customer,\n\nPlease find below the findings about your recent enquiry:\n@{body('Review_Order__Stock___Weather')?['structuredOutput/Summary']} \n\nIndicate to us if you would be happy to wait for your order under its new ETA, or if you would prefer a refund (or exchange, if applicable).\n\nThank you\n" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "connectionName": "shared_office365-1", + "operationId": "SendMailWithOptions" + } + }, + "runAfter": { + "Review_Order__Stock___Weather": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Send context and options", + "nodeId": "connector-2b4cf3f7-936b-4139-bb61-ac0a14daae6e" + } + }, + "Choice_Condition": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@outputs('Send_context_and_options')?['body/SelectedOption']", + "Wait" + ] + } + ] + }, + "actions": { + "Send_reply__Thanks_for_waiting": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "replyParameters/Importance": "Low", + "replyParameters/Body": "
Dear customer,

Thanks for your patience. 

You can keep track of your order in the separate email sent to you. If experiencing issues with your new ETA, please reach out to us.

Thank you
", + "messageId": "@{triggerOutputs()?['body/id']}", + "replyParameters/To": "@{triggerOutputs()?['body/from']}", + "replyParameters/Subject": "BlastBox Service - Your enquiry" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "connectionName": "shared_office365-1", + "operationId": "ReplyToV3" + } + }, + "metadata": { + "description": "Send reply: Thanks for waiting", + "nodeId": "connector-ef8cfaa6-a984-4216-b10a-3408c1c15675" + } + }, + "end-59c4f94f-70bb-4161-ada6-11e1284ea27d": { + "type": "Terminate", + "inputs": { + "runStatus": "Succeeded" + }, + "runAfter": { + "Send_reply__Thanks_for_waiting": [ + "Succeeded" + ] + }, + "metadata": { + "description": "End 1", + "nodeId": "end-59c4f94f-70bb-4161-ada6-11e1284ea27d" + } + }, + "Choice_Condition_Terminate_condition_6c4633ad_a5c4_4c49_91ac_5a7d4fa0ee07": { + "type": "Terminate", + "inputs": { + "runStatus": "Failed", + "runError": { + "code": "PartialJoinNotReached", + "message": "Flow execution ended without reaching a downstream merge node. A branch was taken that has no path to a node that other branches merge into. Reopen this flow in the designer and review the Health Center warning that says this node doesn't run when a dead-end branch is taken (code: CROSS_SCOPE_MERGE). Then update the flow so every branch eventually connects to the merge node, or remove the merge." + } + }, + "metadata": { + "synthetic": true, + "reason": "implicit-terminate-for-partial-join" + }, + "runAfter": { + "end-59c4f94f-70bb-4161-ada6-11e1284ea27d": [ + "Succeeded" + ] + } + } + }, + "else": { + "actions": {} + }, + "runAfter": { + "Send_context_and_options": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Choice Condition", + "nodeId": "ifElse-5d5fffd8-206d-45e5-bc24-6fc4f32b636c" + } + } + } + }, + "Order Defect Issue": { + "case": "Order Defect Issue", + "actions": { + "Review_Order___Policy": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "body/definition": { + "components": [ + { + "diagnostics": [], + "schemaName": "shared_advancedapprovals_RequestForInformation_channel", + "variable": { + "diagnostics": [], + "name": "shared_advancedapprovals_RequestForInformation_channel", + "type": "{\"type\":\"string\"}", + "defaultValue": "\"Outlook\"", + "$kind": "AgentVariable" + }, + "$kind": "GlobalVariableComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_advancedapprovals_RequestForInformation_assignedTo", + "variable": { + "diagnostics": [], + "name": "shared_advancedapprovals_RequestForInformation_assignedTo", + "type": "{\"type\":\"string\"}", + "defaultValue": "\"3f1848f3-2d5c-4958-9033-c9a92b6aed59\"", + "$kind": "AgentVariable" + }, + "$kind": "GlobalVariableComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261_InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "dialog": { + "toolInputs": [], + "allowedTools": [], + "subToolSettings": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "authMode": "Maker", + "connectionReference": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "operationId": "InvokeMCP", + "$kind": "McpTool" + }, + "$kind": "DialogComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261_InvokeMCP", + "displayName": "Warehouse MCP — Warehouse MCP Server", + "description": "MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date.", + "dialog": { + "toolInputs": [], + "allowedTools": [], + "subToolSettings": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261", + "authMode": "Maker", + "connectionReference": "05a0340ff51b4b32b71abf433dd1e354", + "operationId": "InvokeMCP", + "$kind": "McpTool" + }, + "$kind": "DialogComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_advancedapprovals_RequestForInformation", + "displayName": "Advanced Approvals — Request for information", + "description": "Ask a human for additional information.", + "dialog": { + "toolInputs": [ + { + "diagnostics": [], + "name": "channel", + "value": { + "diagnostics": [], + "name": "shared_advancedapprovals_RequestForInformation_channel", + "$kind": "ContextVariableReference" + }, + "$kind": "AgentToolInput" + }, + { + "diagnostics": [], + "name": "assignedTo", + "value": { + "diagnostics": [], + "name": "shared_advancedapprovals_RequestForInformation_assignedTo", + "$kind": "ContextVariableReference" + }, + "$kind": "AgentToolInput" + } + ], + "toolOutputs": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_advancedapprovals", + "authMode": "Maker", + "connectionReference": "new_sharedadvancedapprovals_00cb31fc", + "operationId": "RequestForInformation", + "$kind": "ConnectorTool" + }, + "$kind": "DialogComponent" + } + ], + "environmentVariables": [], + "flows": [], + "dataverseTableSearchs": [], + "dataverseTableSearchGlossaryConfigurations": [], + "dataverseTableSearchEntityConfigurations": [], + "dataverseTableSearchEntityColumnSynonyms": [], + "connectionReferences": [ + { + "diagnostics": [], + "connectionReferenceLogicalName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "$kind": "ConnectionReference" + }, + { + "diagnostics": [], + "connectionReferenceLogicalName": "05a0340ff51b4b32b71abf433dd1e354", + "connectionId": "05a0340ff51b4b32b71abf433dd1e354", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261", + "$kind": "ConnectionReference" + }, + { + "diagnostics": [], + "connectionReferenceLogicalName": "new_sharedadvancedapprovals_00cb31fc", + "connectionId": "be923ac0d38a430a90ba2395510118d3", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_advancedapprovals", + "$kind": "ConnectionReference" + } + ], + "connectorDefinitions": [], + "aIModelDefinitions": [], + "aIPluginOperations": [], + "connectedAgentDefinitions": [], + "componentCollections": [], + "connectedBots": [], + "diagnostics": [], + "entity": { + "authorizedSecurityGroupIds": [], + "supportedLanguages": [], + "diagnostics": [], + "schemaName": "workflow_agentnode", + "language": 1033, + "accessControlPolicy": "Any", + "authenticationMode": "Integrated", + "authenticationTrigger": "Always", + "synchronizationStatus": { + "graphDriveItems": [], + "diagnostics": [], + "lastPublishedDetails": { + "diagnosticDetails": [], + "diagnostics": [], + "authenticationMode": "Integrated", + "$kind": "SuccessfulPublishResultDetails" + }, + "$kind": "BotSynchronizationDetails" + }, + "configuration": { + "categories": [], + "channels": [], + "settings": {}, + "memoryParameters": [], + "diagnostics": [], + "recognizer": { + "diagnostics": [], + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "conversationStarters": [], + "diagnostics": [], + "model": { + "diagnostics": [], + "series": "Sonnet5", + "$kind": "ModelConfig" + }, + "instructions": { + "segments": [], + "diagnostics": [], + "$kind": "Instructions" + }, + "output": { + "diagnostics": [], + "schema": "{\"type\":\"object\",\"properties\":{\"Return\":{\"type\":\"string\",\"enum\":[\"applicable\",\"exception\",\"other\"]},\"Summary\":{\"type\":\"string\"}},\"required\":[\"Return\",\"Summary\"]}", + "$kind": "StructuredAgentOutput" + }, + "$kind": "AgentSettings" + }, + "$kind": "BotConfiguration" + }, + "$kind": "BotEntity" + }, + "$kind": "BotDefinition" + }, + "body/message": "Explore the complain of the customer and the options to help them according to our policy:\n\nComplain: @{triggerOutputs()?['body/body']}\n\nOutput the following:\nReturn: 'applicable' if policy makes a return, refund or exchange applicable straightaway, 'exception' if the conditions are not fully covered by policy but the case is extreme, shows enough ambiguity or has grey areas not covered explicitly in the policy, 'other' otherwise (e.g. clearly not covered by policy or not justified an exception)\nSummary: A summary of the findings that explain the rationale behind return. No introduction, greeting, or commentary, only findings. " + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_agentnode", + "connectionName": "shared_agentnode", + "operationId": "InvokeDefinition" + } + }, + "metadata": { + "description": "Review Order & Policy", + "nodeId": "agent-0a2ca1d4-b735-4d6c-bbed-55834b22195b" + } + }, + "Policy_Condition": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@body('Review_Order___Policy')?['structuredOutput/Return']", + "applicable" + ] + } + ] + }, + "actions": {}, + "else": { + "actions": { + "Policy_Condition_NestedIf_1": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@body('Review_Order___Policy')?['structuredOutput/Return']", + "exception" + ] + } + ] + }, + "actions": { + "Human_Review__Exception": { + "type": "OpenApiConnectionWebhook", + "inputs": { + "parameters": { + "RequestForInformationInput/channel": "Outlook", + "RequestForInformationInput/title": "Action needed: Review Return Policy Exception", + "RequestForInformationInput/message": "Dear manager,\n\nPlease review the following case where return policy has been flagged as applicable.\n\nAssessment Summary:\n@{body('Review_Order___Policy')?['structuredOutput/Summary']}\n\nCustomer Email:\n@{triggerOutputs()?['body/body']}", + "RequestForInformationInput/assignedTo": "@{triggerOutputs()?['body/from']}", + "RequestForInformationInput/input": { + "type": "object", + "properties": { + "boolean": { + "title": "Approve", + "type": "boolean", + "x-ms-content-hint": "BOOLEAN", + "x-ms-dynamically-added": true, + "description": "Please select yes or no" + } + }, + "required": [ + "boolean" + ] + } + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_advancedapprovals", + "connectionName": "shared_advancedapprovals", + "operationId": "RequestForInformation" + } + }, + "metadata": { + "description": "Human Review: Exception", + "nodeId": "connector-7492932a-4693-4b1e-91dc-313e11fd221b" + } + }, + "Approval_Condition": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@body('Human_Review__Exception')?['boolean']", + "Yes" + ] + } + ] + }, + "actions": {}, + "else": { + "actions": {} + }, + "runAfter": { + "Human_Review__Exception": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Approval Condition", + "nodeId": "ifElse-16a2c329-a4a3-4d03-822d-4148fac6c339" + } + } + }, + "else": { + "actions": {} + } + } + } + }, + "runAfter": { + "Review_Order___Policy": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Policy Condition", + "nodeId": "ifElse-bb7a2927-56d2-4e21-ab2d-77ac17526db4" + } + }, + "Send_reply__No_refund": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "replyParameters/Importance": "Low", + "messageId": "@{triggerOutputs()?['body/id']}", + "replyParameters/Body": "
Dear customer,

We have assessed your enquiry and have concluded that your case does not apply for a refund or exchange under our current policy.

Findings summary:@{body('Review_Order___Policy')?['structuredOutput/Summary']}​

We apologize for any inconvenience this may cause to you. If you need further information about our policies, please visit our site.

Thank you
", + "replyParameters/Subject": "BlastBox Service - Your enquiry", + "replyParameters/To": "@{triggerOutputs()?['body/from']}" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "connectionName": "shared_office365-1", + "operationId": "ReplyToV3" + } + }, + "runAfter": { + "Policy_Condition": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Send reply: No refund", + "nodeId": "connector-9ccb141c-3277-4402-b045-e4569768d047" + } + }, + "end-120d8b37-4a78-416c-a48b-63bd0c976443": { + "type": "Terminate", + "inputs": { + "runStatus": "Succeeded" + }, + "runAfter": { + "Send_reply__No_refund": [ + "Succeeded" + ] + }, + "metadata": { + "description": "End 3", + "nodeId": "end-120d8b37-4a78-416c-a48b-63bd0c976443" + } + } + } + } + }, + "default": { + "actions": { + "Call_Store_Policy_Agent": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "body/prompt": "Review the customer query and craft a paragraph that summarizes the answer to the question, no greeting or comments, only the answer to the enquiry:\n\nEnquiry: @{triggerOutputs()?['body/body']} \n", + "body/agentId": "Default_StorePolicyAgent_s9s-u8" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_agentnode", + "connectionName": "shared_agentnode", + "operationId": "InvokeAgent" + } + }, + "metadata": { + "description": "Call Store Policy Agent", + "nodeId": "agent-b6af16be-b502-4322-83cb-bf6f1c2ddf13" + } + }, + "Send_reply": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "replyParameters/Importance": "Low", + "replyParameters/Body": "
Dear customer,

Thank you for reaching out. We have assessed your enquiry. Here is some useful information for you:

@{body('Call_Store_Policy_Agent')?['result']}

Further questions? Please reach out to us or visit our site. 

Thank you​
", + "messageId": "@{triggerOutputs()?['body/id']}", + "replyParameters/To": "@{triggerOutputs()?['body/from']}", + "replyParameters/Subject": "BlastBox Service - Your enquiry" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "connectionName": "shared_office365-1", + "operationId": "ReplyToV3" + } + }, + "runAfter": { + "Call_Store_Policy_Agent": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Send reply", + "nodeId": "connector-5f867ed3-6c32-4c2d-b747-251877850d50" + } + }, + "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b": { + "type": "Terminate", + "inputs": { + "runStatus": "Succeeded" + }, + "runAfter": { + "Send_reply": [ + "Succeeded" + ] + }, + "metadata": { + "description": "End 4", + "nodeId": "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b" + } + }, + "Classify_Switch_Terminate_default": { + "type": "Terminate", + "inputs": { + "runStatus": "Failed", + "runError": { + "code": "PartialJoinNotReached", + "message": "Flow execution ended without reaching a downstream merge node. A branch was taken that has no path to a node that other branches merge into. Reopen this flow in the designer and review the Health Center warning that says this node doesn't run when a dead-end branch is taken (code: CROSS_SCOPE_MERGE). Then update the flow so every branch eventually connects to the merge node, or remove the merge." + } + }, + "metadata": { + "synthetic": true, + "reason": "implicit-terminate-for-partial-join" + }, + "runAfter": { + "end-5c7e1ace-643f-4e2d-bed7-8a71c340bc9b": [ + "Succeeded" + ] + } + } + } + }, + "runAfter": { + "Classify": [ + "Succeeded" + ] + } + }, + "Trigger_Order_Return_Exchange": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "body/definition": { + "components": [ + { + "diagnostics": [], + "schemaName": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261_InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "dialog": { + "toolInputs": [], + "allowedTools": [], + "subToolSettings": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "authMode": "Maker", + "connectionReference": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "operationId": "InvokeMCP", + "$kind": "McpTool" + }, + "$kind": "DialogComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_office365_SendEmailV2", + "displayName": "Send an email", + "description": "This operation sends an email message.", + "dialog": { + "toolInputs": [], + "toolOutputs": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "authMode": "Maker", + "connectionReference": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190", + "operationId": "SendEmailV2", + "$kind": "ConnectorTool" + }, + "$kind": "DialogComponent" + } + ], + "environmentVariables": [], + "flows": [], + "dataverseTableSearchs": [], + "dataverseTableSearchGlossaryConfigurations": [], + "dataverseTableSearchEntityConfigurations": [], + "dataverseTableSearchEntityColumnSynonyms": [], + "connectionReferences": [ + { + "diagnostics": [], + "connectionReferenceLogicalName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "$kind": "ConnectionReference" + }, + { + "diagnostics": [], + "connectionReferenceLogicalName": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190", + "connectionId": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "$kind": "ConnectionReference" + } + ], + "connectorDefinitions": [], + "aIModelDefinitions": [], + "aIPluginOperations": [], + "connectedAgentDefinitions": [], + "componentCollections": [], + "connectedBots": [], + "diagnostics": [], + "entity": { + "authorizedSecurityGroupIds": [], + "supportedLanguages": [], + "diagnostics": [], + "schemaName": "workflow_agentnode", + "language": 1033, + "accessControlPolicy": "Any", + "authenticationMode": "Integrated", + "authenticationTrigger": "Always", + "synchronizationStatus": { + "graphDriveItems": [], + "diagnostics": [], + "lastPublishedDetails": { + "diagnosticDetails": [], + "diagnostics": [], + "authenticationMode": "Integrated", + "$kind": "SuccessfulPublishResultDetails" + }, + "$kind": "BotSynchronizationDetails" + }, + "configuration": { + "categories": [], + "channels": [], + "settings": {}, + "memoryParameters": [], + "diagnostics": [], + "recognizer": { + "diagnostics": [], + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "conversationStarters": [], + "diagnostics": [], + "model": { + "diagnostics": [], + "series": "Sonnet5", + "$kind": "ModelConfig" + }, + "instructions": { + "segments": [], + "diagnostics": [], + "$kind": "Instructions" + }, + "$kind": "AgentSettings" + }, + "$kind": "BotConfiguration" + }, + "$kind": "BotEntity" + }, + "$kind": "BotDefinition" + }, + "body/message": "Initiate a return request for the item(s) in the customer order below that have issues with shipment or that present defects:\n\n@{body('Send_context_and_options')}\n\nSend an email to the customer address indicated below (never other email) indicating that the return request has been initiated:\n\nCustomer email: @{triggerOutputs()?['body/from']}\nSubject: 'BlastBox Service - Your enquiry'" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_agentnode", + "connectionName": "shared_agentnode", + "operationId": "InvokeDefinition" + } + }, + "runAfter": { + "Classify_Switch": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Trigger Order Return/Exchange", + "nodeId": "agent-2444168a-b2f6-48b5-bb75-75d94e0cf8aa" + } + }, + "end-2120af3d-79db-4382-8e87-bc95af0281b7": { + "type": "Terminate", + "inputs": { + "runStatus": "Succeeded" + }, + "runAfter": { + "Trigger_Order_Return_Exchange": [ + "Succeeded" + ] + }, + "metadata": { + "description": "End 2", + "nodeId": "end-2120af3d-79db-4382-8e87-bc95af0281b7" + } + } + }, + "outputs": {}, + "metadata": { + "telemetryMetadata": { + "creationSource": "WorkflowDesigner", + "modifiedSources": "WorkflowDesigner" + } + } + }, + "templateName": null + }, + "schemaVersion": "1.0.0.0" +} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/Workflows/BlastBoxWeeklyInventoryHealth-44D64F96-1130-9B19-8209-34093D4416A3.json b/submissions/blastbox-omega/solution/Workflows/BlastBoxWeeklyInventoryHealth-44D64F96-1130-9B19-8209-34093D4416A3.json new file mode 100644 index 0000000..2aab690 --- /dev/null +++ b/submissions/blastbox-omega/solution/Workflows/BlastBoxWeeklyInventoryHealth-44D64F96-1130-9B19-8209-34093D4416A3.json @@ -0,0 +1,2257 @@ +{ + "properties": { + "connectionReferences": { + "shared_agentnode": { + "api": { + "name": "shared_agentnode" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedagentnode_36bfd7b0" + }, + "runtimeSource": "embedded", + "connectionName": "shared-agentnode-0e6dddd0-d4fc-43e9-a95e-2406784e675f" + }, + "shared_advancedapprovals": { + "api": { + "name": "shared_advancedapprovals" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedadvancedapprovals_00cb31fc" + }, + "runtimeSource": "embedded", + "connectionName": "be923ac0d38a430a90ba2395510118d3" + }, + "shared_office365": { + "api": { + "name": "shared_office365" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedoffice365_37c07" + }, + "runtimeSource": "embedded", + "connectionName": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190" + } + }, + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$authentication": { + "defaultValue": {}, + "type": "SecureObject" + }, + "$connections": { + "defaultValue": {}, + "type": "Object" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Button", + "inputs": { + "schema": { + "type": "object", + "properties": {}, + "required": [] + } + }, + "metadata": { + "associatedData": { + "graph": { + "name": "BlastBox Weekly Inventory Health", + "nodes": [ + { + "id": "start-4b8f612e-9dcd-464b-996f-33cbbf8f4799", + "name": "Start", + "type": "start", + "version": 1, + "position": { + "x": 248, + "y": 461.54541015625 + }, + "data": { + "config": { + "triggerType": "manual" + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {}, + "required": [] + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + }, + "selected": false + }, + { + "id": "canvasNote-4bf9bd4b-1e72-438a-add1-a13e7b9626e7", + "name": "Note", + "type": "canvasNote", + "version": 1, + "position": { + "x": 238, + "y": 290 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Set as manual and indicate users to turn on Recurrence to recreate real life scenario without risk of missing turning off the \"Enabled\" workflow toggle", + "color": "yellow" + }, + "outcomes": [] + }, + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc", + "name": "Call Inventory & Fullfilment Agent", + "type": "agent", + "version": 1, + "position": { + "x": 534.45402091997823, + "y": 461.3158195438242 + }, + "data": { + "config": { + "mode": "invoke", + "instructions": "Extract the following information for all gaming related SKUs (games and consoles):\n- SKUName\n- State of SKU stock aging: Stale, fresh or other if unknown\n- State of inbound stock order: 'planned' if inbound entry is true, 'unplanned' if false or unknown, 'reorder' if a note to reorder inbound exists\n", + "botSchemaName": "Default_InventoryFullfilmentAgent_X-w2GP", + "isHitlEscalationEnabled": false, + "webSearchEnabled": false, + "inlineInstructions": "", + "outputMode": "structured-simple", + "inlineModel": "Sonnet5", + "connectionName": "shared_agentnode", + "instructionsRich": { + "segments": [ + { + "type": "static", + "value": "Extract the following information for all gaming related SKUs (games and consoles):\n- SKUName\n- State of SKU stock aging: Stale, fresh or other if unknown\n- State of inbound stock order: 'planned' if inbound entry is true, 'unplanned' if false or unknown, 'reorder' if a note to reorder inbound exists\n" + } + ] + }, + "simpleProperties": [ + { + "id": "d8ac2073-974d-4008-a45a-8fff32483871", + "name": "SKU", + "type": "list", + "description": "", + "listItems": [ + { + "id": "831c997a-d165-400a-8230-c59a6a5f2da9", + "name": "SKUName", + "type": "string", + "description": "The name of the SKU" + }, + { + "id": "dfdbd413-a672-41bd-8d0b-84a2f6be5eb5", + "name": "StockAge", + "type": "enum", + "description": "The state of SKU stock aging", + "enumValues": [ + "stale", + "fresh", + "other" + ] + }, + { + "id": "9f767652-ba80-45e6-80ed-23d3e1c3e800", + "name": "Inbound", + "type": "enum", + "description": "State of inbound order", + "enumValues": [ + "planned", + "unplanned", + "reorder" + ] + } + ] + } + ], + "jsonSchema": { + "type": "object", + "properties": { + "SKU": { + "type": "array", + "items": { + "type": "object", + "properties": { + "SKUName": { + "type": "string", + "description": "The name of the SKU" + }, + "StockAge": { + "type": "string", + "enum": [ + "stale", + "fresh", + "other" + ], + "description": "The state of SKU stock aging" + }, + "Inbound": { + "type": "string", + "enum": [ + "planned", + "unplanned", + "reorder" + ], + "description": "State of inbound order" + } + }, + "required": [ + "SKUName", + "StockAge", + "Inbound" + ] + } + } + }, + "required": [ + "SKU" + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "result": { + "type": "string", + "description": "The agent response text" + }, + "status": { + "type": "string", + "description": "The completion status (e.g., Completed)" + }, + "conversationId": { + "type": "string", + "description": "Unique identifier for the conversation" + }, + "structuredOutput": { + "type": "object", + "properties": { + "SKU": { + "type": "array", + "items": { + "type": "object", + "properties": { + "SKUName": { + "type": "string", + "description": "The name of the SKU" + }, + "StockAge": { + "type": "string", + "enum": [ + "stale", + "fresh", + "other" + ], + "description": "The state of SKU stock aging" + }, + "Inbound": { + "type": "string", + "enum": [ + "planned", + "unplanned", + "reorder" + ], + "description": "State of inbound order" + } + }, + "required": [ + "SKUName", + "StockAge", + "Inbound" + ] + } + } + }, + "required": [ + "SKU" + ], + "title": "" + } + }, + "required": [ + "result", + "conversationId", + "structuredOutput" + ] + } + } + ] + }, + "measured": { + "width": 294, + "height": 66 + }, + "selected": false + }, + { + "id": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "name": "Loop over each active SKU", + "type": "loop", + "version": 1, + "position": { + "x": 1248.5117621145687, + "y": 301.8746192221007 + }, + "width": 1907, + "height": 697, + "style": { + "width": 1907, + "height": 697 + }, + "data": { + "config": { + "loopType": "foreach", + "foreach": "@body('agent-2d813f06-95ff-4a50-903b-ff36ee884cdc')?['structuredOutput/SKU']", + "foreachRich": { + "segments": [ + { + "type": "token", + "value": "S K U", + "tokenRef": { + "type": "step", + "stepId": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc", + "stepName": "Call Inventory & Fullfilment Agent", + "outputName": "structuredOutput/SKU", + "outputDisplayName": "S K U", + "outputType": "array" + } + } + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default" + } + ] + }, + "measured": { + "width": 1907, + "height": 697 + }, + "resizing": false, + "selected": false + }, + { + "id": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "name": "Stock Aging Condition", + "type": "ifElse", + "version": 1, + "position": { + "x": 68.393996096193177, + "y": 172 + }, + "data": { + "config": { + "conditions": [ + { + "id": "66b1bc7d-62cb-4e20-869a-5c2b0000b523", + "name": "If stale or aging", + "expression": "{\n \"and\": [\n {\n \"equals\": [\n \"@items('Loop_over_each_active_SKU')?['StockAge']\",\n \"stale\"\n ]\n }\n ]\n}" + }, + { + "id": "e8648f23-63f8-4da1-a773-1c5b66317e52", + "name": "If flagged to reorder inbound", + "expression": "{\n \"and\": [\n {\n \"equals\": [\n \"@items('Loop_over_each_active_SKU')?['Inbound']\",\n \"reorder\"\n ]\n }\n ]\n}" + } + ] + }, + "outcomes": [ + { + "id": "condition:66b1bc7d-62cb-4e20-869a-5c2b0000b523", + "label": "If stale or aging" + }, + { + "id": "condition:e8648f23-63f8-4da1-a773-1c5b66317e52", + "label": "If flagged to reorder inbound" + }, + { + "id": "else", + "label": "Else" + } + ] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 250, + "height": 213 + }, + "selected": false + }, + { + "id": "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9", + "name": "Human Review: Discount Range", + "type": "connector", + "version": 1, + "position": { + "x": 731.71649488352523, + "y": 80 + }, + "data": { + "config": { + "apiName": "shared_advancedapprovals", + "operationName": "RequestForInformation", + "displayName": "Human review", + "paletteIconHint": "humanReview", + "operationType": "OpenApiConnectionWebhook", + "iconUri": "https://static.powerapps.com/resource/ppcr/releases/v1.0.1819/1.0.1819.4798/advancedapprovals/icon.png", + "brandColor": "#109CD1", + "parameters": { + "RequestForInformationInput/channel": "Outlook", + "RequestForInformationInput/title": "Action needed: SKU Pricing Adjustment", + "RequestForInformationInput/message": "Dear store manager,\n\nThe following item has been nominated for a promotion or clearance application based on weekly findings.\nSKU: @{items('Loop_over_each_active_SKU')?['SKUName']} \nWeekly Findings: @{body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Findings']} \n\nThe following options are to be reviewed:\n\nIf promotion is chosen, store employees will receive the following guidance:\nPromo: @{body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Promo']} \n\nIf clearance is chosen, store employees will receive the following guidance:\nClearance: @{body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Clearance']} \n\nPlease select your preferred option, as well as (if desired), maximum and minimum discount to be applied.\n\nThank you", + "RequestForInformationInput/assignedTo": "email@email.com", + "RequestForInformationInput/input": { + "type": "object", + "properties": { + "text": { + "title": "New Pricing", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true, + "description": "Please select an option", + "enum": [ + "Accept Promo", + "Accept Clearance" + ] + }, + "number": { + "title": "Max", + "type": "number", + "x-ms-content-hint": "NUMBER", + "x-ms-dynamically-added": true, + "description": "Maximum discount" + }, + "number_1": { + "title": "Min", + "type": "number", + "x-ms-content-hint": "NUMBER", + "x-ms-dynamically-added": true, + "description": "Minimum discount" + } + }, + "required": [ + "text" + ] + } + }, + "parametersSchema": { + "type": "object", + "properties": { + "RequestForInformationInput": { + "type": "object", + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Specify the title of the request.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/title" + }, + "message": { + "type": "string", + "title": "Message", + "description": "Specify the message for the request.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/message" + }, + "assignedTo": { + "type": "string", + "title": "Assigned to (first to respond)", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail", + "itemTitlePath": "displayName" + }, + "format": "email", + "description": "Recipient email addresses, UPNs, or Microsoft Entra user ids separated by semicolons. The response from the first user to respond to the request is used.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/assignedTo" + }, + "channel": { + "type": "string", + "title": "Channel", + "description": "Set this to the notification channel for the request based on user query. Choose one of: Outlook or Teams. Defaults to Outlook if not specified. Use Teams if the user mentions Teams, flow bot, or teams bot.", + "default": "Outlook", + "enum": [ + "Outlook", + "Teams" + ], + "x-ms-visibility": "important", + "x-ms-enum-values": [ + { + "displayName": "Outlook", + "value": "Outlook" + }, + { + "displayName": "Teams", + "value": "Teams" + } + ], + "x-ms-property-name-alias": "RequestForInformationInput/channel" + }, + "input": { + "type": "object", + "title": "Input", + "properties": {}, + "required": [], + "description": "Input definition.", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "RequestForInformationInput/input" + } + }, + "required": [ + "assignedTo", + "channel", + "input", + "message", + "title" + ], + "description": "Request information", + "x-ms-property-name-alias": "RequestForInformationInput" + } + }, + "required": [ + "RequestForInformationInput" + ] + }, + "connectionName": "shared_advancedapprovals" + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "text": { + "title": "New Pricing", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true, + "description": "Please select an option", + "enum": [ + "Accept Promo", + "Accept Clearance" + ] + }, + "number": { + "title": "Max", + "type": "number", + "x-ms-content-hint": "NUMBER", + "x-ms-dynamically-added": true, + "description": "Maximum discount" + }, + "number_1": { + "title": "Min", + "type": "number", + "x-ms-content-hint": "NUMBER", + "x-ms-dynamically-added": true, + "description": "Minimum discount" + } + }, + "required": [ + "text" + ] + } + } + ] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 280, + "height": 66 + }, + "selected": false + }, + { + "id": "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1", + "name": "Initialize variables", + "type": "variable", + "version": 1, + "position": { + "x": 896.50856596401627, + "y": 461.53174408057924 + }, + "data": { + "config": { + "assignments": [ + { + "id": "4db06d92-ad0d-4aa3-912c-f661f45653bc", + "variableName": "SKU Array", + "mode": "initialize", + "type": "array", + "value": "" + }, + { + "id": "204918f0-d7f9-4f3a-bc64-8292a93d0b42", + "variableName": "Email", + "mode": "initialize", + "type": "string", + "value": "" + } + ] + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {}, + "description": "Variables set by this node" + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + }, + "selected": false + }, + { + "id": "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28", + "name": "Set SKU: Adjust price", + "type": "variable", + "version": 1, + "position": { + "x": 1063.5857849535041, + "y": 80 + }, + "data": { + "config": { + "assignments": [ + { + "id": "e7ae29a7-2af7-472a-8ae1-ddca76ff0c1d", + "variableName": "SKU Array", + "mode": "appendToArray", + "value": "@{createArray(items('Loop_over_each_active_SKU')?['SKUName'],concat(\r\n if(equals(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['text'], 'Accept Promo'), body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Promo'], body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Clearance']),\r\n if(not(empty(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number'])), concat(' Max Discount: ', string(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number'])), ''),\r\n if(not(empty(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number_1'])), concat(' Min Discount: ', string(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number_1'])), '')\r\n))}", + "type": "array", + "valueRich": { + "segments": [ + { + "type": "token", + "value": "@{createArray(items('Loop_over_each_active_SKU')?['SKUName'],concat(\r\n if(equals(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['text'], 'Accept Promo'), body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Promo'], body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Clearance']),\r\n if(not(empty(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number'])), concat(' Max Discount: ', string(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number'])), ''),\r\n if(not(empty(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number_1'])), concat(' Min Discount: ', string(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number_1'])), '')\r\n))}", + "tokenRef": { + "type": "function", + "stepId": "function", + "stepName": "Collection", + "outputName": "createArray", + "outputType": "array", + "functionName": "createArray", + "functionCategory": "collection", + "rawExpression": "createArray(items('Loop_over_each_active_SKU')?['SKUName'],concat(\r\n if(equals(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['text'], 'Accept Promo'), body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Promo'], body('agent-7d5d1903-f892-41df-a546-95aef444dbd9')?['structuredOutput/Clearance']),\r\n if(not(empty(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number'])), concat(' Max Discount: ', string(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number'])), ''),\r\n if(not(empty(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number_1'])), concat(' Min Discount: ', string(body('connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9')?['number_1'])), '')\r\n))" + } + } + ] + } + } + ] + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {}, + "description": "Variables set by this node" + } + } + ] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 320, + "height": 75 + }, + "selected": false, + "dragging": false + }, + { + "id": "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4", + "name": "Set SKU: Keep price", + "type": "variable", + "version": 1, + "position": { + "x": 396.22452074359171, + "y": 439 + }, + "data": { + "config": { + "assignments": [ + { + "id": "879f7c7e-0218-450a-9834-7911bac71255", + "variableName": "SKU Array", + "mode": "appendToArray", + "value": "@{createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This item has a good balance of supply and demand. Keep current pricing and store allocation. No action required.')}", + "type": "array", + "valueRich": { + "segments": [ + { + "type": "token", + "value": "@{createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This item has a good balance of supply and demand. Keep current pricing and store allocation. No action required.')}", + "tokenRef": { + "type": "function", + "stepId": "function", + "stepName": "Collection", + "outputName": "createArray", + "outputType": "array", + "functionName": "createArray", + "functionCategory": "collection", + "rawExpression": "createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This item has a good balance of supply and demand. Keep current pricing and store allocation. No action required.')" + } + } + ] + } + } + ] + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {}, + "description": "Variables set by this node" + } + } + ] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 320, + "height": 75 + }, + "selected": false + }, + { + "id": "agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "name": "Review Order History & Policy", + "type": "agent", + "version": 1, + "position": { + "x": 393.35380312869643, + "y": 80 + }, + "data": { + "config": { + "mode": "inline", + "instructions": "", + "botSchemaName": "", + "isHitlEscalationEnabled": false, + "webSearchEnabled": false, + "inlineInstructions": "Get the potential promo or clearance new price scheme for the SKU at hand:\n{items('Loop_over_each_active_SKU')?['SKUName']}\n\nCreate a ready-to-send two lines for store employees with the new potential pricing scheme and rationale. Propose a range of discount prices but do not fix a max and min discount or price, these will be specified by the store managers later. \n\nSummarize the findings in the SKU recent order history and its applicability for promotion or discount, for store manager review.", + "outputMode": "structured-simple", + "inlineModel": "claude-opus-5", + "connectionName": "shared_agentnode", + "inlineTools": [ + { + "id": "09c34998-978b-47d6-9124-b78e42dda025", + "connectorId": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "operationId": "InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "connectionReferenceName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "authMode": "invoker", + "isMcp": true, + "iconUri": "https://defaulticons.powerapps.com/defaulticons/api-dedicated.png", + "brandColor": "#007ee5", + "mcpToolMode": "all", + "mcpEnabledTools": [] + }, + { + "id": "4a486297-d95a-4f64-8dc4-c0b94b470879", + "connectorId": "shared_new-5fpolicy-20rag-20mcp-20v2-5f4fe69411b7a87261", + "operationId": "InvokeMCP", + "displayName": "Policy RAG MCP v2 — Policy RAG MCP v2 Server", + "description": "MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy, get_markdown_policy.", + "connectionReferenceName": "c028995b8824483d8f54bfb93b5b0092", + "connectionId": "c028995b8824483d8f54bfb93b5b0092", + "authMode": "invoker", + "isMcp": true, + "iconUri": "https://defaulticons.powerapps.com/defaulticons/api-dedicated.png", + "brandColor": "#007ee5", + "mcpToolMode": "all", + "mcpEnabledTools": [] + } + ], + "simpleProperties": [ + { + "id": "e1fb8d0a-5f24-48fb-9dee-0d07b588db43", + "name": "Promo", + "type": "string", + "description": "" + }, + { + "id": "fdf09045-417d-4f09-953f-b435d58b4f69", + "name": "Clearance", + "type": "string", + "description": "" + }, + { + "id": "ea9a0583-3443-41a9-9c93-2d56aa310ca6", + "name": "Findings", + "type": "string", + "description": "" + } + ], + "jsonSchema": { + "type": "object", + "properties": { + "Promo": { + "type": "string" + }, + "Clearance": { + "type": "string" + }, + "Findings": { + "type": "string" + } + }, + "required": [ + "Promo", + "Clearance", + "Findings" + ] + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "title": "Agent Response", + "description": "Aggregated message text emitted before the structured output (may be null)" + }, + "activities": { + "type": "array", + "description": "Bot response activities (typing, message, and event types)" + }, + "structuredOutput": { + "type": "object", + "properties": { + "Promo": { + "type": "string" + }, + "Clearance": { + "type": "string" + }, + "Findings": { + "type": "string" + } + }, + "required": [ + "Promo", + "Clearance", + "Findings" + ], + "title": "" + } + }, + "required": [ + "activities", + "structuredOutput" + ] + } + } + ] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 268, + "height": 66 + }, + "selected": false + }, + { + "id": "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "name": "Create HTML table", + "type": "builtinFunction", + "version": 1, + "position": { + "x": 3252.3485818667941, + "y": 617.143631857203 + }, + "data": { + "config": { + "operationId": "htmltable", + "operationName": "htmltable", + "displayName": "Create HTML table", + "category": "providers/Microsoft.ProcessSimple/operationGroups/DataOperation", + "categoryDisplayName": "Data Operations", + "iconUri": "https://logicappsv2resources.blob.core.windows.net/icons/compose.svg", + "brandColor": "#8c6cff", + "parameters": { + "columns": "Automatic", + "from": "@variables('SKU Array')" + }, + "description": "Create an HTML table from an array of objects.", + "parametersSchema": { + "type": "object", + "required": [ + "from" + ], + "properties": { + "from": { + "type": "array", + "title": "From", + "description": "Array to convert" + }, + "columns": { + "type": "string", + "title": "Columns", + "description": "Column selection", + "enum": [ + "Automatic", + "Custom" + ], + "default": "Automatic" + } + } + } + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "description": "Function output" + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + }, + "selected": false + }, + { + "id": "connector-45078208-58d3-45f1-ba7d-b45d24a551e6", + "name": "Send an email", + "type": "connector", + "version": 1, + "position": { + "x": 3557.8040401815115, + "y": 617.143631857203 + }, + "data": { + "config": { + "apiName": "shared_office365", + "displayName": "Office 365 Outlook", + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "operationName": "SendEmailV2", + "operationType": "OpenApiConnection", + "parametersSchema": { + "type": "object", + "properties": { + "emailMessage": { + "type": "object", + "title": "Email", + "properties": { + "To": { + "type": "string", + "title": "To", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Specify email addresses separated by semicolons like someone@contoso.com", + "x-ms-property-name-alias": "emailMessage/To" + }, + "Subject": { + "type": "string", + "title": "Subject", + "description": "Specify the subject of the mail", + "x-ms-property-name-alias": "emailMessage/Subject" + }, + "Body": { + "type": "string", + "title": "Body", + "format": "html", + "description": "Specify the body of the mail", + "x-ms-property-name-alias": "emailMessage/Body" + }, + "From": { + "type": "string", + "title": "From (Send as)", + "format": "email", + "description": "Email address to send mail from (requires \"Send as\" or \"Send on behalf of\" permission for that mailbox). For more info on granting permissions please refer https://docs.microsoft.com/office365/admin/manage/send-email-as-distribution-list", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/From" + }, + "Cc": { + "type": "string", + "title": "CC", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Specify email addresses separated by semicolons like someone@contoso.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Cc" + }, + "Bcc": { + "type": "string", + "title": "BCC", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Specify email addresses separated by semicolons like someone@contoso.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Bcc" + }, + "Attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "Name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "Name" + }, + "ContentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "ContentBytes" + } + }, + "required": [ + "ContentBytes", + "Name" + ], + "description": "Attachment" + }, + "description": "Attachments", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Attachments" + }, + "Sensitivity": { + "type": "string", + "title": "Sensitivity", + "x-ms-dynamic-list": { + "operationId": "GetSensitivityLabels", + "parameters": {}, + "itemValuePath": "Id", + "itemTitlePath": "DisplayName" + }, + "description": "Sensitivity", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "emailMessage/Sensitivity" + }, + "ReplyTo": { + "type": "string", + "title": "Reply To", + "format": "email", + "description": "The email addresses to use when replying", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/ReplyTo" + }, + "Importance": { + "type": "string", + "title": "Importance", + "description": "Importance", + "default": "Normal", + "enum": [ + "Low", + "Normal", + "High" + ], + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Importance" + } + }, + "required": [ + "Body", + "Subject", + "To" + ], + "description": "Email.", + "x-ms-property-name-alias": "emailMessage" + } + }, + "required": [ + "emailMessage" + ] + }, + "parameters": { + "emailMessage/Importance": "Normal", + "emailMessage/Body": "
Dear team,

Please notice and apply the following changes in our products in store, according to the latest review. Pay special attention to promotions, clearance, or high-visibility items to be reallocated:

@{body('builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9')}

Thank you
", + "emailMessage/Subject": "Store Pricing - Weekly Update", + "emailMessage/To": "@{variables('Email')}" + }, + "connectionName": "shared_office365" + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {} + } + } + ] + }, + "measured": { + "width": 240, + "height": 66 + }, + "selected": false + }, + { + "id": "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "name": "Send email: Reorder inbound", + "type": "connector", + "version": 1, + "position": { + "x": 395.77509778735748, + "y": 245.8781549651269 + }, + "data": { + "config": { + "apiName": "shared_office365", + "displayName": "Office 365 Outlook", + "iconUri": "https://res-1.cdn.office.net/files/fabric-cdn-prod_20251008.001/assets/brand-icons/product/svg/outlook_48x1.svg", + "brandColor": "#0078D4", + "operationName": "SendEmailV2", + "operationType": "OpenApiConnection", + "parametersSchema": { + "type": "object", + "properties": { + "emailMessage": { + "type": "object", + "title": "Email", + "properties": { + "To": { + "type": "string", + "title": "To", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Specify email addresses separated by semicolons like someone@contoso.com", + "x-ms-property-name-alias": "emailMessage/To" + }, + "Subject": { + "type": "string", + "title": "Subject", + "description": "Specify the subject of the mail", + "x-ms-property-name-alias": "emailMessage/Subject" + }, + "Body": { + "type": "string", + "title": "Body", + "format": "html", + "description": "Specify the body of the mail", + "x-ms-property-name-alias": "emailMessage/Body" + }, + "From": { + "type": "string", + "title": "From (Send as)", + "format": "email", + "description": "Email address to send mail from (requires \"Send as\" or \"Send on behalf of\" permission for that mailbox). For more info on granting permissions please refer https://docs.microsoft.com/office365/admin/manage/send-email-as-distribution-list", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/From" + }, + "Cc": { + "type": "string", + "title": "CC", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Specify email addresses separated by semicolons like someone@contoso.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Cc" + }, + "Bcc": { + "type": "string", + "title": "BCC", + "x-ms-dynamic-list": { + "builtInOperation": "AadGraph.GetUsers", + "parameters": {}, + "itemValuePath": "mail" + }, + "format": "email", + "description": "Specify email addresses separated by semicolons like someone@contoso.com", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Bcc" + }, + "Attachments": { + "type": "array", + "title": "Attachments", + "items": { + "type": "object", + "properties": { + "Name": { + "type": "string", + "title": "Name", + "description": "Attachment name", + "x-ms-property-name-alias": "Name" + }, + "ContentBytes": { + "type": "string", + "title": "Content", + "format": "byte", + "description": "Attachment content", + "x-ms-property-name-alias": "ContentBytes" + } + }, + "required": [ + "ContentBytes", + "Name" + ], + "description": "Attachment" + }, + "description": "Attachments", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Attachments" + }, + "Sensitivity": { + "type": "string", + "title": "Sensitivity", + "x-ms-dynamic-list": { + "operationId": "GetSensitivityLabels", + "parameters": {}, + "itemValuePath": "Id", + "itemTitlePath": "DisplayName" + }, + "description": "Sensitivity", + "x-ms-visibility": "important", + "x-ms-property-name-alias": "emailMessage/Sensitivity" + }, + "ReplyTo": { + "type": "string", + "title": "Reply To", + "format": "email", + "description": "The email addresses to use when replying", + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/ReplyTo" + }, + "Importance": { + "type": "string", + "title": "Importance", + "description": "Importance", + "default": "Normal", + "enum": [ + "Low", + "Normal", + "High" + ], + "x-ms-visibility": "advanced", + "x-ms-property-name-alias": "emailMessage/Importance" + } + }, + "required": [ + "Body", + "Subject", + "To" + ], + "description": "Email.", + "x-ms-property-name-alias": "emailMessage" + } + }, + "required": [ + "emailMessage" + ] + }, + "parameters": { + "emailMessage/Importance": "Normal", + "emailMessage/Body": "
Dear team,

We require to reorder the last inbound pack of the following product, given demand increase:

@{items('Loop_over_each_active_SKU')?['SKUName']}

Please provide us with an invoice and ETA within the next 2 business days.

Thank you
", + "emailMessage/To": "@{variables('Email')}", + "emailMessage/Subject": "@{items('Loop_over_each_active_SKU')?['SKUName']} - Stock reorder" + }, + "connectionName": "shared_office365" + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {} + } + } + ] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 262, + "height": 66 + }, + "selected": false + }, + { + "id": "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a", + "name": "Set SKU: Keep price, main shelf", + "type": "variable", + "version": 1, + "position": { + "x": 732.78846443237126, + "y": 245.54526527681014 + }, + "data": { + "config": { + "assignments": [ + { + "id": "bc5319cb-5ab8-4c74-abd4-e5a5ddeffbc4", + "variableName": "SKU Array", + "mode": "appendToArray", + "value": "@{createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This is a high demand item. Please keep current pricing and ensure item is located in the main, visible shelf as per the latest store plan, to keep the momentum.')}", + "type": "array", + "valueRich": { + "segments": [ + { + "type": "token", + "value": "@{createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This is a high demand item. Please keep current pricing and ensure item is located in the main, visible shelf as per the latest store plan, to keep the momentum.')}", + "tokenRef": { + "type": "function", + "stepId": "function", + "stepName": "Collection", + "outputName": "createArray", + "outputType": "array", + "functionName": "createArray", + "functionCategory": "collection", + "rawExpression": "createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This is a high demand item. Please keep current pricing and ensure item is located in the main, visible shelf as per the latest store plan, to keep the momentum.')" + } + } + ] + } + } + ] + }, + "outcomes": [ + { + "id": "default", + "label": "Default", + "outcomeSchema": { + "type": "object", + "properties": {}, + "description": "Variables set by this node" + } + } + ] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 320, + "height": 75 + }, + "selected": false + }, + { + "id": "canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d", + "name": "Note 2", + "type": "canvasNote", + "version": 1, + "position": { + "x": 556.22309434606336, + "y": 286.66306645688348 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Extract basic inventory data for each gaming SKU in the inventory, leaving out discontinued SKUs: Stock aging state, inbound stock flag, status notes", + "color": "yellow" + }, + "outcomes": [] + }, + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "canvasNote-ef40d563-4df7-4aff-b6ec-72bc9a0f5f07", + "name": "Note 2 2", + "type": "canvasNote", + "version": 1, + "position": { + "x": 892.97616142978052, + "y": 287.41249762435029 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Initialize array of SKU notes related to store manager approved changes in pricing, to be communicated to employees", + "color": "yellow" + }, + "outcomes": [] + }, + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "canvasNote-331b3285-c7e6-48a6-b6cb-306bd502888d", + "name": "Note 2 2 2", + "type": "canvasNote", + "version": 1, + "position": { + "x": 3402.93884376098, + "y": 447.143631857203 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Create ready-to-send data table and communication draft, communicating relevant SKU updates to store employees", + "color": "yellow" + }, + "outcomes": [] + }, + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "canvasNote-77809ff8-3725-4dbb-a6f5-aecd8d9518a7", + "name": "Note 3", + "type": "canvasNote", + "version": 1, + "position": { + "x": 1424.345573133879, + "y": 80 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Ask store manager for promo or clearance choice and confirm desired price ceiling/floor for SKUs with stale stock", + "color": "blue" + }, + "outcomes": [] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 250, + "height": 150 + } + }, + { + "id": "canvasNote-5562054e-7d46-4bd8-83ed-b5b162183334", + "name": "Note 3 2", + "type": "canvasNote", + "version": 1, + "position": { + "x": 1119.7247698711981, + "y": 228.07204197032797 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Request reorder inbound to provider when a good-selling item has been flagged. Note for store employees to keep price and item visible.", + "color": "red" + }, + "outcomes": [] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 250, + "height": 150 + }, + "dragging": false + }, + { + "id": "canvasNote-17b034a9-13e2-49dc-95c7-9dccaee7b587", + "name": "Note 3 2 2", + "type": "canvasNote", + "version": 1, + "position": { + "x": 822.79770804712189, + "y": 409.49507926698527 + }, + "width": 250, + "height": 150, + "style": { + "width": 250, + "height": 150 + }, + "data": { + "config": { + "markdown": "Note for store employees to keep price of other SKUs, until further notice", + "color": "green" + }, + "outcomes": [] + }, + "parentId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "measured": { + "width": 250, + "height": 150 + }, + "dragging": false + }, + { + "id": "end-2c543046-775a-471f-ab3b-5cc5c32a18bf", + "name": "End", + "type": "end", + "version": 1, + "position": { + "x": 3874, + "y": 617 + }, + "data": { + "config": { + "runStatus": "Succeeded" + }, + "outcomes": [] + }, + "measured": { + "width": 240, + "height": 66 + } + } + ], + "edges": [ + { + "id": "edge-start-4b8f612e-9dcd-464b-996f-33cbbf8f4799-agent-2d813f06-95ff-4a50-903b-ff36ee884cdc", + "source": "start-4b8f612e-9dcd-464b-996f-33cbbf8f4799", + "target": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc" + }, + { + "id": "edge-agent-2d813f06-95ff-4a50-903b-ff36ee884cdc-variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1", + "source": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc", + "target": "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1" + }, + { + "id": "edge-variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1-loop-a4fc4348-44fb-49ea-8236-2acf87569028-external-input", + "source": "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "targetHandle": "external-input" + }, + { + "id": "edge-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-source-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "source": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "target": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "sourceHandle": "internal-source" + }, + { + "id": "edge-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8-condition:66b1bc7d-62cb-4e20-869a-5c2b0000b523-agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "source": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "target": "agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "sourceHandle": "condition:66b1bc7d-62cb-4e20-869a-5c2b0000b523" + }, + { + "id": "edge-agent-7d5d1903-f892-41df-a546-95aef444dbd9-connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9", + "source": "agent-7d5d1903-f892-41df-a546-95aef444dbd9", + "target": "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9" + }, + { + "id": "edge-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8-else-variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4-input", + "source": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "target": "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4", + "sourceHandle": "else", + "targetHandle": "input" + }, + { + "id": "edge-connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9-variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28-input", + "source": "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9", + "target": "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28", + "targetHandle": "input" + }, + { + "id": "edge-loop-a4fc4348-44fb-49ea-8236-2acf87569028-external-output-builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "source": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "target": "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "sourceHandle": "external-output" + }, + { + "id": "edge-builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9-connector-45078208-58d3-45f1-ba7d-b45d24a551e6", + "source": "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9", + "target": "connector-45078208-58d3-45f1-ba7d-b45d24a551e6" + }, + { + "id": "edge-ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8-condition:e8648f23-63f8-4da1-a773-1c5b66317e52-connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "source": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8", + "target": "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "sourceHandle": "condition:e8648f23-63f8-4da1-a773-1c5b66317e52" + }, + { + "id": "edge-variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-target", + "source": "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "targetHandle": "internal-target" + }, + { + "id": "edge-connector-444913ae-a97d-4bea-9bc8-6a44d3acb365-variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a", + "source": "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365", + "target": "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a" + }, + { + "id": "edge-variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4-output-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-target", + "source": "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "sourceHandle": "output", + "targetHandle": "internal-target" + }, + { + "id": "edge-canvasNote-4bf9bd4b-1e72-438a-add1-a13e7b9626e7-canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d-input", + "source": "canvasNote-4bf9bd4b-1e72-438a-add1-a13e7b9626e7", + "target": "canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d", + "targetHandle": "input" + }, + { + "id": "edge-canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d-canvasNote-ef40d563-4df7-4aff-b6ec-72bc9a0f5f07-input", + "source": "canvasNote-4dbca523-4495-4732-a107-e7eb2ca4fd3d", + "target": "canvasNote-ef40d563-4df7-4aff-b6ec-72bc9a0f5f07", + "targetHandle": "input" + }, + { + "id": "edge-variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28-output-loop-a4fc4348-44fb-49ea-8236-2acf87569028-internal-target", + "source": "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28", + "target": "loop-a4fc4348-44fb-49ea-8236-2acf87569028", + "sourceHandle": "output", + "targetHandle": "internal-target" + }, + { + "id": "edge-connector-45078208-58d3-45f1-ba7d-b45d24a551e6-end-2c543046-775a-471f-ab3b-5cc5c32a18bf", + "source": "connector-45078208-58d3-45f1-ba7d-b45d24a551e6", + "target": "end-2c543046-775a-471f-ab3b-5cc5c32a18bf" + } + ], + "connectionReferences": { + "shared_agentnode": { + "api": { + "name": "shared_agentnode" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedagentnode_36bfd7b0" + }, + "runtimeSource": "embedded", + "connectionName": "shared-agentnode-0e6dddd0-d4fc-43e9-a95e-2406784e675f" + }, + "shared_advancedapprovals": { + "api": { + "name": "shared_advancedapprovals" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedadvancedapprovals_00cb31fc" + }, + "runtimeSource": "embedded", + "connectionName": "be923ac0d38a430a90ba2395510118d3" + }, + "shared_office365": { + "api": { + "name": "shared_office365" + }, + "connection": { + "connectionReferenceLogicalName": "new_sharedoffice365_37c07" + }, + "runtimeSource": "embedded", + "connectionName": "shared-office365-b79141e6-d6f8-42c6-b914-3eb3d2491190" + } + } + }, + "nodeActionMapping": { + "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc": [ + "Call_Inventory___Fullfilment_Agent" + ], + "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1": [ + "Initialize_variables", + "Initialize_variables_Init_Email" + ], + "agent-7d5d1903-f892-41df-a546-95aef444dbd9": [ + "Review_Order_History___Policy" + ], + "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9": [ + "Human_Review__Discount_Range" + ], + "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28": [ + "Set_SKU__Adjust_price" + ], + "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365": [ + "Send_email__Reorder_inbound" + ], + "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a": [ + "Set_SKU__Keep_price__main_shelf" + ], + "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4": [ + "Set_SKU__Keep_price" + ], + "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8": [ + "Stock_Aging_Condition" + ], + "loop-a4fc4348-44fb-49ea-8236-2acf87569028": [ + "Loop_over_each_active_SKU" + ], + "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9": [ + "Create_HTML_table" + ], + "connector-45078208-58d3-45f1-ba7d-b45d24a551e6": [ + "Send_an_email" + ], + "end-2c543046-775a-471f-ab3b-5cc5c32a18bf": [ + "end-2c543046-775a-471f-ab3b-5cc5c32a18bf" + ] + } + } + } + } + }, + "actions": { + "Call_Inventory___Fullfilment_Agent": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "body/prompt": "Extract the following information for all gaming related SKUs (games and consoles):\n- SKUName\n- State of SKU stock aging: Stale, fresh or other if unknown\n- State of inbound stock order: 'planned' if inbound entry is true, 'unplanned' if false or unknown, 'reorder' if a note to reorder inbound exists\n", + "body/agentId": "Default_InventoryFullfilmentAgent_X-w2GP", + "body/outputSchema": { + "type": "object", + "properties": { + "SKU": { + "type": "array", + "items": { + "type": "object", + "properties": { + "SKUName": { + "type": "string", + "description": "The name of the SKU" + }, + "StockAge": { + "type": "string", + "enum": [ + "stale", + "fresh", + "other" + ], + "description": "The state of SKU stock aging" + }, + "Inbound": { + "type": "string", + "enum": [ + "planned", + "unplanned", + "reorder" + ], + "description": "State of inbound order" + } + }, + "required": [ + "SKUName", + "StockAge", + "Inbound" + ] + } + } + }, + "required": [ + "SKU" + ] + } + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_agentnode", + "connectionName": "shared_agentnode", + "operationId": "InvokeAgent" + } + }, + "metadata": { + "description": "Call Inventory & Fullfilment Agent", + "nodeId": "agent-2d813f06-95ff-4a50-903b-ff36ee884cdc" + } + }, + "Initialize_variables": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "SKU Array", + "type": "Array", + "value": [] + } + ] + }, + "runAfter": { + "Call_Inventory___Fullfilment_Agent": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Initialize variables", + "nodeId": "variable-44f7b1d2-15a0-473c-a3a8-3e8b43eba5c1" + } + }, + "Initialize_variables_Init_Email": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "Email", + "type": "String", + "value": "" + } + ] + }, + "runAfter": { + "Initialize_variables": [ + "Succeeded" + ] + } + }, + "Loop_over_each_active_SKU": { + "type": "Foreach", + "foreach": "@coalesce(body('Call_Inventory___Fullfilment_Agent')?['structuredOutput/SKU'], json('[]'))", + "actions": { + "Stock_Aging_Condition": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@items('Loop_over_each_active_SKU')?['StockAge']", + "stale" + ] + } + ] + }, + "actions": { + "Review_Order_History___Policy": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "body/definition": { + "components": [ + { + "diagnostics": [], + "schemaName": "shared_new-5forder-20management-20mcp-5f4fe69411b7a87261_InvokeMCP", + "displayName": "Order Management MCP — Order Management MCP Server", + "description": "MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status.", + "dialog": { + "toolInputs": [], + "allowedTools": [], + "subToolSettings": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "authMode": "Maker", + "connectionReference": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "operationId": "InvokeMCP", + "$kind": "McpTool" + }, + "$kind": "DialogComponent" + }, + { + "diagnostics": [], + "schemaName": "shared_new-5fpolicy-20rag-20mcp-20v2-5f4fe69411b7a87261_InvokeMCP", + "displayName": "Policy RAG MCP v2 — Policy RAG MCP v2 Server", + "description": "MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy, get_markdown_policy.", + "dialog": { + "toolInputs": [], + "allowedTools": [], + "subToolSettings": [], + "diagnostics": [], + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5fpolicy-20rag-20mcp-20v2-5f4fe69411b7a87261", + "authMode": "Maker", + "connectionReference": "c028995b8824483d8f54bfb93b5b0092", + "operationId": "InvokeMCP", + "$kind": "McpTool" + }, + "$kind": "DialogComponent" + } + ], + "environmentVariables": [], + "flows": [], + "dataverseTableSearchs": [], + "dataverseTableSearchGlossaryConfigurations": [], + "dataverseTableSearchEntityConfigurations": [], + "dataverseTableSearchEntityColumnSynonyms": [], + "connectionReferences": [ + { + "diagnostics": [], + "connectionReferenceLogicalName": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectionId": "96e5b9bcc9934bd3b4aa3dc7115e4da0", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261", + "$kind": "ConnectionReference" + }, + { + "diagnostics": [], + "connectionReferenceLogicalName": "c028995b8824483d8f54bfb93b5b0092", + "connectionId": "c028995b8824483d8f54bfb93b5b0092", + "connectorId": "/providers/Microsoft.PowerApps/apis/shared_new-5fpolicy-20rag-20mcp-20v2-5f4fe69411b7a87261", + "$kind": "ConnectionReference" + } + ], + "connectorDefinitions": [], + "aIModelDefinitions": [], + "aIPluginOperations": [], + "connectedAgentDefinitions": [], + "componentCollections": [], + "connectedBots": [], + "diagnostics": [], + "entity": { + "authorizedSecurityGroupIds": [], + "supportedLanguages": [], + "diagnostics": [], + "schemaName": "workflow_agentnode", + "language": 1033, + "accessControlPolicy": "Any", + "authenticationMode": "Integrated", + "authenticationTrigger": "Always", + "synchronizationStatus": { + "graphDriveItems": [], + "diagnostics": [], + "lastPublishedDetails": { + "diagnosticDetails": [], + "diagnostics": [], + "authenticationMode": "Integrated", + "$kind": "SuccessfulPublishResultDetails" + }, + "$kind": "BotSynchronizationDetails" + }, + "configuration": { + "categories": [], + "channels": [], + "settings": {}, + "memoryParameters": [], + "diagnostics": [], + "recognizer": { + "diagnostics": [], + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "conversationStarters": [], + "diagnostics": [], + "model": { + "diagnostics": [], + "series": "claude-opus-5", + "$kind": "ModelConfig" + }, + "instructions": { + "segments": [], + "diagnostics": [], + "$kind": "Instructions" + }, + "output": { + "diagnostics": [], + "schema": "{\"type\":\"object\",\"properties\":{\"Promo\":{\"type\":\"string\"},\"Clearance\":{\"type\":\"string\"},\"Findings\":{\"type\":\"string\"}},\"required\":[\"Promo\",\"Clearance\",\"Findings\"]}", + "$kind": "StructuredAgentOutput" + }, + "$kind": "AgentSettings" + }, + "$kind": "BotConfiguration" + }, + "$kind": "BotEntity" + }, + "$kind": "BotDefinition" + }, + "body/message": "Get the potential promo or clearance new price scheme for the SKU at hand:\n@{items('Loop_over_each_active_SKU')?['SKUName']}\n\nCreate a ready-to-send two lines for store employees with the new potential pricing scheme and rationale. Propose a range of discount prices but do not fix a max and min discount or price, these will be specified by the store managers later. \n\nSummarize the findings in the SKU recent order history and its applicability for promotion or discount, for store manager review." + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_agentnode", + "connectionName": "shared_agentnode", + "operationId": "InvokeDefinition" + } + }, + "metadata": { + "description": "Review Order History & Policy", + "nodeId": "agent-7d5d1903-f892-41df-a546-95aef444dbd9" + } + }, + "Human_Review__Discount_Range": { + "type": "OpenApiConnectionWebhook", + "inputs": { + "parameters": { + "RequestForInformationInput/channel": "Outlook", + "RequestForInformationInput/title": "Action needed: SKU Pricing Adjustment", + "RequestForInformationInput/message": "Dear store manager,\n\nThe following item has been nominated for a promotion or clearance application based on weekly findings.\nSKU: @{items('Loop_over_each_active_SKU')?['SKUName']} \nWeekly Findings: @{body('Review_Order_History___Policy')?['structuredOutput/Findings']} \n\nThe following options are to be reviewed:\n\nIf promotion is chosen, store employees will receive the following guidance:\nPromo: @{body('Review_Order_History___Policy')?['structuredOutput/Promo']} \n\nIf clearance is chosen, store employees will receive the following guidance:\nClearance: @{body('Review_Order_History___Policy')?['structuredOutput/Clearance']} \n\nPlease select your preferred option, as well as (if desired), maximum and minimum discount to be applied.\n\nThank you", + "RequestForInformationInput/assignedTo": "email@email.com", + "RequestForInformationInput/input": { + "type": "object", + "properties": { + "text": { + "title": "New Pricing", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true, + "description": "Please select an option", + "enum": [ + "Accept Promo", + "Accept Clearance" + ] + }, + "number": { + "title": "Max", + "type": "number", + "x-ms-content-hint": "NUMBER", + "x-ms-dynamically-added": true, + "description": "Maximum discount" + }, + "number_1": { + "title": "Min", + "type": "number", + "x-ms-content-hint": "NUMBER", + "x-ms-dynamically-added": true, + "description": "Minimum discount" + } + }, + "required": [ + "text" + ] + } + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_advancedapprovals", + "connectionName": "shared_advancedapprovals", + "operationId": "RequestForInformation" + } + }, + "runAfter": { + "Review_Order_History___Policy": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Human Review: Discount Range", + "nodeId": "connector-9dc5e271-f730-4189-8ec6-5dfd1d013fd9" + } + }, + "Set_SKU__Adjust_price": { + "type": "AppendToArrayVariable", + "inputs": { + "name": "SKU Array", + "value": "@createArray(items('Loop_over_each_active_SKU')?['SKUName'],concat(\r\n if(equals(body('Human_Review__Discount_Range')?['text'], 'Accept Promo'), body('Review_Order_History___Policy')?['structuredOutput/Promo'], body('Review_Order_History___Policy')?['structuredOutput/Clearance']),\r\n if(not(empty(body('Human_Review__Discount_Range')?['number'])), concat(' Max Discount: ', string(body('Human_Review__Discount_Range')?['number'])), ''),\r\n if(not(empty(body('Human_Review__Discount_Range')?['number_1'])), concat(' Min Discount: ', string(body('Human_Review__Discount_Range')?['number_1'])), '')\r\n))" + }, + "runAfter": { + "Human_Review__Discount_Range": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Set SKU: Adjust price", + "nodeId": "variable-c0edc099-3a8a-41fd-bf72-c4c0204c6a28" + } + } + }, + "else": { + "actions": { + "Stock_Aging_Condition_NestedIf_1": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@items('Loop_over_each_active_SKU')?['Inbound']", + "reorder" + ] + } + ] + }, + "actions": { + "Send_email__Reorder_inbound": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "emailMessage/Importance": "Normal", + "emailMessage/Body": "
Dear team,

We require to reorder the last inbound pack of the following product, given demand increase:

@{items('Loop_over_each_active_SKU')?['SKUName']}

Please provide us with an invoice and ETA within the next 2 business days.

Thank you
", + "emailMessage/To": "@{variables('Email')}", + "emailMessage/Subject": "@{items('Loop_over_each_active_SKU')?['SKUName']} - Stock reorder" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "connectionName": "shared_office365", + "operationId": "SendEmailV2" + } + }, + "metadata": { + "description": "Send email: Reorder inbound", + "nodeId": "connector-444913ae-a97d-4bea-9bc8-6a44d3acb365" + } + }, + "Set_SKU__Keep_price__main_shelf": { + "type": "AppendToArrayVariable", + "inputs": { + "name": "SKU Array", + "value": "@createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This is a high demand item. Please keep current pricing and ensure item is located in the main, visible shelf as per the latest store plan, to keep the momentum.')" + }, + "runAfter": { + "Send_email__Reorder_inbound": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Set SKU: Keep price, main shelf", + "nodeId": "variable-9c9fd0c3-899e-4645-89ad-fb9d7f14ee9a" + } + } + }, + "else": { + "actions": { + "Set_SKU__Keep_price": { + "type": "AppendToArrayVariable", + "inputs": { + "name": "SKU Array", + "value": "@createArray(items('Loop_over_each_active_SKU')?['SKUName'], 'This item has a good balance of supply and demand. Keep current pricing and store allocation. No action required.')" + }, + "metadata": { + "description": "Set SKU: Keep price", + "nodeId": "variable-5fa3e06a-8244-4ef1-8fb0-7d10f773f2d4" + } + } + } + } + } + } + }, + "metadata": { + "description": "Stock Aging Condition", + "nodeId": "ifElse-9777a477-05d5-449f-a1a0-3018afd17ef8" + } + } + }, + "metadata": { + "description": "Loop over each active SKU", + "nodeId": "loop-a4fc4348-44fb-49ea-8236-2acf87569028" + }, + "runAfter": { + "Initialize_variables_Init_Email": [ + "Succeeded" + ] + } + }, + "Create_HTML_table": { + "type": "Table", + "inputs": { + "from": "@variables('SKU Array')", + "format": "HTML" + }, + "runAfter": { + "Loop_over_each_active_SKU": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Create HTML table", + "nodeId": "builtinFunction-d5757215-ceed-4d57-ad4e-f2b376e681f9" + } + }, + "Send_an_email": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "emailMessage/Importance": "Normal", + "emailMessage/Body": "
Dear team,

Please notice and apply the following changes in our products in store, according to the latest review. Pay special attention to promotions, clearance, or high-visibility items to be reallocated:

@{body('Create_HTML_table')}

Thank you
", + "emailMessage/Subject": "Store Pricing - Weekly Update", + "emailMessage/To": "@{variables('Email')}" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_office365", + "connectionName": "shared_office365", + "operationId": "SendEmailV2" + } + }, + "runAfter": { + "Create_HTML_table": [ + "Succeeded" + ] + }, + "metadata": { + "description": "Send an email", + "nodeId": "connector-45078208-58d3-45f1-ba7d-b45d24a551e6" + } + }, + "end-2c543046-775a-471f-ab3b-5cc5c32a18bf": { + "type": "Terminate", + "inputs": { + "runStatus": "Succeeded" + }, + "runAfter": { + "Send_an_email": [ + "Succeeded" + ] + }, + "metadata": { + "description": "End", + "nodeId": "end-2c543046-775a-471f-ab3b-5cc5c32a18bf" + } + } + }, + "outputs": {}, + "metadata": { + "telemetryMetadata": { + "creationSource": "WorkflowDesigner", + "modifiedSources": "WorkflowDesigner" + } + } + }, + "templateName": null + }, + "schemaVersion": "1.0.0.0" +} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/[Content_Types].xml b/submissions/blastbox-omega/solution/[Content_Types].xml new file mode 100644 index 0000000..884a624 --- /dev/null +++ b/submissions/blastbox-omega/solution/[Content_Types].xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/botcomponent.xml new file mode 100644 index 0000000..5c2048a --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + MCP server for warehouse and fulfillment. Tools: check_stock, get_fulfillment_status, find_alternatives, get_restock_date. + 0 + Warehouse MCP — Warehouse MCP Server + + Default_InventoryFullfilmentAgent_X-w2GP + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/data b/submissions/blastbox-omega/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/data new file mode 100644 index 0000000..fce7688 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/Default_InventoryFullfilmentAgent_X-w2GP.tool.WarehouseMCP-WarehouseMCPServer_rcG/data @@ -0,0 +1,5 @@ +kind: McpTool +authMode: Invoker +connectionReference: Default_InventoryFullfilmentAgent_X-w2GP.cr.shared_new-5fwarehou.05a0340ff51b4b32b71abf433dd1e354 +connectorId: /providers/Microsoft.PowerApps/apis/shared_new-5fwarehouse-20mcp-5f4fe69411b7a87261 +operationId: InvokeMCP \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/botcomponent.xml new file mode 100644 index 0000000..558a62b --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + MCP server for BlastBox Omega policy knowledge. Tools: search_policy, get_tier_refund_policy, get_markdown_policy. + 0 + Policy RAG MCP v2 — Policy RAG MCP v2 Server + + Default_StorePolicyAgent_s9s-u8 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/data b/submissions/blastbox-omega/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/data new file mode 100644 index 0000000..362711d --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/Default_StorePolicyAgent_s9s-u8.tool.PolicyRAGMCPv2-PolicyRAGMCPv2Server_14V/data @@ -0,0 +1,5 @@ +kind: McpTool +authMode: Invoker +connectionReference: Default_StorePolicyAgent_s9s-u8.cr.shared_new-5fpolicy-.c028995b8824483d8f54bfb93b5b0092 +connectorId: /providers/Microsoft.PowerApps/apis/shared_new-5fpolicy-20rag-20mcp-20v2-5f4fe69411b7a87261 +operationId: InvokeMCP \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml new file mode 100644 index 0000000..4be0d43 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Reconcile a customer's BlastPoints loyalty balance after a complex visit (purchases, returns, promo multipliers, expiry) and convert the net balance to store credit. Use when an associate asks about a member's points, or after a return that should claw back previously earned points. The skill runs a bundled Python script that applies the base earn rate, promo multipliers, tier bonus, clawbacks, and expiry, then prints a points ledger and the redeemable store-credit value. + 0 + points-reconciliation + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data new file mode 100644 index 0000000..62e182d --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml new file mode 100644 index 0000000..1132c0b --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Calculate the exact prorated refund when a customer cancels a BlastPass membership, and the net settlement when a defective unit is traded up to a pricier model. Use after the Store Policy Agent confirms the refund rule and the Membership MCP has returned the membership details. The skill runs a bundled Python script so the arithmetic is exact. + 0 + prorated-refund-calculator + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data new file mode 100644 index 0000000..b6fc35e --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml new file mode 100644 index 0000000..e363b80 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill runs a bundled Python script that renders the confirmed slip values into a real PDF file. + 0 + slip-pdf-generator + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data new file mode 100644 index 0000000..5bf2619 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4/data @@ -0,0 +1,2 @@ +kind: InlineAgentSkill +content: \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/botcomponent.xml new file mode 100644 index 0000000..5af61f0 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + MCP server for BlastPass memberships. Tools: get_membership, cancel_membership, reissue_card. + 0 + Membership MCP v2 — Membership MCP v2 Server + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/data b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/data new file mode 100644 index 0000000..ecef943 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.MembershipMCPv2-MembershipMCPv2Server_9Ac/data @@ -0,0 +1,5 @@ +kind: McpTool +authMode: Invoker +connectionReference: cr26e_storeassociateassistant_ZQHUx1.cr.shared_cat-5fmembers.8a9cf60ce2ea4721b0997c58d4948d19 +connectorId: /providers/Microsoft.PowerApps/apis/shared_cat-5fmembership-20mcp-20v2-5f4fe69411b7a87261 +operationId: InvokeMembershipMCPv2 \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/botcomponent.xml new file mode 100644 index 0000000..aa3eb11 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + MCP server for e-commerce order management. Tools: search_orders, get_order, get_shipment, request_return, get_return_status. + 0 + Order Management MCP — Order Management MCP Server + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/data b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/data new file mode 100644 index 0000000..63d5cb3 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.OrderManagementMCP-OrderManagementMCPServer_LQG/data @@ -0,0 +1,5 @@ +kind: McpTool +authMode: Invoker +connectionReference: cr26e_storeassociateassistant_ZQHUx1.cr.shared_new-5forder-2.96e5b9bcc9934bd3b4aa3dc7115e4da0 +connectorId: /providers/Microsoft.PowerApps/apis/shared_new-5forder-20management-20mcp-5f4fe69411b7a87261 +operationId: InvokeMCP \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml new file mode 100644 index 0000000..f30b9d9 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Delegate to the Inventory & Fulfillment Agent for anything about stock and consoles - whether a SKU is in stock locally, restock/ETA dates, in-stock alternatives or upgrades, which BlastBox Omega model a game requires, and the MEGA-only exclusive titles used to upsell a customer choosing between the base console and the MEGA Edition. + 0 + Inventory & Fullfilment Agent + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data new file mode 100644 index 0000000..e1c3b0a --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_InventoryFullfilmentAgent_X-w2GP +historyType: + kind: ConversationHistory diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json new file mode 100644 index 0000000..52b1d1d --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_InventoryFullfilmentAgent_X-w2GP"}] \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml new file mode 100644 index 0000000..6a2d654 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Delegate to the Store Policy Agent for any policy ruling - returns and warranty eligibility, defective-vs-accidental damage, return windows and restocking fees, and BlastPass membership refund/proration rules. Ask it before ruling on a return; it establishes the defect cause first and may return a clarifying question to relay back to the associate. + 0 + Store Policy Agent + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data new file mode 100644 index 0000000..6f26d5d --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_StorePolicyAgent_s9s-u8 +historyType: + kind: ConversationHistory diff --git a/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json new file mode 100644 index 0000000..789e87d --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cr26e_storeassociateassistant_ZQHUx1.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_StorePolicyAgent_s9s-u8"}] \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml new file mode 100644 index 0000000..a9e80db --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + blastpass_refund.py + 0 + blastpass_refund.py + + cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py b/submissions/blastbox-omega/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py new file mode 100644 index 0000000..858173c --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_blastpass_refund_py_1aeb15fe988a/filedata/blastpass_refund.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""BlastPass membership refund + upgrade-settlement calculator. + +Two subcommands, both driven by values that come straight from the Membership MCP +(get_membership), the Store Policy Agent, and the Warehouse MCP. The arithmetic +must be exact, so always run this script rather than computing in your head. + + refund Prorated (or cooling-off) refund for cancelling a BlastPass membership. + settle Net due / refund when a defective unit is credited toward a pricier + model and the membership refund is applied as store credit. + +Refund rules (BlastPass Membership Refund Policy): + 1. Cooling-off: if --within-cooling-off is set, refund the full annual price, + no fee, no proration. + 2. Otherwise: refund the value of the whole unused months remaining in the + 12-month term, minus the tier cancellation fee. + 3. Subtract any non-refundable welcome credit (MEGA tier only). + 4. The refund is never negative. + +Usage: + python3 blastpass_refund.py refund \\ + --annual-price 129.99 --months-remaining 8 --cancellation-fee 10 \\ + [--nonrefundable-credit 0] [--within-cooling-off] + + python3 blastpass_refund.py settle \\ + --defective-credit 399.99 --upgrade-price 499.99 --membership-credit 76.66 +""" + +import argparse +import sys + +TERM_MONTHS = 12 + + +def money(value): + cents = int(value * 100 + (0.5 if value >= 0 else -0.5)) + return cents / 100.0 + + +def compute_refund(annual_price, months_remaining, cancellation_fee, + nonrefundable_credit=0.0, within_cooling_off=False): + lines = [f"Annual price: ${annual_price:.2f}"] + if within_cooling_off: + refund = money(annual_price) + lines.append("Within cooling-off period -> full refund, no fee.") + return {"refund": refund, "basis": "cooling_off", "lines": lines} + + monthly = annual_price / TERM_MONTHS + prorated = money(months_remaining * monthly) + lines.append(f"Monthly value: ${annual_price:.2f} / {TERM_MONTHS} = ${monthly:.4f}") + lines.append(f"Prorated: {months_remaining} unused months x ${monthly:.4f} = ${prorated:.2f}") + refund = prorated + if cancellation_fee > 0: + lines.append(f"Cancellation fee: -${cancellation_fee:.2f}") + refund = money(refund - cancellation_fee) + if nonrefundable_credit > 0: + lines.append(f"Non-refundable welcome credit: -${nonrefundable_credit:.2f}") + refund = money(refund - nonrefundable_credit) + refund = max(refund, 0.0) + return {"refund": refund, "basis": "prorated", "lines": lines} + + +def compute_settlement(defective_credit, upgrade_price, membership_credit): + upgrade_difference = money(upgrade_price - defective_credit) + net = money(upgrade_difference - membership_credit) + lines = [ + f"Defective unit warranty credit: ${defective_credit:.2f}", + f"Upgrade model price: ${upgrade_price:.2f}", + f"Upgrade difference: ${upgrade_price:.2f} - ${defective_credit:.2f} = ${upgrade_difference:.2f}", + f"BlastPass refund applied as store credit: -${membership_credit:.2f}", + ] + return {"net": net, "lines": lines} + + +def _print_lines(lines): + print("\n".join(lines)) + print("-" * 36) + + +def main(argv): + parser = argparse.ArgumentParser(description="BlastPass refund + settlement calculator.") + sub = parser.add_subparsers(dest="command", required=True) + + r = sub.add_parser("refund", help="Prorated / cooling-off membership refund.") + r.add_argument("--annual-price", type=float, required=True) + r.add_argument("--months-remaining", type=int, required=True) + r.add_argument("--cancellation-fee", type=float, default=0.0) + r.add_argument("--nonrefundable-credit", type=float, default=0.0) + r.add_argument("--within-cooling-off", action="store_true") + + s = sub.add_parser("settle", help="Upgrade settlement net due / refund.") + s.add_argument("--defective-credit", type=float, required=True) + s.add_argument("--upgrade-price", type=float, required=True) + s.add_argument("--membership-credit", type=float, default=0.0) + + args = parser.parse_args(argv[1:]) + + if args.command == "refund": + result = compute_refund( + annual_price=args.annual_price, + months_remaining=args.months_remaining, + cancellation_fee=args.cancellation_fee, + nonrefundable_credit=args.nonrefundable_credit, + within_cooling_off=args.within_cooling_off, + ) + _print_lines(result["lines"]) + print(f"REFUND DUE: ${result['refund']:.2f}") + return 0 + + result = compute_settlement( + defective_credit=args.defective_credit, + upgrade_price=args.upgrade_price, + membership_credit=args.membership_credit, + ) + _print_lines(result["lines"]) + net = result["net"] + if net >= 0: + print(f"NET DUE FROM CUSTOMER: ${net:.2f}") + else: + print(f"REFUND TO CUSTOMER: ${-net:.2f}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml new file mode 100644 index 0000000..2d7ed71 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + points_reconciliation.py + 0 + points_reconciliation.py + + cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py b/submissions/blastbox-omega/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py new file mode 100644 index 0000000..95b08ea --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_points_reconciliation_py_ae83bb1d3e6c/filedata/points_reconciliation.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +"""BlastBox Omega — BlastPoints Loyalty Reconciliation calculator (reference logic). + +Reconciles a member's BlastPoints after a complex visit (purchases, returns, +promos, expiry) and converts the net balance to store credit. + +Rules (mirror the BlastPoints Loyalty Program Terms): + * Base earn: 10 points per $1 of merchandise. + * Promo events (e.g. Triple BLAST Weekend) multiply the BASE earn by the promo + multiplier BEFORE the tier bonus is applied. + * Tier bonus on earned points: Plus +0%, Plus Extra +20%, MEGA +50%. + * Returned items are clawed back: points are removed using the SAME formula + (promo multiplier + tier bonus that applied to the original purchase). + * Expired points (older than 90 days while the account was inactive) are + removed as provided. + * Redemption: 1,000 points = $5.00 store credit (whole 1,000-point blocks). +""" +import argparse, json + +TIER_BONUS = {"plus": 0.0, "extra": 0.20, "mega": 0.50} + + +def earn(amount, promo_multiplier, tier_bonus): + base = 10.0 * amount + return int(round(base * promo_multiplier * (1.0 + tier_bonus))) + + +def reconcile(tier, prior_balance, purchases, returns, expired_points): + bonus = TIER_BONUS[tier] + lines = [] + earned = 0 + for p in purchases: + pts = earn(p["amount"], p.get("promo_multiplier", 1.0), bonus) + earned += pts + lines.append({"type": "earn", "amount": p["amount"], + "promo_multiplier": p.get("promo_multiplier", 1.0), + "points": pts}) + clawback = 0 + for r in returns: + pts = earn(r["amount"], r.get("promo_multiplier", 1.0), bonus) + clawback += pts + lines.append({"type": "clawback", "amount": r["amount"], + "promo_multiplier": r.get("promo_multiplier", 1.0), + "points": -pts}) + if expired_points: + lines.append({"type": "expired", "points": -int(expired_points)}) + + net_balance = int(prior_balance + earned - clawback - int(expired_points)) + if net_balance < 0: + net_balance = 0 + blocks = net_balance // 1000 + store_credit = money(blocks * 5.0) + return { + "tier": tier, + "tier_bonus_pct": int(bonus * 100), + "prior_balance": int(prior_balance), + "earned": earned, + "clawback": clawback, + "expired": int(expired_points), + "ledger": lines, + "net_balance": net_balance, + "redeemable_blocks": int(blocks), + "store_credit_value": store_credit, + } + + +def money(v): + cents = int(v * 100 + 0.5) + return cents / 100.0 + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--tier", choices=["plus", "extra", "mega"], required=True) + ap.add_argument("--prior-balance", type=float, default=0) + ap.add_argument("--purchases", default="[]", help="JSON list {amount, promo_multiplier}") + ap.add_argument("--returns", default="[]", help="JSON list {amount, promo_multiplier}") + ap.add_argument("--expired-points", type=float, default=0) + a = ap.parse_args() + print(json.dumps(reconcile(a.tier, a.prior_balance, json.loads(a.purchases), + json.loads(a.returns), a.expired_points), indent=2)) + + +if __name__ == "__main__": + main() diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml new file mode 100644 index 0000000..8d0f9cc --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + SKILL.md + 0 + SKILL.md + + cr26e_storeassociateassistant_ZQHUx1.skill.prorated-refund-calculator_KZe + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md new file mode 100644 index 0000000..6179946 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_30aa9a7b2b7c/filedata/SKILL.md @@ -0,0 +1,83 @@ +--- +name: prorated-refund-calculator +description: Calculate the exact prorated refund when a customer cancels a BlastPass membership, and the net settlement when a defective unit is traded up to a pricier model. Use after the Store Policy Agent confirms the refund rule and the Membership MCP has returned the membership details. The skill runs a bundled Python script so the arithmetic is exact. +--- + +# Prorated Refund Calculator (BlastPass) + +This skill computes the **exact** refund owed when a customer cancels their +BlastPass membership, and the **net settlement** when a defective unit is credited +toward a pricier model. The math must be exact and consistent, so you **run the +bundled `blastpass_refund.py` script** — never compute the refund in your head, and +never write your own code. + +## When to use + +Use this skill in a BlastPass cancellation or trade-up, once you have: + +1. The **Store Policy Agent** has confirmed the member's tier and that a + **prorated refund** (or cooling-off full refund) applies. +2. The **Membership MCP** (`get_membership`) has returned the membership record, + including `annual_price`, `months_remaining`, `cancellation_fee`, + `nonrefundable_credit`, and `within_cooling_off`. + +Do **not** invent any of these numbers. They come from the Membership MCP and the +Store Policy Agent. + +## The refund rules (single source of truth) + +1. **Cooling-off (full refund).** If `within_cooling_off` is true, refund the full + `annual_price`. No fee, no proration. +2. **Prorated cancellation (the usual case).** Otherwise refund the value of the + whole unused months left on the 12-month term, then subtract the tier + cancellation fee. +3. **Non-refundable credit.** Subtract `nonrefundable_credit` (only the MEGA tier + has one). For other tiers it is 0. +4. The refund is **never negative**. + +## Workflow + +### 1. Compute the membership refund + +Take `annual_price`, `months_remaining`, `cancellation_fee`, +`nonrefundable_credit`, and `within_cooling_off` from the `get_membership` result, +and run: + +```bash +python3 blastpass_refund.py refund \ + --annual-price 129.99 --months-remaining 8 --cancellation-fee 10 +``` + +Add `--nonrefundable-credit ` for MEGA members, or `--within-cooling-off` when +the membership is still within the cooling-off window. The script prints the +breakdown and a `REFUND DUE: $...` line. + +Worked example: BlastPass Plus Extra, `annual_price 129.99`, `months_remaining 8`, +`cancellation_fee 10` -> monthly $10.8325, prorated $86.66, minus $10 = **$76.66**. + +### 2. (Trade-up only) Compute the upgrade settlement + +When the cancellation happens alongside a console upgrade — the defective unit is +credited under warranty and the customer puts the value toward a pricier model, +applying the membership refund as store credit — run: + +```bash +python3 blastpass_refund.py settle \ + --defective-credit 399.99 --upgrade-price 499.99 --membership-credit 76.66 +``` + +Use after (a) the Store Policy Agent confirms the defective unit is credited at its +purchase price with no restocking fee, (b) the Warehouse MCP returns the upgrade +model's price, and (c) the prorated refund above is known. The script prints the +breakdown and a `NET DUE FROM CUSTOMER: $...` (or `REFUND TO CUSTOMER: $...`) line. + +Worked example: defective base console credited $399.99, MEGA edition $499.99, +membership refund $76.66 applied as credit -> upgrade difference $100.00, net +**$23.34 due from the customer**. + +### 3. Report the result + +- State the **refund due** and (if a trade-up) the **net due / refund** clearly. +- Show the breakdown lines so the associate can verify how it was derived. +- Offer to cancel the membership via the Membership MCP (`cancel_membership`) and + to hand the final numbers to the `slip-pdf-generator` skill to print the slip. diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml new file mode 100644 index 0000000..5fe9147 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + SKILL.md + 0 + SKILL.md + + cr26e_storeassociateassistant_ZQHUx1.skill.points-reconciliation__iK + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md new file mode 100644 index 0000000..a73618c --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_4e6ebf925ae8/filedata/SKILL.md @@ -0,0 +1,80 @@ +--- +name: points-reconciliation +description: Reconcile a customer's BlastPoints loyalty balance after a complex visit (purchases, returns, promo multipliers, expiry) and convert the net balance to store credit. Use when an associate asks about a member's points, or after a return that should claw back previously earned points. The skill runs a bundled Python script that applies the base earn rate, promo multipliers, tier bonus, clawbacks, and expiry, then prints a points ledger and the redeemable store-credit value. +--- + +# BlastPoints Loyalty Reconciliation (BlastBox Omega) + +This skill reconciles a member's **BlastPoints** after a visit that involves any +mix of earning, returns (clawback), promo events, and expiry, and converts the +net balance to store credit. The points math must be exact, so you **run the +bundled `points_reconciliation.py` script** — never estimate points by hand, and +never write your own code. + +## When to use + +Use this skill when an associate asks things like: +- "How many BlastPoints does this member have after today?" +- "They returned a console — claw back the points they earned on it." +- "What's that worth in store credit?" + +Confirm the member's **tier** with the Membership MCP / Store Policy Agent first, +and confirm any **promo multiplier** in effect with the Store Policy Agent +(Policy RAG MCP, `search_policy "BlastPoints promo"`). + +## The points rules (single source of truth) + +These mirror the BlastPoints Loyalty Program Terms held by the Store Policy Agent: + +1. **Base earn:** 10 points per $1 of merchandise. +2. **Promo multiplier:** promo events (e.g. **Triple BLAST Weekend** = 3x) + multiply the **base** earn **before** the tier bonus is applied. +3. **Tier bonus on earned points:** Plus **+0%**, Plus Extra **+20%**, MEGA + **+50%**. +4. **Clawback:** returned items remove points using the **same** formula (the + promo multiplier and tier bonus that applied to the original purchase). +5. **Expiry:** points older than 90 days while the account was inactive are + removed (provided as a number). +6. **Redemption:** **1,000 points = $5.00** store credit, in whole 1,000-point + blocks. The net balance never goes below 0. + +## Workflow + +### 1. Gather the inputs + +Get the **tier** (`plus` / `extra` / `mega`), the **prior balance**, the list of +**purchases** (each `amount` plus its `promo_multiplier`), the list of **returns** +to claw back (same shape), and any **expired points**. + +### 2. Run the bundled script + +Pass the inputs to `points_reconciliation.py`. Purchases and returns are JSON +lists of `{"amount": , "promo_multiplier": }`: + +```bash +python3 points_reconciliation.py \ + --tier extra \ + --prior-balance 4200 \ + --purchases '[{"amount": 499.99, "promo_multiplier": 3.0}]' \ + --expired-points 800 +``` + +Add `--returns '[{"amount": 79.99, "promo_multiplier": 3.0}]'` when items are +being returned and their points clawed back. The script prints a JSON object with +the ledger, `net_balance`, `redeemable_blocks`, and `store_credit_value`. + +### 3. Report the result + +- State the **net BlastPoints balance** and the **store-credit value**. +- Show the ledger lines (earn / clawback / expired) so the associate can verify. +- Offer to apply the redeemable store credit to the current transaction. + +## Worked example + +Plus Extra member, prior balance 4,200, a Triple BLAST Weekend console purchase of +$499.99 (3x), and 800 expired points: + +- Earn: $499.99 x 3 x 1.2 (Plus Extra +20%) = **18,000** +- Expired: **-800** +- Net = 4,200 + 18,000 - 800 = **21,400 BlastPoints** +- Redeemable: 21 x 1,000 = **$105.00** store credit diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml new file mode 100644 index 0000000..64c60d0 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_d925ad573675/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + SKILL.md + 0 + SKILL.md + + cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4 + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md new file mode 100644 index 0000000..175c092 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_skill_md_d925ad573675/filedata/SKILL.md @@ -0,0 +1,104 @@ +--- +name: slip-pdf-generator +description: Generate a printable BlastBox return / RMA / exchange slip as a PDF for the associate to hand to the customer. Use at the end of a return, warranty swap, membership cancellation, or upgrade once all amounts are final. The skill runs a bundled Python script that renders the confirmed slip values into a real PDF file. +--- + +# Return / RMA Slip PDF Generator + +This skill produces the **printable slip** that closes out a transaction at the +returns desk. You collect the final, already-confirmed values, then **run the +bundled `slip_pdf.py` script** to render the slip to a **PDF** using `reportlab`. +Do not hand-draw the PDF, write your own rendering code, or invent amounts — every +value comes from earlier steps (Policy Agent ruling, Membership MCP, Warehouse MCP, +and the `prorated-refund-calculator` skill). + +## When to use + +Use this skill **last**, after every amount is final: + +- the eligibility / warranty ruling is decided (Store Policy Agent), +- any membership refund is computed (`prorated-refund-calculator`), +- any upgrade settlement / net-due is computed (`prorated-refund-calculator`), +- the customer has agreed to the outcome. + +Do **not** use it to *decide* anything — it only renders the agreed result. + +## Slip types + +Set `slip_type` to the right label: + +- `WARRANTY SWAP` — defective item replaced under warranty. +- `RETURN / REFUND` — item returned for a refund. +- `EXCHANGE / UPGRADE` — item swapped for a different model with a price difference. +- `MEMBERSHIP CANCELLATION` — BlastPass cancelled (often combined with the above). + +## Workflow + +### 1. Assemble the confirmed values + +Build a JSON object with the final values. Every field is optional except those +that apply to this transaction. Keep amounts formatted to 2 decimals with a `$` +prefix. + +| Field | Meaning | +| --- | --- | +| `slip_type` | one of the labels above | +| `rma_ref` | RMA / reference number | +| `date` | `YYYY-MM-DD` | +| `store` / `associate` | store name/number and associate/desk | +| `customer_name` | customer name | +| `member_id` / `member_tier` | BlastPass id and tier | +| `items` | list of `[item, sku, disposition, amount]` rows | +| `settlement` | list of `[label, amount]` rows (upgrade difference, refund applied as credit, etc.) | +| `net_label` / `net_amount` | e.g. `NET DUE FROM CUSTOMER` / `$23.34`, or `REFUND TO CUSTOMER` / `$76.66` | +| `notes` | free-text notes | + +### 2. Run the bundled script + +Pass the JSON to `slip_pdf.py`: + +```bash +python3 slip_pdf.py --slip-json '' --output blastbox_slip.pdf +``` + +For the upgrade-settlement example (defective base console → MEGA edition, with a +BlastPass refund applied as store credit): + +```bash +python3 slip_pdf.py --output blastbox_slip.pdf --slip-json '{ + "slip_type": "EXCHANGE / UPGRADE", + "rma_ref": "RMA-OMEGA-1024", + "date": "2026-06-07", + "store": "BlastBox #214 - Seattle", + "associate": "Associate Desk", + "customer_name": "Jordan Pixel", + "member_id": "MEGA-BLAST-1024", + "member_tier": "BlastPass Plus Extra", + "items": [ + ["BlastBox Omega (defective)", "SKU-OMEGA", "Warranty credit", "$399.99"], + ["BlastBox Omega MEGA Edition", "SKU-OMEGA-MEGA", "Upgrade (new)", "$499.99"] + ], + "settlement": [ + ["Upgrade difference ($499.99 - $399.99)", "$100.00"], + ["BlastPass Plus Extra refund applied as store credit", "-$76.66"] + ], + "net_label": "NET DUE FROM CUSTOMER", + "net_amount": "$23.34", + "notes": "Manufacturing defect confirmed within 30 days - no restocking fee. BlastPass Plus Extra (MEGA-BLAST-1024) cancelled; 8 unused months prorated." +}' +``` + +The script writes the PDF and prints `WROTE blastbox_slip.pdf`. If the JSON is +long, you may write it to a file and pass `--slip-file slip.json` instead. + +### 3. Report + +- Confirm the PDF was written and give its filename. +- Restate the **net amount** (due or refunded) and the slip type. +- Offer to email or reprint the slip if needed. + +## Notes + +- The script uses `reportlab` (the available PDF library). No network calls. +- The layout fits a single page for a normal transaction. +- Pass plain text in the values; the script escapes `&` for you. diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml b/submissions/blastbox-omega/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml new file mode 100644 index 0000000..062babf --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/botcomponent.xml @@ -0,0 +1,14 @@ + + 14 + slip_pdf.py + 0 + slip_pdf.py + + cr26e_storeassociateassistant_ZQHUx1.skill.slip-pdf-generator_2i4 + + + cr26e_storeassociateassistant_ZQHUx1 + + 0 + 1 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py b/submissions/blastbox-omega/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py new file mode 100644 index 0000000..8888d10 --- /dev/null +++ b/submissions/blastbox-omega/solution/botcomponents/cra24a45_slip_pdf_py_e55f2fee6a70/filedata/slip_pdf.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""BlastBox Returns & Service slip -> PDF renderer. + +Renders the printable return / RMA / exchange slip that closes out a transaction +at the returns desk. All amounts and labels are passed in as a single JSON blob +of already-confirmed values; this script only lays them out and writes the PDF. + +Usage: + python3 slip_pdf.py --slip-json '' [--output blastbox_slip.pdf] + python3 slip_pdf.py --slip-file slip.json [--output blastbox_slip.pdf] + +The JSON object accepts these fields (omit any that do not apply): + slip_type str e.g. "EXCHANGE / UPGRADE", "RETURN / REFUND", + "WARRANTY SWAP", "MEMBERSHIP CANCELLATION" + rma_ref str RMA / reference number + date str YYYY-MM-DD + store str store name / number + associate str associate name or desk + customer_name str + member_id str BlastPass member id + member_tier str BlastPass tier + items list of [item, sku, disposition, amount] rows + settlement list of [label, amount] rows + net_label str e.g. "NET DUE FROM CUSTOMER" / "REFUND TO CUSTOMER" + net_amount str e.g. "$23.34" + notes str free-text notes + +Every value comes from earlier steps (Store Policy Agent ruling, Membership MCP, +Warehouse MCP, and the prorated-refund-calculator skill). This script never +decides or invents amounts. +""" + +import argparse +import json +import sys + +ACCENT = "#6b2fb5" +ROW_ALT = "#f3eefb" + + +def _esc(text): + """Escape ampersands for reportlab Paragraph markup.""" + return str(text).replace("&", "&") + + +def build_pdf(slip, output): + from reportlab.lib.pagesizes import LETTER + from reportlab.lib.units import inch + from reportlab.lib import colors + from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table, + TableStyle, HRFlowable) + from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle + + styles = getSampleStyleSheet() + h1 = ParagraphStyle("h1", parent=styles["Title"], fontSize=18, spaceAfter=6) + h2 = ParagraphStyle("h2", parent=styles["Heading2"], fontSize=12, + spaceBefore=10, spaceAfter=4) + body = styles["BodyText"] + + doc = SimpleDocTemplate(output, pagesize=LETTER, + topMargin=0.7 * inch, bottomMargin=0.7 * inch) + flow = [] + flow.append(Paragraph("BlastBox - Returns & Service Slip", h1)) + flow.append(HRFlowable(width="100%", color=colors.HexColor(ACCENT))) + + meta = (f"Slip type: {_esc(slip.get('slip_type', ''))}    " + f"RMA / Ref #: {_esc(slip.get('rma_ref', ''))}
" + f"Date: {_esc(slip.get('date', ''))}    " + f"Store / Associate: {_esc(slip.get('store', ''))} / " + f"{_esc(slip.get('associate', ''))}") + flow.append(Paragraph(meta, body)) + + if slip.get("customer_name"): + flow.append(Paragraph("Customer", h2)) + flow.append(Paragraph( + f"{_esc(slip.get('customer_name', ''))}  |  " + f"BlastPass {_esc(slip.get('member_id', ''))} " + f"({_esc(slip.get('member_tier', ''))})", body)) + + items = slip.get("items") or [] + if items: + flow.append(Paragraph("Items", h2)) + item_data = [["Item", "SKU", "Disposition", "Amount"]] + [list(r) for r in items] + t = Table(item_data, colWidths=[2.6 * inch, 1.3 * inch, 1.5 * inch, 1.0 * inch]) + t.setStyle(TableStyle([ + ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor(ACCENT)), + ("TEXTCOLOR", (0, 0), (-1, 0), colors.white), + ("FONTSIZE", (0, 0), (-1, -1), 9), + ("GRID", (0, 0), (-1, -1), 0.5, colors.grey), + ("ALIGN", (-1, 0), (-1, -1), "RIGHT"), + ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.HexColor(ROW_ALT)]), + ])) + flow.append(t) + + settlement = slip.get("settlement") or [] + if settlement: + flow.append(Paragraph("Settlement", h2)) + for label, amount in settlement: + flow.append(Paragraph(f"{_esc(label)}: {_esc(amount)}", body)) + + if slip.get("net_label") or slip.get("net_amount"): + flow.append(Spacer(1, 6)) + flow.append(Paragraph( + f"{_esc(slip.get('net_label', ''))}: " + f"{_esc(slip.get('net_amount', ''))}", h2)) + + if slip.get("notes"): + flow.append(Paragraph("Notes", h2)) + flow.append(Paragraph(_esc(slip["notes"]), body)) + + flow.append(Spacer(1, 10)) + flow.append(Paragraph("Keep this slip for your records. Warranty swaps carry " + "the remaining original warranty.", body)) + + doc.build(flow) + + +def main(argv): + parser = argparse.ArgumentParser(description="Render a BlastBox return/RMA slip PDF.") + src = parser.add_mutually_exclusive_group(required=True) + src.add_argument("--slip-json", help="Slip data as a JSON string.") + src.add_argument("--slip-file", help="Path to a JSON file with the slip data.") + parser.add_argument("--output", default="blastbox_slip.pdf", + help="Output PDF path (default: blastbox_slip.pdf).") + args = parser.parse_args(argv[1:]) + + try: + if args.slip_json: + slip = json.loads(args.slip_json) + else: + with open(args.slip_file, "r", encoding="utf-8") as fh: + slip = json.load(fh) + except (ValueError, OSError) as exc: + print(f"Error: could not read slip JSON ({exc}).") + return 1 + + try: + build_pdf(slip, args.output) + except ImportError: + print("Error: reportlab is required to render the slip PDF.") + return 1 + + print(f"WROTE {args.output}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/submissions/blastbox-omega/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml b/submissions/blastbox-omega/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml new file mode 100644 index 0000000..5c9f448 --- /dev/null +++ b/submissions/blastbox-omega/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Inventory & Fullfilment Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json b/submissions/blastbox-omega/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json new file mode 100644 index 0000000..ea735b3 --- /dev/null +++ b/submissions/blastbox-omega/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the Inventory \u0026 Fulfillment Agent for BlastBox Omega stores. You support the Returns \u0026 Service Assistant with stock, alternatives, restock, and game compatibility.\u200B\u200C\n\nTools:\u200B\u200C\n- check\\_stock(sku): quantity on hand, price, warehouse, and whether the item is in stock.\u200B\u200C\n- - find\\_alternatives(sku): comparable in-stock products (e.g. a higher console model) with prices.\u200B\u200C\n- - get\\_restock\\_date(sku): next restock date and supplier for an out-of-stock item.\u200B\u200C\n- - check\\_game\\_compatibility(game\\_title): which BlastBox Omega console model a game requires.\u200B\u200C\nRules:\u200B\u200C\n- Always use the tools; never invent stock levels, prices, SKUs, or dates.\u200B\u200C\n- - The base console is SKU-OMEGA ($399.99); the upgraded model is SKU-OMEGA-MEGA, the BlastBox Omega MEGA Edition ($499.99).\u200B\u200C\n- - If the base console is out of stock, report the restock date AND surface the MEGA Edition as an in-stock alternative.\u200B\u200C\n- - If a customer mentions a specific game, call check\\_game\\_compatibility and state which model it needs.\u200B\u200C\nBe concise. Always include SKUs, prices, and quantities you looked up.\u200B\u200C\n\n\nInventory aging (merchandising reviews):\u200B\u200C\n- When the Merchandising Insights Assistant asks about inventory aging, weeks-of-cover, or how long slow-moving SKUs have been sitting, call get\\_inventory\\_aging.\u200B\u200C\n- - Report, per SKU, the aging (days/weeks on hand) and weeks-of-cover so markdown decisions can be justified.\u200B\u200C" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml b/submissions/blastbox-omega/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml new file mode 100644 index 0000000..b58beff --- /dev/null +++ b/submissions/blastbox-omega/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Store Policy Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json b/submissions/blastbox-omega/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json new file mode 100644 index 0000000..82b01ee --- /dev/null +++ b/submissions/blastbox-omega/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "You are the Store Policy Agent \u2014 the authority on BlastBox Omega store policy: returns, warranties, BlastPass membership rules, and merchandising markdown. You serve the Returns \u0026 Service Assistant and the Merchandising Insights Assistant.\n\nGround every answer in your tools; never invent policy numbers, fees, or percentages.\n\nDefective consoles: before ruling, ask whether the failure is a manufacturing fault (won\u0027t power on, no physical damage) or possible accidental/physical/liquid damage. Manufacturing fault inside 30 days is a free warranty swap, no restocking fee. Accidental damage is a standard return that may carry a restocking fee.\n\nBlastPass cancellations: return the tier refund rule; the Returns \u0026 Service Assistant computes the prorated amount.\n\nMarkdown: if the type isn\u0027t specified, relay the promo-vs-clearance clarification. Clearance needs manager sign-off.\n\nBe concise: state the window, the rule, and any fee applied.\n" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml b/submissions/blastbox-omega/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml new file mode 100644 index 0000000..3828bb5 --- /dev/null +++ b/submissions/blastbox-omega/solution/bots/cr26e_storeassociateassistant_ZQHUx1/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Store Associate Assistant + 0 + + 4 + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json b/submissions/blastbox-omega/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json new file mode 100644 index 0000000..ee688ec --- /dev/null +++ b/submissions/blastbox-omega/solution/bots/cr26e_storeassociateassistant_ZQHUx1/configuration.json @@ -0,0 +1 @@ +{"categories":[],"channels":[{"id":"MsTeams","channelId":"MsTeams","channelSpecifier":null,"displayName":null}],"settings":{},"publishOnCreate":false,"publishOnImport":true,"deferredProvisioning":false,"isLightweightBot":false,"$kind":"BotConfiguration","recognizer":{"$kind":"CLICopilotRecognizer"},"agentSettings":{"$kind":"AgentSettings","model":{"$kind":"ModelConfig","series":"Sonnet46"},"instructions":{"$kind":"Instructions","segments":[{"$kind":"StaticSegment","value":"You are the Store Associate Assistant for BlastBox Omega, a retro-gaming store. You help store associates resolve a customer's whole visit - returns, warranty swaps, membership changes, trade-ups, and settlement.\n\nBe warm, concise, and decisive. Talk to the associate as a knowledgeable colleague: lead with the answer, lay out the options with their dollar impact, and end with a single clear question when a decision is needed.\n\nEstablish the facts before you act, ground every figure and ruling in your tools and skills rather than guessing, and confirm with the associate before making any change. When a teammate agent needs more information, relay its question and wait for the answer."}]}}} \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/customizations.xml b/submissions/blastbox-omega/solution/customizations.xml new file mode 100644 index 0000000..0a1b2da --- /dev/null +++ b/submissions/blastbox-omega/solution/customizations.xml @@ -0,0 +1,111 @@ + + + + + + /Workflows/BlastBoxWeeklyInventoryHealth-44D64F96-1130-9B19-8209-34093D4416A3.json + 1 + 0 + 5 + 0 + 4 + 0 + 0 + 0 + 0 + 0 + 1 + 2 + 1 + 1 + 1.0 + 1 + 0 + 1 + 1 + none + + + + + + /Workflows/BlastBoxPostSalesService-62D468F3-4A55-1BA5-2EC3-AE1A4B3A4B35.json + 1 + 0 + 5 + 0 + 4 + 0 + 0 + 0 + 0 + 0 + 1 + 2 + 1 + 1 + 1.0 + 1 + 0 + 1 + 1 + none + + + + + + + + + + + + + + + + 32399478-9062-f111-ab0b-0022480a52c7 + + Policy RAG MCP v2 + #007ee5 + new_policy-20rag-20mcp-20v2 + 1 + [] + /Connector/new_policy-20rag-20mcp-20v2_openapidefinition.json + /Connector/new_policy-20rag-20mcp-20v2_connectionparameters.json + /Connector/new_policy-20rag-20mcp-20v2_policytemplateinstances.json + /Connector/new_policy-20rag-20mcp-20v2_customcodeblobcontent.csx + + + e4e7e713-5c3f-f111-bec6-70a8a59a4421 + + Order Management MCP + #007ee5 + new_order-20management-20mcp + 1 + [] + /Connector/new_order-20management-20mcp_openapidefinition.json + /Connector/new_order-20management-20mcp_connectionparameters.json + /Connector/new_order-20management-20mcp_policytemplateinstances.json + /Connector/new_order-20management-20mcp_customcodeblobcontent.csx + + + 357a348b-d53f-f111-bec6-70a8a59a4421 + Warehouse inventory and fulfillment with tools for checking stock, tracking pipeline stages, finding alternatives, and restock dates. Runs entirely inside the connector — no external MCP server needed. + Warehouse MCP + #007ee5 + new_warehouse-20mcp + 1 + [] + /Connector/new_warehouse-20mcp_openapidefinition.json + /Connector/new_warehouse-20mcp_connectionparameters.json + /Connector/new_warehouse-20mcp_policytemplateinstances.json + /Connector/new_warehouse-20mcp_customcodeblobcontent.csx + /Connector/new_warehouse-20mcp_iconblob.Png + + + + 1033 + + \ No newline at end of file diff --git a/submissions/blastbox-omega/solution/solution.xml b/submissions/blastbox-omega/solution/solution.xml new file mode 100644 index 0000000..460f1ff --- /dev/null +++ b/submissions/blastbox-omega/solution/solution.xml @@ -0,0 +1,129 @@ + + + WorkflowsBlastbox + + + + + 1.0.0.3 + 0 + + AdrianaTrujillo + + + + + + + at + 12099 + +
+ 1 + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + + +
+
+ 2 + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/submissions/campaign-content-engine/README.md b/submissions/campaign-content-engine/README.md new file mode 100644 index 0000000..6a9ebd5 --- /dev/null +++ b/submissions/campaign-content-engine/README.md @@ -0,0 +1,13 @@ +# Campaign Content Engine + +Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls. + +## How it works + +- Read brief +- Load brand voice +- Draft channels +- Review content +- Publish kit + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/campaign-content-engine/metadata.json b/submissions/campaign-content-engine/metadata.json new file mode 100644 index 0000000..9a476c4 --- /dev/null +++ b/submissions/campaign-content-engine/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Campaign Content Engine", + "description": "Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls.", + "tags": [ + "marketing", + "content", + "brand", + "approval", + "demo" + ], + "author": "Maya Chen", + "authorGithub": "mayachen", + "createdAt": "2026-08-07", + "featured": false +} diff --git a/submissions/campaign-content-engine/solution/Workflows/workflow.json b/submissions/campaign-content-engine/solution/Workflows/workflow.json new file mode 100644 index 0000000..4ff994a --- /dev/null +++ b/submissions/campaign-content-engine/solution/Workflows/workflow.json @@ -0,0 +1,150 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Create campaign content kit", + "nodes": [ + { + "id": "node-1", + "name": "When a campaign brief is approved", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "When a campaign brief is approved", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Read brief", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Read brief", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Load brand voice", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Load brand voice", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Draft channels", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Draft channels", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Review content", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Review content", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Publish kit", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Publish kit", + "operationType": "action" + } + } + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + } + } +} diff --git a/submissions/campaign-content-engine/solution/bots/campaign_content_engine_agent/bot.xml b/submissions/campaign-content-engine/solution/bots/campaign_content_engine_agent/bot.xml new file mode 100644 index 0000000..3802f79 --- /dev/null +++ b/submissions/campaign-content-engine/solution/bots/campaign_content_engine_agent/bot.xml @@ -0,0 +1 @@ +Campaign Content Agent diff --git a/submissions/campaign-content-engine/solution/bots/campaign_content_engine_agent/configuration.json b/submissions/campaign-content-engine/solution/bots/campaign_content_engine_agent/configuration.json new file mode 100644 index 0000000..8558ea2 --- /dev/null +++ b/submissions/campaign-content-engine/solution/bots/campaign_content_engine_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "GPT-5" + }, + "instructions": { + "segments": [ + { + "value": "Builds a channel-ready campaign kit from one brief with brand, audience, and approval controls." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/campaign-content-engine/solution/customizations.xml b/submissions/campaign-content-engine/solution/customizations.xml new file mode 100644 index 0000000..c617935 --- /dev/null +++ b/submissions/campaign-content-engine/solution/customizations.xml @@ -0,0 +1 @@ +Create campaign content kitworkflow.jsoncampaign-content-engine-workflow diff --git a/submissions/campaign-content-engine/solution/solution.xml b/submissions/campaign-content-engine/solution/solution.xml new file mode 100644 index 0000000..757d46f --- /dev/null +++ b/submissions/campaign-content-engine/solution/solution.xml @@ -0,0 +1 @@ +Demo_campaign_content_engine1.0.0.0CAT Gallery Demos diff --git a/submissions/contract-review-assistant/README.md b/submissions/contract-review-assistant/README.md new file mode 100644 index 0000000..e37b085 --- /dev/null +++ b/submissions/contract-review-assistant/README.md @@ -0,0 +1,12 @@ +# Contract Review Assistant + +Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval. + +## How it works + +- Extract clauses +- Compare playbook +- Score risk +- Request approval + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/contract-review-assistant/metadata.json b/submissions/contract-review-assistant/metadata.json new file mode 100644 index 0000000..9d347eb --- /dev/null +++ b/submissions/contract-review-assistant/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Contract Review Assistant", + "description": "Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval.", + "tags": [ + "document-ops", + "legal", + "compliance", + "human-in-the-loop", + "demo" + ], + "author": "Sofia Alvarez", + "authorGithub": "sofiaalvarez", + "createdAt": "2026-08-08", + "featured": true +} diff --git a/submissions/contract-review-assistant/solution/Workflows/workflow.json b/submissions/contract-review-assistant/solution/Workflows/workflow.json new file mode 100644 index 0000000..d78febf --- /dev/null +++ b/submissions/contract-review-assistant/solution/Workflows/workflow.json @@ -0,0 +1,130 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Review contract package", + "nodes": [ + { + "id": "node-1", + "name": "When a contract is uploaded", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "When a contract is uploaded", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Extract clauses", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Extract clauses", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Compare playbook", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Compare playbook", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Score risk", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Score risk", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Request approval", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Request approval", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + } + } +} diff --git a/submissions/contract-review-assistant/solution/bots/contract_review_assistant_agent/bot.xml b/submissions/contract-review-assistant/solution/bots/contract_review_assistant_agent/bot.xml new file mode 100644 index 0000000..a3a03d5 --- /dev/null +++ b/submissions/contract-review-assistant/solution/bots/contract_review_assistant_agent/bot.xml @@ -0,0 +1 @@ +Contract Review Agent diff --git a/submissions/contract-review-assistant/solution/bots/contract_review_assistant_agent/configuration.json b/submissions/contract-review-assistant/solution/bots/contract_review_assistant_agent/configuration.json new file mode 100644 index 0000000..19c715a --- /dev/null +++ b/submissions/contract-review-assistant/solution/bots/contract_review_assistant_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "Sonnet 4.6" + }, + "instructions": { + "segments": [ + { + "value": "Extracts key clauses, flags policy deviations, and prepares a review packet for legal approval." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/contract-review-assistant/solution/customizations.xml b/submissions/contract-review-assistant/solution/customizations.xml new file mode 100644 index 0000000..b38b847 --- /dev/null +++ b/submissions/contract-review-assistant/solution/customizations.xml @@ -0,0 +1 @@ +Review contract packageworkflow.jsoncontract-review-assistant-workflow diff --git a/submissions/contract-review-assistant/solution/solution.xml b/submissions/contract-review-assistant/solution/solution.xml new file mode 100644 index 0000000..5204f7c --- /dev/null +++ b/submissions/contract-review-assistant/solution/solution.xml @@ -0,0 +1 @@ +Demo_contract_review_assistant1.0.0.0CAT Gallery Demos diff --git a/submissions/customer-support-triage/README.md b/submissions/customer-support-triage/README.md new file mode 100644 index 0000000..1a808c4 --- /dev/null +++ b/submissions/customer-support-triage/README.md @@ -0,0 +1,13 @@ +# Customer Support Triage + +Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue. + +## How it works + +- Classify case +- Search knowledge +- Draft response +- Human review +- Update case + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/customer-support-triage/metadata.json b/submissions/customer-support-triage/metadata.json new file mode 100644 index 0000000..0782205 --- /dev/null +++ b/submissions/customer-support-triage/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Customer Support Triage", + "description": "Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue.", + "tags": [ + "support", + "customer-service", + "ai", + "human-in-the-loop", + "demo" + ], + "author": "Maya Chen", + "authorGithub": "mayachen", + "createdAt": "2026-08-13", + "featured": true +} diff --git a/submissions/customer-support-triage/solution/Workflows/workflow.json b/submissions/customer-support-triage/solution/Workflows/workflow.json new file mode 100644 index 0000000..eca5bad --- /dev/null +++ b/submissions/customer-support-triage/solution/Workflows/workflow.json @@ -0,0 +1,150 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Triage and route support cases", + "nodes": [ + { + "id": "node-1", + "name": "When a support case is created", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "When a support case is created", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Classify case", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Classify case", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Search knowledge", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Search knowledge", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Draft response", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Draft response", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Human review", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Human review", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Update case", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Update case", + "operationType": "action" + } + } + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + } + } +} diff --git a/submissions/customer-support-triage/solution/bots/customer_support_triage_agent/bot.xml b/submissions/customer-support-triage/solution/bots/customer_support_triage_agent/bot.xml new file mode 100644 index 0000000..6659170 --- /dev/null +++ b/submissions/customer-support-triage/solution/bots/customer_support_triage_agent/bot.xml @@ -0,0 +1 @@ +Support Triage Agent diff --git a/submissions/customer-support-triage/solution/bots/customer_support_triage_agent/configuration.json b/submissions/customer-support-triage/solution/bots/customer_support_triage_agent/configuration.json new file mode 100644 index 0000000..b8a317e --- /dev/null +++ b/submissions/customer-support-triage/solution/bots/customer_support_triage_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "GPT-5" + }, + "instructions": { + "segments": [ + { + "value": "Classifies incoming cases, drafts grounded answers, and routes high-risk issues to a human queue." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/customer-support-triage/solution/customizations.xml b/submissions/customer-support-triage/solution/customizations.xml new file mode 100644 index 0000000..a79b109 --- /dev/null +++ b/submissions/customer-support-triage/solution/customizations.xml @@ -0,0 +1 @@ +Triage and route support casesworkflow.jsoncustomer-support-triage-workflow diff --git a/submissions/customer-support-triage/solution/solution.xml b/submissions/customer-support-triage/solution/solution.xml new file mode 100644 index 0000000..4675eb9 --- /dev/null +++ b/submissions/customer-support-triage/solution/solution.xml @@ -0,0 +1 @@ +Demo_customer_support_triage1.0.0.0CAT Gallery Demos diff --git a/submissions/employee-onboarding-guide/README.md b/submissions/employee-onboarding-guide/README.md new file mode 100644 index 0000000..9f14ec2 --- /dev/null +++ b/submissions/employee-onboarding-guide/README.md @@ -0,0 +1,12 @@ +# Employee Onboarding Guide + +Coordinates a new hire checklist and answers policy, benefits, and first-week questions. + +## How it works + +- Create checklist +- Provision access +- Send welcome plan +- Check progress + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/employee-onboarding-guide/metadata.json b/submissions/employee-onboarding-guide/metadata.json new file mode 100644 index 0000000..dd26b24 --- /dev/null +++ b/submissions/employee-onboarding-guide/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Employee Onboarding Guide", + "description": "Coordinates a new hire checklist and answers policy, benefits, and first-week questions.", + "tags": [ + "hr", + "onboarding", + "knowledge", + "beginner", + "demo" + ], + "author": "Priya Raman", + "authorGithub": "priyaraman", + "createdAt": "2026-08-10", + "featured": false +} diff --git a/submissions/employee-onboarding-guide/solution/Workflows/workflow.json b/submissions/employee-onboarding-guide/solution/Workflows/workflow.json new file mode 100644 index 0000000..73d0354 --- /dev/null +++ b/submissions/employee-onboarding-guide/solution/Workflows/workflow.json @@ -0,0 +1,130 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "New hire onboarding", + "nodes": [ + { + "id": "node-1", + "name": "When an employee joins", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "When an employee joins", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Create checklist", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Create checklist", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Provision access", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Provision access", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Send welcome plan", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Send welcome plan", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Check progress", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Check progress", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + } + } +} diff --git a/submissions/employee-onboarding-guide/solution/bots/employee_onboarding_guide_agent/bot.xml b/submissions/employee-onboarding-guide/solution/bots/employee_onboarding_guide_agent/bot.xml new file mode 100644 index 0000000..c7613d3 --- /dev/null +++ b/submissions/employee-onboarding-guide/solution/bots/employee_onboarding_guide_agent/bot.xml @@ -0,0 +1 @@ +Onboarding Guide diff --git a/submissions/employee-onboarding-guide/solution/bots/employee_onboarding_guide_agent/configuration.json b/submissions/employee-onboarding-guide/solution/bots/employee_onboarding_guide_agent/configuration.json new file mode 100644 index 0000000..0e08131 --- /dev/null +++ b/submissions/employee-onboarding-guide/solution/bots/employee_onboarding_guide_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "GPT-4.1" + }, + "instructions": { + "segments": [ + { + "value": "Coordinates a new hire checklist and answers policy, benefits, and first-week questions." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/employee-onboarding-guide/solution/customizations.xml b/submissions/employee-onboarding-guide/solution/customizations.xml new file mode 100644 index 0000000..f8bf883 --- /dev/null +++ b/submissions/employee-onboarding-guide/solution/customizations.xml @@ -0,0 +1 @@ +New hire onboardingworkflow.jsonemployee-onboarding-guide-workflow diff --git a/submissions/employee-onboarding-guide/solution/solution.xml b/submissions/employee-onboarding-guide/solution/solution.xml new file mode 100644 index 0000000..f26867b --- /dev/null +++ b/submissions/employee-onboarding-guide/solution/solution.xml @@ -0,0 +1 @@ +Demo_employee_onboarding_guide1.0.0.0CAT Gallery Demos diff --git a/submissions/field-service-dispatch/README.md b/submissions/field-service-dispatch/README.md new file mode 100644 index 0000000..6cbf422 --- /dev/null +++ b/submissions/field-service-dispatch/README.md @@ -0,0 +1,12 @@ +# Field Service Dispatch + +Prioritizes service requests, finds the right technician, and keeps customers informed of arrival changes. + +## How it works + +- Assess request +- Find technician +- Reserve slot +- Notify customer + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/field-service-dispatch/metadata.json b/submissions/field-service-dispatch/metadata.json new file mode 100644 index 0000000..33c94ed --- /dev/null +++ b/submissions/field-service-dispatch/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Field Service Dispatch", + "description": "Prioritizes service requests, finds the right technician, and keeps customers informed of arrival changes.", + "tags": [ + "operations", + "field-service", + "scheduling", + "customer-service", + "demo" + ], + "author": "Amina Yusuf", + "authorGithub": "aminayusuf", + "createdAt": "2026-08-05", + "featured": false +} diff --git a/submissions/field-service-dispatch/solution/Workflows/workflow.json b/submissions/field-service-dispatch/solution/Workflows/workflow.json new file mode 100644 index 0000000..464ac53 --- /dev/null +++ b/submissions/field-service-dispatch/solution/Workflows/workflow.json @@ -0,0 +1,130 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Dispatch field service visit", + "nodes": [ + { + "id": "node-1", + "name": "When a service request is approved", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "When a service request is approved", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Assess request", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Assess request", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Find technician", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Find technician", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Reserve slot", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Reserve slot", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Notify customer", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Notify customer", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + } + } +} diff --git a/submissions/field-service-dispatch/solution/customizations.xml b/submissions/field-service-dispatch/solution/customizations.xml new file mode 100644 index 0000000..838d26a --- /dev/null +++ b/submissions/field-service-dispatch/solution/customizations.xml @@ -0,0 +1 @@ +Dispatch field service visitworkflow.jsonfield-service-dispatch-workflow diff --git a/submissions/field-service-dispatch/solution/solution.xml b/submissions/field-service-dispatch/solution/solution.xml new file mode 100644 index 0000000..449774e --- /dev/null +++ b/submissions/field-service-dispatch/solution/solution.xml @@ -0,0 +1 @@ +Demo_field_service_dispatch1.0.0.0CAT Gallery Demos diff --git a/submissions/incident-response-coordinator/README.md b/submissions/incident-response-coordinator/README.md new file mode 100644 index 0000000..e2253d7 --- /dev/null +++ b/submissions/incident-response-coordinator/README.md @@ -0,0 +1,13 @@ +# Incident Response Coordinator + +Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution. + +## How it works + +- Collect telemetry +- Assess severity +- Open bridge +- Post updates +- Close incident + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/incident-response-coordinator/metadata.json b/submissions/incident-response-coordinator/metadata.json new file mode 100644 index 0000000..118a977 --- /dev/null +++ b/submissions/incident-response-coordinator/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Incident Response Coordinator", + "description": "Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution.", + "tags": [ + "it-ops", + "incident-response", + "teams", + "automation", + "demo" + ], + "author": "Elliot Stone", + "authorGithub": "elliotstone", + "createdAt": "2026-08-09", + "featured": true +} diff --git a/submissions/incident-response-coordinator/solution/Workflows/workflow.json b/submissions/incident-response-coordinator/solution/Workflows/workflow.json new file mode 100644 index 0000000..010c2d5 --- /dev/null +++ b/submissions/incident-response-coordinator/solution/Workflows/workflow.json @@ -0,0 +1,150 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Coordinate service incident", + "nodes": [ + { + "id": "node-1", + "name": "When an alert is escalated", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "When an alert is escalated", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Collect telemetry", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Collect telemetry", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Assess severity", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Assess severity", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Open bridge", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Open bridge", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Post updates", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Post updates", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Close incident", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Close incident", + "operationType": "action" + } + } + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + } + } +} diff --git a/submissions/incident-response-coordinator/solution/bots/incident_response_coordinator_agent/bot.xml b/submissions/incident-response-coordinator/solution/bots/incident_response_coordinator_agent/bot.xml new file mode 100644 index 0000000..050fa33 --- /dev/null +++ b/submissions/incident-response-coordinator/solution/bots/incident_response_coordinator_agent/bot.xml @@ -0,0 +1 @@ +Incident Coordinator diff --git a/submissions/incident-response-coordinator/solution/bots/incident_response_coordinator_agent/configuration.json b/submissions/incident-response-coordinator/solution/bots/incident_response_coordinator_agent/configuration.json new file mode 100644 index 0000000..4be8cde --- /dev/null +++ b/submissions/incident-response-coordinator/solution/bots/incident_response_coordinator_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "GPT-5" + }, + "instructions": { + "segments": [ + { + "value": "Collects service signals, assembles a response channel, and keeps stakeholders updated through resolution." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/incident-response-coordinator/solution/customizations.xml b/submissions/incident-response-coordinator/solution/customizations.xml new file mode 100644 index 0000000..74cfaaf --- /dev/null +++ b/submissions/incident-response-coordinator/solution/customizations.xml @@ -0,0 +1 @@ +Coordinate service incidentworkflow.jsonincident-response-coordinator-workflow diff --git a/submissions/incident-response-coordinator/solution/solution.xml b/submissions/incident-response-coordinator/solution/solution.xml new file mode 100644 index 0000000..ba5e1ce --- /dev/null +++ b/submissions/incident-response-coordinator/solution/solution.xml @@ -0,0 +1 @@ +Demo_incident_response_coordinator1.0.0.0CAT Gallery Demos diff --git a/submissions/invoice-exception-handler/README.md b/submissions/invoice-exception-handler/README.md new file mode 100644 index 0000000..511ffa2 --- /dev/null +++ b/submissions/invoice-exception-handler/README.md @@ -0,0 +1,13 @@ +# Invoice Exception Handler + +Validates invoice data, resolves routine mismatches, and routes material exceptions for finance review. + +## How it works + +- Read invoice +- Match purchase order +- Classify exception +- Request approval +- Post result + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/invoice-exception-handler/metadata.json b/submissions/invoice-exception-handler/metadata.json new file mode 100644 index 0000000..923ac22 --- /dev/null +++ b/submissions/invoice-exception-handler/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Invoice Exception Handler", + "description": "Validates invoice data, resolves routine mismatches, and routes material exceptions for finance review.", + "tags": [ + "finance", + "document-ops", + "automation", + "approval", + "demo" + ], + "author": "Noah Williams", + "authorGithub": "noahwilliams", + "createdAt": "2026-08-06", + "featured": false +} diff --git a/submissions/invoice-exception-handler/solution/Workflows/workflow.json b/submissions/invoice-exception-handler/solution/Workflows/workflow.json new file mode 100644 index 0000000..7a63d7c --- /dev/null +++ b/submissions/invoice-exception-handler/solution/Workflows/workflow.json @@ -0,0 +1,150 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Resolve invoice exception", + "nodes": [ + { + "id": "node-1", + "name": "When invoice validation fails", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "When invoice validation fails", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Read invoice", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Read invoice", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Match purchase order", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Match purchase order", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Classify exception", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Classify exception", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Request approval", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Request approval", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Post result", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Post result", + "operationType": "action" + } + } + }, + { + "id": "node-7", + "name": "Complete", + "type": "end", + "position": { + "x": 1440, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + }, + { + "id": "edge-6", + "source": "node-6", + "target": "node-7" + } + ] + } + } + } + } +} diff --git a/submissions/invoice-exception-handler/solution/customizations.xml b/submissions/invoice-exception-handler/solution/customizations.xml new file mode 100644 index 0000000..ef52b48 --- /dev/null +++ b/submissions/invoice-exception-handler/solution/customizations.xml @@ -0,0 +1 @@ +Resolve invoice exceptionworkflow.jsoninvoice-exception-handler-workflow diff --git a/submissions/invoice-exception-handler/solution/solution.xml b/submissions/invoice-exception-handler/solution/solution.xml new file mode 100644 index 0000000..b60d28f --- /dev/null +++ b/submissions/invoice-exception-handler/solution/solution.xml @@ -0,0 +1 @@ +Demo_invoice_exception_handler1.0.0.0CAT Gallery Demos diff --git a/submissions/knowledge-gap-finder/README.md b/submissions/knowledge-gap-finder/README.md new file mode 100644 index 0000000..9cbe761 --- /dev/null +++ b/submissions/knowledge-gap-finder/README.md @@ -0,0 +1,12 @@ +# Knowledge Gap Finder + +Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines. + +## How it works + +- Cluster questions +- Find missing topics +- Prioritize gaps +- Draft article outlines + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/knowledge-gap-finder/metadata.json b/submissions/knowledge-gap-finder/metadata.json new file mode 100644 index 0000000..e47ee15 --- /dev/null +++ b/submissions/knowledge-gap-finder/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Knowledge Gap Finder", + "description": "Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines.", + "tags": [ + "knowledge", + "support", + "analytics", + "ai", + "demo" + ], + "author": "Priya Raman", + "authorGithub": "priyaraman", + "createdAt": "2026-08-04", + "featured": false +} diff --git a/submissions/knowledge-gap-finder/solution/bots/knowledge_gap_finder_agent/bot.xml b/submissions/knowledge-gap-finder/solution/bots/knowledge_gap_finder_agent/bot.xml new file mode 100644 index 0000000..8403fd2 --- /dev/null +++ b/submissions/knowledge-gap-finder/solution/bots/knowledge_gap_finder_agent/bot.xml @@ -0,0 +1 @@ +Knowledge Gap Analyst diff --git a/submissions/knowledge-gap-finder/solution/bots/knowledge_gap_finder_agent/configuration.json b/submissions/knowledge-gap-finder/solution/bots/knowledge_gap_finder_agent/configuration.json new file mode 100644 index 0000000..1a9c00b --- /dev/null +++ b/submissions/knowledge-gap-finder/solution/bots/knowledge_gap_finder_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "GPT-5" + }, + "instructions": { + "segments": [ + { + "value": "Analyzes unresolved conversations to identify missing articles and drafts evidence-backed outlines." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/knowledge-gap-finder/solution/customizations.xml b/submissions/knowledge-gap-finder/solution/customizations.xml new file mode 100644 index 0000000..1ef5d23 --- /dev/null +++ b/submissions/knowledge-gap-finder/solution/customizations.xml @@ -0,0 +1 @@ + diff --git a/submissions/knowledge-gap-finder/solution/solution.xml b/submissions/knowledge-gap-finder/solution/solution.xml new file mode 100644 index 0000000..3d29e59 --- /dev/null +++ b/submissions/knowledge-gap-finder/solution/solution.xml @@ -0,0 +1 @@ +Demo_knowledge_gap_finder1.0.0.0CAT Gallery Demos diff --git a/submissions/project-status-narrator/README.md b/submissions/project-status-narrator/README.md new file mode 100644 index 0000000..2fca383 --- /dev/null +++ b/submissions/project-status-narrator/README.md @@ -0,0 +1,12 @@ +# Project Status Narrator + +Combines delivery signals into a plain-language status update with risks, decisions, and next actions. + +## How it works + +- Collect delivery signals +- Summarize progress +- Highlight risks +- Send update + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/project-status-narrator/metadata.json b/submissions/project-status-narrator/metadata.json new file mode 100644 index 0000000..a3b9ef7 --- /dev/null +++ b/submissions/project-status-narrator/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Project Status Narrator", + "description": "Combines delivery signals into a plain-language status update with risks, decisions, and next actions.", + "tags": [ + "productivity", + "project-management", + "reporting", + "beginner", + "demo" + ], + "author": "Noah Williams", + "authorGithub": "noahwilliams", + "createdAt": "2026-08-01", + "featured": false +} diff --git a/submissions/project-status-narrator/solution/Workflows/workflow.json b/submissions/project-status-narrator/solution/Workflows/workflow.json new file mode 100644 index 0000000..958e4fd --- /dev/null +++ b/submissions/project-status-narrator/solution/Workflows/workflow.json @@ -0,0 +1,130 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Generate weekly project update", + "nodes": [ + { + "id": "node-1", + "name": "Every Friday", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "Every Friday", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Collect delivery signals", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Collect delivery signals", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Summarize progress", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Summarize progress", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Highlight risks", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Highlight risks", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Send update", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Send update", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + } + } +} diff --git a/submissions/project-status-narrator/solution/customizations.xml b/submissions/project-status-narrator/solution/customizations.xml new file mode 100644 index 0000000..4cfa201 --- /dev/null +++ b/submissions/project-status-narrator/solution/customizations.xml @@ -0,0 +1 @@ +Generate weekly project updateworkflow.jsonproject-status-narrator-workflow diff --git a/submissions/project-status-narrator/solution/solution.xml b/submissions/project-status-narrator/solution/solution.xml new file mode 100644 index 0000000..33606e9 --- /dev/null +++ b/submissions/project-status-narrator/solution/solution.xml @@ -0,0 +1 @@ +Demo_project_status_narrator1.0.0.0CAT Gallery Demos diff --git a/submissions/sales-account-brief/README.md b/submissions/sales-account-brief/README.md new file mode 100644 index 0000000..ca20d3f --- /dev/null +++ b/submissions/sales-account-brief/README.md @@ -0,0 +1,12 @@ +# Sales Account Brief Generator + +Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief. + +## How it works + +- Load CRM account +- Find recent signals +- Summarize risks +- Create brief + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/sales-account-brief/metadata.json b/submissions/sales-account-brief/metadata.json new file mode 100644 index 0000000..4a8b8d8 --- /dev/null +++ b/submissions/sales-account-brief/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Sales Account Brief Generator", + "description": "Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief.", + "tags": [ + "sales", + "crm", + "research", + "productivity", + "demo" + ], + "author": "Jon Bell", + "authorGithub": "jonbell", + "createdAt": "2026-08-12", + "featured": true +} diff --git a/submissions/sales-account-brief/solution/Workflows/workflow.json b/submissions/sales-account-brief/solution/Workflows/workflow.json new file mode 100644 index 0000000..c2bcb87 --- /dev/null +++ b/submissions/sales-account-brief/solution/Workflows/workflow.json @@ -0,0 +1,130 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Prepare account brief", + "nodes": [ + { + "id": "node-1", + "name": "Before a customer meeting", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "Before a customer meeting", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Load CRM account", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Load CRM account", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Find recent signals", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Find recent signals", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Summarize risks", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Summarize risks", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Create brief", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Create brief", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + } + } +} diff --git a/submissions/sales-account-brief/solution/bots/sales_account_brief_agent/bot.xml b/submissions/sales-account-brief/solution/bots/sales_account_brief_agent/bot.xml new file mode 100644 index 0000000..7fcf9f6 --- /dev/null +++ b/submissions/sales-account-brief/solution/bots/sales_account_brief_agent/bot.xml @@ -0,0 +1 @@ +Account Research Agent diff --git a/submissions/sales-account-brief/solution/bots/sales_account_brief_agent/configuration.json b/submissions/sales-account-brief/solution/bots/sales_account_brief_agent/configuration.json new file mode 100644 index 0000000..0418ebe --- /dev/null +++ b/submissions/sales-account-brief/solution/bots/sales_account_brief_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "Sonnet 4.6" + }, + "instructions": { + "segments": [ + { + "value": "Turns CRM history, open opportunities, and recent news into a concise pre-meeting account brief." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/sales-account-brief/solution/customizations.xml b/submissions/sales-account-brief/solution/customizations.xml new file mode 100644 index 0000000..4999f2f --- /dev/null +++ b/submissions/sales-account-brief/solution/customizations.xml @@ -0,0 +1 @@ +Prepare account briefworkflow.jsonsales-account-brief-workflow diff --git a/submissions/sales-account-brief/solution/solution.xml b/submissions/sales-account-brief/solution/solution.xml new file mode 100644 index 0000000..e58a022 --- /dev/null +++ b/submissions/sales-account-brief/solution/solution.xml @@ -0,0 +1 @@ +Demo_sales_account_brief1.0.0.0CAT Gallery Demos diff --git a/submissions/security-access-review/README.md b/submissions/security-access-review/README.md new file mode 100644 index 0000000..f82de3f --- /dev/null +++ b/submissions/security-access-review/README.md @@ -0,0 +1,12 @@ +# Security Access Review + +Explains access anomalies, gathers manager evidence, and produces an auditable review decision. + +## How it works + +- Load access list +- Detect anomalies +- Collect evidence +- Record decision + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/security-access-review/metadata.json b/submissions/security-access-review/metadata.json new file mode 100644 index 0000000..2a344d1 --- /dev/null +++ b/submissions/security-access-review/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Security Access Review", + "description": "Explains access anomalies, gathers manager evidence, and produces an auditable review decision.", + "tags": [ + "security", + "it-ops", + "compliance", + "approval", + "demo" + ], + "author": "Elliot Stone", + "authorGithub": "elliotstone", + "createdAt": "2026-08-03", + "featured": false +} diff --git a/submissions/security-access-review/solution/Workflows/workflow.json b/submissions/security-access-review/solution/Workflows/workflow.json new file mode 100644 index 0000000..2284c49 --- /dev/null +++ b/submissions/security-access-review/solution/Workflows/workflow.json @@ -0,0 +1,130 @@ +{ + "trigger": { + "metadata": { + "associatedData": { + "graph": { + "name": "Quarterly access review", + "nodes": [ + { + "id": "node-1", + "name": "On a quarterly schedule", + "type": "start", + "position": { + "x": 0, + "y": 0 + }, + "data": { + "config": { + "displayName": "On a quarterly schedule", + "operationType": "trigger" + } + } + }, + { + "id": "node-2", + "name": "Load access list", + "type": "agent", + "position": { + "x": 240, + "y": 0 + }, + "data": { + "config": { + "displayName": "Load access list", + "operationType": "action" + } + } + }, + { + "id": "node-3", + "name": "Detect anomalies", + "type": "connector", + "position": { + "x": 480, + "y": 0 + }, + "data": { + "config": { + "displayName": "Detect anomalies", + "operationType": "action" + } + } + }, + { + "id": "node-4", + "name": "Collect evidence", + "type": "ifElse", + "position": { + "x": 720, + "y": 90 + }, + "data": { + "config": { + "displayName": "Collect evidence", + "operationType": "action" + } + } + }, + { + "id": "node-5", + "name": "Record decision", + "type": "connector", + "position": { + "x": 960, + "y": -70 + }, + "data": { + "config": { + "displayName": "Record decision", + "operationType": "action" + } + } + }, + { + "id": "node-6", + "name": "Complete", + "type": "end", + "position": { + "x": 1200, + "y": 0 + }, + "data": { + "config": { + "displayName": "Complete", + "operationType": "end" + } + } + } + ], + "edges": [ + { + "id": "edge-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "edge-2", + "source": "node-2", + "target": "node-3" + }, + { + "id": "edge-3", + "source": "node-3", + "target": "node-4" + }, + { + "id": "edge-4", + "source": "node-4", + "target": "node-5" + }, + { + "id": "edge-5", + "source": "node-5", + "target": "node-6" + } + ] + } + } + } + } +} diff --git a/submissions/security-access-review/solution/bots/security_access_review_agent/bot.xml b/submissions/security-access-review/solution/bots/security_access_review_agent/bot.xml new file mode 100644 index 0000000..e926057 --- /dev/null +++ b/submissions/security-access-review/solution/bots/security_access_review_agent/bot.xml @@ -0,0 +1 @@ +Access Review Analyst diff --git a/submissions/security-access-review/solution/bots/security_access_review_agent/configuration.json b/submissions/security-access-review/solution/bots/security_access_review_agent/configuration.json new file mode 100644 index 0000000..c545d7a --- /dev/null +++ b/submissions/security-access-review/solution/bots/security_access_review_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "Sonnet 4.6" + }, + "instructions": { + "segments": [ + { + "value": "Explains access anomalies, gathers manager evidence, and produces an auditable review decision." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/security-access-review/solution/customizations.xml b/submissions/security-access-review/solution/customizations.xml new file mode 100644 index 0000000..20371ff --- /dev/null +++ b/submissions/security-access-review/solution/customizations.xml @@ -0,0 +1 @@ +Quarterly access reviewworkflow.jsonsecurity-access-review-workflow diff --git a/submissions/security-access-review/solution/solution.xml b/submissions/security-access-review/solution/solution.xml new file mode 100644 index 0000000..4af7b5e --- /dev/null +++ b/submissions/security-access-review/solution/solution.xml @@ -0,0 +1 @@ +Demo_security_access_review1.0.0.0CAT Gallery Demos diff --git a/submissions/store-feedback-analyzer/README.md b/submissions/store-feedback-analyzer/README.md new file mode 100644 index 0000000..bb57568 --- /dev/null +++ b/submissions/store-feedback-analyzer/README.md @@ -0,0 +1,12 @@ +# Store Feedback Analyzer + +Summarizes store feedback, detects recurring product issues, and prepares action themes for operations. + +## How it works + +- Collect feedback +- Detect sentiment +- Group themes +- Recommend actions + +> Demo submission for gallery design and discovery testing. diff --git a/submissions/store-feedback-analyzer/metadata.json b/submissions/store-feedback-analyzer/metadata.json new file mode 100644 index 0000000..851305c --- /dev/null +++ b/submissions/store-feedback-analyzer/metadata.json @@ -0,0 +1,15 @@ +{ + "name": "Store Feedback Analyzer", + "description": "Summarizes store feedback, detects recurring product issues, and prepares action themes for operations.", + "tags": [ + "retail", + "analytics", + "customer-service", + "beginner", + "demo" + ], + "author": "Amina Yusuf", + "authorGithub": "aminayusuf", + "createdAt": "2026-08-02", + "featured": false +} diff --git a/submissions/store-feedback-analyzer/solution/bots/store_feedback_analyzer_agent/bot.xml b/submissions/store-feedback-analyzer/solution/bots/store_feedback_analyzer_agent/bot.xml new file mode 100644 index 0000000..e297ce5 --- /dev/null +++ b/submissions/store-feedback-analyzer/solution/bots/store_feedback_analyzer_agent/bot.xml @@ -0,0 +1 @@ +Feedback Insights Agent diff --git a/submissions/store-feedback-analyzer/solution/bots/store_feedback_analyzer_agent/configuration.json b/submissions/store-feedback-analyzer/solution/bots/store_feedback_analyzer_agent/configuration.json new file mode 100644 index 0000000..00edd58 --- /dev/null +++ b/submissions/store-feedback-analyzer/solution/bots/store_feedback_analyzer_agent/configuration.json @@ -0,0 +1,19 @@ +{ + "agentSettings": { + "model": { + "series": "GPT-4.1" + }, + "instructions": { + "segments": [ + { + "value": "Summarizes store feedback, detects recurring product issues, and prepares action themes for operations." + } + ] + }, + "channels": [ + { + "channelId": "MsTeams" + } + ] + } +} diff --git a/submissions/store-feedback-analyzer/solution/customizations.xml b/submissions/store-feedback-analyzer/solution/customizations.xml new file mode 100644 index 0000000..1ef5d23 --- /dev/null +++ b/submissions/store-feedback-analyzer/solution/customizations.xml @@ -0,0 +1 @@ + diff --git a/submissions/store-feedback-analyzer/solution/solution.xml b/submissions/store-feedback-analyzer/solution/solution.xml new file mode 100644 index 0000000..39ef2e4 --- /dev/null +++ b/submissions/store-feedback-analyzer/solution/solution.xml @@ -0,0 +1 @@ +Demo_store_feedback_analyzer1.0.0.0CAT Gallery Demos diff --git a/submissions/store-modern-agent/README.md b/submissions/store-modern-agent/README.md new file mode 100644 index 0000000..bdb6a53 --- /dev/null +++ b/submissions/store-modern-agent/README.md @@ -0,0 +1,18 @@ +# Store Modern Agent + +Store Modern Agent is a compact example of delegation in Copilot Studio. A +customer-facing assistant stays at the center of the conversation and calls +specialist agents when a question needs inventory or store-policy expertise. + +## Agents + +- The primary store assistant coordinates the conversation and owns reusable + store skills. +- The inventory specialist handles stock and fulfillment questions. +- The policy specialist handles returns and other policy-grounded decisions. + +## Import + +Download and import the rebuilt solution ZIP through Power Platform. Review the +connected-agent references and configure any required environment connections +before publishing. diff --git a/submissions/store-modern-agent/metadata.json b/submissions/store-modern-agent/metadata.json new file mode 100644 index 0000000..c454287 --- /dev/null +++ b/submissions/store-modern-agent/metadata.json @@ -0,0 +1,13 @@ +{ + "name": "Store Modern Agent", + "description": "A compact retail assistant that delegates inventory and policy questions while keeping reusable store skills close at hand.", + "tags": [ + "retail", + "customer-service", + "multi-agent", + "inventory", + "policy" + ], + "author": "BlastBox team", + "featured": false +} diff --git a/submissions/store-modern-agent/solution/[Content_Types].xml b/submissions/store-modern-agent/solution/[Content_Types].xml new file mode 100644 index 0000000..64eb1fb --- /dev/null +++ b/submissions/store-modern-agent/solution/[Content_Types].xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill1/botcomponent.xml b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill1/botcomponent.xml new file mode 100644 index 0000000..7dbf2ca --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill1/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + skill1 + 0 + skill1 + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill1/data b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill1/data new file mode 100644 index 0000000..f21bb5c --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill1/data @@ -0,0 +1,26 @@ +kind: InlineAgentSkill +content: | + --- + name: skill1 + description: skill1 + --- + + When this skill is activated: + + 1. [First step or action the agent should take] + 2. [Second step or action] + + ## Guidelines + + - [Key guideline or constraint] + - [Another important consideration] + + ## Examples + + **Example 1: [Scenario name]** + - User request: "[Example user input]" + - Expected behavior: [How the agent should respond] + + ## Notes + + [Any additional context, edge cases, or important information the agent should know.] diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill2/botcomponent.xml b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill2/botcomponent.xml new file mode 100644 index 0000000..f9a865a --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill2/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + skill2 + 0 + skill2 + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill2/data b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill2/data new file mode 100644 index 0000000..1ed71d3 --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.skill.skill2/data @@ -0,0 +1,26 @@ +kind: InlineAgentSkill +content: | + --- + name: skill2 + description: skill2 + --- + + When this skill is activated: + + 1. [First step or action the agent should take] + 2. [Second step or action] + + ## Guidelines + + - [Key guideline or constraint] + - [Another important consideration] + + ## Examples + + **Example 1: [Scenario name]** + - User request: "[Example user input]" + - Expected behavior: [How the agent should respond] + + ## Notes + + [Any additional context, edge cases, or important information the agent should know.] diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml new file mode 100644 index 0000000..cc7a9fd --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Inventory & Fulfillment Agent + 0 + Inventory & Fullfilment Agent + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data new file mode 100644 index 0000000..fad3434 --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_InventoryFullfilmentAgent_X-w2GP +historyType: + kind: ConversationHistory \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json new file mode 100644 index 0000000..52b1d1d --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_InventoryFullfilmentAgent_X-w2GP/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_InventoryFullfilmentAgent_X-w2GP"}] \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml new file mode 100644 index 0000000..471ef91 --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/botcomponent.xml @@ -0,0 +1,11 @@ + + 9 + Story Policy Agent + 0 + Store Policy Agent + + Default_draft_IsBewO + + 0 + 1 + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data new file mode 100644 index 0000000..ff1141a --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/data @@ -0,0 +1,4 @@ +kind: ConnectedAgentTool +botSchemaName: Default_StorePolicyAgent_s9s-u8 +historyType: + kind: ConversationHistory \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json new file mode 100644 index 0000000..789e87d --- /dev/null +++ b/submissions/store-modern-agent/solution/botcomponents/Default_draft_IsBewO.tool.connected-agent.Default_StorePolicyAgent_s9s-u8/dependencies.json @@ -0,0 +1 @@ +[{"type":"bot","schemaName":"Default_StorePolicyAgent_s9s-u8"}] \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml b/submissions/store-modern-agent/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml new file mode 100644 index 0000000..5c9f448 --- /dev/null +++ b/submissions/store-modern-agent/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Inventory & Fullfilment Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json b/submissions/store-modern-agent/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json new file mode 100644 index 0000000..9bb1fed --- /dev/null +++ b/submissions/store-modern-agent/solution/bots/Default_InventoryFullfilmentAgent_X-w2GP/configuration.json @@ -0,0 +1,16 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions" + } + } +} \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml b/submissions/store-modern-agent/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml new file mode 100644 index 0000000..b58beff --- /dev/null +++ b/submissions/store-modern-agent/solution/bots/Default_StorePolicyAgent_s9s-u8/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Store Policy Agent + 0 + + 4 + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json b/submissions/store-modern-agent/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json new file mode 100644 index 0000000..9bb1fed --- /dev/null +++ b/submissions/store-modern-agent/solution/bots/Default_StorePolicyAgent_s9s-u8/configuration.json @@ -0,0 +1,16 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions" + } + } +} \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/bots/Default_draft_IsBewO/bot.xml b/submissions/store-modern-agent/solution/bots/Default_draft_IsBewO/bot.xml new file mode 100644 index 0000000..47cd693 --- /dev/null +++ b/submissions/store-modern-agent/solution/bots/Default_draft_IsBewO/bot.xml @@ -0,0 +1,11 @@ + + 2 + 1 + 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 + 0 + 1033 + Returns & Service Assistant + 0 + + 4 + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/bots/Default_draft_IsBewO/configuration.json b/submissions/store-modern-agent/solution/bots/Default_draft_IsBewO/configuration.json new file mode 100644 index 0000000..dc5db92 --- /dev/null +++ b/submissions/store-modern-agent/solution/bots/Default_draft_IsBewO/configuration.json @@ -0,0 +1,22 @@ +{ + "$kind": "BotConfiguration", + "recognizer": { + "$kind": "CLICopilotRecognizer" + }, + "agentSettings": { + "$kind": "AgentSettings", + "model": { + "$kind": "ModelConfig", + "series": "Sonnet46" + }, + "instructions": { + "$kind": "Instructions", + "segments": [ + { + "$kind": "StaticSegment", + "value": "" + } + ] + } + } +} \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/customizations.xml b/submissions/store-modern-agent/solution/customizations.xml new file mode 100644 index 0000000..e146a3e --- /dev/null +++ b/submissions/store-modern-agent/solution/customizations.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + 1033 + + \ No newline at end of file diff --git a/submissions/store-modern-agent/solution/solution.xml b/submissions/store-modern-agent/solution/solution.xml new file mode 100644 index 0000000..ec30ad0 --- /dev/null +++ b/submissions/store-modern-agent/solution/solution.xml @@ -0,0 +1,82 @@ + + + StoreModernAgent + + + + + 1.0.0.1 + 0 + + CAT + + + + + + + cat + 76486 + +
+ 1 + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + + +
+
+ 2 + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + + +
+
+
+ + +
+
\ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2510b83 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "jsx": "react-jsx", + "jsxImportSource": "react" + } +}