@botpress/adk-cli 1.19.0-beta.4 → 1.19.0-beta.6

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.
Files changed (87) hide show
  1. package/{dist/index-a3ZL0_Jr-q145h35p.js → assets/ui-dist/assets/index-BC_iUF2d.js} +2 -2
  2. package/assets/ui-dist/assets/index-BxpvDT_K.css +1 -0
  3. package/{dist/index-BESZeTO5-2rpmcefa.js → assets/ui-dist/assets/index-C7gAokRv.js} +2 -2
  4. package/assets/ui-dist/assets/{index-B3b7PPgV.js → index-exyr_Djl.js} +424 -424
  5. package/assets/ui-dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  6. package/assets/ui-dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  7. package/assets/ui-dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  8. package/assets/ui-dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  9. package/assets/ui-dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  10. package/assets/ui-dist/index.html +2 -2
  11. package/dist/{README-wjb7z7f0.md → README-824wa4jj.md} +1 -1
  12. package/dist/SKILL-75ym6tcj.md +257 -0
  13. package/dist/SKILL-chnwzmt1.md +548 -0
  14. package/dist/SKILL-d4bs53y3.md +244 -0
  15. package/dist/SKILL-h4b2f1n9.md +114 -0
  16. package/dist/SKILL-hbw5nt95.md +111 -0
  17. package/dist/actions-h7hxxvxr.md +823 -0
  18. package/dist/adk-build-emhwd0tg.md +39 -0
  19. package/dist/adk-debug-yrrdtpmb.md +23 -0
  20. package/dist/adk-dev-console-7z9rtjpy.md +17 -0
  21. package/dist/adk-document-ras6zzzh.md +68 -0
  22. package/dist/adk-eval-vx76dwq0.md +17 -0
  23. package/dist/adk-explain-fcsaw852.md +22 -0
  24. package/dist/adk-init-9kmxmbbe.md +30 -0
  25. package/dist/adk-ship-dybzn884.md +29 -0
  26. package/dist/adk-test-xq67savs.md +27 -0
  27. package/dist/adk-validate-1p4skv4m.md +31 -0
  28. package/dist/advanced-patterns-z86m9h1a.md +669 -0
  29. package/dist/agent-config-1aqvkrgf.md +450 -0
  30. package/dist/agent-steps-zj4c1n9t.md +115 -0
  31. package/dist/assets-q7jt8r2q.md +237 -0
  32. package/dist/autonomous-execution-fymc12e5.md +747 -0
  33. package/dist/cli-c31e06yr.md +1334 -0
  34. package/dist/cli.js +4241 -6449
  35. package/dist/commands.json +25 -4
  36. package/dist/common-failures-ht29w2db.md +231 -0
  37. package/dist/component-registry-bbpw0see.md +43 -0
  38. package/dist/context-api-760cvh0h.md +461 -0
  39. package/dist/conversation-analysis-8ka4nrm4.md +420 -0
  40. package/dist/conversation-lifecycle-4seqqjc1.md +311 -0
  41. package/dist/conversations-gwfvxaee.md +1111 -0
  42. package/dist/custom-components-y5wdgpey.md +167 -0
  43. package/dist/debug-workflow-vz84q80v.md +239 -0
  44. package/dist/desk-k54ktvsd.md +852 -0
  45. package/dist/doc-standards-ck6ax2jd.md +124 -0
  46. package/dist/eval-format-7n9brja4.md +354 -0
  47. package/dist/evals-y1zcr1mc.md +11 -0
  48. package/dist/explain-config-1yaq3zzd.md +424 -0
  49. package/dist/files-dzt0wxn3.md +918 -0
  50. package/dist/{index-3t354nbw.html → index-0qts7wfk.html} +2 -2
  51. package/{assets/ui-dist/assets/index-a3ZL0_Jr.js → dist/index-BC_iUF2d-3h9mqazs.js} +2 -2
  52. package/dist/index-BxpvDT_K-62864175.css +1 -0
  53. package/{assets/ui-dist/assets/index-BESZeTO5.js → dist/index-C7gAokRv-zr7dzkba.js} +2 -2
  54. package/dist/{index-B3b7PPgV-zetkb01w.js → index-exyr_Djl-ksys08wc.js} +424 -424
  55. package/dist/integration-actions-xang7tma.md +659 -0
  56. package/dist/integrations-k5ss07f4.md +266 -0
  57. package/dist/interfaces-6t0bpxs4.md +97 -0
  58. package/dist/jetbrains-mono-cyrillic-wght-normal-D73BlboJ-6vpq1jfh.woff2 +0 -0
  59. package/dist/jetbrains-mono-greek-wght-normal-Bw9x6K1M-jv5dqe09.woff2 +0 -0
  60. package/dist/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a-h2ysfc3f.woff2 +0 -0
  61. package/dist/jetbrains-mono-latin-wght-normal-B9CIFXIH-hj2e42wt.woff2 +0 -0
  62. package/dist/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq-wttk1pwp.woff2 +0 -0
  63. package/dist/knowledge-bases-20rg8p5c.md +456 -0
  64. package/dist/llm-debugging-76ng8rzm.md +209 -0
  65. package/dist/mcp-server-n1asyzhv.md +112 -0
  66. package/dist/messages-gkkg0b54.md +769 -0
  67. package/dist/model-configuration-f273kgd0.md +201 -0
  68. package/dist/multi-agent-dashboard-1mdfjdts.md +70 -0
  69. package/dist/pages-xn18xn73.md +273 -0
  70. package/dist/patterns-mistakes-5vb99dhf.md +469 -0
  71. package/dist/plugins-ttnx8f49.md +280 -0
  72. package/dist/tables-x45t36dd.md +1136 -0
  73. package/dist/tags-46td3qrg.md +516 -0
  74. package/dist/test-patterns-hj47w4nc.md +355 -0
  75. package/dist/testing-workflow-2vkp932f.md +225 -0
  76. package/dist/tools-wk7bq24z.md +581 -0
  77. package/dist/trace-summarization-hdyx1c2e.md +313 -0
  78. package/dist/traces-and-logs-f2kjfh19.md +203 -0
  79. package/dist/triggers-4j2cc86x.md +719 -0
  80. package/dist/ui-server-entry.js +1839 -791
  81. package/dist/workflow-steps-mrdrf1c5.md +851 -0
  82. package/dist/workflows-gadx3s69.md +1408 -0
  83. package/dist/zai-agent-reference-e6644aad.md +454 -0
  84. package/dist/zai-complete-guide-mnna5d6f.md +1408 -0
  85. package/package.json +4 -4
  86. package/assets/ui-dist/assets/index-DIFGtVmI.css +0 -1
  87. package/dist/index-DIFGtVmI-d4dhjg97.css +0 -1
@@ -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