@hahnfeld/teams-adapter 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +286 -0
- package/dist/activity.d.ts +23 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +3 -0
- package/dist/activity.js.map +1 -0
- package/dist/adapter.d.ts +174 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +1583 -0
- package/dist/adapter.js.map +1 -0
- package/dist/app-package.d.ts +7 -0
- package/dist/app-package.d.ts.map +1 -0
- package/dist/app-package.js +158 -0
- package/dist/app-package.js.map +1 -0
- package/dist/assistant.d.ts +7 -0
- package/dist/assistant.d.ts.map +1 -0
- package/dist/assistant.js +32 -0
- package/dist/assistant.js.map +1 -0
- package/dist/commands/admin.d.ts +27 -0
- package/dist/commands/admin.d.ts.map +1 -0
- package/dist/commands/admin.js +146 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/agents.d.ts +13 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +98 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +49 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/index.d.ts +16 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +253 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/integrate.d.ts +8 -0
- package/dist/commands/integrate.d.ts.map +1 -0
- package/dist/commands/integrate.js +45 -0
- package/dist/commands/integrate.js.map +1 -0
- package/dist/commands/menu.d.ts +16 -0
- package/dist/commands/menu.d.ts.map +1 -0
- package/dist/commands/menu.js +92 -0
- package/dist/commands/menu.js.map +1 -0
- package/dist/commands/new-session.d.ts +13 -0
- package/dist/commands/new-session.d.ts.map +1 -0
- package/dist/commands/new-session.js +105 -0
- package/dist/commands/new-session.js.map +1 -0
- package/dist/commands/session.d.ts +22 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +110 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/settings.d.ts +8 -0
- package/dist/commands/settings.d.ts.map +1 -0
- package/dist/commands/settings.js +54 -0
- package/dist/commands/settings.js.map +1 -0
- package/dist/conversation-store.d.ts +38 -0
- package/dist/conversation-store.d.ts.map +1 -0
- package/dist/conversation-store.js +101 -0
- package/dist/conversation-store.js.map +1 -0
- package/dist/draft-manager.d.ts +47 -0
- package/dist/draft-manager.d.ts.map +1 -0
- package/dist/draft-manager.js +136 -0
- package/dist/draft-manager.js.map +1 -0
- package/dist/formatting.d.ts +121 -0
- package/dist/formatting.d.ts.map +1 -0
- package/dist/formatting.js +392 -0
- package/dist/formatting.js.map +1 -0
- package/dist/graph.d.ts +59 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +261 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/media.d.ts +29 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +120 -0
- package/dist/media.js.map +1 -0
- package/dist/permissions.d.ts +15 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +221 -0
- package/dist/permissions.js.map +1 -0
- package/dist/plugin.d.ts +13 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +689 -0
- package/dist/plugin.js.map +1 -0
- package/dist/renderer.d.ts +49 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +55 -0
- package/dist/renderer.js.map +1 -0
- package/dist/send-utils.d.ts +15 -0
- package/dist/send-utils.d.ts.map +1 -0
- package/dist/send-utils.js +64 -0
- package/dist/send-utils.js.map +1 -0
- package/dist/task-modules.d.ts +34 -0
- package/dist/task-modules.d.ts.map +1 -0
- package/dist/task-modules.js +136 -0
- package/dist/task-modules.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validators.d.ts +54 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +142 -0
- package/dist/validators.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenACP
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# @hahnfeld/teams-adapter
|
|
2
|
+
|
|
3
|
+
Microsoft Teams adapter plugin for [OpenACP](https://github.com/Open-ACP/OpenACP) — Adaptive Cards, slash commands, streaming.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Adaptive Cards** — Rich tool card rendering with progress indicators and action buttons
|
|
8
|
+
- **Slash Commands** — Full command suite: `/new`, `/cancel`, `/agents`, `/menu`, and more
|
|
9
|
+
- **Streaming** — Real-time text updates via Teams message editing
|
|
10
|
+
- **Threads** — Session threads within Teams channels
|
|
11
|
+
- **Permissions** — Allow/Deny/Always Allow via Adaptive Card buttons
|
|
12
|
+
- **Output Modes** — Low/Medium/High detail levels
|
|
13
|
+
- **File Sharing** — Upload and share files via Microsoft Graph / OneDrive
|
|
14
|
+
- **Interactive Install Wizard** — Guided setup with credential validation, auto-discovery, and app package generation
|
|
15
|
+
- **Auto-session Creation** — First message automatically creates a session with the default agent
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- [OpenACP CLI](https://github.com/Open-ACP/OpenACP) `>= 2026.0.0`
|
|
20
|
+
- Node.js 18+
|
|
21
|
+
- An **Azure Bot registration** with the Microsoft Teams channel enabled
|
|
22
|
+
- A Microsoft 365 tenant with a Teams team and channel (a [Business Basic trial](https://www.microsoft.com/en-us/microsoft-365/business/compare-all-plans) works for testing)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
### Option A: OpenACP plugin install (recommended)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
openacp plugin install @hahnfeld/teams-adapter
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This launches an interactive wizard that walks you through Azure Bot setup, credential validation, team/channel selection, and generates a Teams app package for sideloading.
|
|
33
|
+
|
|
34
|
+
### Option B: Manual npm install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install @hahnfeld/teams-adapter
|
|
38
|
+
# or
|
|
39
|
+
pnpm add @hahnfeld/teams-adapter
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Azure Bot Setup
|
|
43
|
+
|
|
44
|
+
Before configuring the adapter you need an Azure Bot registration. If you don't have one yet:
|
|
45
|
+
|
|
46
|
+
1. Go to the [Azure Bot creation page](https://portal.azure.com/#create/Microsoft.AzureBot)
|
|
47
|
+
2. Fill in:
|
|
48
|
+
- **Bot handle**: any unique name (e.g. `openacp-bot`)
|
|
49
|
+
- **Pricing tier**: Free (F0) for testing
|
|
50
|
+
- **App type**: "Single Tenant" for enterprise use
|
|
51
|
+
- **Creation type**: "Create new Microsoft App ID"
|
|
52
|
+
3. Click **Create** and wait for deployment
|
|
53
|
+
4. Go to the Bot resource > **Settings** > **Configuration**
|
|
54
|
+
- Copy the **Microsoft App ID** — this is your `botAppId`
|
|
55
|
+
5. Click **Manage Password** > **New client secret**
|
|
56
|
+
- Copy the secret value immediately (shown only once) — this is your `botAppPassword`
|
|
57
|
+
6. Under **Channels**, add the **Microsoft Teams** channel
|
|
58
|
+
|
|
59
|
+
For full details see the [Azure Bot Service docs](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration).
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
### Interactive wizard
|
|
64
|
+
|
|
65
|
+
If you installed via `openacp plugin install`, the wizard runs automatically. To re-run it later:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
openacp plugin configure @hahnfeld/teams-adapter
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The wizard guides you through:
|
|
72
|
+
1. Bot credential entry and real-time validation
|
|
73
|
+
2. Tenant type selection (single-tenant enterprise vs. multi-tenant)
|
|
74
|
+
3. Team and channel selection — auto-discovered via Graph API, or paste a Teams channel link
|
|
75
|
+
4. Optional notification channel for session completions and errors
|
|
76
|
+
5. Optional Graph API file sharing (OneDrive)
|
|
77
|
+
6. **Auto-generates a Teams app package** (`openacp-bot.zip`) for sideloading
|
|
78
|
+
|
|
79
|
+
### Adding the bot to Teams
|
|
80
|
+
|
|
81
|
+
After the wizard completes, you need to upload the app package to Teams:
|
|
82
|
+
|
|
83
|
+
1. The wizard generates `openacp-bot.zip` — note the path it prints
|
|
84
|
+
2. Open Microsoft Teams
|
|
85
|
+
3. Go to **Apps** (left sidebar) > **Manage your apps** > **Upload a custom app**
|
|
86
|
+
4. Select the `openacp-bot.zip` file
|
|
87
|
+
5. Click **Add to a team** > select your team > **Set up a bot**
|
|
88
|
+
|
|
89
|
+
The bot will now appear in your team. You can @mention it in channels or DM it directly.
|
|
90
|
+
|
|
91
|
+
### Manual configuration
|
|
92
|
+
|
|
93
|
+
Add the following to your `openacp.yaml`:
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
channels:
|
|
97
|
+
teams:
|
|
98
|
+
enabled: true
|
|
99
|
+
botAppId: "${TEAMS_BOT_APP_ID}"
|
|
100
|
+
botAppPassword: "${TEAMS_BOT_APP_PASSWORD}"
|
|
101
|
+
tenantId: "${TEAMS_TENANT_ID}"
|
|
102
|
+
teamId: "${TEAMS_TEAM_ID}"
|
|
103
|
+
channelId: "${TEAMS_CHANNEL_ID}"
|
|
104
|
+
botPort: 3978 # Bot Framework port (default)
|
|
105
|
+
notificationChannelId: "${TEAMS_NOTIFICATION_CHANNEL_ID}" # optional
|
|
106
|
+
assistantThreadId: null # auto-set after first run
|
|
107
|
+
graphClientSecret: "${TEAMS_GRAPH_CLIENT_SECRET}" # optional, for file sharing
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Configuration reference
|
|
111
|
+
|
|
112
|
+
| Field | Type | Required | Description |
|
|
113
|
+
|-------|------|----------|-------------|
|
|
114
|
+
| `enabled` | `boolean` | Yes | Enable the Teams adapter |
|
|
115
|
+
| `botAppId` | `string` | Yes | Azure AD App ID for the bot |
|
|
116
|
+
| `botAppPassword` | `string` | Yes | Bot client secret |
|
|
117
|
+
| `tenantId` | `string` | Yes | Microsoft tenant ID (GUID), or `botframework.com` for multi-tenant |
|
|
118
|
+
| `teamId` | `string` | Yes | Default team ID (groupId GUID) |
|
|
119
|
+
| `channelId` | `string` | Yes | Primary channel for sessions (e.g. `19:abc@thread.tacv2`) |
|
|
120
|
+
| `botPort` | `number` | No | Bot Framework HTTP server port (default: `3978`) |
|
|
121
|
+
| `notificationChannelId` | `string \| null` | No | Separate channel for notifications |
|
|
122
|
+
| `assistantThreadId` | `string \| null` | No | Thread for the assistant (auto-populated) |
|
|
123
|
+
| `graphClientSecret` | `string` | No | Azure AD client secret for Graph API file sharing |
|
|
124
|
+
|
|
125
|
+
### Finding your Team and Channel IDs
|
|
126
|
+
|
|
127
|
+
**From a channel link (easiest):**
|
|
128
|
+
1. Open Microsoft Teams
|
|
129
|
+
2. Right-click the channel name > **Get link to channel**
|
|
130
|
+
3. The link contains `groupId` (Team ID) and the channel path
|
|
131
|
+
|
|
132
|
+
Both `teams.microsoft.com` and `teams.cloud.microsoft` link formats are supported.
|
|
133
|
+
|
|
134
|
+
**From the Azure/Graph API:**
|
|
135
|
+
- Team ID = the `groupId` parameter from the Teams URL
|
|
136
|
+
- Channel ID = the encoded string like `19:xxx@thread.tacv2`
|
|
137
|
+
|
|
138
|
+
### Networking: Bot port vs API port
|
|
139
|
+
|
|
140
|
+
The Teams adapter runs its **own HTTP server** for Bot Framework webhook traffic. This is separate from the OpenACP API server:
|
|
141
|
+
|
|
142
|
+
| Server | Default Port | Purpose |
|
|
143
|
+
|--------|-------------|---------|
|
|
144
|
+
| **Bot Framework** (this adapter) | `3978` | Receives messages from Azure Bot Service |
|
|
145
|
+
| **OpenACP API** | `21420` | REST API, SSE, web UI |
|
|
146
|
+
|
|
147
|
+
**Your tunnel must point to the bot port (3978), not the OpenACP API port.** If you use OpenACP's built-in tunnel, it tunnels the API port — the Teams adapter requests its own separate tunnel on the bot port automatically.
|
|
148
|
+
|
|
149
|
+
The bot port is configurable via the `botPort` setting (default: `3978`, the Bot Framework standard).
|
|
150
|
+
|
|
151
|
+
### Messaging endpoint
|
|
152
|
+
|
|
153
|
+
After installation, set the bot's messaging endpoint in Azure:
|
|
154
|
+
|
|
155
|
+
1. Azure Portal > Bot resource > **Configuration** > **Messaging endpoint**
|
|
156
|
+
2. Set it to: `https://<your-tunnel-url>/api/messages`
|
|
157
|
+
3. The URL must reach port 3978 (or your configured `botPort`) on the machine running OpenACP
|
|
158
|
+
|
|
159
|
+
### Tunneling
|
|
160
|
+
|
|
161
|
+
The adapter automatically requests a tunnel on the bot port at startup if an OpenACP tunnel provider is available. The tunnel URL is logged on boot.
|
|
162
|
+
|
|
163
|
+
**Recommended: `@hahnfeld/devtunnel-provider`** — a tunnel provider plugin using [Microsoft Dev Tunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started), which aligns with the Microsoft/Azure ecosystem:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
openacp plugin install @hahnfeld/devtunnel-provider
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Manual tunnel setup** (if not using an OpenACP tunnel provider):
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Install Dev Tunnels CLI
|
|
173
|
+
brew install --cask devtunnel # macOS
|
|
174
|
+
|
|
175
|
+
# Login and create a persistent tunnel
|
|
176
|
+
devtunnel user login
|
|
177
|
+
devtunnel create --allow-anonymous
|
|
178
|
+
devtunnel port create -p 3978
|
|
179
|
+
|
|
180
|
+
# Host it (use the same tunnel ID each time for a stable URL)
|
|
181
|
+
devtunnel host <tunnel-id> --allow-anonymous
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Set the tunnel URL as the messaging endpoint in Azure: `https://<id>.devtunnels.ms/api/messages`
|
|
185
|
+
|
|
186
|
+
> **Important:** Always use `--allow-anonymous` — Azure Bot Service cannot authenticate with Dev Tunnel auth.
|
|
187
|
+
|
|
188
|
+
## Slash Commands
|
|
189
|
+
|
|
190
|
+
| Command | Description |
|
|
191
|
+
|---------|-------------|
|
|
192
|
+
| `/new [agent]` | Create a new agent session |
|
|
193
|
+
| `/newchat` | New chat, same agent & workspace |
|
|
194
|
+
| `/cancel` | Cancel the current session |
|
|
195
|
+
| `/status` | Show session or global status |
|
|
196
|
+
| `/sessions` | List all sessions |
|
|
197
|
+
| `/agents` | List available agents |
|
|
198
|
+
| `/install <name>` | Install an agent by name |
|
|
199
|
+
| `/menu` | Show the action menu |
|
|
200
|
+
| `/help` | Show help |
|
|
201
|
+
| `/outputmode low\|medium\|high` | Set output detail level |
|
|
202
|
+
| `/bypass` | Auto-approve permissions |
|
|
203
|
+
| `/doctor` | Run system diagnostics |
|
|
204
|
+
| `/handoff` | Generate terminal resume command |
|
|
205
|
+
| `/restart` | Restart OpenACP |
|
|
206
|
+
| `/respawn` | Restart the assistant session |
|
|
207
|
+
| `/update` | Update to latest version |
|
|
208
|
+
| `/settings` | Show configuration settings |
|
|
209
|
+
| `/integrate` | Manage agent integrations |
|
|
210
|
+
| `/clear` | Reset the assistant session |
|
|
211
|
+
| `/tts [on\|off]` | Toggle Text to Speech |
|
|
212
|
+
| `/mode` | Switch session mode |
|
|
213
|
+
| `/model` | Switch AI model |
|
|
214
|
+
| `/thought` | Adjust thinking level |
|
|
215
|
+
|
|
216
|
+
## Uninstalling
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
openacp plugin uninstall @hahnfeld/teams-adapter --purge
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The `--purge` flag removes all saved settings. After uninstalling, you may also want to:
|
|
223
|
+
1. Remove the bot from your Teams team
|
|
224
|
+
2. Delete the Azure Bot resource in the Azure Portal
|
|
225
|
+
|
|
226
|
+
## Development
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Install dependencies
|
|
230
|
+
pnpm install
|
|
231
|
+
|
|
232
|
+
# Build
|
|
233
|
+
pnpm build
|
|
234
|
+
|
|
235
|
+
# Watch mode
|
|
236
|
+
pnpm dev
|
|
237
|
+
|
|
238
|
+
# Run tests
|
|
239
|
+
pnpm test
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Architecture
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
src/
|
|
246
|
+
├── index.ts # Plugin entry point & public exports
|
|
247
|
+
├── plugin.ts # Plugin factory (install wizard, configure, setup/teardown)
|
|
248
|
+
├── adapter.ts # TeamsAdapter — extends MessagingAdapter
|
|
249
|
+
├── app-package.ts # Teams app manifest package generator
|
|
250
|
+
├── renderer.ts # TeamsRenderer (Adaptive Card rendering)
|
|
251
|
+
├── activity.ts # ActivityTracker (tool card state, streaming)
|
|
252
|
+
├── formatting.ts # Tool card formatting, usage cards, citations
|
|
253
|
+
├── draft-manager.ts # Message draft handling
|
|
254
|
+
├── permissions.ts # PermissionHandler (Adaptive Card buttons)
|
|
255
|
+
├── graph.ts # GraphFileClient (OneDrive file sharing)
|
|
256
|
+
├── media.ts # File download/upload utilities
|
|
257
|
+
├── conversation-store.ts # Conversation reference storage
|
|
258
|
+
├── send-utils.ts # Message sending helpers (Teams SDK compat)
|
|
259
|
+
├── task-modules.ts # Task module dialogs (new session, settings)
|
|
260
|
+
├── assistant.ts # Assistant session spawning
|
|
261
|
+
├── validators.ts # Credential & tenant validation, Teams link parsing
|
|
262
|
+
├── types.ts # TeamsChannelConfig, TeamsPlatformData
|
|
263
|
+
└── commands/
|
|
264
|
+
├── index.ts # Command router + SLASH_COMMANDS registry
|
|
265
|
+
├── new-session.ts # /new, /newchat
|
|
266
|
+
├── session.ts # /cancel, /status, /sessions, /handoff
|
|
267
|
+
├── admin.ts # /bypass, /tts, /restart, /respawn, /update, /outputmode
|
|
268
|
+
├── menu.ts # /menu, /help, /clear
|
|
269
|
+
├── agents.ts # /agents, /install
|
|
270
|
+
├── doctor.ts # /doctor
|
|
271
|
+
├── integrate.ts # /integrate
|
|
272
|
+
└── settings.ts # /settings
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Tech Stack
|
|
276
|
+
|
|
277
|
+
- [`@microsoft/teams.apps`](https://www.npmjs.com/package/@microsoft/teams.apps) — App class, server hosting, activity routing
|
|
278
|
+
- [`@microsoft/teams.botbuilder`](https://www.npmjs.com/package/@microsoft/teams.botbuilder) — Bot Framework adapter plugin
|
|
279
|
+
- [`@microsoft/agents-hosting`](https://www.npmjs.com/package/@microsoft/agents-hosting) — Express server hosting
|
|
280
|
+
- [`botbuilder`](https://www.npmjs.com/package/botbuilder) — CloudAdapter for single-tenant auth
|
|
281
|
+
- [`botframework-connector`](https://www.npmjs.com/package/botframework-connector) — Credential factory for token validation
|
|
282
|
+
- [`@openacp/plugin-sdk`](https://github.com/Open-ACP/OpenACP) — OpenACP plugin interface
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { ToolStateMap, ThoughtBuffer, DisplaySpecBuilder } from "@openacp/plugin-sdk";
|
|
2
|
+
export type { OutputMode, ToolDisplaySpec, ToolCardSnapshot, PlanEntry, ToolCallMeta, ViewerLinks, } from "@openacp/plugin-sdk";
|
|
3
|
+
export interface ToolEntry {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
kind: string;
|
|
7
|
+
rawInput: unknown;
|
|
8
|
+
content: string | null;
|
|
9
|
+
status: string;
|
|
10
|
+
viewerLinks?: {
|
|
11
|
+
file?: string;
|
|
12
|
+
diff?: string;
|
|
13
|
+
};
|
|
14
|
+
diffStats?: {
|
|
15
|
+
added: number;
|
|
16
|
+
removed: number;
|
|
17
|
+
};
|
|
18
|
+
displaySummary?: string;
|
|
19
|
+
displayTitle?: string;
|
|
20
|
+
displayKind?: string;
|
|
21
|
+
isNoise: boolean;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEtF,YAAY,EACV,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
package/dist/activity.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { TurnContext } from "@microsoft/agents-hosting";
|
|
2
|
+
import type { OutgoingMessage, PermissionRequest, NotificationMessage, OpenACPCore, DisplayVerbosity, AdapterCapabilities, IRenderer } from "@openacp/plugin-sdk";
|
|
3
|
+
import { MessagingAdapter } from "@openacp/plugin-sdk";
|
|
4
|
+
import type { TeamsChannelConfig } from "./types.js";
|
|
5
|
+
export declare class TeamsAdapter extends MessagingAdapter {
|
|
6
|
+
readonly name = "teams";
|
|
7
|
+
readonly renderer: IRenderer;
|
|
8
|
+
readonly capabilities: AdapterCapabilities;
|
|
9
|
+
readonly core: OpenACPCore;
|
|
10
|
+
private app;
|
|
11
|
+
private teamsConfig;
|
|
12
|
+
private sendQueue;
|
|
13
|
+
private draftManager;
|
|
14
|
+
private permissionHandler;
|
|
15
|
+
private notificationChannelId?;
|
|
16
|
+
private assistantSession;
|
|
17
|
+
private assistantInitializing;
|
|
18
|
+
private fileService;
|
|
19
|
+
private graphClient?;
|
|
20
|
+
private conversationStore;
|
|
21
|
+
/**
|
|
22
|
+
* Per-session TurnContext references, set during inbound message handling.
|
|
23
|
+
* Handler overrides read from this map during sendMessage dispatch.
|
|
24
|
+
*/
|
|
25
|
+
private _sessionContexts;
|
|
26
|
+
private _sessionOutputModes;
|
|
27
|
+
/**
|
|
28
|
+
* Per-session serial dispatch queues — matches Telegram's _dispatchQueues pattern.
|
|
29
|
+
* SessionBridge fires sendMessage() as fire-and-forget, so multiple events can arrive
|
|
30
|
+
* concurrently. Without serialization, fast handlers overtake slow ones, causing
|
|
31
|
+
* out-of-order delivery. This queue ensures events are processed in arrival order.
|
|
32
|
+
*
|
|
33
|
+
* Entries are replaced with Promise.resolve() once their chain settles, preventing
|
|
34
|
+
* unbounded closure growth for long-lived sessions.
|
|
35
|
+
*/
|
|
36
|
+
private _dispatchQueues;
|
|
37
|
+
/** Track processed activity IDs to handle Teams 15-second retry deduplication */
|
|
38
|
+
private _processedActivities;
|
|
39
|
+
private _processedCleanupTimer?;
|
|
40
|
+
/** Messages buffered during assistant initialization — replayed once ready. Capped to prevent unbounded growth. */
|
|
41
|
+
private static readonly MAX_INIT_BUFFER;
|
|
42
|
+
private _assistantInitBuffer;
|
|
43
|
+
/** Bot token cache for proactive messaging via connector REST API */
|
|
44
|
+
private _botTokenCache?;
|
|
45
|
+
constructor(core: OpenACPCore, config: TeamsChannelConfig);
|
|
46
|
+
private _started;
|
|
47
|
+
start(): Promise<void>;
|
|
48
|
+
stop(): Promise<void>;
|
|
49
|
+
private setupMessageHandler;
|
|
50
|
+
/**
|
|
51
|
+
* Handle Action.Submit payloads from Adaptive Cards.
|
|
52
|
+
*
|
|
53
|
+
* Action.Submit (v1.2 compatible) sends the card's data directly as
|
|
54
|
+
* activity.value with no text. This handles permission responses, command
|
|
55
|
+
* buttons, and output mode changes — all of which embed a `verb` field.
|
|
56
|
+
*/
|
|
57
|
+
private handleSubmitAction;
|
|
58
|
+
/**
|
|
59
|
+
* Shared card action dispatch — handles verbs from both Action.Submit and
|
|
60
|
+
* Action.Execute (invoke) paths. Eliminates duplication between
|
|
61
|
+
* handleSubmitAction and cardActionHandler.
|
|
62
|
+
*/
|
|
63
|
+
private dispatchCardVerb;
|
|
64
|
+
/**
|
|
65
|
+
* Handle message reactions (like, heart, laugh, surprised, sad, angry).
|
|
66
|
+
*
|
|
67
|
+
* Teams sends messageReaction activities when users react to bot messages.
|
|
68
|
+
* We log them as engagement signals and emit an event so plugins can act
|
|
69
|
+
* on them (e.g., aggregate feedback, adjust behavior).
|
|
70
|
+
*/
|
|
71
|
+
private setupReactionHandler;
|
|
72
|
+
/**
|
|
73
|
+
* Create a session in the background — sends status updates to the conversation.
|
|
74
|
+
* Runs async without blocking the invoke response.
|
|
75
|
+
*/
|
|
76
|
+
private createSessionInBackground;
|
|
77
|
+
/**
|
|
78
|
+
* Handle form submissions from inline wizard cards (dialogAction payloads).
|
|
79
|
+
* These come from Action.Execute buttons on cards rendered directly in chat.
|
|
80
|
+
*/
|
|
81
|
+
private handleDialogAction;
|
|
82
|
+
private setupCardActionHandler;
|
|
83
|
+
private cardActionHandler;
|
|
84
|
+
private getCommandRegistry;
|
|
85
|
+
handleCommand(text: string, context: TurnContext, sessionId: string | null, userId: string): Promise<void>;
|
|
86
|
+
private renderCommandResponse;
|
|
87
|
+
private setupAssistant;
|
|
88
|
+
respawnAssistant(): Promise<void>;
|
|
89
|
+
restartAssistant(): Promise<void>;
|
|
90
|
+
/** Send a typing indicator to the user. Non-critical — failures are silently ignored. */
|
|
91
|
+
private sendTyping;
|
|
92
|
+
/**
|
|
93
|
+
* Acquire a bot framework token for proactive messaging via the MSA/AAD endpoint.
|
|
94
|
+
* Required when posting to the Bot Connector REST API outside of a turn context.
|
|
95
|
+
*/
|
|
96
|
+
private acquireBotToken;
|
|
97
|
+
/**
|
|
98
|
+
* Validate that a serviceUrl is a trusted Bot Framework endpoint.
|
|
99
|
+
* Prevents SSRF where a spoofed serviceUrl could redirect bot tokens.
|
|
100
|
+
*
|
|
101
|
+
* @see https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference
|
|
102
|
+
*/
|
|
103
|
+
private static readonly TRUSTED_SERVICE_URL_PATTERNS;
|
|
104
|
+
static isValidServiceUrl(url: string): boolean;
|
|
105
|
+
/** AI-generated content entity — attached to all outbound messages for the Teams "AI generated" badge */
|
|
106
|
+
private static readonly AI_ENTITY;
|
|
107
|
+
/**
|
|
108
|
+
* Send a Teams activity with exponential backoff retry on transient failures.
|
|
109
|
+
* Handles HTTP 429 (rate limited), 502, 504 per Microsoft best practices.
|
|
110
|
+
*/
|
|
111
|
+
private sendActivityWithRetry;
|
|
112
|
+
private resolveMode;
|
|
113
|
+
/**
|
|
114
|
+
* Primary outbound dispatch — routes agent messages to Teams.
|
|
115
|
+
*
|
|
116
|
+
* Wraps the base class `sendMessage` in a per-session promise chain (_dispatchQueues)
|
|
117
|
+
* so concurrent events fired from SessionBridge are serialized and delivered in order,
|
|
118
|
+
* preventing fast handlers from overtaking slower ones (matches Telegram pattern).
|
|
119
|
+
*
|
|
120
|
+
* Context is NOT deleted after dispatch — it persists from the inbound message handler
|
|
121
|
+
* and is available for the entire session lifetime, avoiding the race condition where
|
|
122
|
+
* async handlers lose their context mid-execution.
|
|
123
|
+
*/
|
|
124
|
+
sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
125
|
+
protected handleThought(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise<void>;
|
|
126
|
+
protected handleText(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
127
|
+
protected handleToolCall(sessionId: string, content: OutgoingMessage, verbosity: DisplayVerbosity): Promise<void>;
|
|
128
|
+
protected handleToolUpdate(sessionId: string, content: OutgoingMessage, verbosity: DisplayVerbosity): Promise<void>;
|
|
129
|
+
protected handlePlan(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise<void>;
|
|
130
|
+
protected handleUsage(sessionId: string, content: OutgoingMessage, _verbosity: DisplayVerbosity): Promise<void>;
|
|
131
|
+
/** Suggested quick-reply actions (Teams restricts these to 1:1 personal chat only) */
|
|
132
|
+
private static readonly QUICK_ACTIONS;
|
|
133
|
+
/** Return QUICK_ACTIONS only if the conversation is 1:1 personal chat (Teams requirement) */
|
|
134
|
+
private getQuickActions;
|
|
135
|
+
/**
|
|
136
|
+
* Clean up all per-session state (contexts, drafts, dispatch queues, output modes).
|
|
137
|
+
* Removes both sessionId and threadId entries from _sessionContexts to prevent leaks.
|
|
138
|
+
*/
|
|
139
|
+
private cleanupSessionState;
|
|
140
|
+
protected handleSessionEnd(sessionId: string, _content: OutgoingMessage): Promise<void>;
|
|
141
|
+
protected handleError(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
142
|
+
protected handleAttachment(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
143
|
+
protected handleSystem(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
144
|
+
protected handleModeChange(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
145
|
+
protected handleConfigUpdate(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
146
|
+
protected handleModelUpdate(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
147
|
+
protected handleUserReplay(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
148
|
+
protected handleResource(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
149
|
+
protected handleResourceLink(sessionId: string, content: OutgoingMessage): Promise<void>;
|
|
150
|
+
sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>;
|
|
151
|
+
sendNotification(notification: NotificationMessage): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Create a new conversation thread for a session.
|
|
154
|
+
*
|
|
155
|
+
* Attempts to create a real Teams channel conversation via the Bot Framework
|
|
156
|
+
* connector API. If that fails (e.g., missing permissions, no stored conversation
|
|
157
|
+
* reference), falls back to using the existing conversation ID as thread context.
|
|
158
|
+
*/
|
|
159
|
+
createSessionThread(sessionId: string, name: string): Promise<string>;
|
|
160
|
+
/**
|
|
161
|
+
* Rename a session thread. This is a no-op for Teams — the Teams API does not
|
|
162
|
+
* support renaming channel conversations. Renaming a group chat requires
|
|
163
|
+
* Graph API with Chat.ReadWrite.All permission, which most bot registrations
|
|
164
|
+
* don't have. The new name is stored in the session record for display purposes.
|
|
165
|
+
*/
|
|
166
|
+
renameSessionThread(sessionId: string, newName: string): Promise<void>;
|
|
167
|
+
deleteSessionThread(sessionId: string): Promise<void>;
|
|
168
|
+
getChannelId(): string;
|
|
169
|
+
getTeamId(): string;
|
|
170
|
+
getAssistantSessionId(): string | null;
|
|
171
|
+
getAssistantThreadId(): string | null;
|
|
172
|
+
setSessionOutputMode(sessionId: string, mode: "low" | "medium" | "high"): void;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|