@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.
Files changed (65) hide show
  1. package/dist/{README-wjb7z7f0.md → README-824wa4jj.md} +1 -1
  2. package/dist/SKILL-75ym6tcj.md +257 -0
  3. package/dist/SKILL-chnwzmt1.md +548 -0
  4. package/dist/SKILL-d4bs53y3.md +244 -0
  5. package/dist/SKILL-h4b2f1n9.md +114 -0
  6. package/dist/SKILL-hbw5nt95.md +111 -0
  7. package/dist/actions-h7hxxvxr.md +823 -0
  8. package/dist/adk-build-emhwd0tg.md +39 -0
  9. package/dist/adk-debug-yrrdtpmb.md +23 -0
  10. package/dist/adk-dev-console-7z9rtjpy.md +17 -0
  11. package/dist/adk-document-ras6zzzh.md +68 -0
  12. package/dist/adk-eval-vx76dwq0.md +17 -0
  13. package/dist/adk-explain-fcsaw852.md +22 -0
  14. package/dist/adk-init-9kmxmbbe.md +30 -0
  15. package/dist/adk-ship-dybzn884.md +29 -0
  16. package/dist/adk-test-xq67savs.md +27 -0
  17. package/dist/adk-validate-1p4skv4m.md +31 -0
  18. package/dist/advanced-patterns-z86m9h1a.md +669 -0
  19. package/dist/agent-config-1aqvkrgf.md +450 -0
  20. package/dist/agent-steps-zj4c1n9t.md +115 -0
  21. package/dist/assets-q7jt8r2q.md +237 -0
  22. package/dist/autonomous-execution-fymc12e5.md +747 -0
  23. package/dist/cli-c31e06yr.md +1334 -0
  24. package/dist/cli.js +970 -566
  25. package/dist/commands.json +24 -3
  26. package/dist/common-failures-ht29w2db.md +231 -0
  27. package/dist/component-registry-bbpw0see.md +43 -0
  28. package/dist/context-api-760cvh0h.md +461 -0
  29. package/dist/conversation-analysis-8ka4nrm4.md +420 -0
  30. package/dist/conversation-lifecycle-4seqqjc1.md +311 -0
  31. package/dist/conversations-gwfvxaee.md +1111 -0
  32. package/dist/custom-components-y5wdgpey.md +167 -0
  33. package/dist/debug-workflow-vz84q80v.md +239 -0
  34. package/dist/desk-k54ktvsd.md +852 -0
  35. package/dist/doc-standards-ck6ax2jd.md +124 -0
  36. package/dist/eval-format-7n9brja4.md +354 -0
  37. package/dist/evals-y1zcr1mc.md +11 -0
  38. package/dist/explain-config-1yaq3zzd.md +424 -0
  39. package/dist/files-dzt0wxn3.md +918 -0
  40. package/dist/integration-actions-xang7tma.md +659 -0
  41. package/dist/integrations-k5ss07f4.md +266 -0
  42. package/dist/interfaces-6t0bpxs4.md +97 -0
  43. package/dist/knowledge-bases-20rg8p5c.md +456 -0
  44. package/dist/llm-debugging-76ng8rzm.md +209 -0
  45. package/dist/mcp-server-n1asyzhv.md +112 -0
  46. package/dist/messages-gkkg0b54.md +769 -0
  47. package/dist/model-configuration-f273kgd0.md +201 -0
  48. package/dist/multi-agent-dashboard-1mdfjdts.md +70 -0
  49. package/dist/pages-xn18xn73.md +273 -0
  50. package/dist/patterns-mistakes-5vb99dhf.md +469 -0
  51. package/dist/plugins-ttnx8f49.md +280 -0
  52. package/dist/tables-x45t36dd.md +1136 -0
  53. package/dist/tags-46td3qrg.md +516 -0
  54. package/dist/test-patterns-hj47w4nc.md +355 -0
  55. package/dist/testing-workflow-2vkp932f.md +225 -0
  56. package/dist/tools-wk7bq24z.md +581 -0
  57. package/dist/trace-summarization-hdyx1c2e.md +313 -0
  58. package/dist/traces-and-logs-f2kjfh19.md +203 -0
  59. package/dist/triggers-4j2cc86x.md +719 -0
  60. package/dist/ui-server-entry.js +952 -137
  61. package/dist/workflow-steps-mrdrf1c5.md +851 -0
  62. package/dist/workflows-gadx3s69.md +1408 -0
  63. package/dist/zai-agent-reference-e6644aad.md +454 -0
  64. package/dist/zai-complete-guide-mnna5d6f.md +1408 -0
  65. package/package.json +4 -4
@@ -0,0 +1,918 @@
1
+ # Files API
2
+
3
+ The Files API provides structured file storage with automatic indexing, semantic search capabilities, and CDN-backed content delivery.
4
+
5
+ ## Accessing the Files API
6
+
7
+ All file operations use the Botpress client. Get the client using the Context API:
8
+
9
+ ```typescript
10
+ import { context } from "@botpress/runtime";
11
+
12
+ const client = context.get("client");
13
+ ```
14
+
15
+ This pattern is used in all examples below. See **[Context API](./context-api.md)** for more details.
16
+
17
+ ## Basic Concepts
18
+
19
+ ### What is the Files API?
20
+ - **Persistent storage**: Upload and manage files for your bot
21
+ - **Semantic search**: Optional vector indexing for searchable content
22
+ - **CDN delivery**: Fast file access through CloudFront
23
+ - **Access control**: Fine-grained permissions and policies
24
+ - **Metadata & tags**: Organize files with custom attributes
25
+
26
+ ### File Properties
27
+
28
+ Files have the following core properties:
29
+ - **id**: Unique file identifier
30
+ - **key**: User-defined unique key per bot
31
+ - **url**: CDN URL for file content access
32
+ - **size**: File size in bytes
33
+ - **contentType**: MIME type (e.g., "application/pdf")
34
+ - **tags**: Key-value pairs for organization
35
+ - **metadata**: Custom JSON metadata
36
+ - **accessPolicies**: Array of access control policies
37
+ - **status**: Upload/indexing status
38
+ - **createdAt/updatedAt**: Timestamps
39
+
40
+ ## Creating and Uploading Files
41
+
42
+ ### Basic File Upload
43
+
44
+ ```typescript
45
+ const { file } = await client.uploadFile({
46
+ key: "documents/report-2024.pdf", // Unique key
47
+ content: fileBuffer, // File content (Buffer/ArrayBuffer/Blob/string)
48
+ contentType: "application/pdf", // MIME type
49
+ tags: {
50
+ category: "reports",
51
+ year: "2024"
52
+ },
53
+ index: true, // Enable semantic search
54
+ accessPolicies: ["public_content"] // Make publicly accessible
55
+ });
56
+
57
+ console.log("File ID:", file.id);
58
+ console.log("File URL:", file.url);
59
+ ```
60
+
61
+ ### Upload Options
62
+
63
+ ```typescript
64
+ await client.uploadFile({
65
+ key: "unique-file-key", // Required: Unique identifier
66
+ content: fileBuffer, // Required: File content
67
+ contentType: "text/plain", // Optional: MIME type
68
+
69
+ // Indexing options
70
+ index: true, // Enable semantic search
71
+ indexing: {
72
+ configuration: {
73
+ vision: {
74
+ transcribePages: true, // OCR for images/PDFs
75
+ indexPages: "all" // Index all pages
76
+ }
77
+ }
78
+ },
79
+
80
+ // Access control
81
+ accessPolicies: [
82
+ "public_content", // Public URL access
83
+ "integrations" // Integration access
84
+ ],
85
+
86
+ // Organization
87
+ tags: {
88
+ category: "documents",
89
+ department: "sales"
90
+ },
91
+
92
+ // Custom metadata
93
+ metadata: {
94
+ author: "John Doe",
95
+ version: "1.0"
96
+ },
97
+
98
+ // Expiry (max 90 days)
99
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
100
+
101
+ // Immediate public access (requires public_content policy)
102
+ publicContentImmediatelyAccessible: true
103
+ });
104
+ ```
105
+
106
+ ### Uploading Text Content
107
+
108
+ For simple text/JSON content:
109
+
110
+ ```typescript
111
+ const { file } = await client.uploadFile({
112
+ key: "config/settings.json",
113
+ content: jsonContent, // String content
114
+ contentType: "application/json",
115
+ index: false
116
+ });
117
+ ```
118
+
119
+ ### Uploading from URL
120
+
121
+ To upload from an external URL:
122
+
123
+ ```typescript
124
+ const { file } = await client.uploadFile({
125
+ key: "imported/image.jpg",
126
+ url: "https://example.com/image.jpg", // Fetch from URL
127
+ contentType: "image/jpeg",
128
+ index: false
129
+ });
130
+ ```
131
+
132
+ ## Retrieving Files
133
+
134
+ ### Get File Metadata
135
+
136
+ ```typescript
137
+ // Get by ID
138
+ const { file } = await client.getFile({
139
+ id: "file_abc123"
140
+ });
141
+
142
+ // Get by key
143
+ const { file } = await client.getFile({
144
+ id: "documents/report-2024.pdf" // Can use key as ID
145
+ });
146
+
147
+ console.log("File URL:", file.url);
148
+ console.log("File size:", file.size);
149
+ console.log("Status:", file.status);
150
+ ```
151
+
152
+ ### Access File Content
153
+
154
+ Files are accessed via URL. The URL type depends on access policies:
155
+
156
+ ```typescript
157
+ const { file } = await client.getFile({ id: fileId });
158
+
159
+ // For public files: permanent CDN URL
160
+ // For private files: temporary presigned S3 URL
161
+
162
+ const response = await fetch(file.url);
163
+ const content = await response.text();
164
+
165
+ // For JSON files
166
+ const data = await response.json();
167
+
168
+ // For binary files
169
+ const buffer = await response.arrayBuffer();
170
+ ```
171
+
172
+ ### File Status States
173
+
174
+ ```typescript
175
+ type FileStatus =
176
+ | "upload_pending" // Awaiting content upload
177
+ | "upload_completed" // Upload successful
178
+ | "upload_failed" // Upload failed
179
+ | "indexing_pending" // Queued for indexing
180
+ | "indexing_completed" // Indexed and searchable
181
+ | "indexing_failed" // Indexing failed
182
+
183
+ // Check file status
184
+ const { file } = await client.getFile({ id: fileId });
185
+ if (file.status === "indexing_completed") {
186
+ console.log("File is indexed and searchable");
187
+ }
188
+ ```
189
+
190
+ ## Listing Files
191
+
192
+ ### Basic Listing
193
+
194
+ ```typescript
195
+ const { files, meta } = await client.listFiles({
196
+ limit: 20,
197
+ offset: 0
198
+ });
199
+
200
+ files.forEach(file => {
201
+ console.log(file.key, file.size, file.status);
202
+ });
203
+
204
+ // Pagination
205
+ if (meta.nextToken) {
206
+ const nextPage = await client.listFiles({
207
+ nextToken: meta.nextToken
208
+ });
209
+ }
210
+ ```
211
+
212
+ ### Filtering by Tags
213
+
214
+ ```typescript
215
+ // Single tag filter
216
+ const { files } = await client.listFiles({
217
+ tags: { category: "reports" }
218
+ });
219
+
220
+ // Multiple values (OR condition)
221
+ const { files } = await client.listFiles({
222
+ tags: {
223
+ category: ["reports", "documents"]
224
+ }
225
+ });
226
+
227
+ // Exclude values
228
+ const { files } = await client.listFiles({
229
+ tags: {
230
+ category: {
231
+ not: "archived"
232
+ }
233
+ }
234
+ });
235
+ ```
236
+
237
+ ### Filtering by IDs
238
+
239
+ ```typescript
240
+ const { files } = await client.listFiles({
241
+ ids: ["file_123", "file_456", "file_789"]
242
+ });
243
+ ```
244
+
245
+ ### Sorting
246
+
247
+ ```typescript
248
+ const { files } = await client.listFiles({
249
+ sortBy: "createdAt",
250
+ sortOrder: "desc",
251
+ limit: 10
252
+ });
253
+
254
+ // Available sort fields:
255
+ // - key
256
+ // - size
257
+ // - createdAt
258
+ // - updatedAt
259
+ // - status
260
+ ```
261
+
262
+ ## Semantic Search
263
+
264
+ ### Searching File Content
265
+
266
+ ```typescript
267
+ const { passages } = await client.searchFiles({
268
+ query: "machine learning algorithms",
269
+ limit: 5
270
+ });
271
+
272
+ passages.forEach(passage => {
273
+ console.log("File:", passage.file.key);
274
+ console.log("Content:", passage.content);
275
+ console.log("Similarity:", passage.score);
276
+ console.log("Page:", passage.meta.pageNumber);
277
+ });
278
+ ```
279
+
280
+ ### Search Options
281
+
282
+ ```typescript
283
+ const { passages } = await client.searchFiles({
284
+ query: "customer feedback",
285
+
286
+ // Filtering
287
+ tags: { category: "support" },
288
+
289
+ // Context
290
+ contextDepth: 2, // Include 2 surrounding passages
291
+ includeBreadcrumb: true, // Include document structure
292
+ withContext: true, // Return detailed context
293
+
294
+ // Consolidation
295
+ consolidate: true, // Group by file, sort by position
296
+
297
+ // Pagination
298
+ limit: 10
299
+ });
300
+ ```
301
+
302
+ ### Search Result Structure
303
+
304
+ ```typescript
305
+ {
306
+ passages: [
307
+ {
308
+ content: "The matched text passage...",
309
+ score: 0.89, // Similarity score (0-1)
310
+ meta: {
311
+ type: "paragraph", // passage | title | subtitle | etc.
312
+ subtype: "text",
313
+ pageNumber: 5,
314
+ position: 12,
315
+ sourceUrl: "https://..."
316
+ },
317
+ file: {
318
+ id: "file_abc123",
319
+ key: "documents/report.pdf",
320
+ contentType: "application/pdf",
321
+ tags: { category: "reports" },
322
+ createdAt: "2024-01-15T10:30:00Z",
323
+ updatedAt: "2024-01-15T10:30:00Z"
324
+ },
325
+ context: [ // Only if withContext: true
326
+ {
327
+ type: "preceding",
328
+ content: "Previous passage..."
329
+ },
330
+ {
331
+ type: "current",
332
+ content: "The matched text passage..."
333
+ },
334
+ {
335
+ type: "subsequent",
336
+ content: "Next passage..."
337
+ }
338
+ ]
339
+ }
340
+ ]
341
+ }
342
+ ```
343
+
344
+ ## Updating Files
345
+
346
+ ### Update File Metadata
347
+
348
+ ```typescript
349
+ // Update without changing content
350
+ const { file } = await client.updateFileMetadata({
351
+ id: fileId,
352
+
353
+ metadata: {
354
+ version: "2.0",
355
+ updatedBy: "Jane Doe"
356
+ },
357
+
358
+ tags: {
359
+ status: "reviewed",
360
+ newTag: "value"
361
+ },
362
+
363
+ accessPolicies: ["public_content"],
364
+
365
+ expiresAt: new Date("2024-12-31").toISOString()
366
+ });
367
+ ```
368
+
369
+ ### Metadata Update Behavior
370
+
371
+ ```typescript
372
+ // Partial updates - existing keys preserved
373
+ await client.updateFileMetadata({
374
+ id: fileId,
375
+ metadata: {
376
+ newKey: "newValue"
377
+ // Other metadata keys remain unchanged
378
+ }
379
+ });
380
+
381
+ // Delete metadata key
382
+ await client.updateFileMetadata({
383
+ id: fileId,
384
+ metadata: {
385
+ oldKey: null // Removes this key
386
+ }
387
+ });
388
+
389
+ // Delete tag
390
+ await client.updateFileMetadata({
391
+ id: fileId,
392
+ tags: {
393
+ oldTag: null // Removes this tag
394
+ }
395
+ });
396
+ ```
397
+
398
+ ### Replace File Content
399
+
400
+ ```typescript
401
+ // Upload new content with same key (replaces old content)
402
+ const { file } = await client.uploadFile({
403
+ key: existingFileKey,
404
+ content: newContent,
405
+ contentType: "text/plain"
406
+ });
407
+ ```
408
+
409
+ ## Managing File Passages
410
+
411
+ ### List File Passages
412
+
413
+ ```typescript
414
+ const { passages, meta } = await client.listFilePassages({
415
+ id: fileId,
416
+ limit: 20
417
+ });
418
+
419
+ passages.forEach(passage => {
420
+ console.log("Passage ID:", passage.id);
421
+ console.log("Content:", passage.content);
422
+ console.log("Type:", passage.meta.type);
423
+ console.log("Page:", passage.meta.pageNumber);
424
+ });
425
+ ```
426
+
427
+ ### Set Custom Passages
428
+
429
+ ```typescript
430
+ // Replace all passages with custom content
431
+ await client.setFilePassages({
432
+ id: fileId,
433
+ passages: [
434
+ {
435
+ content: "# Introduction\n\nThis is the first section...",
436
+ type: "title"
437
+ },
438
+ {
439
+ content: "Main content paragraph here.",
440
+ type: "paragraph",
441
+ pageNumber: 1
442
+ },
443
+ {
444
+ content: "## Subsection\n\nMore details...",
445
+ type: "subtitle"
446
+ }
447
+ ]
448
+ });
449
+
450
+ // File status becomes "indexing_pending"
451
+ // Check status later to confirm completion
452
+ ```
453
+
454
+ ## Deleting Files
455
+
456
+ ### Delete Single File
457
+
458
+ ```typescript
459
+ // Delete by ID
460
+ await client.deleteFile({ id: "file_abc123" });
461
+
462
+ // Delete by key
463
+ await client.deleteFile({ id: "documents/report.pdf" });
464
+ ```
465
+
466
+ ### Cleanup
467
+
468
+ When a file is deleted:
469
+ - File metadata removed from database
470
+ - File content removed from S3
471
+ - Vector embeddings removed (if indexed)
472
+ - All passages deleted
473
+
474
+ ## Knowledge Bases
475
+
476
+ For high-level RAG implementation with automatic indexing and search integration, see **[Knowledge Bases](./knowledge-bases.md)**.
477
+
478
+ The Files API provides low-level file management. Knowledge Bases use the Files API internally but provide a simpler interface for document indexing and semantic search in AI responses.
479
+
480
+ ## Copying Files
481
+
482
+ ### Copy Within Bot
483
+
484
+ ```typescript
485
+ const { file } = await client.copyFile({
486
+ idOrKey: sourceFileId,
487
+ destinationKey: "backups/report-copy.pdf",
488
+ overwrite: false // Fail if destination exists
489
+ });
490
+ ```
491
+
492
+ ### Copy Across Bots
493
+
494
+ ```typescript
495
+ const { file } = await client.copyFile({
496
+ idOrKey: sourceFileId,
497
+ destinationKey: "imported/report.pdf",
498
+ destinationBotId: targetBotId,
499
+ overwrite: true
500
+ });
501
+ ```
502
+
503
+ ## Supported File Types
504
+
505
+ ### Indexable Formats
506
+
507
+ These file types support semantic search indexing:
508
+
509
+ ```typescript
510
+ const INDEXABLE_TYPES = {
511
+ // Documents
512
+ pdf: "application/pdf",
513
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
514
+
515
+ // Text
516
+ txt: "text/plain",
517
+ md: "text/markdown",
518
+ html: "text/html",
519
+
520
+ // Images (with vision processing)
521
+ jpg: "image/jpeg",
522
+ jpeg: "image/jpeg",
523
+ png: "image/png"
524
+ };
525
+ ```
526
+
527
+ ### Vision Processing
528
+
529
+ For images and PDFs with vision enabled:
530
+
531
+ ```typescript
532
+ await client.uploadFile({
533
+ key: "documents/invoice.pdf",
534
+ content: pdfBuffer,
535
+ contentType: "application/pdf",
536
+ index: true,
537
+ indexing: {
538
+ configuration: {
539
+ vision: {
540
+ transcribePages: true, // OCR text extraction
541
+ indexPages: "all" // or [1, 2, 3] for specific pages
542
+ }
543
+ }
544
+ }
545
+ });
546
+ ```
547
+
548
+ ## Usage in Actions/Workflows
549
+
550
+ ### In Actions
551
+
552
+ ```typescript
553
+ import { Action, z, context } from "@botpress/runtime";
554
+
555
+ export const uploadDocument = new Action({
556
+ name: "uploadDocument",
557
+ input: z.object({
558
+ content: z.string(),
559
+ filename: z.string()
560
+ }),
561
+ output: z.object({
562
+ fileId: z.string(),
563
+ fileUrl: z.string()
564
+ }),
565
+
566
+ async handler({ input }) {
567
+ const client = context.get("client");
568
+
569
+ // Upload file
570
+ const { file } = await client.uploadFile({
571
+ key: `uploads/${input.filename}`,
572
+ content: input.content,
573
+ contentType: "text/plain",
574
+ index: true,
575
+ tags: {
576
+ source: "action",
577
+ type: "user-upload"
578
+ }
579
+ });
580
+
581
+ return {
582
+ fileId: file.id,
583
+ fileUrl: file.url
584
+ };
585
+ }
586
+ });
587
+ ```
588
+
589
+ ### In Workflows
590
+
591
+ ```typescript
592
+ import { Workflow, z, context } from "@botpress/runtime";
593
+
594
+ export const ProcessDocumentWorkflow = new Workflow({
595
+ name: "processDocument",
596
+ input: z.object({ documentKey: z.string() }),
597
+
598
+ async handler({ input, step }) {
599
+ const client = context.get("client");
600
+
601
+ // Get file
602
+ const file = await step("get-file", async () => {
603
+ const { file } = await client.getFile({
604
+ id: input.documentKey
605
+ });
606
+ return file;
607
+ });
608
+
609
+ // Wait for indexing
610
+ await step("wait-indexing", async () => {
611
+ let status = file.status;
612
+ while (status === "INDEXING_PENDING") {
613
+ await new Promise(resolve => setTimeout(resolve, 1000));
614
+ const { file: updated } = await client.getFile({
615
+ id: file.id
616
+ });
617
+ status = updated.status;
618
+ }
619
+ return status;
620
+ });
621
+
622
+ // Search content
623
+ const results = await step("search", async () => {
624
+ const { passages } = await client.searchFiles({
625
+ query: "important findings",
626
+ tags: { key: input.documentKey }
627
+ });
628
+ return passages;
629
+ });
630
+
631
+ return {
632
+ processed: true,
633
+ findings: results.length
634
+ };
635
+ }
636
+ });
637
+ ```
638
+
639
+ ## Best Practices
640
+
641
+ ### 1. Use Meaningful Keys
642
+
643
+ ```typescript
644
+ // ✅ Good - Clear, hierarchical structure
645
+ await client.uploadFile({
646
+ key: "documents/2024/reports/q1-sales.pdf"
647
+ });
648
+
649
+ // ❌ Bad - Unclear, no structure
650
+ await client.uploadFile({
651
+ key: "file123.pdf"
652
+ });
653
+ ```
654
+
655
+ ### 2. Tag Appropriately
656
+
657
+ ```typescript
658
+ // ✅ Good - Useful, queryable tags
659
+ tags: {
660
+ category: "reports",
661
+ year: "2024",
662
+ quarter: "Q1",
663
+ department: "sales"
664
+ }
665
+
666
+ // ❌ Bad - Too specific or redundant
667
+ tags: {
668
+ filename: "q1-sales.pdf", // Already in key
669
+ timestamp: "1234567890" // Use createdAt instead
670
+ }
671
+ ```
672
+
673
+ ### 3. Handle File Status
674
+
675
+ ```typescript
676
+ // ✅ Good - Check and handle status
677
+ const { file } = await client.getFile({ id: fileId });
678
+
679
+ switch (file.status) {
680
+ case "UPLOAD_PENDING":
681
+ console.log("Waiting for upload...");
682
+ break;
683
+ case "INDEXING_PENDING":
684
+ console.log("Indexing in progress...");
685
+ break;
686
+ case "INDEXING_COMPLETED":
687
+ // File is searchable
688
+ break;
689
+ case "INDEXING_FAILED":
690
+ console.error("Indexing failed:", file.failedStatusReason);
691
+ break;
692
+ }
693
+ ```
694
+
695
+ ### 4. Set Appropriate Expiry
696
+
697
+ ```typescript
698
+ // ✅ Good - Reasonable expiry for temporary files
699
+ await client.uploadFile({
700
+ key: "temp/cache-data.json",
701
+ expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 1 day
702
+ });
703
+
704
+ // For permanent files, omit expiresAt
705
+ await client.uploadFile({
706
+ key: "documents/permanent-record.pdf"
707
+ // No expiresAt = never expires
708
+ });
709
+ ```
710
+
711
+ ### 5. Optimize Indexing
712
+
713
+ ```typescript
714
+ // ✅ Good - Index only searchable content
715
+ await client.uploadFile({
716
+ key: "documents/report.pdf",
717
+ content: pdfBuffer,
718
+ index: true // Searchable document
719
+ });
720
+
721
+ await client.uploadFile({
722
+ key: "images/logo.png",
723
+ content: imageBuffer,
724
+ index: false // Not searchable, save quota
725
+ });
726
+
727
+ // ✅ Good - Use vision only when needed
728
+ await client.uploadFile({
729
+ key: "documents/scanned-invoice.pdf",
730
+ content: pdfBuffer,
731
+ index: true,
732
+ indexing: {
733
+ configuration: {
734
+ vision: {
735
+ transcribePages: true // OCR needed
736
+ }
737
+ }
738
+ }
739
+ });
740
+ ```
741
+
742
+ ## Common Patterns
743
+
744
+ ### File Upload with Validation
745
+
746
+ ```typescript
747
+ export const uploadWithValidation = async (
748
+ fileContent: Buffer,
749
+ filename: string
750
+ ) => {
751
+ const client = context.get("client");
752
+
753
+ // Validate file size (max 100MB)
754
+ const MAX_SIZE = 100 * 1024 * 1024;
755
+ if (fileContent.length > MAX_SIZE) {
756
+ throw new Error("File too large");
757
+ }
758
+
759
+ // Validate file type
760
+ const contentType = getContentType(filename);
761
+ if (!ALLOWED_TYPES.includes(contentType)) {
762
+ throw new Error("File type not allowed");
763
+ }
764
+
765
+ // Upload
766
+ const { file } = await client.uploadFile({
767
+ key: `uploads/${Date.now()}-${filename}`,
768
+ content: fileContent,
769
+ contentType,
770
+ index: true,
771
+ tags: {
772
+ originalName: filename,
773
+ uploadedAt: new Date().toISOString()
774
+ }
775
+ });
776
+
777
+ return file;
778
+ };
779
+ ```
780
+
781
+ ### Search with Context
782
+
783
+ ```typescript
784
+ export const searchWithContext = async (
785
+ query: string,
786
+ category?: string
787
+ ) => {
788
+ const client = context.get("client");
789
+
790
+ const { passages } = await client.searchFiles({
791
+ query,
792
+ tags: category ? { category } : undefined,
793
+ contextDepth: 2,
794
+ includeBreadcrumb: true,
795
+ limit: 5
796
+ });
797
+
798
+ return passages.map(p => ({
799
+ content: p.content,
800
+ source: p.file.key,
801
+ relevance: p.score,
802
+ location: `Page ${p.meta.pageNumber || "N/A"}`
803
+ }));
804
+ };
805
+ ```
806
+
807
+ ### Bulk File Operations
808
+
809
+ ```typescript
810
+ export const bulkUpload = async (files: FileData[]) => {
811
+ const client = context.get("client");
812
+
813
+ const results = await Promise.allSettled(
814
+ files.map(async (fileData) => {
815
+ const { file } = await client.uploadFile({
816
+ key: fileData.key,
817
+ content: fileData.content,
818
+ contentType: fileData.contentType,
819
+ index: true
820
+ });
821
+
822
+ return file;
823
+ })
824
+ );
825
+
826
+ const successful = results
827
+ .filter(r => r.status === "fulfilled")
828
+ .map(r => r.value);
829
+
830
+ const failed = results
831
+ .filter(r => r.status === "rejected")
832
+ .map(r => r.reason);
833
+
834
+ return { successful, failed };
835
+ };
836
+ ```
837
+
838
+ ## Troubleshooting
839
+
840
+ ### Common Issues
841
+
842
+ 1. **Upload fails with 403 error**
843
+ - Ensure you have proper access policies set
844
+ - Check that the bot token has file upload permissions
845
+ - Verify the file size is within limits
846
+
847
+ 2. **File not searchable**
848
+ - Check `file.status` is `INDEXING_COMPLETED`
849
+ - Verify `index: true` was set during upload
850
+ - Ensure file type is in supported indexable formats
851
+ - Check billing plan supports indexing features
852
+
853
+ 3. **Search returns no results**
854
+ - Verify files are indexed (`status: "INDEXING_COMPLETED"`)
855
+ - Check tag filters aren't too restrictive
856
+ - Try broader search queries
857
+ - Ensure files have searchable content
858
+
859
+ 4. **File access denied**
860
+ - Verify access policies are correctly set
861
+ - Check workspace member permissions
862
+ - For integration files, ensure proper namespace
863
+
864
+ 5. **Quota exceeded**
865
+ - Check storage usage in workspace settings
866
+ - Delete unused files or increase quota
867
+ - Consider file expiry policies for temporary files
868
+
869
+ ## Limits and Quotas
870
+
871
+ - **Maximum file size**: 100 MB
872
+ - **Maximum expiry**: 90 days from upload
873
+ - **Maximum tags per file**: 50
874
+ - **Maximum indexable file size**: varies by file type
875
+ - **Search result limit**: Configurable, recommended max 100
876
+
877
+ ## Migration Notes
878
+
879
+ ### Current API (ADK 1.13+)
880
+
881
+ The current Files API uses direct content upload:
882
+
883
+ ```typescript
884
+ // ✅ CURRENT - Direct content parameter
885
+ await client.uploadFile({
886
+ key: "unique-key",
887
+ content: fileBuffer, // Direct upload
888
+ contentType: "application/pdf",
889
+ tags: { category: "docs" }
890
+ });
891
+
892
+ // Alternative: Upload from URL
893
+ await client.uploadFile({
894
+ key: "unique-key",
895
+ url: "https://example.com/file.pdf", // Fetch from URL
896
+ tags: { category: "docs" }
897
+ });
898
+ ```
899
+
900
+ ### Retrieving File Content
901
+
902
+ Files are accessed via their URL:
903
+
904
+ ```typescript
905
+ const { file } = await client.getFile({ id: fileId });
906
+
907
+ // Fetch file content
908
+ const response = await fetch(file.url);
909
+ const content = await response.arrayBuffer();
910
+
911
+ // For text/JSON
912
+ const text = await response.text();
913
+ const json = await response.json();
914
+ ```
915
+
916
+ **URL Types:**
917
+ - **Public files** (`public_content` policy): Permanent CDN URL
918
+ - **Private files**: Temporary presigned URL (use shortly after retrieval)