From 1573fa40cb72de21ced5e95443e443c4a98fedc4 Mon Sep 17 00:00:00 2001 From: Tracy Boehrer Date: Fri, 7 Nov 2025 09:46:49 -0600 Subject: [PATCH 1/2] Node quickstart - parity and simplify --- samples/nodejs/quickstart/README.md | 4 +- samples/nodejs/quickstart/package-lock.json | 15 ++++- samples/nodejs/quickstart/package.json | 2 +- samples/nodejs/quickstart/src/agent.ts | 74 --------------------- samples/nodejs/quickstart/src/index.ts | 58 ++++++++++------ 5 files changed, 53 insertions(+), 100 deletions(-) delete mode 100644 samples/nodejs/quickstart/src/agent.ts diff --git a/samples/nodejs/quickstart/README.md b/samples/nodejs/quickstart/README.md index 42d20122..7d49dd12 100644 --- a/samples/nodejs/quickstart/README.md +++ b/samples/nodejs/quickstart/README.md @@ -63,12 +63,12 @@ npm test Refresh the browser to start a new conversation with the Echo bot. -You should see a message from the bot like: `Echo running on Agents SDK version: {version}` +You should see a message from the agent: `Hello and Welcome!` ### Interact with the agent with WebChat UI using Azure Bot Service -1. [Create an Azure Bot](https://aka.ms/AgentsSDK-CreateBot) +1. [Create an Azure Bot](https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/azure-bot-create-single-secret) - Record the Application ID, the Tenant ID, and the Client Secret for use below 1. Configuring the token connection in the Agent settings diff --git a/samples/nodejs/quickstart/package-lock.json b/samples/nodejs/quickstart/package-lock.json index 8e2f8d07..6841425a 100644 --- a/samples/nodejs/quickstart/package-lock.json +++ b/samples/nodejs/quickstart/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@microsoft/agents-hosting": "^1.0.0", + "@microsoft/agents-hosting-express": "^1.0.0", "express": "^5.1.0" }, "devDependencies": { @@ -118,6 +118,19 @@ "node": ">=20.0.0" } }, + "node_modules/@microsoft/agents-hosting-express": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@microsoft/agents-hosting-express/-/agents-hosting-express-1.0.15.tgz", + "integrity": "sha512-MyepMCp58fFEWr64fS21XDUG1rwSh5z5H90JJm25aOfbkeTvKgypVI3sP2pCo2zGW68TORC91xrvRuxYGHEoMQ==", + "license": "MIT", + "dependencies": { + "@microsoft/agents-hosting": "1.0.15", + "express": "^5.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@microsoft/m365agentsplayground": { "version": "0.2.18", "resolved": "https://registry.npmjs.org/@microsoft/m365agentsplayground/-/m365agentsplayground-0.2.18.tgz", diff --git a/samples/nodejs/quickstart/package.json b/samples/nodejs/quickstart/package.json index 2472d8a9..5647f874 100644 --- a/samples/nodejs/quickstart/package.json +++ b/samples/nodejs/quickstart/package.json @@ -17,7 +17,7 @@ "test": "npm-run-all -p -r start:anon test-tool" }, "dependencies": { - "@microsoft/agents-hosting": "^1.0.0", + "@microsoft/agents-hosting-express": "^1.0.0", "express": "^5.1.0" }, "devDependencies": { diff --git a/samples/nodejs/quickstart/src/agent.ts b/samples/nodejs/quickstart/src/agent.ts deleted file mode 100644 index c0bc4cb1..00000000 --- a/samples/nodejs/quickstart/src/agent.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -import { TurnState, MemoryStorage, TurnContext, AgentApplication, AttachmentDownloader } - from '@microsoft/agents-hosting' -import { version } from '@microsoft/agents-hosting/package.json' -import { ActivityTypes } from '@microsoft/agents-activity' - -interface ConversationState { - count: number; -} -type ApplicationTurnState = TurnState - -const downloader = new AttachmentDownloader() - -// Define storage and application -const storage = new MemoryStorage() -export const agentApp = new AgentApplication({ - storage, - fileDownloaders: [downloader] -}) - -// Listen for user to say '/reset' and then delete conversation state -agentApp.onMessage('/reset', async (context: TurnContext, state: ApplicationTurnState) => { - state.deleteConversationState() - await context.sendActivity('Ok I\'ve deleted the current conversation state.') -}) - -agentApp.onMessage('/count', async (context: TurnContext, state: ApplicationTurnState) => { - const count = state.conversation.count ?? 0 - await context.sendActivity(`The count is ${count}`) -}) - -agentApp.onMessage('/diag', async (context: TurnContext, state: ApplicationTurnState) => { - await state.load(context, storage) - await context.sendActivity(JSON.stringify(context.activity)) -}) - -agentApp.onMessage('/state', async (context: TurnContext, state: ApplicationTurnState) => { - await state.load(context, storage) - await context.sendActivity(JSON.stringify(state)) -}) - -agentApp.onMessage('/runtime', async (context: TurnContext, state: ApplicationTurnState) => { - const runtime = { - nodeversion: process.version, - sdkversion: version - } - await context.sendActivity(JSON.stringify(runtime)) -}) - -agentApp.onConversationUpdate('membersAdded', async (context: TurnContext, state: ApplicationTurnState) => { - await context.sendActivity('🚀 Echo bot running on Agents SDK version: ' + version) -}) - -// Listen for ANY message to be received. MUST BE AFTER ANY OTHER MESSAGE HANDLERS -agentApp.onActivity(ActivityTypes.Message, async (context: TurnContext, state: ApplicationTurnState) => { - // Increment count state - let count = state.conversation.count ?? 0 - state.conversation.count = ++count - - // Echo back users request - await context.sendActivity(`[${count}] you said: ${context.activity.text}`) -}) - -agentApp.onActivity(/^message/, async (context: TurnContext, state: ApplicationTurnState) => { - await context.sendActivity(`Matched with regex: ${context.activity.type}`) -}) - -agentApp.onActivity( - async (context: TurnContext) => Promise.resolve(context.activity.type === 'message'), - async (context, state) => { - await context.sendActivity(`Matched function: ${context.activity.type}`) - } -) diff --git a/samples/nodejs/quickstart/src/index.ts b/samples/nodejs/quickstart/src/index.ts index 91b1e98b..01f35e2f 100644 --- a/samples/nodejs/quickstart/src/index.ts +++ b/samples/nodejs/quickstart/src/index.ts @@ -1,25 +1,39 @@ -import { AuthConfiguration, authorizeJWT, CloudAdapter, loadAuthConfigFromEnv, Request } from '@microsoft/agents-hosting' -import express, { Response } from 'express' -import { agentApp } from './agent' - -const authConfig: AuthConfiguration = loadAuthConfigFromEnv() -const adapter = new CloudAdapter(authConfig) - -const server = express() -server.use(express.json()) -server.use(authorizeJWT(authConfig)) - -server.post('/api/messages', async (req: Request, res: Response) => { - await adapter.process(req, res, async (context) => { - const app = agentApp - await app.run(context) - }) +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { startServer } from '@microsoft/agents-hosting-express' +import { TurnState, MemoryStorage, TurnContext, AgentApplication } + from '@microsoft/agents-hosting' +import { ActivityTypes } from '@microsoft/agents-activity' + +interface ConversationState { + count: number; +} +type ApplicationTurnState = TurnState + +// Register IStorage. For development, MemoryStorage is suitable. +// For production Agents, persisted storage should be used so +// that state survives Agent restarts, and operates correctly +// in a cluster of Agent instances. +const storage = new MemoryStorage() + +const agentApp = new AgentApplication({ + storage +}) + +// Display a welcome message when members are added +agentApp.onConversationUpdate('membersAdded', async (context: TurnContext, state: ApplicationTurnState) => { + await context.sendActivity('Hello and Welcome!') }) -const port = process.env.PORT || 3978 -server.listen(port, () => { - console.log(`\nServer listening to port ${port} for appId ${authConfig.clientId} debug ${process.env.DEBUG}`) -}).on('error', (err) => { - console.error(err) - process.exit(1) +// Listen for ANY message to be received. MUST BE AFTER ANY OTHER MESSAGE HANDLERS +agentApp.onActivity(ActivityTypes.Message, async (context: TurnContext, state: ApplicationTurnState) => { + // Increment count state + let count = state.conversation.count ?? 0 + state.conversation.count = ++count + + // Echo back users message + await context.sendActivity(`[${count}] You said: ${context.activity.text}`) }) + +startServer(agentApp) + From 820aae1f15bfecd25c74fe9646dd3cc49fe18c9f Mon Sep 17 00:00:00 2001 From: Tracy Boehrer Date: Fri, 7 Nov 2025 10:03:43 -0600 Subject: [PATCH 2/2] Node quickstart lint corrections --- samples/nodejs/quickstart/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/nodejs/quickstart/src/index.ts b/samples/nodejs/quickstart/src/index.ts index 01f35e2f..531982e6 100644 --- a/samples/nodejs/quickstart/src/index.ts +++ b/samples/nodejs/quickstart/src/index.ts @@ -5,6 +5,8 @@ import { TurnState, MemoryStorage, TurnContext, AgentApplication } from '@microsoft/agents-hosting' import { ActivityTypes } from '@microsoft/agents-activity' +// Create custom conversation state properties. This is +// used to store customer properties in conversation state. interface ConversationState { count: number; } @@ -36,4 +38,3 @@ agentApp.onActivity(ActivityTypes.Message, async (context: TurnContext, state: A }) startServer(agentApp) -