@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,669 @@
1
+ # Advanced Patterns
2
+
3
+ Production-ready patterns for building sophisticated ADK agents with guardrails, authentication, observability, and modular architecture.
4
+
5
+ ## Pattern 1: Guardrails
6
+
7
+ Enforce behavioral constraints on autonomous agents to prevent unwanted actions and ensure data validation.
8
+
9
+ ### Use Cases
10
+
11
+ - Ensure knowledge search before answering questions
12
+ - Validate preconditions before executing actions (e.g., credit checks before purchases)
13
+ - Prevent hallucinations by enforcing data lookup
14
+
15
+ ### Implementation
16
+
17
+ ```typescript
18
+ // src/conversations/extensions/guardrails.ts
19
+ import { adk, Autonomous } from "@botpress/runtime";
20
+
21
+ export const makeGuardrails = (message: any) => {
22
+ // Use zai to determine if knowledge search is needed
23
+ const isKnowledgeSearchAsync = adk.zai.check(
24
+ message,
25
+ `Is this a question that requires knowledge search?`
26
+ );
27
+
28
+ let hasSearched = false;
29
+
30
+ const onBeforeToolGuard: Autonomous.Hooks["onBeforeTool"] = async ({
31
+ iteration,
32
+ tool,
33
+ input,
34
+ controller,
35
+ }) => {
36
+ if (tool.name === "search_knowledge") {
37
+ hasSearched = true;
38
+ }
39
+
40
+ const isKnowledgeSearch = await isKnowledgeSearchAsync;
41
+
42
+ if (tool.name === "Message" && isKnowledgeSearch && !hasSearched) {
43
+ throw new Error(
44
+ `Knowledge search is required for this question. Please use "search_knowledge" first.`
45
+ );
46
+ }
47
+ };
48
+
49
+ return { onBeforeToolGuard };
50
+ };
51
+ ```
52
+
53
+ > **Note:** `adk.zai.check()` is the recommended approach for AI-powered validation. An advanced pattern `adk.zai.learn().check()` exists for cached/reusable checks, but `adk.zai.check()` is simpler and covers most cases. See [zai-agent-reference.md](./zai-agent-reference.md) for all Zai operations and edge cases.
54
+
55
+ ### Usage in Conversation
56
+
57
+ ```typescript
58
+ // src/conversations/webchat.ts
59
+ import { Conversation } from "@botpress/runtime";
60
+ import { makeGuardrails } from "./extensions/guardrails";
61
+
62
+ export default new Conversation({
63
+ channel: "*",
64
+ handler: async ({ execute, message }) => {
65
+ const guardrail = makeGuardrails(message);
66
+
67
+ await execute({
68
+ instructions: `You are a helpful assistant...`,
69
+ hooks: {
70
+ onBeforeTool: async (props) => guardrail.onBeforeToolGuard(props),
71
+ },
72
+ });
73
+ },
74
+ });
75
+ ```
76
+
77
+ ### Variations
78
+
79
+ **Credit Check Guardrail:**
80
+
81
+ ```typescript
82
+ export const makeCreditCheckGuardrail = () => {
83
+ let creditChecked = false;
84
+
85
+ const onBeforeToolGuard: Autonomous.Hooks["onBeforeTool"] = async ({
86
+ iteration,
87
+ tool,
88
+ input,
89
+ controller,
90
+ }) => {
91
+ if (tool.name === "check_credit") {
92
+ creditChecked = true;
93
+ }
94
+ if (tool.name === "process_payment" && !creditChecked) {
95
+ throw new Error("Credit check must be performed before processing payment");
96
+ }
97
+ };
98
+
99
+ return { onBeforeToolGuard };
100
+ };
101
+ ```
102
+
103
+ **Data Validation Guardrail:**
104
+
105
+ ```typescript
106
+ export const makeDataValidationGuardrail = () => {
107
+ const onBeforeToolGuard: Autonomous.Hooks["onBeforeTool"] = async ({
108
+ iteration,
109
+ tool,
110
+ input,
111
+ controller,
112
+ }) => {
113
+ if (tool.name === "send_email") {
114
+ const hasValidEmail = await adk.zai.check(
115
+ input,
116
+ "Does this contain a valid email address?"
117
+ );
118
+ if (!hasValidEmail) {
119
+ throw new Error("Invalid email address provided");
120
+ }
121
+ }
122
+ };
123
+
124
+ return { onBeforeToolGuard };
125
+ };
126
+ ```
127
+
128
+ ### Key Techniques
129
+
130
+ 1. **Async Validation**: Use `adk.zai.check()` for AI-powered validation
131
+ 2. **State Tracking**: Track actions with closure variables (`hasSearched`)
132
+ 3. **Hook Interception**: Use `onBeforeTool` to intercept and validate tool calls
133
+ 4. **Error Throwing**: Throw errors to prevent unwanted tool execution
134
+
135
+ ---
136
+
137
+ ## Pattern 2: Admin Mode
138
+
139
+ Implement privileged access control with temporary authentication for administrative functions.
140
+
141
+ ### Use Cases
142
+
143
+ - Knowledge base re-indexing
144
+ - Agent configuration changes
145
+ - Debugging tools for support teams
146
+ - System maintenance operations
147
+
148
+ ### Implementation
149
+
150
+ ```typescript
151
+ // src/conversations/extensions/admin-mode.ts
152
+ import { adk, Autonomous, context, user, z } from "@botpress/runtime";
153
+
154
+ // Define user state schema for admin mode
155
+ export const AdminModeUserSchema: z.ZodRawShape = {
156
+ admin: z
157
+ .object({
158
+ adminUtil: z.string().nullable().describe("Expiration date of admin status (ISO)."),
159
+ code: z.string().nullable().describe("The admin access code."),
160
+ codeValidUntil: z.string().nullable().describe("Expiration date of admin code (ISO)."),
161
+ })
162
+ .default({ adminUtil: null, code: null, codeValidUntil: null }),
163
+ };
164
+
165
+ // Admin-only tool: refresh knowledge bases
166
+ const getIndexKnowledgeBasesTool = () => {
167
+ const ctx = context.getAll();
168
+ return new Autonomous.Tool({
169
+ name: "refreshKnowledgeBases",
170
+ description: "Refresh and re-index all knowledge bases.",
171
+ output: z.string().describe("Confirmation message after refreshing."),
172
+ handler: async () => {
173
+ context.enterWith(ctx); // Restore context for async operations
174
+ await Promise.all(adk.project.knowledge.map((kb) => kb.refresh()));
175
+ return `Started re-indexing: ${adk.project.knowledge.map((kb) => kb.name).join(", ")}`;
176
+ },
177
+ });
178
+ };
179
+
180
+ // Generate one-time login code
181
+ const getLoginTool = () => {
182
+ const ctx = context.getAll();
183
+ user.state ??= {};
184
+
185
+ const CODE_VALIDITY_DURATION_MS = 5 * 60 * 1000; // 5 minutes
186
+
187
+ const expectedCode = user.state.admin?.code?.toLowerCase().trim();
188
+ const codeGenerated =
189
+ expectedCode &&
190
+ user.state.admin?.codeValidUntil &&
191
+ new Date(user.state.admin?.codeValidUntil) > new Date();
192
+
193
+ if (codeGenerated) {
194
+ return new Autonomous.Tool({
195
+ name: "loginWithCode",
196
+ description: "Log in as admin using an access code.",
197
+ input: z.string().describe('The admin access code, e.g. "ABC346"'),
198
+ output: z.boolean().describe("Returns true if login is successful."),
199
+ handler: async (code: string) => {
200
+ context.enterWith(ctx);
201
+ const providedCode = code.toLowerCase().trim();
202
+
203
+ if (expectedCode && providedCode === expectedCode) {
204
+ user.state.admin = {
205
+ adminUtil: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
206
+ code: null,
207
+ codeValidUntil: null,
208
+ };
209
+ return true;
210
+ }
211
+
212
+ throw new Autonomous.ThinkSignal("Invalid or expired admin access code");
213
+ },
214
+ });
215
+ }
216
+
217
+ return new Autonomous.Tool({
218
+ name: "generateLoginCode",
219
+ description: "Generate a one-time access code for admin login.",
220
+ handler: async () => {
221
+ context.enterWith(ctx);
222
+ const generatedCode = Math.random().toString(36).substring(2, 8).toUpperCase();
223
+
224
+ user.state.admin = {
225
+ adminUtil: null,
226
+ code: generatedCode,
227
+ codeValidUntil: new Date(Date.now() + CODE_VALIDITY_DURATION_MS).toISOString(),
228
+ };
229
+
230
+ console.log(`Generated admin login code: ${generatedCode}`);
231
+
232
+ throw new Autonomous.ThinkSignal(
233
+ `An admin login code has been generated and logged in the developer console. Retrieve it from the Botpress dashboard.`
234
+ );
235
+ },
236
+ });
237
+ };
238
+
239
+ function isUserAdmin() {
240
+ return (
241
+ user.state.admin?.adminUtil &&
242
+ new Date(user.state.admin.adminUtil) > new Date()
243
+ );
244
+ }
245
+
246
+ export const getAdminModeObject = () =>
247
+ new Autonomous.Object({
248
+ name: "admin",
249
+ description: getAdminStatus(), // Dynamic description based on state
250
+ tools: isUserAdmin() ? [getIndexKnowledgeBasesTool()] : [getLoginTool()],
251
+ });
252
+ ```
253
+
254
+ ### Configuration in agent.config.ts
255
+
256
+ ```typescript
257
+ import { z, defineConfig } from "@botpress/runtime";
258
+ import { AdminModeUserSchema } from "./src/conversations/extensions/admin-mode";
259
+
260
+ export default defineConfig({
261
+ name: "my-agent",
262
+ user: {
263
+ state: z.object({}).extend(AdminModeUserSchema),
264
+ },
265
+ });
266
+ ```
267
+
268
+ ### Usage in Conversation
269
+
270
+ ```typescript
271
+ import { Conversation } from "@botpress/runtime";
272
+ import { getAdminModeObject } from "./extensions/admin-mode";
273
+
274
+ export default new Conversation({
275
+ channel: "*",
276
+ handler: async ({ execute }) => {
277
+ await execute({
278
+ instructions: `You are a helpful assistant...`,
279
+ objects: [getAdminModeObject()],
280
+ });
281
+ },
282
+ });
283
+ ```
284
+
285
+ ### Key Techniques
286
+
287
+ 1. **Dynamic Tools**: Tools change based on authentication state
288
+ 2. **Time-Based Auth**: Expiration timestamps for codes and sessions (codes: 5 min, sessions: 1 hour)
289
+ 3. **Context Restoration**: `context.enterWith()` for async operations
290
+ 4. **Think Signals**: Use `Autonomous.ThinkSignal` to control agent reasoning flow
291
+ 5. **Console Logging**: Log codes for secure out-of-band delivery (not shown in chat)
292
+
293
+ ---
294
+
295
+ ## Pattern 3: Logging & Observability
296
+
297
+ Comprehensive logging and error tracking for production agents.
298
+
299
+ ### Basic Trace Logging
300
+
301
+ ```typescript
302
+ // src/conversations/extensions/logging.ts
303
+ import { Autonomous } from "@botpress/runtime";
304
+
305
+ export const onTraceLogging: Autonomous.Hooks["onTrace"] = ({ trace, iteration }) => {
306
+ if (trace.type === "code_execution_exception") {
307
+ console.error(`Code Execution Error: ${trace.message}`, trace.stackTrace);
308
+ }
309
+
310
+ if (trace.type === "tool_call" && !trace.success) {
311
+ console.error(
312
+ `Error during tool call to "${trace.tool_name}" with input "${JSON.stringify(trace.input)}":`,
313
+ trace.error
314
+ );
315
+ }
316
+ };
317
+ ```
318
+
319
+ ### Usage in Conversation
320
+
321
+ ```typescript
322
+ import { Conversation } from "@botpress/runtime";
323
+ import { onTraceLogging } from "./extensions/logging";
324
+
325
+ export default new Conversation({
326
+ channel: "*",
327
+ handler: async ({ execute }) => {
328
+ await execute({
329
+ instructions: `You are a helpful assistant...`,
330
+ hooks: {
331
+ onTrace: (props) => onTraceLogging!(props),
332
+ },
333
+ });
334
+ },
335
+ });
336
+ ```
337
+
338
+ ### Advanced: Comprehensive Trace Logging
339
+
340
+ ```typescript
341
+ export const onTraceLogging: Autonomous.Hooks["onTrace"] = ({ trace, iteration }) => {
342
+ switch (trace.type) {
343
+ case "code_execution_exception":
344
+ console.error(`[CODE ERROR] ${trace.message}`, trace.stackTrace);
345
+ break;
346
+ case "tool_call":
347
+ if (trace.success) {
348
+ console.log(`[TOOL SUCCESS] ${trace.tool_name}`, trace.output);
349
+ } else {
350
+ console.error(`[TOOL ERROR] ${trace.tool_name}`, trace.error);
351
+ }
352
+ break;
353
+ case "think":
354
+ console.debug(`[THINK] ${trace.content}`);
355
+ break;
356
+ default:
357
+ console.log(`[TRACE] ${trace.type}`, trace);
358
+ }
359
+ };
360
+ ```
361
+
362
+ ### Advanced: Performance Monitoring
363
+
364
+ ```typescript
365
+ export const makePerformanceMonitor = () => {
366
+ const startTime = Date.now();
367
+ const toolMetrics = new Map<string, number[]>();
368
+
369
+ const onBeforeTool: Autonomous.Hooks["onBeforeTool"] = async ({
370
+ iteration,
371
+ tool,
372
+ input,
373
+ controller,
374
+ }) => {
375
+ (tool as any)._startTime = Date.now();
376
+ };
377
+
378
+ const onAfterTool: Autonomous.Hooks["onAfterTool"] = async ({ tool, output }) => {
379
+ const duration = Date.now() - ((tool as any)._startTime || 0);
380
+ const metrics = toolMetrics.get(tool.name) || [];
381
+ metrics.push(duration);
382
+ toolMetrics.set(tool.name, metrics);
383
+ console.log(`[PERF] ${tool.name}: ${duration}ms`);
384
+ };
385
+
386
+ const onTrace: Autonomous.Hooks["onTrace"] = ({ trace, iteration }) => {
387
+ if (trace.type === "end") {
388
+ console.log(`[PERF] Total conversation: ${Date.now() - startTime}ms`);
389
+ console.log(`[PERF] Tool metrics:`, Object.fromEntries(toolMetrics));
390
+ }
391
+ };
392
+
393
+ return { onBeforeTool, onAfterTool, onTrace };
394
+ };
395
+ ```
396
+
397
+ ### Hook Signatures Reference
398
+
399
+ | Hook | Parameters | Description |
400
+ |------|-----------|-------------|
401
+ | `onBeforeTool` | `{ iteration, tool, input, controller }` | Fires before each tool call |
402
+ | `onAfterTool` | `{ tool, output }` | Fires after each tool call |
403
+ | `onTrace` | `{ trace, iteration }` | Fires on every trace event |
404
+
405
+ ---
406
+
407
+ ## Pattern 4: Extension Composition
408
+
409
+ Combine multiple extensions into a cohesive agent architecture.
410
+
411
+ ```typescript
412
+ // src/conversations/webchat.ts
413
+ import { Conversation } from "@botpress/runtime";
414
+ import { WebsiteKB } from "../knowledge/website-docs";
415
+ import { getAdminModeObject } from "./extensions/admin-mode";
416
+ import { makeGuardrails } from "./extensions/guardrails";
417
+ import { onTraceLogging } from "./extensions/logging";
418
+
419
+ export default new Conversation({
420
+ channel: "*",
421
+ handler: async ({ execute, message }) => {
422
+ const guardrail = makeGuardrails(message);
423
+
424
+ await execute({
425
+ instructions: `You are a helpful assistant that provides accurate information.`,
426
+ knowledge: [WebsiteKB],
427
+ objects: [getAdminModeObject()],
428
+ hooks: {
429
+ onBeforeTool: async (props) => guardrail.onBeforeToolGuard(props),
430
+ onTrace: (props) => onTraceLogging!(props),
431
+ },
432
+ });
433
+ },
434
+ });
435
+ ```
436
+
437
+ ### Architecture
438
+
439
+ ```
440
+ Conversation Handler
441
+ ├── Instructions (system prompt)
442
+ ├── Knowledge (RAG knowledge bases)
443
+ ├── Objects (admin mode, custom tool groups)
444
+ └── Hooks
445
+ ├── onBeforeTool (guardrails, validation)
446
+ ├── onAfterTool (logging, cleanup)
447
+ └── onTrace (monitoring, debugging)
448
+ ```
449
+
450
+ ### Multi-Extension Composition
451
+
452
+ ```typescript
453
+ import { makeGuardrails } from "./extensions/guardrails";
454
+ import { onTraceLogging } from "./extensions/logging";
455
+ import { makePerformanceMonitor } from "./extensions/performance";
456
+
457
+ export default new Conversation({
458
+ channel: "*",
459
+ handler: async ({ execute, message }) => {
460
+ const guardrail = makeGuardrails(message);
461
+ const perfMonitor = makePerformanceMonitor();
462
+
463
+ await execute({
464
+ instructions: `You are a documentation assistant. Always search before answering.`,
465
+ knowledge: [DocsKB],
466
+ objects: [getAdminModeObject()],
467
+ hooks: {
468
+ onBeforeTool: async (props) => {
469
+ await guardrail.onBeforeToolGuard(props);
470
+ await perfMonitor.onBeforeTool(props);
471
+ },
472
+ onAfterTool: async (props) => {
473
+ await perfMonitor.onAfterTool(props);
474
+ },
475
+ onTrace: (props) => {
476
+ onTraceLogging!(props);
477
+ perfMonitor.onTrace(props);
478
+ },
479
+ },
480
+ });
481
+ },
482
+ });
483
+ ```
484
+
485
+ ### Best Practices
486
+
487
+ 1. **Modular Extensions**: Separate files per concern (`guardrails.ts`, `logging.ts`, `admin-mode.ts`)
488
+ 2. **Factory Functions**: Export factory functions for flexibility
489
+ 3. **Hook Composition**: Combine multiple hooks in `execute()`
490
+ 4. **Type-Safe Hooks**: Always type hooks via `Autonomous.Hooks["hookName"]`
491
+
492
+ ```
493
+ src/conversations/
494
+ ├── webchat.ts # Main conversation handler
495
+ └── extensions/
496
+ ├── admin-mode.ts # Admin authentication
497
+ ├── guardrails.ts # Behavioral constraints
498
+ ├── logging.ts # Observability
499
+ └── performance.ts # Performance monitoring
500
+ ```
501
+
502
+ ---
503
+
504
+ ## Pattern 5: Context Management
505
+
506
+ Properly manage async context in tool handlers and workflows.
507
+
508
+ ### Problem
509
+
510
+ Tool handlers run in async contexts where the ADK runtime context (`user`, `conversation`, `client`) is not automatically available.
511
+
512
+ ### Solution
513
+
514
+ Use `context.enterWith()` to restore context in async handlers:
515
+
516
+ ```typescript
517
+ import { context, Autonomous } from "@botpress/runtime";
518
+
519
+ const myTool = () => {
520
+ const ctx = context.getAll(); // Capture context at tool creation
521
+
522
+ return new Autonomous.Tool({
523
+ name: "myTool",
524
+ handler: async () => {
525
+ context.enterWith(ctx); // Restore context in async handler
526
+ const userId = user.id;
527
+ await client.createMessage({ ... });
528
+ },
529
+ });
530
+ };
531
+ ```
532
+
533
+ ### When to Use
534
+
535
+ - Tool handlers that access `user`, `conversation`, `client`
536
+ - Async operations within tools
537
+ - Long-running operations
538
+ - Workflows that spawn async tasks
539
+
540
+ > **Note:** Context reassignment should always use `context.enterWith()`. Do not use `context.set("key", value)` to reassign the full context — `enterWith()` atomically restores the entire context snapshot.
541
+
542
+ ### Common Pitfalls
543
+
544
+ ```typescript
545
+ // ❌ BAD - Context not available
546
+ handler: async () => {
547
+ await client.createMessage({ ... }); // Error: client not available
548
+ };
549
+
550
+ // ✅ GOOD - Restore context first
551
+ const ctx = context.getAll();
552
+ handler: async () => {
553
+ context.enterWith(ctx);
554
+ await client.createMessage({ ... });
555
+ };
556
+ ```
557
+
558
+ ---
559
+
560
+ ## Pattern 6: Shared Schema
561
+
562
+ Centralize Zod schemas to ensure consistency across Actions, Tables, Workflows, and validation logic.
563
+
564
+ ### Central Schema Definition
565
+
566
+ ```typescript
567
+ // src/schemas.ts
568
+ import { z } from "@botpress/runtime";
569
+
570
+ export type User = z.infer<typeof UserSchema>;
571
+ export const UserSchema = z.object({
572
+ id: z.string(),
573
+ email: z.string().email(),
574
+ name: z.string().optional(),
575
+ role: z.enum(["admin", "user", "guest"]).default("user"),
576
+ createdAt: z.string().describe("ISO 8601 timestamp"),
577
+ });
578
+ ```
579
+
580
+ ### Reuse Across Components
581
+
582
+ **In Actions:**
583
+
584
+ ```typescript
585
+ import { Action } from "@botpress/runtime";
586
+ import { UserSchema } from "../schemas";
587
+
588
+ export const GetUser = new Action({
589
+ name: "getUser",
590
+ input: z.object({ userId: z.string() }),
591
+ output: UserSchema,
592
+ handler: async ({ input }) => {
593
+ return await fetchUserFromAPI(input.userId);
594
+ },
595
+ });
596
+ ```
597
+
598
+ **In Tables:**
599
+
600
+ ```typescript
601
+ import { Table } from "@botpress/runtime";
602
+ import { UserSchema } from "../schemas";
603
+
604
+ export const UsersTable = new Table({
605
+ name: "UsersTable",
606
+ description: "Store user information",
607
+ columns: {
608
+ email: { schema: UserSchema.shape.email, searchable: true },
609
+ name: { schema: UserSchema.shape.name, searchable: true },
610
+ },
611
+ });
612
+ ```
613
+
614
+ **In Workflows:**
615
+
616
+ ```typescript
617
+ import { Workflow } from "@botpress/runtime";
618
+ import { UserSchema, ConversationSchema } from "../schemas";
619
+
620
+ export const ProcessUser = new Workflow({
621
+ name: "processUser",
622
+ input: z.object({ user: UserSchema, conversationId: z.string() }),
623
+ state: z.object({ conversation: ConversationSchema.optional() }),
624
+ handler: async ({ input, state, step }) => {
625
+ console.log(`Processing user: ${input.user.email}`);
626
+ return { userId: input.user.id };
627
+ },
628
+ });
629
+ ```
630
+
631
+ ### Schema Composition
632
+
633
+ ```typescript
634
+ // Base schema
635
+ export const BaseEntitySchema = z.object({
636
+ id: z.string(),
637
+ createdAt: z.string(),
638
+ updatedAt: z.string(),
639
+ });
640
+
641
+ // Extend
642
+ export const ConversationSchema = BaseEntitySchema.extend({
643
+ userId: z.string(),
644
+ status: z.enum(["open", "closed", "snoozed"]),
645
+ });
646
+
647
+ // Pick / Omit
648
+ export const UserSummarySchema = UserSchema.pick({ id: true, email: true, name: true });
649
+ export const PublicUserSchema = UserSchema.omit({ metadata: true });
650
+ ```
651
+
652
+ ### Best Practices
653
+
654
+ - Organize schemas by domain (`src/schemas/users.ts`, `src/schemas/conversations.ts`)
655
+ - Export both types and schemas (`export type User = z.infer<typeof UserSchema>`)
656
+ - Add `.describe()` annotations for documentation
657
+ - Use `.extend()`, `.pick()`, `.omit()` to build variations without duplication
658
+
659
+ ---
660
+
661
+ ## See Also
662
+
663
+ - [./actions.md](./actions.md) — Actions reference
664
+ - [./conversations.md](./conversations.md) — Conversation handlers
665
+ - [./workflows.md](./workflows.md) — Workflow patterns
666
+ - [./tools.md](./tools.md) — Tools reference
667
+ - [./context-api.md](./context-api.md) — Context API details
668
+ - [./agent-config.md](./agent-config.md) — Configuration reference
669
+ - [./zai-complete-guide.md](./zai-complete-guide.md) — Zai validation guide