Repository navigation
Add scrum-master scenario: Scrum Master autopilot using Node.js SDK #334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d33aecc
feb98d9
1c5a4af
251de83
ca578fa
aa27069
ff11662
2335201
5e8a957
0c5b4a7
812c25d
94afea8
69bc367
a371dc1
08a7d3d
38160d3
f0d35a6
e9d68a5
98cbda7
247229a
7bfb010
9034de4
f472fd7
621135e
946e39e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| name: CI - Scrum Master Scenario | ||
|
|
||
| on: | ||
| workflow_call: | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| scrum-master: | ||
| name: Scrum Master (Node 22) | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: ./scenarios/scrum-master | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 | ||
| - name: Set up Node.js 22 | ||
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | ||
| with: | ||
| node-version: '22' | ||
| cache: npm | ||
| cache-dependency-path: scenarios/scrum-master/package-lock.json | ||
| - name: Install dependencies | ||
| run: npm ci | ||
| - name: Build | ||
| run: npm run build | ||
| - name: Test | ||
| run: npm test |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,97 +1,95 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| name: CI Orchestrator | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main, master] | ||
| pull_request: | ||
| branches: [main, master] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| # .NET Jobs - Call existing workflows | ||
| dotnet-agentframework: | ||
| name: .NET Agent Framework | ||
| uses: ./.github/workflows/ci-dotnet-agentframework-sampleagent.yml | ||
|
|
||
| dotnet-semantickernel: | ||
| name: .NET Semantic Kernel | ||
| uses: ./.github/workflows/ci-dotnet-semantickernel-sampleagent.yml | ||
|
|
||
| # Node.js Jobs - Call existing workflows | ||
| nodejs-claude: | ||
| name: Node.js Claude | ||
| uses: ./.github/workflows/ci-nodejs-claude-sampleagent.yml | ||
|
|
||
| nodejs-langchain: | ||
| name: Node.js LangChain | ||
| uses: ./.github/workflows/ci-nodejs-langchain-sampleagent.yml | ||
|
|
||
| nodejs-openai: | ||
| name: Node.js OpenAI | ||
| uses: ./.github/workflows/ci-nodejs-openai-sampleagent.yml | ||
|
|
||
| nodejs-vercelsdk: | ||
| name: Node.js Vercel SDK | ||
| uses: ./.github/workflows/ci-nodejs-vercelsdk-sampleagent.yml | ||
|
|
||
| # Python Jobs - Call existing workflows | ||
| python-agentframework: | ||
| name: Python Agent Framework | ||
| uses: ./.github/workflows/ci-python-agentframework-sampleagent.yml | ||
|
|
||
| python-googleadk: | ||
| name: Python Google ADK | ||
| uses: ./.github/workflows/ci-python-googleadk-sampleagent.yml | ||
|
|
||
| python-openai: | ||
| name: Python OpenAI | ||
| uses: ./.github/workflows/ci-python-openai-sampleagent.yml | ||
|
|
||
| python-claude: | ||
| name: Python Claude | ||
| uses: ./.github/workflows/python-claude-sample.yml | ||
|
|
||
| observability: | ||
| name: Observability Offline Tests | ||
| uses: ./.github/workflows/ci-observability.yml | ||
|
|
||
| # Final status check - ALWAYS runs and reports success | ||
| # This is the ONLY check you need to mark as "Required" in branch protection | ||
| ci-status: | ||
| name: CI Status | ||
| runs-on: ubuntu-latest | ||
| needs: | ||
| - dotnet-agentframework | ||
| - dotnet-semantickernel | ||
| - nodejs-claude | ||
| - nodejs-langchain | ||
| - nodejs-openai | ||
| - nodejs-vercelsdk | ||
| - python-agentframework | ||
| - python-googleadk | ||
| - python-openai | ||
| - python-claude | ||
| - observability | ||
| if: always() | ||
| steps: | ||
| - name: Check CI Status | ||
| run: | | ||
| echo "Checking CI job results..." | ||
|
|
||
| # Get all job results (skipped jobs are fine, failed jobs are not) | ||
| results="${{ needs.dotnet-agentframework.result }} ${{ needs.dotnet-semantickernel.result }} ${{ needs.nodejs-claude.result }} ${{ needs.nodejs-langchain.result }} ${{ needs.nodejs-openai.result }} ${{ needs.nodejs-vercelsdk.result }} ${{ needs.python-agentframework.result }} ${{ needs.python-googleadk.result }} ${{ needs.python-openai.result }} ${{ needs.python-claude.result }} ${{ needs.observability.result }}" | ||
|
|
||
| echo "Job results: $results" | ||
|
|
||
| # Check if any job failed or was cancelled | ||
| if echo "$results" | grep -qE "failure|cancelled"; then | ||
| echo "❌ One or more CI jobs failed or were cancelled" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "✅ All CI jobs passed" | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| name: CI Orchestrator | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main, master] | ||
| pull_request: | ||
| branches: [main, master] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| # .NET Jobs - Call existing workflows | ||
| dotnet-agentframework: | ||
| name: .NET Agent Framework | ||
| uses: ./.github/workflows/ci-dotnet-agentframework-sampleagent.yml | ||
|
|
||
| dotnet-semantickernel: | ||
| name: .NET Semantic Kernel | ||
| uses: ./.github/workflows/ci-dotnet-semantickernel-sampleagent.yml | ||
|
|
||
| # Node.js Jobs - Call existing workflows | ||
| nodejs-claude: | ||
| name: Node.js Claude | ||
| uses: ./.github/workflows/ci-nodejs-claude-sampleagent.yml | ||
|
|
||
| nodejs-langchain: | ||
| name: Node.js LangChain | ||
| uses: ./.github/workflows/ci-nodejs-langchain-sampleagent.yml | ||
|
|
||
| nodejs-openai: | ||
| name: Node.js OpenAI | ||
| uses: ./.github/workflows/ci-nodejs-openai-sampleagent.yml | ||
|
|
||
| nodejs-vercelsdk: | ||
| name: Node.js Vercel SDK | ||
| uses: ./.github/workflows/ci-nodejs-vercelsdk-sampleagent.yml | ||
|
|
||
| nodejs-scrum-master: | ||
| name: Node.js Scrum Master | ||
| uses: ./.github/workflows/ci-nodejs-scrum-master.yml | ||
|
|
||
| # Python Jobs - Call existing workflows | ||
| python-agentframework: | ||
| name: Python Agent Framework | ||
| uses: ./.github/workflows/ci-python-agentframework-sampleagent.yml | ||
|
|
||
| python-googleadk: | ||
| name: Python Google ADK | ||
| uses: ./.github/workflows/ci-python-googleadk-sampleagent.yml | ||
|
|
||
| python-openai: | ||
| name: Python OpenAI | ||
| uses: ./.github/workflows/ci-python-openai-sampleagent.yml | ||
|
|
||
| python-claude: | ||
| name: Python Claude | ||
| uses: ./.github/workflows/python-claude-sample.yml | ||
|
|
||
| observability: | ||
| name: Observability Offline Tests | ||
| uses: ./.github/workflows/ci-observability.yml | ||
|
|
||
| # This aggregate check is required in branch protection. | ||
| ci-status: | ||
| name: CI Status | ||
| runs-on: ubuntu-latest | ||
| needs: | ||
| - dotnet-agentframework | ||
| - dotnet-semantickernel | ||
| - nodejs-claude | ||
| - nodejs-langchain | ||
| - nodejs-openai | ||
| - nodejs-vercelsdk | ||
| - nodejs-scrum-master | ||
| - python-agentframework | ||
| - python-googleadk | ||
| - python-openai | ||
| - python-claude | ||
| - observability | ||
| if: always() | ||
| steps: | ||
| - name: Check CI Status | ||
| run: | | ||
| echo "Checking CI job results..." | ||
| results="${{ needs.dotnet-agentframework.result }} ${{ needs.dotnet-semantickernel.result }} ${{ needs.nodejs-claude.result }} ${{ needs.nodejs-langchain.result }} ${{ needs.nodejs-openai.result }} ${{ needs.nodejs-vercelsdk.result }} ${{ needs.nodejs-scrum-master.result }} ${{ needs.python-agentframework.result }} ${{ needs.python-googleadk.result }} ${{ needs.python-openai.result }} ${{ needs.python-claude.result }} ${{ needs.observability.result }}" | ||
| echo "Job results: $results" | ||
| if echo "$results" | grep -qE "failure|cancelled"; then | ||
| echo "❌ One or more CI jobs failed or were cancelled" | ||
| exit 1 | ||
| fi | ||
| echo "✅ All CI jobs passed" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| # OpenAI Configuration | ||
| # Use EITHER standard OpenAI OR Azure OpenAI (not both) | ||
|
|
||
| # Option 1: Standard OpenAI API | ||
| OPENAI_API_KEY= | ||
| OPENAI_MODEL=gpt-4o | ||
|
|
||
| # Option 2: Azure OpenAI (takes precedence if AZURE_OPENAI_API_KEY is set) | ||
| AZURE_OPENAI_API_KEY= | ||
| AZURE_OPENAI_ENDPOINT= | ||
| AZURE_OPENAI_DEPLOYMENT= | ||
| AZURE_OPENAI_API_VERSION=2024-10-21 | ||
|
|
||
| # MCP Tooling Configuration | ||
| BEARER_TOKEN= | ||
|
|
||
| # Enable to use observability exporter, default is false which means using console exporter. | ||
| # MUST be true in any deployed/dev-tunnel environment or MAC Activity stays empty. | ||
| ENABLE_A365_OBSERVABILITY_EXPORTER=false | ||
| # Use by the sample to demo using custom token resolver and token cache when it is true, otherwise use the built-in AgenticTokenCache | ||
| Use_Custom_Resolver=true | ||
| # optional - set to enable observability logs, value can be 'info', 'warn', or 'error', default to 'none' if not set | ||
| A365_OBSERVABILITY_LOG_LEVEL= | ||
| # Uncomment together with A365_OBSERVABILITY_LOG_LEVEL to debug export failures. | ||
| # OTEL_LOG_LEVEL=INFO | ||
|
|
||
| # --- A365 observability identity --- | ||
| # Runtime agent identity (the AGENTIC INSTANCE, not the blueprint). Normally | ||
| # populated automatically from activity.recipient.agenticAppId. Set explicitly | ||
| # only if you invoke the observability helpers outside the standard A365 | ||
| # hosting flow (e.g. a stand-alone script). See src/observability.ts. | ||
| agent365Observability__agentInstanceId= | ||
|
|
||
| # Environment Settings | ||
| NODE_ENV=development # Retrieve mcp servers from ToolingManifest | ||
| HOST=127.0.0.1 # Agents Playground connects to 127.0.0.1; set this so the server binds to the same address | ||
|
|
||
| # Telemetry and Tracing Configuration | ||
| DEBUG=agents:* | ||
|
|
||
| # --- Scenario-level logging (this sample only) ------------------------------- | ||
| # Level-based logger in src/util/logger.ts. | ||
| # LOG_LEVEL: error | warn | info | debug | trace (default=info) | ||
| # LOG_HTTP: when 'true', traces instrumented axios requests (including Jira) | ||
| # with status + latency. Not Graph/fetch or MCP transport tracing. | ||
| # Credentials are redacted. Keep false in production. | ||
| LOG_LEVEL=info | ||
| LOG_HTTP=false | ||
|
|
||
| # Use Agentic Authentication rather than OBO | ||
| USE_AGENTIC_AUTH=false | ||
|
|
||
| # Service Connection Settings | ||
| connections__service_connection__settings__clientId= | ||
| connections__service_connection__settings__clientSecret= | ||
| connections__service_connection__settings__tenantId= | ||
|
|
||
| # Set service connection as default | ||
| connectionsMap__0__serviceUrl=* | ||
| connectionsMap__0__connection=service_connection | ||
|
|
||
| # AgenticAuthentication Options | ||
| agentic_type=agentic | ||
| agentic_altBlueprintConnectionName=service_connection | ||
| agentic_scopes=ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default # Prod Agentic scope | ||
|
|
||
| # ============================================================================= | ||
| # Scrum Master Assistant POC configuration (added on top of the base sample) | ||
| # ============================================================================= | ||
|
|
||
| # --- Jira (Atlassian Cloud) --- | ||
| # Set JIRA_MODE=mock to run against the built-in mock sprint (no Atlassian creds needed). | ||
| JIRA_MODE=mock # live | mock | ||
| JIRA_BASE_URL=https://your-org.atlassian.net | ||
| JIRA_EMAIL= | ||
| JIRA_API_TOKEN= | ||
| JIRA_PROJECT_KEY=SCRUM # mock board key; replace for live Jira | ||
| JIRA_BOARD_ID=1 | ||
|
|
||
| # --- SharePoint (Microsoft Graph, delegated) --- | ||
| # Site URL of the site that will host the SMA_* lists + SprintReports library. | ||
| SHAREPOINT_SITE_URL= | ||
| SHAREPOINT_LISTS_PREFIX=SMA_ | ||
| # App registration used by scripts/setup-sharepoint.ts (device-code) and runtime Graph calls. | ||
| GRAPH_TENANT_ID=common | ||
| # Blank uses Microsoft Graph Command Line Tools public client | ||
| # (14d82eec-204b-4c2f-b7e8-296a70dab67e). Tenant policy/admin consent still applies. | ||
| # For production, register your own public client and set its app id here. | ||
| GRAPH_CLIENT_ID= | ||
|
|
||
| # --- Scrum ceremony scheduling --- | ||
| STANDUP_CRON=0 30 3 * * 1-5 # UTC = 09:00 Asia/Kolkata on weekdays | ||
| NIGHTLY_CRON=0 0 19 * * * # UTC = 00:30 Asia/Kolkata daily | ||
|
Comment on lines
+92
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should fix: Cron defaults are documented as UTC, but node-cron evaluates them in TIMEZONE, so the standup fires at 03:30 IST The template describes these as UTC expressions ( With
Evidence
Scenario: create With the Function App also deployed:
Suggested fix: Pick one convention and make the template, config defaults and README agree:
In either case, drop 'Display only' for TIMEZONE in the README.
Comment on lines
+92
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should fix: Cron defaults are documented as UTC, but node-cron evaluates them in TIMEZONE, so the standup fires at 03:30 IST The template describes these as UTC expressions ( With
Evidence
Scenario: create With the Function App also deployed:
Suggested fix: Pick one convention and make the template, config defaults and README agree:
In either case, drop 'Display only' for TIMEZONE in the README. |
||
| STANDUP_CUTOFF_HOURS=4 | ||
| TIMEZONE=Asia/Kolkata | ||
| # Both schedulers evaluate the expressions in UTC. TIMEZONE controls local | ||
| # date-based gates, card labels and meeting-slot searches, not cron evaluation. | ||
| # Leave WEBSITE_TIME_ZONE unset on the Functions app. | ||
|
|
||
| # --- Warn thresholds (Sprint risk detection) --- | ||
| WARN_TODO_PCT=0.40 # >40% of committed points still in To Do | ||
| WARN_SPRINT_PROGRESS_PCT=0.50 # ... once >=50% of sprint duration has elapsed | ||
|
|
||
| # --- Runtime toggles --- | ||
| LOCAL_CRON=true # run the in-process scheduler in dev; false in prod | ||
| AGENT_CALLBACK_URL= # Functions app setting: reachable agent base URL (no /api/messages suffix) | ||
| INTERNAL_TRIGGER_TOKEN= # required nonempty unless NODE_ENV is exactly development | ||
| # Cloud agent overrides: NODE_ENV=production, HOST=0.0.0.0, LOCAL_CRON=false | ||
| # when Functions timers are deployed. Do not copy the development values above. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # dependencies + build | ||
| node_modules/ | ||
| !package-lock.json | ||
| dist/ | ||
| *.tsbuildinfo | ||
|
|
||
| # TS compile artefacts inside src (only source .ts should be tracked) | ||
| src/**/*.js | ||
| src/**/*.d.ts | ||
| src/**/*.js.map | ||
| azure-functions/src/**/*.js | ||
| azure-functions/src/**/*.d.ts | ||
| azure-functions/src/**/*.js.map | ||
|
|
||
| # env & secrets — never commit | ||
| .env | ||
| .env.local | ||
| .env.*.local | ||
| .mstoken-cache.json | ||
|
|
||
| # generated at first `npx a365` run — contains tenant/agent IDs | ||
| a365.generated.config.json | ||
| a365.config.json | ||
| .a365-workspace-detection.local.json | ||
|
|
||
| # dev logs | ||
| dev-out.txt | ||
| seed-out.txt | ||
| setup-out.txt | ||
| *.log | ||
|
|
||
| # agent skills installed via `gh skill add microsoft/agent365-skills` — workspace-local | ||
| .agents/ | ||
| .claude/ | ||
| .github/copilot-instructions.md | ||
| .github/copilot-instructions.md.bak |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should fix: Cron defaults are documented as UTC, but node-cron evaluates them in TIMEZONE, so the standup fires at 03:30 IST
The template describes these as UTC expressions (
0 30 3 * * 1-5 # UTC = 09:00 Asia/Kolkata), and the README calls them 'UTC cron'. But the local scheduler passes{ timezone: cfg.timezone }to node-cron, which matches the expression against wall-clock time in that zone.With
TIMEZONE=Asia/Kolkata:config.tsuses the same UTC strings as defaults, so leaving the vars unset has the same problem. The local scheduler and the Azure Function timers (which run these strings as UTC NCRONTAB) also fire 5.5 hours apart, not 'simultaneously'. README.md:221 callsTIMEZONE'Display only', but it drives scheduling, the T-2 gate in sprint-summary and the meeting-slot search.Evidence
.env.template:88-89:STANDUP_CRON=0 30 3 * * 1-5 # UTC = 09:00 Asia/Kolkata on weekdaysandNIGHTLY_CRON=0 0 19 * * * # UTC = 00:30 Asia/Kolkata daily..env.template:91:TIMEZONE=Asia/Kolkata..env.template:98:LOCAL_CRON=true.src/cron/local-scheduler.ts:37-44:cron.schedule(cfg.standupCron, async () => {...}, { timezone: cfg.timezone });(same at :46-52 for nightly).src/config.ts:106-109defaults:'0 30 3 * * 1-5','0 0 19 * * *',timezone: optional('TIMEZONE', 'Asia/Kolkata').timezoneoption compares against the time in that zone, so0 30 3 * * 1-5matches at 03:30 Asia/Kolkata.azure-functions/src/index.ts:31-34:// 09:00 Asia/Kolkata Mon–Fri = 03:30 UTC,schedule: '0 30 3 * * 1-5', as NCRONTAB in UTC.getScheduleConfig().timezoneis used in local-scheduler.ts:44/52, sprint-summary.ts:253-263 and chase.ts:332.Scenario: create
.envfrom the template and runnpm run devon Monday. Standup cards are DM'd at 03:30 IST and the cutoff summary posts at about 07:30 IST.With the Function App also deployed:
101#<Mon>).Suggested fix: Pick one convention and make the template, config defaults and README agree:
{ timezone: 'Etc/UTC' }in local-scheduler.ts). The documented values and the Function App timers then line up.0 0 9 * * 1-5and0 30 0 * * *, described as 'evaluated in TIMEZONE'.In either case, drop 'Display only' for TIMEZONE in the README.