Repository navigation
Add career-coach scenario: Career Coach autopilot using Node.js SDK #338
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
bd4e262
e27d31d
fc93651
52223e5
f35c140
e8f6d35
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,130 @@ | ||||||||||||||||||
| # ───────────────────────────────────────────────────────────────────────────── | ||||||||||||||||||
| # Career Coach autopilot — environment template | ||||||||||||||||||
| # Copy this file to .env and fill in the values marked <CHANGE_ME>. | ||||||||||||||||||
| # All non-secret defaults are kept — you can leave those as-is. | ||||||||||||||||||
| # Never commit the filled-in .env. | ||||||||||||||||||
| # ───────────────────────────────────────────────────────────────────────────── | ||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 1. OPENAI CONFIGURATION | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # Use EITHER standard OpenAI OR Azure OpenAI (not both). Azure OpenAI takes | ||||||||||||||||||
| # precedence if AZURE_OPENAI_API_KEY is set. | ||||||||||||||||||
|
|
||||||||||||||||||
| # --- Option A: Standard OpenAI --- | ||||||||||||||||||
| OPENAI_API_KEY= | ||||||||||||||||||
| OPENAI_MODEL=gpt-4o | ||||||||||||||||||
|
|
||||||||||||||||||
| # --- Option B: Azure OpenAI (recommended for this project) --- | ||||||||||||||||||
| AZURE_OPENAI_API_KEY=<CHANGE_ME_azure_openai_key> | ||||||||||||||||||
| AZURE_OPENAI_ENDPOINT=https://<CHANGE_ME_resource_name>.openai.azure.com/ | ||||||||||||||||||
| AZURE_OPENAI_DEPLOYMENT=gpt-4o | ||||||||||||||||||
| AZURE_OPENAI_API_VERSION=2024-10-21 | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 2. MCP + OBSERVABILITY | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| BEARER_TOKEN=<CHANGE_ME_bearer_token_optional_for_MCP_tools> | ||||||||||||||||||
|
|
||||||||||||||||||
| # A365 observability exporter (Defender / Purview / Microsoft 365 admin center reporting | ||||||||||||||||||
| # needs true). Set to false only for console-only local runs. | ||||||||||||||||||
| ENABLE_A365_OBSERVABILITY_EXPORTER=true | ||||||||||||||||||
|
|
||||||||||||||||||
| # Exporter diagnostics: none | info | warn | error (pipe-separated). warn|error surfaces failed exports; | ||||||||||||||||||
| # add info to also log successful export / token activity (useful while verifying telemetry, noisy afterwards). | ||||||||||||||||||
| A365_OBSERVABILITY_LOG_LEVEL=info|warn|error | ||||||||||||||||||
| # OpenTelemetry SDK diagnostics. Uncomment together with the line above to see export results. | ||||||||||||||||||
| # OTEL_LOG_LEVEL=INFO | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 3. ENVIRONMENT SETTINGS | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # NODE_ENV=development | ||||||||||||||||||
| # HOST=127.0.0.1 | ||||||||||||||||||
| # PORT defaults to 3978. | ||||||||||||||||||
| # PORT=3978 | ||||||||||||||||||
|
|
||||||||||||||||||
| # Debug namespace. Narrowed from 'agents:*' to cut MSAL/connector noise while | ||||||||||||||||||
| # keeping onTurnError + adapter events visible. | ||||||||||||||||||
| DEBUG=agents:app:*,agents:cloud-adapter:* | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 4. SHAREPOINT CONFIGURATION | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| SP_SITE_HOST=<CHANGE_ME_tenant>.sharepoint.com | ||||||||||||||||||
| SP_SITE_PATH=/sites/CareerCoach | ||||||||||||||||||
| SP_LIST_COMPETENCY_FRAMEWORK=CompetencyFramework_v2 | ||||||||||||||||||
| SP_LIST_LEARNING_CATALOG=LearningCatalog_v2 | ||||||||||||||||||
| SP_LIST_USER_STATE=UserState | ||||||||||||||||||
| SP_LIST_LEARNING_PORTAL_STATUS=LearningPortalStatus | ||||||||||||||||||
| SP_LIST_QUIZ_RESPONSES=QuizResponses | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 5. FEATURE 1 — REAL-TIME WEBHOOK (Graph subscription → proactive DM) | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # Random shared secret. Generate one with: | ||||||||||||||||||
| # [Guid]::NewGuid().ToString() + [Guid]::NewGuid().ToString() | ||||||||||||||||||
| PORTAL_EVENT_SECRET=<CHANGE_ME_random_guid_pair> | ||||||||||||||||||
|
Comment on lines
+69
to
+71
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. Minor: Shipped PORTAL_EVENT_SECRET placeholder is accepted as a valid webhook secret The template ships
README line 164 understates the risk by describing the value as 'Any random string (used for a manual POST test path)'. It is also the only check on Graph notifications. The repo guidance asks to 'Confirm required secrets are validated at startup with fail-fast behavior (no silent defaults for security-critical config)'. Evidence
Scenario:
Suggested fix: Ship
Suggested change
|
||||||||||||||||||
|
|
||||||||||||||||||
| # Public HTTPS URL that Microsoft Graph POSTs change notifications to. | ||||||||||||||||||
| # Point at your dev tunnel + /api/portal-event. Example: | ||||||||||||||||||
| # https://spiffy-dog-abc.inc1.devtunnels.ms/api/portal-event | ||||||||||||||||||
| PORTAL_WEBHOOK_URL=https://<CHANGE_ME_devtunnel>.inc1.devtunnels.ms/api/portal-event | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 6. A365 HOSTED-AGENT AUTH (agentic identity, no manual token refresh) | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| USE_AGENTIC_AUTH=true | ||||||||||||||||||
|
|
||||||||||||||||||
| # Values below come from the Entra ID app registration that A365 provisions for | ||||||||||||||||||
| # your hosted-agent project. Get them from: Azure portal → App registrations | ||||||||||||||||||
| connections__service_connection__settings__clientId=<CHANGE_ME_agent_client_id> | ||||||||||||||||||
| connections__service_connection__settings__clientSecret=<CHANGE_ME_agent_client_secret> | ||||||||||||||||||
| connections__service_connection__settings__tenantId=<CHANGE_ME_tenant_id> | ||||||||||||||||||
|
|
||||||||||||||||||
| connectionsMap__0__serviceUrl=* | ||||||||||||||||||
| connectionsMap__0__connection=service_connection | ||||||||||||||||||
|
|
||||||||||||||||||
| agentic_type=agentic | ||||||||||||||||||
| agentic_altBlueprintConnectionName=service_connection | ||||||||||||||||||
| agentic_scopes=https://graph.microsoft.com/.default | ||||||||||||||||||
| agent_id=<CHANGE_ME_agent_client_id> | ||||||||||||||||||
| connections__service_connection__settings__scopes=5a807f24-c9de-44ee-a3a7-329e88a00ffc/.default | ||||||||||||||||||
| agentic_connectionName=AgenticAuthentication | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 7. A365 OBSERVABILITY (Agent 365 telemetry — safe to leave duplicated) | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # agentId = the runtime Agent Identity (the app id of the agent instance, NOT the blueprint id). The | ||||||||||||||||||
| # exporter authenticates as this identity; the live id is read from each turn and this is only the fallback. | ||||||||||||||||||
| # Find it in the "[A365 Observability] App-only export token acquired for agent <id>" startup log line. | ||||||||||||||||||
| agent365Observability__agentId=<CHANGE_ME_agent_identity_app_id> | ||||||||||||||||||
| agent365Observability__agentName=EmployeeCareerCoachIdentity | ||||||||||||||||||
| agent365Observability__agentDescription=EmployeeCareerCoach | ||||||||||||||||||
| agent365Observability__tenantId=<CHANGE_ME_tenant_id> | ||||||||||||||||||
| # agentBlueprintId = the blueprint's app id (a365.generated.config.json -> agentBlueprintId). | ||||||||||||||||||
| agent365Observability__agentBlueprintId=<CHANGE_ME_blueprint_app_id> | ||||||||||||||||||
| agent365Observability__clientId=<CHANGE_ME_agent_client_id> | ||||||||||||||||||
| agent365Observability__clientSecret=<CHANGE_ME_agent_client_secret> | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # 8. SETUP / SEED SCRIPTS (device-code auth, developer only) | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # setup:sharepoint / seed:reference / mark:complete sign in with MSAL device-code. | ||||||||||||||||||
| # Defaults: a well-known public Microsoft Graph client + the 'common' tenant. | ||||||||||||||||||
| # Override only if your tenant blocks the default public client. | ||||||||||||||||||
| GRAPH_AUTH_CLIENT_ID= | ||||||||||||||||||
| GRAPH_AUTH_TENANT_ID= | ||||||||||||||||||
| # Default test user the helper scripts (mark:complete / reset:milestones / backup:user) | ||||||||||||||||||
| # act on. Set to your test user's AAD object id + display name. | ||||||||||||||||||
| TEST_USER_AAD_ID= | ||||||||||||||||||
| TEST_USER_NAME= | ||||||||||||||||||
| # Optional override for the seed CSV directory (defaults to ./SharePoint Data). | ||||||||||||||||||
| SP_SEED_CSV_DIR= | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # dependencies + build | ||
| node_modules/ | ||
| dist/ | ||
| *.tsbuildinfo | ||
|
|
||
| # TS compile artefacts inside src (only source .ts should be tracked) | ||
| src/**/*.js | ||
| src/**/*.d.ts | ||
| src/**/*.js.map | ||
|
|
||
| # env & secrets — never commit | ||
| .env | ||
| .env.local | ||
| .env.*.local | ||
| .mstoken-cache.json | ||
|
|
||
| # generated at first `a365` run — contains tenant/agent IDs | ||
| a365.config.json | ||
| a365.generated.config.json | ||
| .a365-workspace-detection.local.json | ||
|
|
||
| # runtime state + backups | ||
| .proactive-storage.json | ||
| .proactive-refs.json | ||
| .sp-subscription.json | ||
| .email-plans.json | ||
| backups/ | ||
|
|
||
| # dev logs | ||
| dev.log | ||
| *.log | ||
|
|
||
| # packaged Teams app | ||
| manifest/manifest.zip | ||
|
|
||
| # 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.
Minor: BEARER_TOKEN placeholder replaces the agentic MCP token, so WorkIQ tools never attach
The template ships
BEARER_TOKEN=<CHANGE_ME_bearer_token_optional_for_MCP_tools>.attachWorkIqToolspassesprocess.env.BEARER_TOKEN || ''toaddToolServersToAgent, so on every turn this non-empty placeholder is used as the MCP auth token. The agentic user token is only obtained when that argument is empty, so it is never fetched.The README describes the variable as an optional dev override ('Leave empty normally'), so most people will leave the shipped value alone. Tool-server registration or connection then fails. The error is caught and only logged as a warning, and the Mail and Calendar tools silently never attach. As a result, 'email me my plan' and 'find time for my 1:1' (README §8) don't work. The sibling Node templates ship this key empty.
Evidence
.env.template:28:BEARER_TOKEN=<CHANGE_ME_bearer_token_optional_for_MCP_tools>.src/client.ts:413:await toolService.addToolServersToAgent(this.agent, ctx.authorization, this.authHandlerName, ctx.turnContext, process.env.BEARER_TOKEN || '');. A non-empty value is passed through as the token.src/client.ts:414-418: catches the failure, logs[WorkIQ] Failed to register MCP tool servers (continuing without them)and setsthis.agent.mcpServers = [].README.md:149: 'Token froma365 develop get-token, only for running MCP tools without the platform token. Leave empty normally. Tokens expire.'AGENT-CODE-WALKTHROUGH.md:224: 'WorkIQ (dev only) |BEARER_TOKEN'.BEARER_TOKEN=:nodejs/openai/sample-agent/.env.template:21andnodejs/claude/sample-agent/.env.template:11.Scenario: copy the template, fill in the values the README calls required, then ask the agent 'email me my plan'. No Mail tool is available, and the only sign of the problem is the WorkIQ warning in the log.
Suggested fix: Ship
BEARER_TOKEN=empty, with a comment that it is an optional, dev-only override froma365 develop get-token.