Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions nodejs/devin/sample-agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# TeamsFx files
env/.env.*.user
env/.env.local
env/.env.sandbox
.localConfigs
.localConfigs.playground
.notification.localstore.json
.notification.playgroundstore.json
.notification.testtoolstore.json
appPackage/build

# dependencies
node_modules/

# misc
.env
.deployment
.DS_Store

# build
dist/

# Dev tool directories
/devTools/
5 changes: 5 additions & 0 deletions nodejs/devin/sample-agent/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"TeamsDevApp.ms-teams-vscode-extension"
]
}
31 changes: 31 additions & 0 deletions nodejs/devin/sample-agent/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Local Service",
"type": "node",
"request": "attach",
"port": 9239,
"restart": true,
"presentation": {
"group": "all",
"hidden": true
},
"internalConsoleOptions": "neverOpen"
}
],
"compounds": [
{
"name": "Debug in Microsoft 365 Agents Playground",
"configurations": [
"Attach to Local Service"
],
"preLaunchTask": "Start App in Microsoft 365 Agents Playground",
"presentation": {
"group": "1-playground",
"order": 1
},
"stopAll": true
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
$ErrorActionPreference = 'Stop'

$workspace = Get-Location
$playgroundEnvPath = Join-Path $workspace 'env/.env.playground'
$playgroundUserEnvPath = Join-Path $workspace 'env/.env.playground.user'

$a365Command = Get-Command a365 -ErrorAction SilentlyContinue
if (-not $a365Command) {
throw "a365 CLI is not installed or not on PATH. Install with: dotnet tool install --global Microsoft.Agents.A365.DevTools.Cli"
}

if (-not (Test-Path $playgroundEnvPath)) {
throw "Missing env file: $playgroundEnvPath"
}

$appIdLine = Get-Content $playgroundEnvPath | Where-Object { $_ -match '^\s*CLIENT_APP_ID\s*=\s*.+$' } | Select-Object -First 1
if (-not $appIdLine) {
throw "CLIENT_APP_ID is required in env/.env.playground"
}

$appId = ($appIdLine -split '=', 2)[1].Trim()
if ([string]::IsNullOrWhiteSpace($appId)) {
throw "CLIENT_APP_ID in env/.env.playground is empty"
}

Write-Host "Running a365 develop add-permissions for app id $appId"
& a365 develop add-permissions --app-id $appId
if ($LASTEXITCODE -ne 0) {
throw "a365 develop add-permissions failed"
}

Write-Host "Getting bearer token via a365..."
Write-Host "This may complete silently using cached credentials, or it may require interactive Windows sign-in (WAM)."
Write-Host "If interactive sign-in is required and no prompt appears, check the taskbar for a hidden sign-in window and bring it to front."
Write-Host "Running a365 develop get-token for app id $appId"
$tokenOutput = & a365 develop get-token --app-id $appId --output raw
if ($LASTEXITCODE -ne 0) {
throw "a365 develop get-token failed"
}

$rawOutput = [string]::Join("`n", $tokenOutput)
$rawOutput = $rawOutput -replace "`r", ''

$bearerToken = $null
$jwtRegex = '(?<token>[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)'
$jwtMatches = [regex]::Matches($rawOutput, $jwtRegex)
if ($jwtMatches.Count -gt 0) {
$bearerToken = ($jwtMatches | ForEach-Object { $_.Groups['token'].Value } | Sort-Object Length -Descending | Select-Object -First 1).Trim()
}

if ([string]::IsNullOrWhiteSpace($bearerToken)) {
throw "Unable to extract a bearer token from a365 develop get-token output"
}

$userEnvLines = @()
if (Test-Path $playgroundUserEnvPath) {
$userEnvLines = Get-Content $playgroundUserEnvPath
}

$updated = $false
for ($i = 0; $i -lt $userEnvLines.Count; $i++) {
if ($userEnvLines[$i] -match '^\s*SECRET_BEARER_TOKEN\s*=') {
$userEnvLines[$i] = "SECRET_BEARER_TOKEN=$bearerToken"
$updated = $true
break
}
}

if (-not $updated) {
if ($userEnvLines.Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($userEnvLines[$userEnvLines.Count - 1])) {
$userEnvLines += ''
}
$userEnvLines += "SECRET_BEARER_TOKEN=$bearerToken"
}

Set-Content -Path $playgroundUserEnvPath -Value $userEnvLines -Encoding UTF8
Write-Host 'SECRET_BEARER_TOKEN has been updated in env/.env.playground.user'
117 changes: 117 additions & 0 deletions nodejs/devin/sample-agent/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Start App in Microsoft 365 Agents Playground",
"dependsOn": [
"Validate prerequisites (Microsoft 365 Agents Playground)",
"Refresh bearer token (Microsoft 365 Agents Playground)",
"Deploy (Microsoft 365 Agents Playground)",
"Start application (Microsoft 365 Agents Playground)",
"Start Microsoft 365 Agents Playground"
],
"dependsOrder": "sequence"
},
{
"label": "Validate prerequisites (Microsoft 365 Agents Playground)",
"type": "teamsfx",
"command": "debug-check-prerequisites",
"args": {
"prerequisites": [
"nodejs",
"portOccupancy"
],
"portOccupancy": [
3978,
9239,
56150
]
}
},
{
"label": "Refresh bearer token (Microsoft 365 Agents Playground)",
"type": "shell",
"command": "powershell",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${workspaceFolder}/.vscode/scripts/refresh-bearer-token.ps1"
],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Deploy (Microsoft 365 Agents Playground)",
"dependsOrder": "sequence",
"type": "teamsfx",
"command": "deploy",
"args": {
"env": "playground"
}
},
{
"label": "Start application (Microsoft 365 Agents Playground)",
"type": "shell",
"command": "npm run dev:teamsfx:playground",
"isBackground": true,
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": {
"pattern": [
{
"regexp": "^.*$",
"file": 0,
"location": 1,
"message": 2
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "[nodemon] starting",
"endsPattern": "Server listening on|Bot/ME service listening at|[nodemon] app crashed"
Comment on lines +74 to +75
}
}
},
{
"label": "Start Microsoft 365 Agents Playground",
"type": "shell",
"command": "npm run dev:teamsfx:launch-playground",
"isBackground": true,
"options": {
"env": {
"PATH": "${workspaceFolder}/devTools/playground/node_modules/.bin;${env:PATH}"
}
},
"windows": {
"options": {
"env": {
"PATH": "${workspaceFolder}/devTools/playground/node_modules/.bin;${env:PATH}"
}
}
},
"problemMatcher": {
"pattern": [
{
"regexp": "^.*$",
"file": 0,
"location": 1,
"message": 2
}
],
"background": {
"activeOnStart": true,
"beginsPattern": ".*",
"endsPattern": "Listening on"
}
},
"presentation": {
"panel": "dedicated",
"reveal": "silent"
}
}
]
}
10 changes: 10 additions & 0 deletions nodejs/devin/sample-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ For comprehensive documentation and guidance on building agents with the Microso
- Microsoft Agent 365 SDK
- Devin API credentials

## Running the Agent in Microsoft 365 Agents Playground

1. First, select the Microsoft 365 Agents Toolkit icon on the left in the VS Code toolbar.
1. In file *env/.env.playground.user*, fill in your Devin API key `SECRET_DEVIN_API_KEY=<your-key>`.
1. In file *env/.env.playground*, fill in your custom app registration client id `CLIENT_APP_ID`.
1. Press F5 to start debugging which launches your agent in Microsoft 365 Agents Playground using a web browser. Select `Debug in Microsoft 365 Agents Playground`.
1. You can send any message to get a response from the agent.

**Congratulations**! You are running an agent that can now interact with users in Microsoft 365 Agents Playground.

## Working with User Identity

On every incoming message, the A365 platform populates `activity.from` with basic user
Expand Down
33 changes: 33 additions & 0 deletions nodejs/devin/sample-agent/env/.env.playground
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This file includes environment variables that can be committed to git. It's gitignored by default because it represents your local development environment.

# Built-in environment variables
TEAMSFX_ENV=playground

# Environment variables used by Microsoft 365 Agents Playground
TEAMSAPPTESTER_PORT=56150
TEAMSFX_NOTIFICATION_STORE_FILENAME=.notification.testtoolstore.json

# Custom app registration needed for bearer token
CLIENT_APP_ID=

# Use Agentic Authentication rather than OBO
USE_AGENTIC_AUTH=false

# Set service connection as default
connectionsMap__0__serviceUrl=*
connectionsMap__0__connection=serviceConnection

# AgenticAuthentication Options
agentic_type=agentic
agentic_scopes=https://graph.microsoft.com/.default
agentic_connectionName=serviceConnection

# Devin Configuration
DEVIN_BASE_URL=https://api.devin.ai/v1
POLLING_INTERVAL_SECONDS=10

# Observability Configuration
ENABLE_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true
CLUSTER_CATEGORY=dev
4 changes: 4 additions & 0 deletions nodejs/devin/sample-agent/env/.env.playground.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Devin Configuration
SECRET_DEVIN_API_KEY=

SECRET_BEARER_TOKEN=
39 changes: 39 additions & 0 deletions nodejs/devin/sample-agent/m365agents.playground.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.11/yaml.schema.json
# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file
# Visit https://aka.ms/teamsfx-actions for details on actions
version: v1.11

environmentFolderPath: ./env

deploy:
# Install development tool(s)
- uses: devTool/install
with:
testTool:
version: ~0.2.7
symlinkDir: ./devTools/playground

# Run npm command
- uses: cli/runNpmCommand
with:
args: install

- uses: file/createOrUpdateEnvironmentFile
with:
target: ./.localConfigs.playground
envs:
NODE_ENV: local
PORT: 3978
TEAMSFX_NOTIFICATION_STORE_FILENAME: ${{TEAMSFX_NOTIFICATION_STORE_FILENAME}}
DEVIN_API_KEY: ${{SECRET_DEVIN_API_KEY}}
DEVIN_BASE_URL: ${{DEVIN_BASE_URL}}
POLLING_INTERVAL_SECONDS: ${{POLLING_INTERVAL_SECONDS}}
BEARER_TOKEN: ${{SECRET_BEARER_TOKEN}}
USE_AGENTIC_AUTH: ${{USE_AGENTIC_AUTH}}
connectionsMap__0__serviceUrl: ${{connectionsMap__0__serviceUrl}}
connectionsMap__0__connection: ${{connectionsMap__0__connection}}
agentic_type: ${{agentic_type}}
agentic_scopes: ${{agentic_scopes}}
connections__service_connection__settings__clientId: ""
connections__service_connection__settings__clientSecret: ""
connections__service_connection__settings__tenantId: ""
4 changes: 4 additions & 0 deletions nodejs/devin/sample-agent/m365agents.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.11/yaml.schema.json
# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file
# Visit https://aka.ms/teamsfx-actions for details on actions
version: v1.11
9 changes: 8 additions & 1 deletion nodejs/devin/sample-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"scripts": {
"build": "tsc",
"start": "node --env-file=.env dist/index.js",
"test-tool": "agentsplayground"
"dev": "nodemon --exec node --inspect=9239 --signal SIGINT -r ts-node/register src/index.ts",
"test-tool": "agentsplayground",
"dev:teamsfx:playground": "env-cmd --silent -f .localConfigs.playground npm run dev",
"dev:teamsfx:launch-playground": "env-cmd --silent -f env/.env.playground agentsplayground start"
},
"engines": {
"node": ">=24.0.0"
Expand All @@ -23,6 +26,10 @@
},
"devDependencies": {
"@microsoft/m365agentsplayground": "^0.2.20",
"@types/node": "^20.14.9",
"env-cmd": "^11.0.0",
"nodemon": "^3.1.10",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
}
}
Loading
Loading