From 9da958b5623ebcd511f1704ff12937f191c06e47 Mon Sep 17 00:00:00 2001 From: Yimin Jin Date: Tue, 2 Jun 2026 10:52:22 +0800 Subject: [PATCH 1/6] ci(catalog): default commit_sha to latest foundry-samples@main Make the workflow_dispatch `commit_sha` input optional. When the user leaves it blank, a new `Resolve commit SHA` step queries the GitHub API for the current tip of `microsoft-foundry/foundry-samples@main` and pins the catalog generation to that SHA. - `commit_sha`: required=false, default empty. - New step uses `gh api` (default GITHUB_TOKEN, contents:read is enough for a public repo) and validates the result looks like a SHA before using it. - The pinned SHA + its source (user input vs. resolved-from-main) is echoed to the step summary so reviewers can see what the run targeted. - `Generate sample catalog` now reads from `steps.resolve-sha.outputs.sha` instead of `inputs.commit_sha` directly. --- .github/workflows/sync-sample-catalog.yml | 32 ++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync-sample-catalog.yml b/.github/workflows/sync-sample-catalog.yml index 3fecaaf..0055a41 100644 --- a/.github/workflows/sync-sample-catalog.yml +++ b/.github/workflows/sync-sample-catalog.yml @@ -4,9 +4,10 @@ on: workflow_dispatch: inputs: commit_sha: - description: "Commit SHA from microsoft-foundry/foundry-samples to pin to" - required: true + description: "Commit SHA from microsoft-foundry/foundry-samples to pin to (leave blank to use the latest main commit)" + required: false type: string + default: "" # Least-privilege for the default GITHUB_TOKEN: only what `actions/checkout` # needs. All writes (push branch + create PR) are performed via the GitHub @@ -47,6 +48,31 @@ jobs: with: node-version: '20' + - name: Resolve commit SHA + id: resolve-sha + shell: bash + env: + INPUT_SHA: ${{ inputs.commit_sha }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + if [[ -n "$INPUT_SHA" ]]; then + sha="$INPUT_SHA" + source="user-supplied input" + else + # No SHA provided: pin to the current tip of foundry-samples@main + # so the dispatcher doesn't have to look it up by hand. + sha=$(gh api repos/microsoft-foundry/foundry-samples/commits/main --jq .sha) + source="latest microsoft-foundry/foundry-samples@main" + fi + if [[ ! "$sha" =~ ^[0-9a-f]{7,40}$ ]]; then + echo "Resolved SHA is not a valid git object: $sha" >&2 + exit 1 + fi + echo "Using SHA $sha (source: $source)" + echo "sha=$sha" >> "$GITHUB_OUTPUT" + echo "- Pinned SHA: \`$sha\` ($source)" >> "$GITHUB_STEP_SUMMARY" + - name: Generate sample catalog env: REPO_ROOT: ${{ github.workspace }} @@ -54,7 +80,7 @@ jobs: AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }} AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }} AZURE_OPENAI_DEPLOYMENT: ${{ secrets.AZURE_OPENAI_DEPLOYMENT }} - run: node .github/scripts/generate_sample_catalog.mjs "${{ inputs.commit_sha }}" + run: node .github/scripts/generate_sample_catalog.mjs "${{ steps.resolve-sha.outputs.sha }}" - name: Detect catalog changes id: diff From 525e3dca2e893306c44ebb4fc9c3770afc2e7f71 Mon Sep 17 00:00:00 2001 From: Yimin-Jin <139844715+Yimin-Jin@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:29:53 +0000 Subject: [PATCH 2/6] ci: sync sample catalog from foundry-samples (template/dev) --- samples/hosted-agent/sample-catalog.json | 103 ++++++++++++++++++----- 1 file changed, 83 insertions(+), 20 deletions(-) diff --git a/samples/hosted-agent/sample-catalog.json b/samples/hosted-agent/sample-catalog.json index 62af205..7908c20 100644 --- a/samples/hosted-agent/sample-catalog.json +++ b/samples/hosted-agent/sample-catalog.json @@ -1,7 +1,7 @@ { - "commitSha": "ef510c47b3ec768596577ba65ca6b2c2ab015855", + "commitSha": "d3bb92638f131ce0fff1fe67862130d5b190383f", "repo": "https://github.com/microsoft-foundry/foundry-samples/", - "generatedAt": "2026-06-01T10:21:01Z", + "generatedAt": "2026-06-16T07:29:52Z", "dimensions": { "language": { "title": "Select a Language", @@ -158,15 +158,6 @@ "path": "samples/python/hosted-agents/agent-framework/responses/09-declarative-customer-support", "requiresModel": true }, - { - "language": "python", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Downstream Azure", - "description": "Agent performs data operations on Blob Storage and Service Bus using its own identity.", - "path": "samples/python/hosted-agents/agent-framework/responses/10-downstream-azure", - "requiresModel": true - }, { "language": "python", "framework": "agent-framework", @@ -203,6 +194,15 @@ "path": "samples/python/hosted-agents/agent-framework/responses/14-browser-automation-agent", "requiresModel": true }, + { + "language": "python", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Optimization Travel Approver", + "description": "Approves or rejects travel requests based on provided details and approval criteria.", + "path": "samples/python/hosted-agents/agent-framework/responses/15-optimization-travel-approver", + "requiresModel": true + }, { "language": "python", "framework": "bring-your-own", @@ -221,15 +221,6 @@ "path": "samples/python/hosted-agents/bring-your-own/invocations/claude-agent-sdk", "requiresModel": false }, - { - "language": "python", - "framework": "bring-your-own", - "protocol": "invocations", - "displayName": "Event Grid Trigger", - "description": "Agent triggered by Azure Storage events that summarizes uploaded blobs and saves results separately.", - "path": "samples/python/hosted-agents/bring-your-own/invocations/event-grid-trigger", - "requiresModel": true - }, { "language": "python", "framework": "copilot-sdk", @@ -374,6 +365,33 @@ "path": "samples/python/hosted-agents/bring-your-own/responses/openai-agents-sdk", "requiresModel": true }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Optimization Customer Support", + "description": "Customer support agent for troubleshooting and product assistance.", + "path": "samples/python/hosted-agents/bring-your-own/responses/optimization-customer-support", + "requiresModel": true + }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Optimization Hello World", + "description": "Minimal hosted agent that responds to greetings using a custom optimization service.", + "path": "samples/python/hosted-agents/bring-your-own/responses/optimization-hello-world", + "requiresModel": true + }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Foreground Background Agents Responses Voicelive", + "description": "Agent architecture that delegates long-running tasks to a background worker for async results.", + "path": "samples/python/hosted-agents/bring-your-own/voicelive/foreground-background-agents-responses-voicelive", + "requiresModel": true + }, { "language": "python", "framework": "bring-your-own", @@ -392,6 +410,15 @@ "path": "samples/python/hosted-agents/bring-your-own/voicelive/hello-world-invocations-voicelive", "requiresModel": true }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "invocations", + "displayName": "Hotel Booking Invocations Voicelive", + "description": "Hotel booking agent with voice and UI actions, streaming responses and session state.", + "path": "samples/python/hosted-agents/bring-your-own/voicelive/hotel-booking-invocations-voicelive", + "requiresModel": true + }, { "language": "python", "framework": "langgraph", @@ -410,6 +437,15 @@ "path": "samples/python/hosted-agents/langgraph/responses/01-langgraph-chat", "requiresModel": true }, + { + "language": "python", + "framework": "langgraph", + "protocol": "responses", + "displayName": "Langgraph Toolbox", + "description": "Agent accesses web search and GitHub Copilot tools via a credential-managed toolbox.", + "path": "samples/python/hosted-agents/langgraph/responses/02-langgraph-toolbox", + "requiresModel": true + }, { "language": "python", "framework": "langgraph", @@ -455,6 +491,24 @@ "path": "samples/python/hosted-agents/langgraph/responses/08-observability", "requiresModel": true }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Caller", + "description": "Concierge agent that delegates questions to a remote specialist and summarizes answers.", + "path": "samples/csharp/hosted-agents/agent-framework/a2a/01-delegation/caller", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Executor", + "description": "Answers arithmetic and math questions as an expert agent.", + "path": "samples/csharp/hosted-agents/agent-framework/a2a/01-delegation/executor", + "requiresModel": true + }, { "language": "csharp", "framework": "agent-framework", @@ -491,6 +545,15 @@ "path": "samples/csharp/hosted-agents/agent-framework/foundry-memory-rag", "requiresModel": true }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Foundry Toolbox MCP Skills", + "description": "Agent that dynamically discovers and uses skills from a remote toolbox endpoint.", + "path": "samples/csharp/hosted-agents/agent-framework/foundry-toolbox-mcp-skills", + "requiresModel": true + }, { "language": "csharp", "framework": "agent-framework", From d5eb860ef373e3bce3507dc16daa8ea259c1923a Mon Sep 17 00:00:00 2001 From: Yimin Jin Date: Tue, 23 Jun 2026 16:54:58 +0800 Subject: [PATCH 3/6] ci(catalog): replace path blacklist with category allow-list Switch sample discovery from a fail-open blacklist (BLOCKED_PATH_SEGMENTS) to a fail-closed category allow-list (ALLOWED_CATEGORY_SEGMENTS = responses, invocations, voicelive). Flat templates directly under a framework (csharp agent-framework layout) are always kept; nested templates must live under an allow-listed category, so new upstream groupings like a2a and invocations_ws stay out of the picker until explicitly opted in. Validated against foundry-samples@main: 76 templates kept; a2a and invocations_ws excluded; 4 voicelive and 17 flat csharp agent-framework templates retained. --- .github/scripts/generate_sample_catalog.mjs | 46 +++++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/.github/scripts/generate_sample_catalog.mjs b/.github/scripts/generate_sample_catalog.mjs index ba6eb18..8be4433 100644 --- a/.github/scripts/generate_sample_catalog.mjs +++ b/.github/scripts/generate_sample_catalog.mjs @@ -77,10 +77,20 @@ const TEMPLATE_SELECTION = { // Path segments must be alphanumeric, hyphens, underscores, or dots const SAFE_PATH_SEGMENT = /^[a-zA-Z0-9._-]+$/; -// Path segments that should never surface as catalog templates, even when -// they contain a valid `agent.yaml`. Use this to drop upstream folders we -// don't want in the picker yet (e.g. `invocations_ws` LiveKit samples). -const BLOCKED_PATH_SEGMENTS = new Set(['invocations_ws']); +// Category segments allowed at the position immediately under a framework +// (`//...`). Upstream groups nested templates under these +// folders. This is an ALLOW-LIST: any other category (e.g. `a2a`, +// `invocations_ws`) is treated as not-yet-supported and dropped, so new +// upstream folders never auto-leak into the picker — opt them in here. Flat +// templates that sit directly under a framework (e.g. csharp +// `agent-framework/hello-world`) have no category segment and are always +// surfaced (see findTemplateDirsUnder). +const ALLOWED_CATEGORY_SEGMENTS = new Set(['responses', 'invocations', 'voicelive']); + +// De-dupes the per-category "skipped" log line so an excluded category that +// spans many templates (e.g. `a2a`) is reported once, not once per template. +/** @type {Set} */ +const skippedCategoriesLogged = new Set(); /** * Collected anomaly messages surfaced in CI step summary so reviewers do not @@ -192,6 +202,12 @@ async function fetchRepoTree(ref) { * `.`, e.g. `.claude/skills`) are skipped, as are segments that fail the * `SAFE_PATH_SEGMENT` check. * + * Nested templates must live under an allow-listed category segment + * (`ALLOWED_CATEGORY_SEGMENTS`); flat templates directly under the framework + * (csharp's `agent-framework/