Add scrum-master scenario: Scrum Master autopilot using Node.js SDK - #334
Add scrum-master scenario: Scrum Master autopilot using Node.js SDK#334keshav Keshari (keshavk-msft) wants to merge 7 commits into
Conversation
Scenario extension on top of the OpenAI + Agent 365 base sample: an autonomous Scrum Master that runs standups, reconciles the Jira board, chases blockers via MCP Calendar, warns on sprint risk, answers grounded Q&A, and posts a management-ready sprint close report. Runs out of the box with JIRA_MODE=mock (no Atlassian account required). Live mode uses Jira Cloud REST + delegated Microsoft Graph (Sites.ReadWrite.All + Sites.Manage.All) via device-code flow. * nodejs / Microsoft Agent 365 SDK + OpenAI Agents SDK + MCP tools * Adaptive Card DMs, per-user rolling context, deterministic classifier * Sibling azure-functions/ package hosts the nightly/weekly timers
Enables reviewers to try live mode (JIRA_MODE=live) without hand-crafting issues: * src/scripts/seed-jira-sample.ts — creates 2 stories + 5 sub-tasks and a future sprint on a pre-existing Jira Scrum project. Idempotent by summary. Reuses accountIds from team.sample.json so a single edit propagates through the whole seed. * src/scripts/sprint.sample.json — topology (edit to customise). * docs/sharepoint-schema.md — human-readable reference for every SMA_* list column, matching LIST_SCHEMAS in services/sharepoint.ts. * package.json — new npm script: seed:jira
Consolidates a single top-level H1 (was two) and reorders content for a first-time reader: intro paragraph -> features (7 MVPs) -> architecture diagram -> prerequisites -> quick start in mock mode -> full setup in live mode -> capability try-it table -> reference sections. Adds: * Mermaid architecture diagram * Quick start (mock mode, no external deps) as the fast path * Full configuration reference table with defaults and required-for * Try-each-capability table with trigger + expected + handler link * Link to docs/sharepoint-schema.md Drops: * Duplicated base-sample content (identity, install, typing, multi-message) now referenced via a link at the top * Redundant POC-flavoured framing
Additive-only improvements ported from patterns in the Chief-of-Staff sample (PR microsoft#333). Zero behaviour change to existing handlers. * src/util/logger.ts (new) — level-based logger with LOG_LEVEL, timestamped scope tags, and automatic secret redaction. Available for future use; existing console.log calls left in place. * src/util/httpLogger.ts (new) — global axios interceptor for outbound Jira / Graph / MCP tracing. Off by default, gated behind LOG_HTTP=true. * src/startup-check.ts (new) — one-shot boot banner printing every relevant env var with [MISSING] markers so misconfig surfaces before the first handler runs. * src/index.ts — wire installHttpLogging + printStartupBanner immediately after configDotenv(); add unhandledRejection and uncaughtException handlers so a stray connector 502 no longer tears down the scheduler. * docs/design.md — replace stub with full design doc: 12 numbered sections including per-flow mermaid sequence diagrams, determinism boundary, concurrency guarantees, and extension points. * README.md — trim architecture / reconcile rules / warn thresholds / calendar path (moved to design.md); add TOC, First live proof smoke test, Troubleshooting matrix, and Deploy to Azure sections. * .env.template — document LOG_LEVEL and LOG_HTTP toggles.
There was a problem hiding this comment.
Pull request overview
Adds a new Scrum Master autonomous scenario under scenarios/scrum-master/, built on the Microsoft Agent 365 SDK + OpenAI Agents SDK, with integrations for Jira (live + mock), SharePoint Lists state, proactive Teams messaging via Adaptive Cards, and optional Azure Functions timers to drive scheduled ceremonies.
Changes:
- Introduces the Scrum Master scenario agent (message routing, slash commands, standup/reconcile/chase/warn/sprint-summary flows) plus supporting services (Jira, SharePoint, Graph delegated auth, MCP Calendar).
- Adds provisioning + seed scripts for SharePoint lists, helper roster, and optional Jira sample data.
- Adds scenario packaging/config (ToolingManifest, env template, manifests, Azure Functions sibling project, build tooling).
Reviewed changes
Copilot reviewed 59 out of 63 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| scenarios/scrum-master/tsconfig.json | TypeScript compiler configuration for the scenario package. |
| scenarios/scrum-master/ToolingManifest.json | Declares MCP servers (Mail/Calendar) used by the scenario. |
| scenarios/scrum-master/src/util/logger.ts | Scenario-level log helper with redaction/truncation. |
| scenarios/scrum-master/src/util/httpLogger.ts | Optional global axios request/response tracing. |
| scenarios/scrum-master/src/token-cache.ts | In-memory token cache + token resolver for observability export. |
| scenarios/scrum-master/src/startup-check.ts | Startup banner summarizing key env configuration. |
| scenarios/scrum-master/src/services/team-roster.ts | SharePoint-backed team roster with cached ConversationReferences. |
| scenarios/scrum-master/src/services/sharepoint.ts | Graph CRUD wrapper for SharePoint lists + report upload helper. |
| scenarios/scrum-master/src/services/session-store.ts | In-memory standup session tracking for fan-in/cutoffs. |
| scenarios/scrum-master/src/services/proactive.ts | Proactive messaging helper (continueConversation wrapper). |
| scenarios/scrum-master/src/services/jira.ts | Jira client abstraction (live REST + mock implementation). |
| scenarios/scrum-master/src/services/jira-tool.ts | OpenAI Agents function-tools for grounded Jira Q&A. |
| scenarios/scrum-master/src/services/issue-labels.ts | Helpers to map Jira keys to “Task-N” labels and clean titles. |
| scenarios/scrum-master/src/services/helperMatcher.ts | Keyword-based helper matching for blocker chase flow. |
| scenarios/scrum-master/src/services/graph.ts | Delegated Graph auth via MSAL device-code + file token cache. |
| scenarios/scrum-master/src/services/calendar.ts | Calendar ops via MCP CalendarTools using a scenario-specific agent. |
| scenarios/scrum-master/src/scripts/team.sample.json | Sample team roster seed data (placeholder identities). |
| scenarios/scrum-master/src/scripts/sprint.sample.json | Sample sprint topology for optional Jira seeding. |
| scenarios/scrum-master/src/scripts/setup-sharepoint.ts | Idempotent SharePoint list + library provisioning script. |
| scenarios/scrum-master/src/scripts/seed-team.ts | Seeds TeamMembers list (optionally patches current user AAD id). |
| scenarios/scrum-master/src/scripts/seed-jira-sample.ts | Optional live Jira sample data seeding (stories/subtasks/sprint). |
| scenarios/scrum-master/src/scripts/seed-helper-roster.ts | Seeds HelperRoster list rows used by blocker chase. |
| scenarios/scrum-master/src/openai-config.ts | OpenAI/Azure OpenAI client configuration wiring for agents SDK. |
| scenarios/scrum-master/src/mock/jira-mock.ts | In-memory mutable mock sprint for offline demo mode. |
| scenarios/scrum-master/src/index.ts | Express host + internal endpoints + scheduler kickoff. |
| scenarios/scrum-master/src/handlers/warn.ts | Nightly sprint risk detection + SM DM. |
| scenarios/scrum-master/src/handlers/sprint-summary.ts | T-2 mid-sprint markdown summary report builder + delivery. |
| scenarios/scrum-master/src/handlers/reconcile.ts | Deterministic status classifier + safe Jira transitions + SM approval card. |
| scenarios/scrum-master/src/handlers/config.ts | /config channel capture of channel ConversationReference. |
| scenarios/scrum-master/src/handlers/commands.ts | Slash-command router for scenario commands. |
| scenarios/scrum-master/src/handlers/answer.ts | Grounded Jira Q&A agent with per-user rolling history. |
| scenarios/scrum-master/src/cron/local-scheduler.ts | In-process node-cron scheduler for dev mode. |
| scenarios/scrum-master/src/config.ts | Central env-var parsing for scenario configuration. |
| scenarios/scrum-master/src/client.ts | OpenAI Agents + MCP tool registration + observability scope wrapper. |
| scenarios/scrum-master/src/cards/transition-confirm.card.ts | Adaptive Card for SM approval of non-safe Jira transitions. |
| scenarios/scrum-master/src/cards/standup-summary.card.ts | Adaptive Card for consolidated standup summary posting. |
| scenarios/scrum-master/src/cards/standup-request.card.ts | Adaptive Card DM for standup collection + “submitted” confirmation. |
| scenarios/scrum-master/src/cards/meeting-propose.card.ts | Adaptive Card for SM meeting-slot selection and booking. |
| scenarios/scrum-master/src/cards/blocker-escalation.card.ts | Adaptive Card DM escalating a blocker to the SM. |
| scenarios/scrum-master/src/agent.ts | AgentApplication wiring: message routing, card submits, notifications, token preload. |
| scenarios/scrum-master/package.json | Scenario package metadata, dependencies, and scripts. |
| scenarios/scrum-master/manifest/manifest.json | Teams manifest template for the scenario. |
| scenarios/scrum-master/manifest/agenticUserTemplateManifest.json | Agentic user template manifest for the scenario. |
| scenarios/scrum-master/docs/sharepoint-schema.md | Human-readable SharePoint list schema reference. |
| scenarios/scrum-master/azure-functions/tsconfig.json | TS config for the Azure Functions sibling project. |
| scenarios/scrum-master/azure-functions/src/index.ts | Azure Functions timers that POST to agent internal endpoints. |
| scenarios/scrum-master/azure-functions/README.md | Azure Functions setup/run/deploy documentation. |
| scenarios/scrum-master/azure-functions/package.json | Azure Functions package metadata and deps. |
| scenarios/scrum-master/azure-functions/local.settings.sample.json | Local settings template for Functions (callback URL + token). |
| scenarios/scrum-master/azure-functions/host.json | Azure Functions host configuration. |
| scenarios/scrum-master/azure-functions/.gitignore | Ignores dist, node_modules, and local.settings.json secrets. |
| scenarios/scrum-master/.gitignore | Ignores build output, env files, MSAL cache, and logs. |
| scenarios/scrum-master/.env.template | Comprehensive env template covering OpenAI, Agent 365, Jira, SharePoint, scheduling. |
Comments suppressed due to low confidence (2)
scenarios/scrum-master/src/util/httpLogger.ts:50
- The response log line includes
res.config.urlverbatim, which may contain query strings. Strip?…/#…before logging.
scenarios/scrum-master/src/util/httpLogger.ts:60 - The error log line includes
cfg?.urlverbatim, which may contain query strings. Strip?…/#…before logging.
Applies 7 of the 8 review comments; the 8th (client.ts:140 MCP transport close-in-finally) is deferred because it changes runtime behaviour on a codepath inherited from the base sample. * config.ts — default JIRA_MODE to 'mock' (was 'live') so a fresh clone runs offline in mock mode as the README documents, instead of throwing on missing Jira credentials. * index.ts — remove duplicate unhandledRejection/uncaughtException handlers (kept the more-detailed pair at top of file). * index.ts — emit a one-shot boot warning when INTERNAL_TRIGGER_TOKEN is empty so production deployments notice open /api/internal/* endpoints. * token-cache.ts — replace 'All rights reserved' header with the repo-standard MIT license header. * client.ts — rename observability service from 'TypeScript Claude Sample Agent' to 'Scrum Master Sample Agent'. * client.ts — fix fallback agentId/agentName (was 'typescript-compliance-agent' / 'TypeScript Compliance Agent'). * util/httpLogger.ts — add safePath() helper and strip query strings + URL fragments from request/response/error log lines so credentials in ?token=… never leak. * AGENT-CODE-WALKTHROUGH.md — sync doc snippet to new agentId strings.
|
Thanks copilot-pull-request-reviewer for the thorough pass. Pushed
Deferred: comment #7 (
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 63 changed files in this pull request and generated 8 comments.
Comments suppressed due to low confidence (4)
scenarios/scrum-master/src/services/sharepoint.ts:127
- findByField() has the same issue as findByTitle(): URL-encoding the value inside the OData literal can break matches (e.g., spaces become %20). Encode the entire $filter expression and keep escapeODataString limited to OData single-quote escaping.
scenarios/scrum-master/src/client.ts:139 - invokeAgent() always closes MCP servers in finally. In this same scenario, calendar.ts documents that closing Streamable-HTTP MCP transports can break subsequent calls with "Session not found" because McpToolRegistrationService can reuse an already-closed transport. Consider not closing MCP servers here (or only closing on process shutdown) to avoid intermittent MCP failures across turns.
await this.closeServers();
scenarios/scrum-master/azure-functions/README.md:24
- This second link to
../openai/sample-agentis also incorrect for this scenario (the agent is..). Updating it avoids sending readers to a non-existent path.
- The agent already running somewhere reachable — either a dev tunnel URL, App Service, or Container Apps endpoint that exposes `/api/internal/*` from [`../openai/sample-agent`](../openai/sample-agent).
scenarios/scrum-master/src/index.ts:94
- When INTERNAL_TRIGGER_TOKEN is empty, /api/internal/* endpoints become unauthenticated even if the server is bound to 0.0.0.0 (non-development). Logging a warning is easy to miss in deployment logs; it's safer to reject requests unless you're explicitly in development mode.
| const graph = getGraphClient(); | ||
| const res = await graph | ||
| .api(`/sites/${siteId}/lists/${listName(key)}/items?$expand=fields&$filter=fields/Title eq '${escapeODataString(title)}'`) | ||
| .header('Prefer', 'HonorNonIndexedQueriesWarningMayFailRandomly') | ||
| .get(); |
| // inside our filter value as URL syntax. Our IDs use `#` as a separator (e.g. | ||
| // `<sprintId>#<yyyy-mm-dd>`), which without encoding gets treated as a URL fragment | ||
| // and truncates the filter — silently returning zero rows. | ||
| return encodeURIComponent(s.replace(/'/g, "''")); |
| export function getWarnConfig(): WarnConfig { | ||
| return { | ||
| todoPct: Number(optional('WARN_TODO_PCT', '0.40')), | ||
| sprintProgressPct: Number(optional('WARN_SPRINT_PROGRESS_PCT', '0.50')), | ||
| }; | ||
| } |
| scopes: getObservabilityAuthenticationScope() | ||
| }); | ||
|
|
||
| console.log(`Preloaded Observability token for agentId=${agentId}, tenantId=${tenantId} token=${aauToken?.token?.substring(0, 10)}...`); |
| const response = await client.invokeAgentWithScope( | ||
| `You have received the following email. Please follow any instructions in it. ${emailContent}` | ||
| ); |
| }, | ||
| "devDependencies": { | ||
| "@microsoft/m365agentsplayground": "^0.2.18", | ||
| "@types/express": "^4.17.21", |
| @@ -0,0 +1,121 @@ | |||
| # Scrum Master Assistant — Azure Functions | |||
|
|
|||
| Two timer-triggered functions that drive the SMA scheduled ceremonies. The agent process (in [`../openai/sample-agent`](../openai/sample-agent)) owns all state, tokens, and Adaptive Card logic — these functions only *nudge* it via HTTP. | |||
| /** | ||
| * Simple custom in-memory token cache with expiration handling | ||
| * In production, use a more robust caching solution like Redis | ||
| */ |
cce56fe to
aa27069
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 63 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
scenarios/scrum-master/src/agent.ts:238
- The prompt
Please follow any instructions in itmakes the agent treat email content as executable instructions, which is a classic prompt-injection vector. Even with general safety guidance elsewhere, it’s better to frame email content as untrusted data and ask for summarization + a drafted response.
// Then process the email
const response = await client.invokeAgentWithScope(
`You have received the following email. Please follow any instructions in it. ${emailContent}`
);
scenarios/scrum-master/src/client.ts:199
closeServers()closes every MCP server after each agent invocation. This conflicts with the calendar MCP guidance inservices/calendar.ts/ README troubleshooting: streamable HTTP MCP sessions are intended to be long-lived, and closing can lead to-32001 Session not foundon subsequent turns when the registration service reuses a closed transport.
private async closeServers(): Promise<void> {
if (this.agent.mcpServers && this.agent.mcpServers.length > 0) {
for (const server of this.agent.mcpServers) {
await server.close();
}
}
}
| const internalToken = getInternalTriggerToken(); | ||
| if (!internalToken) { | ||
| // Loud one-shot warning so production deployments don't accidentally ship | ||
| // open internal endpoints. Dev convention still allows an empty token so a | ||
| // solo developer can curl the endpoints without setting a secret. | ||
| console.warn( | ||
| '[security] INTERNAL_TRIGGER_TOKEN is empty — /api/internal/* is UNAUTHENTICATED. ' + | ||
| 'Set INTERNAL_TRIGGER_TOKEN in .env before deploying anywhere non-local.', | ||
| ); | ||
| } | ||
| function requireInternalToken(req: express.Request, res: Response): boolean { | ||
| const provided = req.get('x-internal-token') ?? ''; | ||
| if (!internalToken) { | ||
| // Convention: empty token in .env => endpoint is open (dev-only). The one-shot | ||
| // warning above logs this condition; each request stays quiet. | ||
| return true; | ||
| } | ||
| if (provided !== internalToken) { | ||
| res.status(401).json({ error: 'invalid internal token' }); | ||
| return false; | ||
| } | ||
| return true; | ||
| } |
| const adapter = agentApplication.adapter as CloudAdapter; | ||
| const botAppId = getBotAppId(); | ||
| try { | ||
| // Cast: stored references always come from `activity.getConversationReference()` | ||
| // which returns a fully-populated ConversationReference, but our storage layer | ||
| // deserializes as `Partial<>`. Adapter throws at runtime on truly-incomplete refs. | ||
| await adapter.continueConversation(botAppId, reference as ConversationReference, async (context) => { | ||
| await logic(context); | ||
| }); |
| type: 'Input.Toggle', | ||
| id: `approve_${t.issueKey}`, | ||
| title: 'Approve this change', | ||
| value: 'true', |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 63 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (6)
scenarios/scrum-master/src/agent.ts:191
- Logging even a token prefix can leak credentials via console logs (often shipped to centralized log stores). Avoid printing any part of the observability token; log only metadata like agentId/tenantId.
console.log(`Preloaded Observability token for agentId=${agentId}, tenantId=${tenantId} token=${aauToken?.token?.substring(0, 10)}...`);
scenarios/scrum-master/src/agent.ts:238
- This prompt tells the model to "follow any instructions" inside an email, which is a direct prompt-injection vector (email content is untrusted). Ask the model to summarize/extract action items instead, without executing instructions from the email body.
const response = await client.invokeAgentWithScope(
`You have received the following email. Please follow any instructions in it. ${emailContent}`
);
scenarios/scrum-master/src/client.ts:140
- This closes MCP server sessions after every run(). In this same scenario (see calendar.ts), closing Streamable-HTTP MCP transports can break subsequent tool calls with "Session not found" because McpToolRegistrationService may reuse the underlying transport across agents. Consider keeping MCP sessions long-lived here as well.
} finally {
await this.closeServers();
}
scenarios/scrum-master/src/index.ts:94
- If INTERNAL_TRIGGER_TOKEN is empty, /api/internal/* becomes unauthenticated for all environments (including production misconfig). It would be safer to only allow the "open" behavior in development and fail closed otherwise.
scenarios/scrum-master/azure-functions/README.md:3 - This README links to
../openai/sample-agent, but the agent process for this scenario is the parentscenarios/scrum-masterpackage (and its README). The current link is broken/misleading in this repo layout.
Two timer-triggered functions that drive the SMA scheduled ceremonies. The agent process (in [`../openai/sample-agent`](../openai/sample-agent)) owns all state, tokens, and Adaptive Card logic — these functions only *nudge* it via HTTP.
scenarios/scrum-master/azure-functions/README.md:24
- Same as above: this link points at
../openai/sample-agent, but the internal endpoints live in the parent scenario package. Update the reference so readers can find the right code/docs.
- The agent already running somewhere reachable — either a dev tunnel URL, App Service, or Container Apps endpoint that exposes `/api/internal/*` from [`../openai/sample-agent`](../openai/sample-agent).
| scope.recordInputTokens(45); | ||
| scope.recordOutputTokens(78); |
| console.log('[OpenAI Config] Using Azure OpenAI'); | ||
| console.log(`[OpenAI Config] Endpoint: ${process.env.AZURE_OPENAI_ENDPOINT}`); | ||
| console.log(`[OpenAI Config] Deployment: ${process.env.AZURE_OPENAI_DEPLOYMENT}`); |
The MCP calendar tool loop occasionally succeeds at creating the event but the LLM returns a garbled response (e.g. webLink="onlineMeetingUrl" as a literal string, or id="errorEvent verification failed..." from a mis-formed rule-microsoft#6 error path). The existing !out.webLink check misses this because the values are truthy strings that pass Zod validation. * services/calendar.ts — after the existing empty-webLink guard, check that webLink is actually a URL (^https?://) and that id doesn't start with "error". On mismatch, throw a distinct CALENDAR_VERIFY_FAILED: error with an actionable "verify in Outlook" message. * handlers/chase.ts — catch the CALENDAR_VERIFY_FAILED: prefix separately; mark the blocker as booked (event was almost certainly created) and DM the SM a warning that asks them to verify in Outlook, instead of the scary "couldn't book the meeting" error. Tested in the POC with real MCP calendar output that reproduced the shape described.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 63 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (7)
scenarios/scrum-master/src/agent.ts:191
- This log line prints a prefix of the exchanged observability token. Even partial token material can be used for correlation/attacks and shouldn’t be emitted to logs.
console.log(`Preloaded Observability token for agentId=${agentId}, tenantId=${tenantId} token=${aauToken?.token?.substring(0, 10)}...`);
scenarios/scrum-master/src/agent.ts:238
- The email-notification flow explicitly instructs the model to “follow any instructions” found in the email content. That creates a prompt-injection path where untrusted email content can drive unintended tool use or actions. The agent should treat email as untrusted data: summarize it, extract requests, and ask for user confirmation before acting.
// Then process the email
const response = await client.invokeAgentWithScope(
`You have received the following email. Please follow any instructions in it. ${emailContent}`
);
scenarios/scrum-master/src/token-cache.ts:37
- The comments describe “expiration handling” and “store a token with expiration”, but the implementation is a plain Map with no TTL/expiry logic. This is misleading for readers and for anyone reusing the sample code.
scenarios/scrum-master/src/client.ts:199 - This closes all MCP server transports after every agent invocation. In this scenario, calendar.ts documents that closing a Streamable-HTTP MCP transport can break subsequent tool calls with “Session not found” when McpToolRegistrationService reuses the closed transport across turns. To avoid intermittent failures, consider not closing MCP servers here (or ensure each agent gets a non-shared transport instance).
private async closeServers(): Promise<void> {
if (this.agent.mcpServers && this.agent.mcpServers.length > 0) {
for (const server of this.agent.mcpServers) {
await server.close();
}
}
}
scenarios/scrum-master/src/handlers/report.ts:268
- The action-items table hardcodes the target sprint as "Sprint 2". That will be incorrect for most real sprint names; use a neutral label like "Next sprint" (or derive it from sprint metadata if available).
scenarios/scrum-master/src/handlers/chase.ts:17 - The header comment describes the blocker-chase flow as using direct Graph calls (/me/findMeetingTimes, POST /events). The implementation now drives mcp_CalendarTools via services/calendar.ts, so this comment is outdated/misleading.
scenarios/scrum-master/src/handlers/report.ts:260 - The action-items table hardcodes the target sprint as "Sprint 2". That will be incorrect for most real sprint names (and even for mock data) and can confuse readers; consider using a neutral label like "Next sprint".
This issue also appears on line 268 of the same file.
| const internalToken = getInternalTriggerToken(); | ||
| if (!internalToken) { | ||
| // Loud one-shot warning so production deployments don't accidentally ship | ||
| // open internal endpoints. Dev convention still allows an empty token so a | ||
| // solo developer can curl the endpoints without setting a secret. | ||
| console.warn( | ||
| '[security] INTERNAL_TRIGGER_TOKEN is empty — /api/internal/* is UNAUTHENTICATED. ' + | ||
| 'Set INTERNAL_TRIGGER_TOKEN in .env before deploying anywhere non-local.', | ||
| ); | ||
| } | ||
| function requireInternalToken(req: express.Request, res: Response): boolean { | ||
| const provided = req.get('x-internal-token') ?? ''; | ||
| if (!internalToken) { | ||
| // Convention: empty token in .env => endpoint is open (dev-only). The one-shot | ||
| // warning above logs this condition; each request stays quiet. | ||
| return true; | ||
| } | ||
| if (provided !== internalToken) { | ||
| res.status(401).json({ error: 'invalid internal token' }); | ||
| return false; | ||
| } | ||
| return true; | ||
| } |
Add scrum-master scenario: Scrum Master autopilot using Node.js SDK
What this adds
A scenario extension under
scenarios/scrum-master/— an autonomous Scrum Master built on the Microsoft Agent 365 SDK + OpenAI Agents SDK that runs a scrum team's ceremonies end-to-end: daily standups, board reconciliation, blocker chase, mid-sprint risk warnings, grounded Q&A, and the sprint close report. Complements the existing Chief-of-Staff scenario (#333) by covering the delivery-team side of leader / squad workflows.Capabilities
Seven flows, each a handler in
src/handlers/:SMA_HelperRoster→ MCP calendar tool books an unblock meeting on the agent's mailboxFull per-flow mermaid sequence diagrams:
scenarios/scrum-master/docs/design.md.Design principles
Verbatim from
docs/design.md:setImmediate.SMA_*lists survive restarts.What's included
Runs offline
JIRA_MODE=mock(the default) drives the sample against a mutable in-memory Jira sprint insrc/mock/jira-mock.ts. Contributors can clone,npm install,npm run dev,npm run test-tooland try/standupin the Agents Playground without an Atlassian account, without a SharePoint site, and without a Teams tenant.Live mode (
JIRA_MODE=live) requires:Auth model — delegated only
All Microsoft Graph calls use MSAL device-code + delegated scopes (
Sites.ReadWrite.All,Sites.Manage.All,Files.ReadWrite.All). Onboarding is a single interactive sign-in. No application-permission client credentials, no admin consent, no Teams policies to provision.Verification
00000000-…,<your-org>.atlassian.net,alice@contoso.cometc.Placement — why
scenarios/Placed at the repo root under
scenarios/scrum-master/rather than under a language tier (e.g.nodejs/openai/) because a scrum-master workflow doesn't map cleanly to a single SDK — it composes Agent 365 SDK, OpenAI Agents SDK, MCP Calendar, and multiple external services. This mirrors how the Chief-of-Staff sample (#333) is placed. Happy to relocate tonodejs/openai/scenarios/scrum-master/if reviewers prefer a language-tier scoping.Not included / future work
SMA_Teamslist +TeamIdcolumn + per-team Jira credentials in Key Vault is documented indocs/design.md..mstoken-cache.json, gitignored). Production swap-in is called out indocs/design.md.tests/e2e/. Recommend follow-up PR once initial review lands.Commits
d33aecc— initial import of the scenario code (57 files)feb98d9— Jira sample-data seed script + SharePoint schema doc1c5a4af— README rewrite (intro-features-prereqs-setup structure)62b0cad— observability + docs polish (logger, http-logger, startup banner, process safety nets, design.md, TOC, troubleshooting, deploy-to-Azure)Related
nodejs/openai/sample-agent