@botpress/adk-cli 1.19.0-beta.4 → 1.19.0-beta.5
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/dist/{README-wjb7z7f0.md → README-824wa4jj.md} +1 -1
- package/dist/SKILL-75ym6tcj.md +257 -0
- package/dist/SKILL-chnwzmt1.md +548 -0
- package/dist/SKILL-d4bs53y3.md +244 -0
- package/dist/SKILL-h4b2f1n9.md +114 -0
- package/dist/SKILL-hbw5nt95.md +111 -0
- package/dist/actions-h7hxxvxr.md +823 -0
- package/dist/adk-build-emhwd0tg.md +39 -0
- package/dist/adk-debug-yrrdtpmb.md +23 -0
- package/dist/adk-dev-console-7z9rtjpy.md +17 -0
- package/dist/adk-document-ras6zzzh.md +68 -0
- package/dist/adk-eval-vx76dwq0.md +17 -0
- package/dist/adk-explain-fcsaw852.md +22 -0
- package/dist/adk-init-9kmxmbbe.md +30 -0
- package/dist/adk-ship-dybzn884.md +29 -0
- package/dist/adk-test-xq67savs.md +27 -0
- package/dist/adk-validate-1p4skv4m.md +31 -0
- package/dist/advanced-patterns-z86m9h1a.md +669 -0
- package/dist/agent-config-1aqvkrgf.md +450 -0
- package/dist/agent-steps-zj4c1n9t.md +115 -0
- package/dist/assets-q7jt8r2q.md +237 -0
- package/dist/autonomous-execution-fymc12e5.md +747 -0
- package/dist/cli-c31e06yr.md +1334 -0
- package/dist/cli.js +970 -566
- package/dist/commands.json +24 -3
- package/dist/common-failures-ht29w2db.md +231 -0
- package/dist/component-registry-bbpw0see.md +43 -0
- package/dist/context-api-760cvh0h.md +461 -0
- package/dist/conversation-analysis-8ka4nrm4.md +420 -0
- package/dist/conversation-lifecycle-4seqqjc1.md +311 -0
- package/dist/conversations-gwfvxaee.md +1111 -0
- package/dist/custom-components-y5wdgpey.md +167 -0
- package/dist/debug-workflow-vz84q80v.md +239 -0
- package/dist/desk-k54ktvsd.md +852 -0
- package/dist/doc-standards-ck6ax2jd.md +124 -0
- package/dist/eval-format-7n9brja4.md +354 -0
- package/dist/evals-y1zcr1mc.md +11 -0
- package/dist/explain-config-1yaq3zzd.md +424 -0
- package/dist/files-dzt0wxn3.md +918 -0
- package/dist/integration-actions-xang7tma.md +659 -0
- package/dist/integrations-k5ss07f4.md +266 -0
- package/dist/interfaces-6t0bpxs4.md +97 -0
- package/dist/knowledge-bases-20rg8p5c.md +456 -0
- package/dist/llm-debugging-76ng8rzm.md +209 -0
- package/dist/mcp-server-n1asyzhv.md +112 -0
- package/dist/messages-gkkg0b54.md +769 -0
- package/dist/model-configuration-f273kgd0.md +201 -0
- package/dist/multi-agent-dashboard-1mdfjdts.md +70 -0
- package/dist/pages-xn18xn73.md +273 -0
- package/dist/patterns-mistakes-5vb99dhf.md +469 -0
- package/dist/plugins-ttnx8f49.md +280 -0
- package/dist/tables-x45t36dd.md +1136 -0
- package/dist/tags-46td3qrg.md +516 -0
- package/dist/test-patterns-hj47w4nc.md +355 -0
- package/dist/testing-workflow-2vkp932f.md +225 -0
- package/dist/tools-wk7bq24z.md +581 -0
- package/dist/trace-summarization-hdyx1c2e.md +313 -0
- package/dist/traces-and-logs-f2kjfh19.md +203 -0
- package/dist/triggers-4j2cc86x.md +719 -0
- package/dist/ui-server-entry.js +952 -137
- package/dist/workflow-steps-mrdrf1c5.md +851 -0
- package/dist/workflows-gadx3s69.md +1408 -0
- package/dist/zai-agent-reference-e6644aad.md +454 -0
- package/dist/zai-complete-guide-mnna5d6f.md +1408 -0
- package/package.json +4 -4
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
# Desk Integration
|
|
2
|
+
|
|
3
|
+
Desk is Botpress's AI-augmented support workspace where human agents and AI bots collaborate on customer tickets. This guide covers how to build ADK agents that participate in the Desk support workflow.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
Minimal working example for a Desk support bot:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// src/conversations/desk.ts
|
|
11
|
+
import { Autonomous, context, Conversation, z } from "@botpress/runtime";
|
|
12
|
+
import { actions, joinMarkdownChildren } from "@botpress/runtime/runtime";
|
|
13
|
+
|
|
14
|
+
export default new Conversation({
|
|
15
|
+
channel: "desk.ticket",
|
|
16
|
+
state: z.object({
|
|
17
|
+
lastSync: z.string().default(""),
|
|
18
|
+
}),
|
|
19
|
+
events: ["desk:ticketActivity"],
|
|
20
|
+
handler: async ({ conversation, execute, send }) => {
|
|
21
|
+
// CRITICAL: Only process tickets assigned to this bot
|
|
22
|
+
if (conversation.tags["desk:dassignedself"] !== "true") {
|
|
23
|
+
console.log("Ticket not assigned to bot, ignoring...");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await execute({
|
|
28
|
+
tools: [],
|
|
29
|
+
instructions: "You are a helpful support agent...",
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Configuration (agent.config.ts)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { defineConfig } from "@botpress/runtime";
|
|
39
|
+
|
|
40
|
+
export default defineConfig({
|
|
41
|
+
name: "support-bot",
|
|
42
|
+
|
|
43
|
+
dependencies: {
|
|
44
|
+
integrations: {
|
|
45
|
+
desk: {
|
|
46
|
+
enabled: true,
|
|
47
|
+
configuration: {
|
|
48
|
+
displayName: "Support Bot",
|
|
49
|
+
botHandle: "SupportBot",
|
|
50
|
+
canBeAssigned: true,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Overview
|
|
61
|
+
|
|
62
|
+
### What is Desk?
|
|
63
|
+
|
|
64
|
+
Desk is a unified support workspace that:
|
|
65
|
+
- Aggregates tickets from multiple sources (Intercom, Zendesk, native)
|
|
66
|
+
- Enables human agents and AI bots to collaborate on customer tickets
|
|
67
|
+
- Provides real-time activity feeds and assignment management
|
|
68
|
+
- Supports internal notes (staff-only) and customer-visible replies
|
|
69
|
+
|
|
70
|
+
### Bot Participation Modes
|
|
71
|
+
|
|
72
|
+
| Mode | `canBeAssigned` | Description |
|
|
73
|
+
|------|-----------------|-------------|
|
|
74
|
+
| **Assigned** | `true` | Bot owns tickets and replies directly to customers |
|
|
75
|
+
| **Assisting** | `false` | Bot helps human agents with suggestions and automation |
|
|
76
|
+
|
|
77
|
+
### Multi-Bot Teams
|
|
78
|
+
|
|
79
|
+
Multiple bots can be registered with Desk, each with different capabilities:
|
|
80
|
+
- A triage bot that classifies and routes tickets
|
|
81
|
+
- A knowledge bot that searches documentation
|
|
82
|
+
- A specialist bot for specific product areas
|
|
83
|
+
|
|
84
|
+
Each bot is identified by its `botHandle` (e.g., "@SupportBot", "@TriageBot").
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
All Desk configuration lives in `agent.config.ts` inside the `dependencies.integrations.desk` block:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { defineConfig } from "@botpress/runtime";
|
|
94
|
+
|
|
95
|
+
export default defineConfig({
|
|
96
|
+
name: "my-agent",
|
|
97
|
+
|
|
98
|
+
dependencies: {
|
|
99
|
+
integrations: {
|
|
100
|
+
desk: {
|
|
101
|
+
enabled: true,
|
|
102
|
+
configuration: {
|
|
103
|
+
displayName: "My Support Bot",
|
|
104
|
+
botHandle: "MySupportBot",
|
|
105
|
+
displayAvatarUrl: "https://example.com/bot-avatar.png",
|
|
106
|
+
canBeAssigned: true,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Configuration Options
|
|
115
|
+
|
|
116
|
+
| Option | Type | Required | Description |
|
|
117
|
+
|--------|------|----------|-------------|
|
|
118
|
+
| `displayName` | string | Yes | Name shown in Desk UI |
|
|
119
|
+
| `botHandle` | string | Yes | Handle for @mentions (3-20 chars, starts with letter, alphanumeric + underscores) |
|
|
120
|
+
| `displayAvatarUrl` | string | No | URL of avatar image |
|
|
121
|
+
| `canBeAssigned` | boolean | No | Whether bot can be assigned to tickets (default: `false`) |
|
|
122
|
+
|
|
123
|
+
### Handle Rules
|
|
124
|
+
|
|
125
|
+
The `botHandle` must:
|
|
126
|
+
- Be 3-20 characters
|
|
127
|
+
- Start with a letter
|
|
128
|
+
- Contain only letters, numbers, and underscores
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
- `SupportBot`, `Triage_Bot`, `KB123`
|
|
132
|
+
- ❌ `Bot`, `123Bot`, `support-bot`
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Channel & Events
|
|
137
|
+
|
|
138
|
+
### Channel: desk.ticket
|
|
139
|
+
|
|
140
|
+
The Desk integration exposes a single channel: `desk.ticket`. All ticket interactions flow through this channel.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
export default new Conversation({
|
|
144
|
+
channel: "desk.ticket",
|
|
145
|
+
// ...
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Event: desk:ticketActivity
|
|
150
|
+
|
|
151
|
+
Emitted when new activities (comments, notes, or system activities) are added to a ticket the bot is assigned to.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
export default new Conversation({
|
|
155
|
+
channel: "desk.ticket",
|
|
156
|
+
events: ["desk:ticketActivity"],
|
|
157
|
+
handler: async ({ event }) => {
|
|
158
|
+
// event.payload contains:
|
|
159
|
+
// - ticketId: string
|
|
160
|
+
// - activityCount: number
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Important:** The event does NOT include activities created by the bot itself (prevents infinite loops).
|
|
166
|
+
|
|
167
|
+
### Conversation Tags
|
|
168
|
+
|
|
169
|
+
| Tag | Description | Example |
|
|
170
|
+
|-----|-------------|---------|
|
|
171
|
+
| `desk:did` | Desk ticket ID | `tkt_abc123` |
|
|
172
|
+
| `desk:status` | Ticket status | `open`, `closed`, `snoozed` |
|
|
173
|
+
| `desk:title` | Ticket title | `"Login issue"` |
|
|
174
|
+
| `desk:system` | Source system | `intercom`, `zendesk`, `native` |
|
|
175
|
+
| `desk:url` | URL to view in Desk | `https://desk.botpress.com/...` |
|
|
176
|
+
| `desk:snoozeduntil` | Wake time if snoozed | ISO timestamp |
|
|
177
|
+
| `desk:dtags` | Desk tags (comma-separated) | `urgent,billing` |
|
|
178
|
+
| `desk:dcustomerid` | Customer ID | `cus_xyz789` |
|
|
179
|
+
| `desk:dassignedid` | Assigned admin/bot ID | `adm_123`, `ibot_456` |
|
|
180
|
+
| `desk:dassignedself` | **Is this bot assigned?** | `"true"` or `"false"` |
|
|
181
|
+
| `desk:dsyncactivityid` | Last synced activity ID | `tka_abc123` |
|
|
182
|
+
|
|
183
|
+
### Message Tags
|
|
184
|
+
|
|
185
|
+
| Tag | Description |
|
|
186
|
+
|-----|-------------|
|
|
187
|
+
| `desk:did` | Activity ID (`tka_...`) |
|
|
188
|
+
| `desk:dassignedid` | Who was assigned when sent |
|
|
189
|
+
| `desk:dstatus` | Ticket status when sent |
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Message Types
|
|
194
|
+
|
|
195
|
+
The Desk channel supports three message types:
|
|
196
|
+
|
|
197
|
+
### 1. Reply (text) — Customer-Visible
|
|
198
|
+
|
|
199
|
+
Replies are visible to the customer and may be sent via email.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
await send({
|
|
203
|
+
type: "text",
|
|
204
|
+
payload: {
|
|
205
|
+
format: "markdown",
|
|
206
|
+
text: "Hello! How can I help you today?",
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Schema:**
|
|
212
|
+
```typescript
|
|
213
|
+
{
|
|
214
|
+
format: "plain" | "html" | "markdown",
|
|
215
|
+
text: string,
|
|
216
|
+
attachments?: Array<{
|
|
217
|
+
id: string,
|
|
218
|
+
url: string,
|
|
219
|
+
name: string,
|
|
220
|
+
contentType: string,
|
|
221
|
+
size: number,
|
|
222
|
+
width?: number,
|
|
223
|
+
height?: number,
|
|
224
|
+
}>
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 2. Note — Internal Staff-Only
|
|
229
|
+
|
|
230
|
+
Notes are only visible to support staff (admins and bots), never to customers.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
await send({
|
|
234
|
+
type: "note",
|
|
235
|
+
payload: {
|
|
236
|
+
format: "markdown",
|
|
237
|
+
text: "Internal note: Customer has billing issues in CRM",
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Schema:**
|
|
243
|
+
```typescript
|
|
244
|
+
{
|
|
245
|
+
format: "plain" | "html" | "markdown",
|
|
246
|
+
text: string,
|
|
247
|
+
mentions?: Array<{
|
|
248
|
+
id: string,
|
|
249
|
+
type: "admin" | "bot",
|
|
250
|
+
name: string,
|
|
251
|
+
}>
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 3. Activity — System Events
|
|
256
|
+
|
|
257
|
+
Activities represent system events like assignment changes, status changes, etc.
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
await send({
|
|
261
|
+
type: "activity",
|
|
262
|
+
payload: {
|
|
263
|
+
type: "ticket.activity",
|
|
264
|
+
private: true,
|
|
265
|
+
activity: {
|
|
266
|
+
type: "ticket.assigned",
|
|
267
|
+
assignedTo: {
|
|
268
|
+
type: "bot",
|
|
269
|
+
id: "ibot_abc123",
|
|
270
|
+
reply_as: "SupportBot",
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Activity Types:** `ticket.snoozed`, `ticket.unsnoozed`, `ticket.assigned`, `ticket.unassigned`, `ticket.closed`, `ticket.reopened`, `ticket.renamed`, `ticket.tags_updated`
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Conversation Handler Pattern
|
|
282
|
+
|
|
283
|
+
### Complete Handler Structure
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { Autonomous, context, Conversation, z } from "@botpress/runtime";
|
|
287
|
+
import { actions, joinMarkdownChildren, TranscriptItem } from "@botpress/runtime/runtime";
|
|
288
|
+
|
|
289
|
+
export default new Conversation({
|
|
290
|
+
channel: "desk.ticket",
|
|
291
|
+
state: z.object({
|
|
292
|
+
customer: z.object({
|
|
293
|
+
id: z.string(),
|
|
294
|
+
name: z.string(),
|
|
295
|
+
email: z.string().optional(),
|
|
296
|
+
}),
|
|
297
|
+
admins: z.array(
|
|
298
|
+
z.object({ id: z.string(), name: z.string(), email: z.string().optional() })
|
|
299
|
+
),
|
|
300
|
+
ticket: z.object({
|
|
301
|
+
id: z.string(),
|
|
302
|
+
title: z.string(),
|
|
303
|
+
status: z.string(),
|
|
304
|
+
priority: z.string().optional(),
|
|
305
|
+
assignedTo: z.string().optional(),
|
|
306
|
+
createdAt: z.string(),
|
|
307
|
+
}),
|
|
308
|
+
lastSync: z.string().default(""),
|
|
309
|
+
}),
|
|
310
|
+
events: ["desk:ticketActivity"],
|
|
311
|
+
handler: async ({ type, conversation, message, state, event, execute, client }) => {
|
|
312
|
+
// ... handler logic
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Assignment Check Pattern (CRITICAL)
|
|
318
|
+
|
|
319
|
+
**This is the most important pattern.** Without this check, your bot will process ALL tickets in the workspace.
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
handler: async ({ conversation }) => {
|
|
323
|
+
if (conversation.tags["desk:dassignedself"] !== "true") {
|
|
324
|
+
console.log("Ticket not assigned to bot, ignoring...");
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
// Only process assigned tickets below this line
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Why?** The Desk integration fires events for ALL ticket activity. Without the assignment check:
|
|
332
|
+
- Your bot processes every ticket in the workspace
|
|
333
|
+
- Customers receive confusing or duplicate responses
|
|
334
|
+
- Unnecessary resource and API consumption
|
|
335
|
+
|
|
336
|
+
### Transcript Management
|
|
337
|
+
|
|
338
|
+
Sync messages to maintain conversation context:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
handler: async ({ conversation, state, client }) => {
|
|
342
|
+
// Check assignment first...
|
|
343
|
+
|
|
344
|
+
const chat = context.get("chat");
|
|
345
|
+
const transcript = await chat.fetchTranscript();
|
|
346
|
+
|
|
347
|
+
const messages = await client._inner.list
|
|
348
|
+
.messages({
|
|
349
|
+
conversationId: conversation.id,
|
|
350
|
+
afterDate: state.lastSync,
|
|
351
|
+
})
|
|
352
|
+
.collect({})
|
|
353
|
+
.then((messages) =>
|
|
354
|
+
messages.sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1))
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
const newMessages = [
|
|
358
|
+
...transcript,
|
|
359
|
+
...messages.map((m) => ({
|
|
360
|
+
id: m.id,
|
|
361
|
+
content: JSON.stringify(m.payload),
|
|
362
|
+
createdAt: m.createdAt,
|
|
363
|
+
role: m.direction === "incoming" ? "user" : "assistant",
|
|
364
|
+
attachments: [],
|
|
365
|
+
name: m.userId,
|
|
366
|
+
}) satisfies TranscriptItem),
|
|
367
|
+
];
|
|
368
|
+
|
|
369
|
+
chat.setTranscript(newMessages);
|
|
370
|
+
|
|
371
|
+
state.lastSync = newMessages
|
|
372
|
+
.filter((x) => x.role === "user" || x.role === "assistant")
|
|
373
|
+
.slice(-1)[0]?.createdAt;
|
|
374
|
+
};
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
> **Note:** Use `context.enterWith()` when reassigning context in async operations. Do not use `context.set("chat", ...)` for full context reassignment.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Reply and Note Components
|
|
382
|
+
|
|
383
|
+
For AI-driven responses, register custom components that map to Desk message types.
|
|
384
|
+
|
|
385
|
+
### Reply Component
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
const Reply = new Autonomous.Component({
|
|
389
|
+
name: "Reply",
|
|
390
|
+
description: "Sends a reply to the customer, visible to them and via email.",
|
|
391
|
+
type: "leaf",
|
|
392
|
+
leaf: { props: z.object({}) },
|
|
393
|
+
aliases: [],
|
|
394
|
+
examples: [
|
|
395
|
+
{
|
|
396
|
+
name: "Reply",
|
|
397
|
+
description: "Sends a reply to the customer",
|
|
398
|
+
code: `
|
|
399
|
+
yield <Message><Reply>
|
|
400
|
+
Some **markdown** reply to the customer!
|
|
401
|
+
</Reply></Message>`,
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Note Component
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
const Note = new Autonomous.Component({
|
|
411
|
+
name: "Note",
|
|
412
|
+
description: "Adds an internal note to the ticket. Not visible to the customer.",
|
|
413
|
+
type: "leaf",
|
|
414
|
+
leaf: { props: z.object({}) },
|
|
415
|
+
aliases: [],
|
|
416
|
+
examples: [
|
|
417
|
+
{
|
|
418
|
+
name: "Note",
|
|
419
|
+
description: "Adds an internal note to the ticket",
|
|
420
|
+
code: `yield <Message><Note>This is a **note** with markdown!</Note></Message>`,
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
});
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Component Registration
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
handler: async ({ conversation }) => {
|
|
430
|
+
// ... assignment check ...
|
|
431
|
+
|
|
432
|
+
const chat = context.get("chat");
|
|
433
|
+
|
|
434
|
+
// Remove default message components
|
|
435
|
+
chat.removeComponent("MESSAGE");
|
|
436
|
+
chat.removeComponent("Text");
|
|
437
|
+
chat.removeComponent("TEXT");
|
|
438
|
+
|
|
439
|
+
// Register Reply component
|
|
440
|
+
chat.registerComponent({
|
|
441
|
+
component: Reply as any,
|
|
442
|
+
handler: async ({ props, children }) => {
|
|
443
|
+
await conversation.send({
|
|
444
|
+
type: "text",
|
|
445
|
+
payload: { format: "markdown", text: joinMarkdownChildren(children) },
|
|
446
|
+
});
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Register Note component
|
|
451
|
+
chat.registerComponent({
|
|
452
|
+
component: Note as any,
|
|
453
|
+
handler: async ({ props, children }) => {
|
|
454
|
+
await conversation.send({
|
|
455
|
+
type: "note",
|
|
456
|
+
payload: { format: "markdown", text: joinMarkdownChildren(children) },
|
|
457
|
+
});
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Update chat context with new components
|
|
462
|
+
const newChat = Object.assign(chat, {
|
|
463
|
+
getComponents: async () => [Reply, Note],
|
|
464
|
+
} satisfies Partial<typeof chat>);
|
|
465
|
+
|
|
466
|
+
context.enterWith({ ...context.getAll(), chat: newChat });
|
|
467
|
+
};
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Available Actions
|
|
473
|
+
|
|
474
|
+
The Desk integration provides 11 actions for ticket management.
|
|
475
|
+
|
|
476
|
+
### Ticket Queries
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// listTickets - List tickets with pagination
|
|
480
|
+
const result = await actions.desk.listTickets({
|
|
481
|
+
orderBy: "updatedAt",
|
|
482
|
+
orderDirection: "desc",
|
|
483
|
+
limit: 50,
|
|
484
|
+
cursor: undefined,
|
|
485
|
+
});
|
|
486
|
+
// Returns: { tickets: Ticket[], hasMore: boolean, nextCursor?: string }
|
|
487
|
+
|
|
488
|
+
// getTicket - Get a single ticket by ID
|
|
489
|
+
const result = await actions.desk.getTicket({ id: "tkt_abc123" });
|
|
490
|
+
|
|
491
|
+
// listTicketActivities - List activities for a ticket
|
|
492
|
+
const result = await actions.desk.listTicketActivities({
|
|
493
|
+
ticketId: "tkt_abc123",
|
|
494
|
+
limit: 50,
|
|
495
|
+
cursor: undefined,
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Assignment Actions
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
// assignTicket - Assign to admin or bot
|
|
503
|
+
await actions.desk.assignTicket({
|
|
504
|
+
ticketId: "tkt_abc123",
|
|
505
|
+
assigneeId: "adm_xyz789", // or "ibot_..." for a bot
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// unassignTicket - Remove assignment
|
|
509
|
+
await actions.desk.unassignTicket({ ticketId: "tkt_abc123" });
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Lifecycle Actions
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
// closeTicket
|
|
516
|
+
await actions.desk.closeTicket({ ticketId: "tkt_abc123" });
|
|
517
|
+
|
|
518
|
+
// reopenTicket
|
|
519
|
+
await actions.desk.reopenTicket({ ticketId: "tkt_abc123" });
|
|
520
|
+
|
|
521
|
+
// snoozeTicket
|
|
522
|
+
await actions.desk.snoozeTicket({
|
|
523
|
+
ticketId: "tkt_abc123",
|
|
524
|
+
snoozedUntil: "2025-01-21T09:00:00Z",
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// unsnoozeTicket
|
|
528
|
+
await actions.desk.unsnoozeTicket({ ticketId: "tkt_abc123" });
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Metadata Actions
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// renameTicket
|
|
535
|
+
await actions.desk.renameTicket({
|
|
536
|
+
ticketId: "tkt_abc123",
|
|
537
|
+
title: "Updated: Login Issue with 2FA",
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// addTags
|
|
541
|
+
await actions.desk.addTags({
|
|
542
|
+
ticketId: "tkt_abc123",
|
|
543
|
+
tags: ["urgent", "billing", "enterprise"],
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// removeTags
|
|
547
|
+
await actions.desk.removeTags({
|
|
548
|
+
ticketId: "tkt_abc123",
|
|
549
|
+
tags: ["low-priority"],
|
|
550
|
+
});
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## Converting Actions to Tools
|
|
556
|
+
|
|
557
|
+
Convert Desk actions to AI-callable tools using `asTool()` and `setStaticInputValues()`:
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
handler: async ({ conversation, execute }) => {
|
|
561
|
+
// Check assignment...
|
|
562
|
+
|
|
563
|
+
const ticketId = conversation.tags["desk:did"];
|
|
564
|
+
|
|
565
|
+
// Create tools with bound ticketId
|
|
566
|
+
const tools = [
|
|
567
|
+
actions.desk.unassignTicket.asTool().setStaticInputValues({ ticketId }),
|
|
568
|
+
actions.desk.closeTicket.asTool().setStaticInputValues({ ticketId }),
|
|
569
|
+
actions.desk.snoozeTicket.asTool().setStaticInputValues({ ticketId }),
|
|
570
|
+
actions.desk.addTags.asTool().setStaticInputValues({ ticketId }),
|
|
571
|
+
actions.desk.removeTags.asTool().setStaticInputValues({ ticketId }),
|
|
572
|
+
];
|
|
573
|
+
|
|
574
|
+
await execute({
|
|
575
|
+
tools,
|
|
576
|
+
instructions: "You are a helpful support agent...",
|
|
577
|
+
});
|
|
578
|
+
};
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## AI Execution with Desk
|
|
584
|
+
|
|
585
|
+
### Execute with Instructions
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
handler: async ({ conversation, execute }) => {
|
|
589
|
+
if (conversation.tags["desk:dassignedself"] !== "true") return;
|
|
590
|
+
|
|
591
|
+
const ticketId = conversation.tags["desk:did"];
|
|
592
|
+
|
|
593
|
+
const UnassignTool = actions.desk.unassignTicket
|
|
594
|
+
.asTool()
|
|
595
|
+
.setStaticInputValues({ ticketId });
|
|
596
|
+
|
|
597
|
+
await execute({
|
|
598
|
+
iterations: 5,
|
|
599
|
+
tools: [UnassignTool],
|
|
600
|
+
model: "anthropic:claude-sonnet-4-20250514",
|
|
601
|
+
instructions: `You are a helpful assistant for customer support tickets.
|
|
602
|
+
|
|
603
|
+
You can choose to:
|
|
604
|
+
- Reply to the customer using <Reply>
|
|
605
|
+
- Add an internal note using <Note>
|
|
606
|
+
- Take no action
|
|
607
|
+
|
|
608
|
+
## Rules
|
|
609
|
+
- Always use <Reply> for customer-visible messages
|
|
610
|
+
- Always use <Note> for internal staff communication
|
|
611
|
+
- Be polite, concise, and don't make up information
|
|
612
|
+
- Ask for human help if unsure
|
|
613
|
+
|
|
614
|
+
## Current Ticket
|
|
615
|
+
${JSON.stringify(conversation.tags, null, 2)}
|
|
616
|
+
`,
|
|
617
|
+
});
|
|
618
|
+
};
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### System Prompt Guidelines
|
|
622
|
+
|
|
623
|
+
**DO:**
|
|
624
|
+
- Explain when to use Reply vs Note
|
|
625
|
+
- Set clear escalation criteria
|
|
626
|
+
- Include ticket context via tags
|
|
627
|
+
- Define what "taking no action" means
|
|
628
|
+
|
|
629
|
+
**DON'T:**
|
|
630
|
+
- Allow direct customer replies without components
|
|
631
|
+
- Let the bot make up information
|
|
632
|
+
- Process messages the bot sent itself
|
|
633
|
+
- Reply to every message (e.g., "thank you")
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## Escalation Pattern
|
|
638
|
+
|
|
639
|
+
When a bot needs to hand off to a human agent, follow this 4-step pattern:
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
async function escalateToHuman(conversation: any, reason: string): Promise<void> {
|
|
643
|
+
// Step 1: Internal note explaining why
|
|
644
|
+
await conversation.send({
|
|
645
|
+
type: "note",
|
|
646
|
+
payload: {
|
|
647
|
+
format: "markdown",
|
|
648
|
+
text: `Escalating to human: ${reason}`,
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// Step 2: Customer reply
|
|
653
|
+
await conversation.send({
|
|
654
|
+
type: "text",
|
|
655
|
+
payload: {
|
|
656
|
+
format: "markdown",
|
|
657
|
+
text: "I've connected you with a member of our team who can help. They'll be with you shortly!",
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
// Step 3: Unassign bot
|
|
662
|
+
await actions.desk.unassignTicket({
|
|
663
|
+
ticketId: conversation.tags["desk:did"],
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// In handler:
|
|
668
|
+
handler: async ({ conversation }) => {
|
|
669
|
+
if (conversation.tags["desk:dassignedself"] !== "true") return;
|
|
670
|
+
|
|
671
|
+
if (needsEscalation) {
|
|
672
|
+
await escalateToHuman(conversation, "Complex billing issue");
|
|
673
|
+
return; // Step 4: Stop processing
|
|
674
|
+
}
|
|
675
|
+
// ... continue normal processing
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### When to Escalate
|
|
680
|
+
|
|
681
|
+
- Technical issues beyond basic troubleshooting
|
|
682
|
+
- Billing or account access issues
|
|
683
|
+
- Angry or frustrated customers
|
|
684
|
+
- Any uncertainty about the correct answer
|
|
685
|
+
|
|
686
|
+
---
|
|
687
|
+
|
|
688
|
+
## Data Schemas
|
|
689
|
+
|
|
690
|
+
### Ticket
|
|
691
|
+
|
|
692
|
+
```typescript
|
|
693
|
+
interface Ticket {
|
|
694
|
+
id: string; // "tkt_abc123"
|
|
695
|
+
title: string;
|
|
696
|
+
customer: Customer;
|
|
697
|
+
priority: "low" | "medium" | "high" | "urgent";
|
|
698
|
+
system: "native" | "intercom" | "zendesk";
|
|
699
|
+
systemTicketId: string;
|
|
700
|
+
assignedTo?: Admin | Bot;
|
|
701
|
+
status: "open" | "closed" | "snoozed";
|
|
702
|
+
createdAt: string;
|
|
703
|
+
updatedAt: string;
|
|
704
|
+
closedAt?: string;
|
|
705
|
+
snoozedUntil?: string;
|
|
706
|
+
tags: string[];
|
|
707
|
+
metadata?: Record<string, any>;
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### Customer
|
|
712
|
+
|
|
713
|
+
```typescript
|
|
714
|
+
interface Customer {
|
|
715
|
+
id: string; // "cus_xyz789"
|
|
716
|
+
name?: string;
|
|
717
|
+
email?: string;
|
|
718
|
+
phone?: string;
|
|
719
|
+
avatarUrl?: string;
|
|
720
|
+
}
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
### Admin
|
|
724
|
+
|
|
725
|
+
```typescript
|
|
726
|
+
interface Admin {
|
|
727
|
+
id: string; // "adm_abc123"
|
|
728
|
+
name: string;
|
|
729
|
+
email: string;
|
|
730
|
+
avatarUrl?: string;
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### Bot
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
interface Bot {
|
|
738
|
+
id: string; // "ibot_def456"
|
|
739
|
+
displayName: string;
|
|
740
|
+
displayAvatarUrl?: string;
|
|
741
|
+
botHandle: string;
|
|
742
|
+
canBeAssigned: boolean;
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
### Activity
|
|
747
|
+
|
|
748
|
+
```typescript
|
|
749
|
+
interface Activity {
|
|
750
|
+
id: string; // "tka_abc123"
|
|
751
|
+
ticketId: string;
|
|
752
|
+
activityType: "comment" | "note" | "system_public" | "system_internal";
|
|
753
|
+
authorType: "admin" | "customer" | "bot" | "user";
|
|
754
|
+
authorId: string;
|
|
755
|
+
body: string;
|
|
756
|
+
bodyFormat: "plain" | "html" | "markdown" | "json";
|
|
757
|
+
isRedacted: boolean;
|
|
758
|
+
createdAt: string;
|
|
759
|
+
updatedAt: string;
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Activity Event Types
|
|
764
|
+
|
|
765
|
+
```typescript
|
|
766
|
+
type TicketActivityType =
|
|
767
|
+
| { type: "ticket.snoozed"; author: Author }
|
|
768
|
+
| { type: "ticket.unsnoozed"; author: Author }
|
|
769
|
+
| { type: "ticket.assigned"; assignedTo: { type: "admin" | "bot"; id: string; reply_as?: string } }
|
|
770
|
+
| { type: "ticket.unassigned"; author: Author }
|
|
771
|
+
| { type: "ticket.closed"; author: Author }
|
|
772
|
+
| { type: "ticket.reopened"; author: Author }
|
|
773
|
+
| { type: "ticket.renamed"; title: string }
|
|
774
|
+
| { type: "ticket.tags_updated"; author: Author; added: string[]; removed: string[] };
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
---
|
|
778
|
+
|
|
779
|
+
## Common Mistakes
|
|
780
|
+
|
|
781
|
+
### Missing Assignment Check
|
|
782
|
+
|
|
783
|
+
```typescript
|
|
784
|
+
// ❌ WRONG - Processes ALL tickets
|
|
785
|
+
handler: async ({ conversation, execute }) => {
|
|
786
|
+
await execute({ ... });
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// ✅ CORRECT - Check assignment first
|
|
790
|
+
handler: async ({ conversation, execute }) => {
|
|
791
|
+
if (conversation.tags["desk:dassignedself"] !== "true") return;
|
|
792
|
+
await execute({ ... });
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### Using Reply When Should Use Note
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
// ❌ WRONG - Customer sees internal info
|
|
800
|
+
await send({ type: "text", payload: { text: "Customer has overdue invoices in CRM" } });
|
|
801
|
+
|
|
802
|
+
// ✅ CORRECT - Note for internal communication
|
|
803
|
+
await send({ type: "note", payload: { text: "Customer has overdue invoices in CRM" } });
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
### Not Extracting ticketId for Tools
|
|
807
|
+
|
|
808
|
+
```typescript
|
|
809
|
+
// ❌ WRONG - Tool has no ticketId
|
|
810
|
+
const UnassignTool = actions.desk.unassignTicket.asTool();
|
|
811
|
+
|
|
812
|
+
// ✅ CORRECT - Bind ticketId
|
|
813
|
+
const ticketId = conversation.tags["desk:did"];
|
|
814
|
+
const UnassignTool = actions.desk.unassignTicket
|
|
815
|
+
.asTool()
|
|
816
|
+
.setStaticInputValues({ ticketId });
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### Using Wrong Tag Names
|
|
820
|
+
|
|
821
|
+
```typescript
|
|
822
|
+
// ❌ WRONG
|
|
823
|
+
conversation.tags["deskId"]
|
|
824
|
+
conversation.tags["desk:assignedSelf"] // wrong case
|
|
825
|
+
|
|
826
|
+
// ✅ CORRECT
|
|
827
|
+
conversation.tags["desk:did"]
|
|
828
|
+
conversation.tags["desk:dassignedself"] // lowercase
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### Replying to Every Message
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
// ❌ WRONG
|
|
835
|
+
instructions: "Always reply to every message from the customer"
|
|
836
|
+
|
|
837
|
+
// ✅ CORRECT
|
|
838
|
+
instructions: `You DON'T have to reply to every message.
|
|
839
|
+
If the customer says "thanks" or "got it", take no action.
|
|
840
|
+
Only reply when you have something meaningful to add.`
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
---
|
|
844
|
+
|
|
845
|
+
## See Also
|
|
846
|
+
|
|
847
|
+
- [./conversations.md](./conversations.md) — General conversation handling
|
|
848
|
+
- [./integration-actions.md](./integration-actions.md) — Using integration actions
|
|
849
|
+
- [./actions.md](./actions.md) — Actions and Tools reference
|
|
850
|
+
- [./tags.md](./tags.md) — Tags reference
|
|
851
|
+
- [./patterns-mistakes.md](./patterns-mistakes.md) — General best practices
|
|
852
|
+
- [./agent-config.md](./agent-config.md) — Configuration reference
|