@ai-matrx/media 0.7.15 → 0.8.0

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.
@@ -0,0 +1,1171 @@
1
+ import { AudioMediaPart, AudioOutputData, VideoMediaPart, VideoOutputData, ImageMediaPart, ImageOutputData, PartialImageData, RenderBlockPayload } from '@ai-matrx/agents/generated/stream-events';
2
+ import * as react from 'react';
3
+
4
+ /**
5
+ * files/ref-types — the Matrx file reference vocabulary shared by every client:
6
+ * visibility, permission level, the wire `MediaRef`, and the identity hint a caller
7
+ * may already know. Moved from matrx-frontend `features/files/types.ts` (P16f).
8
+ */
9
+ type Visibility = "personal" | "internal" | "link" | "public";
10
+ type PermissionLevel = "read" | "write" | "admin";
11
+ interface MediaRef {
12
+ /** cld_files UUID — preferred form for any file we own. */
13
+ file_id?: string;
14
+ /** Any URL we issued (durable download route, share link) OR external https://. */
15
+ url?: string;
16
+ /** Optional client hint; backend overrides with `cld_files.mime_type` for owned files. */
17
+ mime_type?: string;
18
+ /** Free-form per-call metadata. Keep small — this rides on every request. */
19
+ metadata?: Record<string, unknown>;
20
+ }
21
+ /**
22
+ * Metadata a caller may already know when all it has is a durable file id.
23
+ * These values seed the canonical Redux record before field hydration runs;
24
+ * omitted keys remain genuinely unloaded and are fetched on demand.
25
+ */
26
+ interface FileIdentityHint {
27
+ fileName?: string;
28
+ mimeType?: string | null;
29
+ fileSize?: number | null;
30
+ visibility?: Visibility;
31
+ /** Permanent public delivery only. Never place a signed URL here. */
32
+ publicUrl?: string | null;
33
+ /** Permanent public CDN delivery only. Never place a signed URL here. */
34
+ cdnUrl?: string | null;
35
+ }
36
+ /** Reads a stored visibility; anything unknown is the private default. */
37
+ declare function toVisibility(raw: string | null | undefined): Visibility;
38
+ /**
39
+ * Build a MediaRef from just a `file_id` (e.g. when an upload completes
40
+ * and the caller has the id but not the full record yet).
41
+ */
42
+ declare function fileIdToMediaRef(fileId: string, mimeType?: string | null): MediaRef;
43
+ /**
44
+ * Build a MediaRef from an external URL (public website image, signed URL
45
+ * we don't own, etc.). Use this ONLY when you don't have a `file_id` —
46
+ * otherwise the backend has to follow the URL to resolve the file.
47
+ */
48
+ declare function urlToMediaRef(url: string, mimeType?: string | null): MediaRef;
49
+
50
+ /**
51
+ * features/files/blocks/types.ts
52
+ *
53
+ * THE canonical TypeScript shape for every piece of media (image / video /
54
+ * audio / document / youtube) in the app — the FE mirror of Python's
55
+ * `UnifiedMediaBlock` Pydantic union. See:
56
+ * - docs/PYTHON_UPDATES.md — Phase 0/1/2 wire contract & migration plan
57
+ * - features/files/blocks/image/UNIFIED_IMAGE_BLOCK.md — backstory
58
+ * - packages/matrx-connect/matrx_connect/context/media_block.py (backend)
59
+ *
60
+ * Naming convention:
61
+ * - This file uses **camelCase** (TS domain shape).
62
+ * - The wire shape (`MediaBlockData.block` in stream events,
63
+ * `cld_files.metadata.generation` on assets) uses **snake_case**.
64
+ * - The adapter `./adapters/from-media-block.ts` converts the wire shape
65
+ * to this shape at the boundary. Components only ever see camelCase.
66
+ *
67
+ * Two discriminators:
68
+ * - `kind` : "image" | "video" | "audio" | "document" | "youtube"
69
+ * Narrow on this first to access kind-specific fields
70
+ * (e.g. `width`/`height` on image, `durationMs` on audio).
71
+ * - `origin` : "matrx" | "external"
72
+ * Narrow on this second to access ownership-specific fields
73
+ * (`fileId` on matrx, `externalUrl` on external).
74
+ *
75
+ * Invariants (enforced at adapter boundary):
76
+ * 1. `origin === "matrx"` → `fileId` non-null when `status === "complete"`.
77
+ * 2. `origin === "external"` → `externalUrl` non-null OR `base64` non-null.
78
+ * 3. `status === "streaming"` → `base64` non-null (in-flight bytes).
79
+ * 4. `status === "error"` → `errorMessage` non-null.
80
+ */
81
+ type MediaKind = "image" | "video" | "audio" | "document" | "youtube";
82
+ type MediaOrigin = "matrx" | "external";
83
+ type MediaStatus = "complete" | "streaming" | "error";
84
+ /**
85
+ * THE canonical `platform.visibility` enum — identical to
86
+ * `features/files/types.ts#Visibility`, the DB, and the server. This used to
87
+ * be its own dialect (`public | personal | shared`), which is how `internal`
88
+ * and `link` rows got mis-bucketed on the way to URL resolution.
89
+ */
90
+ type MediaVisibility = "personal" | "internal" | "link" | "public";
91
+ interface MediaBlockBase {
92
+ /**
93
+ * "complete" — final media; all configured URLs are valid as of emission
94
+ * "streaming" — generating; `base64` (partial preview) is the only render source
95
+ * "error" — generation failed; `errorMessage` carries the reason
96
+ */
97
+ status: MediaStatus;
98
+ /** 0–1 progress. Meaningful only when status === "streaming". */
99
+ progress: number | null;
100
+ /** Populated when status === "error". null otherwise. */
101
+ errorMessage: string | null;
102
+ /** Canonical MIME `type/subtype`. */
103
+ mimeType: string | null;
104
+ /** Display name (often AI-generated for ai-produced media). */
105
+ fileName: string | null;
106
+ /**
107
+ * File size in bytes (renamed from `file_size` in Phase 0 — see
108
+ * docs/PYTHON_UPDATES.md §3). null when unknown (typical for streaming
109
+ * partials and pre-finalize states).
110
+ */
111
+ sizeBytes: number | null;
112
+ /**
113
+ * Inline base64 bytes. Used for streaming partials and tiny inline assets.
114
+ * Cleared once `status === "complete"` and a URL is available.
115
+ */
116
+ base64: string | null;
117
+ /**
118
+ * Free-form. Phase 2 stamps `metadata.generation` (typed as
119
+ * `MediaGenerationMetadata` below) for any AI-generated asset.
120
+ * Existing top-level keys (`model`, `provider`, `prompt`, `cost`) are
121
+ * preserved for back-compat — new code should read `metadata.generation`.
122
+ */
123
+ metadata: Record<string, unknown> | null;
124
+ /**
125
+ * The reference role this media played as an INPUT (subject, style,
126
+ * first_frame, extend, …) and its `@name`, carried from the persisted
127
+ * part so a reload redisplays it. Absent for generated output.
128
+ */
129
+ referenceRole?: string | null;
130
+ referenceName?: string | null;
131
+ }
132
+ /**
133
+ * Fields present on every matrx-owned (cld_files-backed) media block.
134
+ * `fileId` is the permanent identity the URL resolver re-mints from on
135
+ * expiry (via fileHandler). Native storage locations are server-only and
136
+ * never appear on the client.
137
+ */
138
+ interface MatrxOriginFields {
139
+ origin: "matrx";
140
+ /** cld_files.id — the permanent identity. */
141
+ fileId: string;
142
+ /**
143
+ * cld_files.visibility — drives URL resolution:
144
+ * "public" — prefer cdnUrl; permanent URL, no auth required
145
+ * everything else ("personal" / "internal" / "link") — render through
146
+ * the durable authenticated URL (`fileUrls(fileId).inline`), which the
147
+ * browser authenticates via the `mx_files_session` cookie
148
+ */
149
+ visibility: MediaVisibility;
150
+ /** Permanent CDN URL — no expiry, no auth required. */
151
+ cdnUrl: string | null;
152
+ /** Durable attachment-disposition variant. Used for the download action. */
153
+ downloadUrl: string | null;
154
+ /** cld_files.parent_file_id — derivation lineage. */
155
+ parentFileId: string | null;
156
+ /**
157
+ * cld_files.derivation_kind — how this was produced from `parentFileId`.
158
+ * E.g. "manual_upload", "extracted_pages", "cropped", "rendered_page_image".
159
+ */
160
+ derivationKind: string | null;
161
+ }
162
+ /**
163
+ * Fields present on every external (third-party / user-pasted) media block.
164
+ * We never refresh these and never try to mint fresh URLs.
165
+ */
166
+ interface ExternalOriginFields {
167
+ origin: "external";
168
+ /** The external URL. Always present — empty string fallback means broken. */
169
+ externalUrl: string;
170
+ /** Optional provenance label — e.g. "Wikimedia", "Tool: web_search". */
171
+ sourceLabel: string | null;
172
+ }
173
+ interface ImageKindFields {
174
+ kind: "image";
175
+ /** Pixel width. Null when unknown / pre-finalize. */
176
+ width: number | null;
177
+ /** Pixel height. Null when unknown / pre-finalize. */
178
+ height: number | null;
179
+ /**
180
+ * Optional content-class hint from vision models — e.g. "photograph",
181
+ * "diagram", "ui-screenshot". Free-form; consumers should treat as a UI
182
+ * hint only.
183
+ */
184
+ visionClass: string | null;
185
+ }
186
+ interface VideoKindFields {
187
+ kind: "video";
188
+ width: number | null;
189
+ height: number | null;
190
+ /** Duration in milliseconds. */
191
+ durationMs: number | null;
192
+ /**
193
+ * Poster/cover frame URL (extracted at ~10% of timeline). Phase 1
194
+ * populates this for matrx-owned videos via `Asset.variants["poster_url"]`.
195
+ */
196
+ posterUrl: string | null;
197
+ }
198
+ interface AudioKindFields {
199
+ kind: "audio";
200
+ /** Duration in milliseconds. */
201
+ durationMs: number | null;
202
+ /**
203
+ * Inline transcript text when present. Long-form transcripts go in a
204
+ * separate processed_documents row; this carries the short-form summary
205
+ * or null.
206
+ */
207
+ transcript: string | null;
208
+ }
209
+ interface DocumentKindFields {
210
+ kind: "document";
211
+ /** Page count for paginated documents (PDF, DOCX, …). null otherwise. */
212
+ pageCount: number | null;
213
+ /**
214
+ * Page-1 preview rendered to image. Phase 1 populates this for PDFs.
215
+ * Useful when displaying an inline document chip.
216
+ */
217
+ page1Url: string | null;
218
+ }
219
+ interface YouTubeKindFields {
220
+ kind: "youtube";
221
+ /** Extracted YouTube video id (the `?v=` token). */
222
+ videoId: string | null;
223
+ }
224
+ type MatrxImageBlock = MediaBlockBase & MatrxOriginFields & ImageKindFields;
225
+ type ExternalImageBlock = MediaBlockBase & ExternalOriginFields & ImageKindFields;
226
+ type ImageBlock = MatrxImageBlock | ExternalImageBlock;
227
+ type MatrxVideoBlock = MediaBlockBase & MatrxOriginFields & VideoKindFields;
228
+ type ExternalVideoBlock = MediaBlockBase & ExternalOriginFields & VideoKindFields;
229
+ type VideoBlock = MatrxVideoBlock | ExternalVideoBlock;
230
+ type MatrxAudioBlock = MediaBlockBase & MatrxOriginFields & AudioKindFields;
231
+ type ExternalAudioBlock = MediaBlockBase & ExternalOriginFields & AudioKindFields;
232
+ type AudioBlock = MatrxAudioBlock | ExternalAudioBlock;
233
+ type MatrxDocumentBlock = MediaBlockBase & MatrxOriginFields & DocumentKindFields;
234
+ type ExternalDocumentBlock = MediaBlockBase & ExternalOriginFields & DocumentKindFields;
235
+ type DocumentBlock = MatrxDocumentBlock | ExternalDocumentBlock;
236
+ /** YouTube is always external per the Python contract. */
237
+ type YouTubeBlock = MediaBlockBase & ExternalOriginFields & YouTubeKindFields;
238
+ /**
239
+ * The unified discriminated union for every media reference in the app.
240
+ *
241
+ * Narrowing recipe:
242
+ * if (block.kind === "image") {
243
+ * if (block.origin === "matrx") {
244
+ * // block.fileId, block.visibility, block.cdnUrl, ...
245
+ * } else {
246
+ * // block.externalUrl, block.sourceLabel
247
+ * }
248
+ * // block.width, block.height (image-specific)
249
+ * }
250
+ */
251
+ type UnifiedMediaBlock = ImageBlock | VideoBlock | AudioBlock | DocumentBlock | YouTubeBlock;
252
+ type MediaGenerationKind = "image" | "video" | "audio" | "speech" | "music";
253
+ interface MediaGenerationMetadata {
254
+ kind: MediaGenerationKind;
255
+ provider: string;
256
+ model: string;
257
+ prompt: string;
258
+ negativePrompt: string | null;
259
+ /**
260
+ * Provider's rewrite of the user's prompt (OpenAI's `revised_prompt`).
261
+ * Surface to the user — improves transparency and trust.
262
+ */
263
+ revisedPrompt: string | null;
264
+ width: number | null;
265
+ height: number | null;
266
+ /** Free-form, e.g. "16:9", "1:1", "9:16". */
267
+ aspectRatio: string | null;
268
+ durationSeconds: number | null;
269
+ quality: string | null;
270
+ style: string | null;
271
+ seed: number | null;
272
+ steps: number | null;
273
+ cfgScale: number | null;
274
+ nRequested: number;
275
+ nReturned: number;
276
+ responseId: string | null;
277
+ durationMs: number | null;
278
+ costUsd: number | null;
279
+ /** E.g. "completed", "content_filter", "error". */
280
+ finishReason: string | null;
281
+ safetyFlagged: boolean;
282
+ /** Catch-all for provider-native fields we haven't canonicalized yet. */
283
+ providerExtras: Record<string, unknown>;
284
+ }
285
+ /**
286
+ * Wire shape (snake_case) as Python emits it. Adapters read this; consumers
287
+ * generally don't.
288
+ */
289
+ interface MediaGenerationMetadataWire {
290
+ kind: MediaGenerationKind;
291
+ provider: string;
292
+ model: string;
293
+ prompt: string;
294
+ negative_prompt?: string | null;
295
+ revised_prompt?: string | null;
296
+ width?: number | null;
297
+ height?: number | null;
298
+ aspect_ratio?: string | null;
299
+ duration_seconds?: number | null;
300
+ quality?: string | null;
301
+ style?: string | null;
302
+ seed?: number | null;
303
+ steps?: number | null;
304
+ cfg_scale?: number | null;
305
+ n_requested?: number;
306
+ n_returned?: number;
307
+ response_id?: string | null;
308
+ duration_ms?: number | null;
309
+ cost_usd?: number | null;
310
+ finish_reason?: string | null;
311
+ safety_flagged?: boolean;
312
+ provider_extras?: Record<string, unknown>;
313
+ }
314
+ /**
315
+ * Pull the typed `MediaGenerationMetadata` out of a block's free-form
316
+ * metadata bag. Returns null when no `generation` key is present.
317
+ *
318
+ * Usage:
319
+ * const gen = parseGenerationMetadata(block.metadata);
320
+ * if (gen?.revisedPrompt) { renderRevisedPromptBanner(gen.revisedPrompt); }
321
+ */
322
+ declare function parseGenerationMetadata(metadata: Record<string, unknown> | null | undefined): MediaGenerationMetadata | null;
323
+
324
+ /**
325
+ * features/files/blocks/guards.ts
326
+ *
327
+ * Runtime type guards for `UnifiedMediaBlock` and its variants. Used at
328
+ * trust boundaries (Redux reads, adapter inputs) to prove the shape
329
+ * rather than force-cast.
330
+ *
331
+ * All guards accept `unknown` so they're safe to use against raw
332
+ * `Record<string, unknown>` data (e.g. `block.data` on a render-block
333
+ * envelope, `block.metadata` on a free-form bag).
334
+ */
335
+
336
+ declare function isMatrxMediaBlock(value: unknown): value is Extract<UnifiedMediaBlock, {
337
+ origin: "matrx";
338
+ }>;
339
+ declare function isExternalMediaBlock(value: unknown): value is Extract<UnifiedMediaBlock, {
340
+ origin: "external";
341
+ }>;
342
+ declare function isUnifiedMediaBlock(value: unknown): value is UnifiedMediaBlock;
343
+ declare function isImageBlock(value: unknown): value is ImageBlock;
344
+ declare function isVideoBlock(value: unknown): value is VideoBlock;
345
+ declare function isAudioBlock(value: unknown): value is AudioBlock;
346
+ declare function isDocumentBlock(value: unknown): value is DocumentBlock;
347
+ declare function isYouTubeBlock(value: unknown): value is YouTubeBlock;
348
+ declare function isMatrxImageBlock(value: unknown): value is MatrxImageBlock;
349
+ declare function isExternalImageBlock(value: unknown): value is ExternalImageBlock;
350
+
351
+ /**
352
+ * features/files/blocks/image/types.ts
353
+ *
354
+ * Image-specific types — re-exported from the canonical `UnifiedMediaBlock`
355
+ * union in `../types.ts`. THIS FILE IS THIN BY DESIGN: there is no
356
+ * image-only shape anymore; an `ImageBlock` is just the `kind: "image"`
357
+ * variant of the platform-wide media block.
358
+ *
359
+ * Keep this file alive so existing imports
360
+ * import type { UnifiedImageBlock, MatrxImageBlock, ExternalImageBlock }
361
+ * from "@/features/files/blocks/image/types";
362
+ * continue to work without churn. New code should prefer importing from
363
+ * `@/features/files/blocks/types` directly.
364
+ *
365
+ * See ../UNIFIED_IMAGE_BLOCK.md and docs/PYTHON_UPDATES.md for the wire
366
+ * contract.
367
+ */
368
+
369
+ /**
370
+ * Back-compat alias. Past consumers know "UnifiedImageBlock" — this is the
371
+ * same thing as `ImageBlock` from the canonical union. New code should
372
+ * spell it `ImageBlock` for consistency with the other kinds.
373
+ */
374
+ type UnifiedImageBlock = ImageBlock;
375
+
376
+ /**
377
+ * features/files/blocks/image/guards.ts
378
+ *
379
+ * Image-specific runtime guards. Re-exported from the canonical guards in
380
+ * `../guards.ts`. New code should prefer importing from there directly.
381
+ *
382
+ * Usage:
383
+ *
384
+ * if (!isUnifiedImageBlock(block.data)) return null;
385
+ * // block.data is now narrowed to ImageBlock (kind: "image")
386
+ * // by TypeScript.
387
+ *
388
+ * These guards check BOTH `kind === "image"` and the origin discriminator,
389
+ * so a stray video/audio/document block will NOT pass through them.
390
+ */
391
+
392
+ /**
393
+ * Back-compat alias for `isImageBlock` — narrows to `UnifiedImageBlock`
394
+ * (a.k.a. the `kind: "image"` variant of `UnifiedMediaBlock`).
395
+ */
396
+ declare function isUnifiedImageBlock(value: unknown): value is UnifiedImageBlock;
397
+
398
+ /**
399
+ * features/files/blocks/adapters/from-cx-av-part.ts
400
+ *
401
+ * Convert a DB-stored `cx_message.content[]` media part (kind: "audio" |
402
+ * "video") into the `AudioOutputData` / `VideoOutputData` render-block payload
403
+ * the chat media renderers consume.
404
+ *
405
+ * This is the audio/video twin of the image adapter
406
+ * (`../image/adapters/from-cx-media-part.ts`) and exists for the same reason:
407
+ * **media identity is the `file_id`, never a URL.** The stored `url` is
408
+ * whatever happened to be visible at save time (legacy rows: a long-dead
409
+ * signed S3 URL). `file_id` is what lets `buildMediaSource` → `useFileSrc`
410
+ * resolve the durable URL on every render.
411
+ *
412
+ * `file_id` lives at the TOP LEVEL of the stored media part (and is sometimes
413
+ * mirrored into `metadata`); the URL flavors are dumped into `metadata`. Both
414
+ * are lifted back out here so the round-trip is lossless.
415
+ */
416
+
417
+ declare function fromCxAudioPart(part: AudioMediaPart): AudioOutputData & {
418
+ transcription_result: string | null;
419
+ };
420
+ declare function fromCxVideoPart(part: VideoMediaPart): VideoOutputData & {
421
+ reference_role: string | null;
422
+ reference_name: string | null;
423
+ };
424
+
425
+ /**
426
+ * features/files/blocks/adapters/from-media-block.ts
427
+ *
428
+ * THE primary inbound adapter for the new `data.type === "media_block"`
429
+ * stream event (Phase 0 of Python's unified-media rollout — see
430
+ * docs/PYTHON_UPDATES.md).
431
+ *
432
+ * The wire shape (Python's `UnifiedMediaBlock` Pydantic union) is
433
+ * snake_case and nearly identical to our domain shape. This adapter does
434
+ * exactly two things:
435
+ * 1. Rename fields snake_case → camelCase.
436
+ * 2. Enforce invariants:
437
+ * - matrx → fileId non-null
438
+ * - external → externalUrl non-null
439
+ * Anything that doesn't satisfy these falls through to an external
440
+ * "broken" block; the renderer shows an error state.
441
+ *
442
+ * Anything kind-specific (image's `vision_class`, video's `duration_ms`,
443
+ * etc.) is just propagated — no per-kind logic here.
444
+ *
445
+ * Note: Python `main` hasn't deployed yet (as of 2026-05-16). The shape
446
+ * below is what we'll receive once it does. Until then this adapter is
447
+ * cold-path; the existing `image_output` / `partial_image` legacy
448
+ * adapters carry traffic.
449
+ */
450
+
451
+ interface WireMediaBlockBase {
452
+ origin: "matrx" | "external";
453
+ status?: MediaStatus | null;
454
+ progress?: number | null;
455
+ error_message?: string | null;
456
+ mime_type?: string | null;
457
+ file_name?: string | null;
458
+ size_bytes?: number | null;
459
+ base64?: string | null;
460
+ metadata?: Record<string, unknown> | null;
461
+ file_id?: string | null;
462
+ visibility?: MediaVisibility | null;
463
+ cdn_url?: string | null;
464
+ download_url?: string | null;
465
+ parent_file_id?: string | null;
466
+ derivation_kind?: string | null;
467
+ external_url?: string | null;
468
+ source_label?: string | null;
469
+ }
470
+ interface WireImageBlock extends WireMediaBlockBase {
471
+ kind: "image";
472
+ width?: number | null;
473
+ height?: number | null;
474
+ vision_class?: string | null;
475
+ }
476
+ interface WireVideoBlock extends WireMediaBlockBase {
477
+ kind: "video";
478
+ width?: number | null;
479
+ height?: number | null;
480
+ duration_ms?: number | null;
481
+ poster_url?: string | null;
482
+ }
483
+ interface WireAudioBlock extends WireMediaBlockBase {
484
+ kind: "audio";
485
+ duration_ms?: number | null;
486
+ transcript?: string | null;
487
+ }
488
+ interface WireDocumentBlock extends WireMediaBlockBase {
489
+ kind: "document";
490
+ page_count?: number | null;
491
+ page1_url?: string | null;
492
+ }
493
+ interface WireYouTubeBlock extends WireMediaBlockBase {
494
+ kind: "youtube";
495
+ video_id?: string | null;
496
+ }
497
+ type WireMediaBlock = WireImageBlock | WireVideoBlock | WireAudioBlock | WireDocumentBlock | WireYouTubeBlock;
498
+ /**
499
+ * Wire shape for the `media_block` stream event envelope. Always has
500
+ * `type: "media_block"` and a `block: WireMediaBlock` payload.
501
+ */
502
+ interface WireMediaBlockData {
503
+ type: "media_block";
504
+ block: WireMediaBlock;
505
+ }
506
+ /**
507
+ * Lift a wire-shape `UnifiedMediaBlock` (as emitted by Python's new
508
+ * `media_block` data event) into our domain `UnifiedMediaBlock` shape.
509
+ *
510
+ * Discriminates on `kind` and delegates to the kind-specific lifter.
511
+ * Each lifter handles the matrx-vs-external origin split internally.
512
+ */
513
+ declare function fromMediaBlock(wire: WireMediaBlock): UnifiedMediaBlock;
514
+ /**
515
+ * Predicate for narrowing a raw `data` payload (typed as
516
+ * `Record<string, unknown>` in Redux) down to a `media_block` event.
517
+ *
518
+ * Use at the stream-event boundary:
519
+ * if (isMediaBlockData(d)) { upsert(fromMediaBlock(d.block)); }
520
+ */
521
+ declare function isMediaBlockData(value: unknown): value is WireMediaBlockData;
522
+
523
+ /**
524
+ * features/files/blocks/adapters/from-media-ref.ts
525
+ *
526
+ * Generic platform primitive: turn a bare `MediaRef` (or file_id / URL)
527
+ * into a minimal `UnifiedMediaBlock` so it can be fed to the canonical
528
+ * renderers (`UnifiedImageBlockRenderer`, `UnifiedVideoBlockRenderer`)
529
+ * which consume a `block`, not a `MediaRef`.
530
+ *
531
+ * Why this exists: many callsites only ever have a durable reference to a
532
+ * file — a `{file_id}` (we own it) or a `{url}` (external / already-public)
533
+ * — yet they want the full rich-media affordances (expand → fullscreen,
534
+ * the "…" menu, mobile long-press, share, download). Those affordances
535
+ * live ONLY on the canonical block renderers. This adapter bridges the
536
+ * gap without forcing every caller to hand-build a block literal (which
537
+ * the CLAUDE.md file-handling rules forbid).
538
+ *
539
+ * Mapping (consistent with the invariants in `../types.ts`):
540
+ * - `{ file_id }` ref → an `origin: "matrx"` block. `fileId` is set;
541
+ * `visibility` is "personal" (unknown) and `cdnUrl` is null, so
542
+ * `useBlockMediaSource` resolves the durable URL via the injected
543
+ * `MediaClient`.
544
+ * - `{ url }` ref → an `origin: "external"` block. `externalUrl` is set;
545
+ * the renderer uses it as-is.
546
+ * - A ref that resolves to neither → an `external` "broken" block with a
547
+ * null/empty `externalUrl`; the renderer shows its error state.
548
+ *
549
+ * `status` is always "complete" — these refs point at finished media. For
550
+ * streaming partials, use the streaming adapters in `from-media-block.ts`.
551
+ */
552
+
553
+ /** The kinds this generic adapter can synthesize a block for. */
554
+ type MediaBlockKindArg = "image" | "video";
555
+ /**
556
+ * Build a minimal `ImageBlock` from a durable reference.
557
+ */
558
+ declare function imageBlockFromMediaRef(ref: MediaRef | null): ImageBlock | null;
559
+ /**
560
+ * Build a minimal `VideoBlock` from a durable reference.
561
+ */
562
+ declare function videoBlockFromMediaRef(ref: MediaRef | null): VideoBlock | null;
563
+ /**
564
+ * Kind-dispatching convenience: build an image or video block from a ref.
565
+ * Returns `null` when `ref` is null.
566
+ */
567
+ declare function blockFromMediaRef(ref: MediaRef | null, kind: MediaBlockKindArg): ImageBlock | VideoBlock | null;
568
+
569
+ /**
570
+ * features/files/blocks/image/adapters/from-cx-media-part.ts
571
+ *
572
+ * Convert a DB-stored `cx_message.content[]` media part (kind: "image") into
573
+ * a UnifiedImageBlock.
574
+ *
575
+ * Today's storage shape (CxMediaContent → ImageMediaPart):
576
+ * { type: "media", kind: "image", url?, mime_type?, base64_data?,
577
+ * metadata? }
578
+ *
579
+ * Everything else (file_id, cdn_url, visibility, thumbnails,
580
+ * dimensions, file_name, prompt, model, etc.) gets dumped into `metadata`
581
+ * today by `assembleMessageParts`. This adapter pulls them BACK out so the
582
+ * round-trip is lossless.
583
+ *
584
+ * Delete when `cx_message.content[]` storage shape switches to
585
+ * UnifiedImageBlock natively (Phase 3).
586
+ */
587
+
588
+ declare function fromCxMediaPart(part: ImageMediaPart): UnifiedImageBlock;
589
+
590
+ /**
591
+ * features/files/blocks/image/adapters/from-image-output-data.ts
592
+ *
593
+ * Convert a Python `image_output` data event into a UnifiedImageBlock.
594
+ *
595
+ * Today's Python wire shape (ImageOutputData):
596
+ * { type: "image_output", url, mime_type, file_id?, cdn_url?, download_url? }
597
+ *
598
+ * What this adapter does:
599
+ * - Lifts `file_id` to identify a matrx-owned file (most common case).
600
+ * - Tries to extract `file_id` from `url` if Python didn't supply one
601
+ * (legacy fallback — eventually deletable).
602
+ * - Promotes additional fields from `metadata` if Python included them
603
+ * there as a transitional shim:
604
+ * visibility, thumbnail_url, parent_file_id, derivation_kind,
605
+ * file_name, width, height, size_bytes.
606
+ * - When no `file_id` is recoverable, falls back to an external block
607
+ * using whichever URL is most likely permanent.
608
+ *
609
+ * Delete when Python emits UnifiedImageBlock directly (Phase 2).
610
+ */
611
+
612
+ declare function fromImageOutputData(data: ImageOutputData, carriedMetadata?: Record<string, unknown> | null): UnifiedImageBlock;
613
+
614
+ /**
615
+ * features/files/blocks/image/adapters/from-partial-image-data.ts
616
+ *
617
+ * Convert a Python `partial_image` event into an in-flight UnifiedImageBlock.
618
+ *
619
+ * Partial images are base64 frames Python emits during image generation.
620
+ * They arrive BEFORE the final `image_output` event lands, so there's no
621
+ * fileId yet. We model them as an external block in "streaming" status
622
+ * whose `base64` carries the latest frame.
623
+ *
624
+ * When the final `image_output` arrives, the stream-ingest layer should
625
+ * upsert by `blockId` so a single block transitions from streaming
626
+ * (base64-only) → complete (matrx variant with URLs). This means partial
627
+ * and final SHARE the same blockId in Redux.
628
+ *
629
+ * Delete when Python emits UnifiedImageBlock directly with status: "streaming"
630
+ * (Phase 2).
631
+ */
632
+
633
+ declare function fromPartialImageData(data: PartialImageData, carriedMetadata?: Record<string, unknown> | null): UnifiedImageBlock;
634
+
635
+ /**
636
+ * features/files/blocks/image/adapters/from-render-block.ts
637
+ *
638
+ * Convert a markdown-parsed image render_block (e.g. from a text chunk that
639
+ * contained `![alt](url)`) into a UnifiedImageBlock.
640
+ *
641
+ * Today's wire shape is minimal — `{ src, alt }`. We treat these as external
642
+ * unless the URL pattern matches our canonical S3 key scheme, in which case
643
+ * we promote to matrx via `extractFileIdFromUrl` (inside `fromImageOutputData`).
644
+ *
645
+ * Input type is `RenderBlockPayload` (not `ImageRenderBlock`) so callers can
646
+ * pass the redux-stored loose shape without a force-cast. We validate the
647
+ * payload structure internally; an empty src yields an external block with an
648
+ * empty URL — the renderer's error state covers it.
649
+ *
650
+ * Delete when render_block:image emits UnifiedImageBlock in its `data` field
651
+ * (Phase 2).
652
+ */
653
+
654
+ declare function fromRenderBlock(block: RenderBlockPayload): UnifiedImageBlock;
655
+
656
+ /**
657
+ * features/files/blocks/image/adapters/to-cx-media-part.ts
658
+ *
659
+ * Convert a `UnifiedImageBlock` into the generated DB on-disk
660
+ * `ImageMediaPart` shape.
661
+ *
662
+ * On-disk shape today (`ImageMediaPart`):
663
+ * { type: "media", kind: "image", file_id? | url?, origin?,
664
+ * mime_type?, size_bytes?, width?, height?, metadata? }
665
+ *
666
+ * Strategy:
667
+ * - Keep generated top-level identity fields populated so
668
+ * legacy readers that haven't migrated keep working.
669
+ * - Pack EVERY canonical field (origin, fileId, cdnUrl, downloadUrl,
670
+ * visibility, thumbnails, dimensions, etc.) into
671
+ * `metadata` under stable keys so `fromCxMediaPart` can re-lift them
672
+ * losslessly when the message is reloaded.
673
+ *
674
+ * Delete when `cx_message.content[]` storage switches to UnifiedImageBlock
675
+ * natively (Phase 3).
676
+ */
677
+
678
+ declare function toCxMediaPart(block: UnifiedImageBlock): ImageMediaPart;
679
+
680
+ /**
681
+ * features/files/blocks/image/helpers/derive-viewer-url.ts
682
+ *
683
+ * Derive the internal viewer route for a matrx-owned image. The route
684
+ * `/files/f/{fileId}` is the canonical "deep link" for any cld_files row.
685
+ *
686
+ * External blocks do not have a viewer URL — return null.
687
+ */
688
+
689
+ declare function deriveViewerUrl(block: UnifiedImageBlock): string | null;
690
+
691
+ /**
692
+ * features/files/blocks/image/helpers/extract-file-id-from-url.ts
693
+ *
694
+ * Best-effort extraction of a cld_files UUID from a storage / CDN URL.
695
+ *
696
+ * Canonical S3 key scheme: `/{owner_id}/{file_id}` (no subfolder, no extension).
697
+ * Legacy fallback: `/{owner_id}/{folder}/{file_id}.{ext}`.
698
+ *
699
+ * Used by adapters as a last-resort when Python's payload doesn't carry an
700
+ * explicit `file_id` field — e.g. legacy stream events. Once Python emits
701
+ * `file_id` consistently, this becomes dead code.
702
+ */
703
+ declare function extractFileIdFromUrl(url: string | null | undefined): string | null;
704
+
705
+ /**
706
+ * features/files/blocks/image/helpers/parse-filename-from-url.ts
707
+ *
708
+ * S3 signed URLs the backend mints for AI-generated images carry the
709
+ * intended filename in `response-content-disposition`, e.g.:
710
+ *
711
+ * ?response-content-disposition=inline%3B%20filename%3D%22kitten.png%22
712
+ *
713
+ * Python's AI-naming step (`features/ai/...`) writes a meaningful name
714
+ * there based on the prompt, so the browser shows it on download. But
715
+ * when we use `<a download="...">` we override that — we need the name
716
+ * in `block.fileName` to pass it back.
717
+ *
718
+ * This helper extracts the filename from the query param if present.
719
+ * Returns null on anything malformed so callers always get a string or
720
+ * null (never an empty string).
721
+ *
722
+ * Handles both RFC 5987 forms:
723
+ * - `filename="kitten.png"` (quoted, ASCII)
724
+ * - `filename*=UTF-8''ki%CC%88tten.png` (extended, percent-encoded)
725
+ */
726
+ declare function parseFilenameFromUrl(url: string | null | undefined): string | null;
727
+
728
+ /** One compact publish-date treatment shared by every video surface. */
729
+ declare function VideoPublishDate({ publishedAt, className, }: {
730
+ publishedAt?: string | null;
731
+ className?: string;
732
+ }): react.JSX.Element;
733
+
734
+ /**
735
+ * features/files/handler/errors.ts
736
+ *
737
+ * Error taxonomy for the universal file handler. Every failure inside the
738
+ * handler maps to ONE of these classes — callers `instanceof`-check to
739
+ * decide whether to retry, refresh, surface a UI, or reject.
740
+ *
741
+ * The crucial distinction the handler exists to enforce:
742
+ *
743
+ * - FileAccessDeniedError → user does NOT have access. Reject. Never retry.
744
+ * - a transient auth failure on a durable URL → refresh the file session
745
+ * (see ../session.ts) and retry the SAME URL; never a terminal error.
746
+ */
747
+ type FileHandlerErrorCode = "access_denied" | "not_found" | "deleted" | "share_link_invalid" | "external_fetch_failed" | "cors_blocked" | "mime_unknown" | "upload_failed" | "upload_cancelled" | "quota_exceeded" | "in_flight" | "internal";
748
+ declare class FileHandlerError extends Error {
749
+ readonly code: FileHandlerErrorCode;
750
+ readonly fileId?: string | undefined;
751
+ readonly cause?: unknown;
752
+ constructor(code: FileHandlerErrorCode, message: string, opts?: {
753
+ fileId?: string;
754
+ cause?: unknown;
755
+ });
756
+ }
757
+ declare class FileAccessDeniedError extends FileHandlerError {
758
+ constructor(message?: string, opts?: {
759
+ fileId?: string;
760
+ });
761
+ }
762
+ declare class FileNotFoundError extends FileHandlerError {
763
+ constructor(message?: string, opts?: {
764
+ fileId?: string;
765
+ });
766
+ }
767
+ declare class FileDeletedError extends FileHandlerError {
768
+ constructor(message?: string, opts?: {
769
+ fileId?: string;
770
+ });
771
+ }
772
+ declare class ShareLinkInvalidError extends FileHandlerError {
773
+ constructor(message?: string);
774
+ }
775
+ declare class ExternalFetchError extends FileHandlerError {
776
+ constructor(message?: string, opts?: {
777
+ cause?: unknown;
778
+ });
779
+ }
780
+ declare class FileUploadError extends FileHandlerError {
781
+ constructor(message: string, opts?: {
782
+ cause?: unknown;
783
+ });
784
+ }
785
+ /**
786
+ * The person was asked which workspace an upload belongs to and declined.
787
+ *
788
+ * Deliberately a distinct class, not a `FileUploadError` with a flag: nothing
789
+ * failed. No bytes moved, no row was written, and there is nothing to retry or
790
+ * report. Every catch site must swallow it silently — cancelling has to return
791
+ * the person exactly where they were, which a red toast does not.
792
+ */
793
+ declare class UploadCancelledError extends FileHandlerError {
794
+ constructor(message?: string);
795
+ }
796
+ declare function isUploadCancelledError(error: unknown): boolean;
797
+
798
+ /** Compose a canonical Files folder path from an optional logical root. */
799
+ declare function composeUploadFolderPath(root?: string, path?: string): string;
800
+
801
+ /**
802
+ * features/files/utils/folder-conventions.ts
803
+ *
804
+ * Canonical folder paths for every feature that uploads on behalf of the
805
+ * user. The rule is simple: **organize files the way a user would expect to
806
+ * find them.** When a user opens `/files`, they should be able to
807
+ * point at a folder and say "that's where X goes" without us explaining it.
808
+ *
809
+ * Two buckets:
810
+ *
811
+ * 1. Visible folders — real, user-facing. Files go here when the user
812
+ * produced them, uploaded them, or attached them to something. Names are
813
+ * human-readable (Title Case) because they're shown in the tree.
814
+ *
815
+ * 2. Hidden folders (leading `.`) — ephemeral / infrastructure files the
816
+ * user doesn't need to see or manage. Auto-collapsed in the UI. These
817
+ * should be rare; only use them for true transients (short-lived URL
818
+ * staging, drafts) and prefer deletion on completion.
819
+ *
820
+ * Every consumer migration should pick its folder from this file or add a
821
+ * new constant here — do NOT hand-roll folder paths at call sites.
822
+ *
823
+ * This file is also the source of truth for the **default visibility** of
824
+ * every folder — see `resolveDefaultVisibility` near the bottom. Callers
825
+ * should pick visibility based on render context, not user role: anything
826
+ * rendered on a public page (org logo, podcast cover, agent-app icon)
827
+ * defaults to `"public"` so it gets a CDN URL; anything user-scoped
828
+ * (chat attachment, task attachment, ephemeral temp) stays `"private"`.
829
+ */
830
+
831
+ declare const CloudFolders: {
832
+ /** Top-level "Images" drawer. */
833
+ readonly IMAGES: "Images";
834
+ /** Images pasted/attached inside a chat. */
835
+ readonly IMAGES_CHAT: "Images/Chat";
836
+ /** Screenshots dragged or pasted into the app. */
837
+ readonly IMAGES_SCREENSHOTS: "Images/Screenshots";
838
+ /** User avatars / profile images. */
839
+ readonly IMAGES_AVATARS: "Images/Avatars";
840
+ /** Images generated by AI tools. */
841
+ readonly IMAGES_GENERATED: "Images/Generated";
842
+ /** Screenshots and images flattened with user-authored markup. */
843
+ readonly IMAGES_ANNOTATED: "Images/Annotated";
844
+ /** Top-level "Audio" drawer. */
845
+ readonly AUDIO: "Audio";
846
+ /** User-initiated audio recordings (voice-to-text, voice pad, etc.). */
847
+ readonly AUDIO_RECORDINGS: "Audio/Recordings";
848
+ /** Podcast asset uploads. */
849
+ readonly AUDIO_PODCASTS: "Audio/Podcasts";
850
+ /** Top-level "Captures" drawer — everything the in-browser media-capture
851
+ * system saves (features/media-capture). User-produced, so visible. */
852
+ readonly CAPTURES: "Captures";
853
+ /** Photos taken with the in-app camera (Capture Studio / device fallback). */
854
+ readonly CAPTURES_PHOTOS: "Captures/Photos";
855
+ /** Videos recorded with the in-app camera (media-capture Phase 7). */
856
+ readonly CAPTURES_VIDEOS: "Captures/Videos";
857
+ /** Audio recorded through the Capture Studio (media-capture Phase 7). */
858
+ readonly CAPTURES_AUDIO: "Captures/Audio";
859
+ /** Product-capture staging (features/product-capture) — per-item folders of
860
+ * photos/videos/voice notes shot ahead of listing creation. Org-namespaced
861
+ * via `folderForProductCaptureItem` (same server one-path-one-org rule as
862
+ * Captures). User-produced, so visible. */
863
+ readonly PRODUCT_CAPTURES: "Product Captures";
864
+ /** Commerce intake (W4 capture app): per-asset media filed under
865
+ * `Commerce Intake/<orgId>/<batchId>/<asset-leaf>` via
866
+ * `folderForIntakeAsset` (same server one-path-one-org rule as Captures).
867
+ * User-produced, so visible. */
868
+ readonly COMMERCE_INTAKE: "Commerce Intake";
869
+ /**
870
+ * Static app assets (sounds, hero images, model-card thumbnails, demo
871
+ * data) owned by an admin service account and rendered globally.
872
+ * Always public — CDN URL is the goal. Source of truth for the
873
+ * static-asset migration (see scripts/migrate-public-assets-to-cdn.ts).
874
+ */
875
+ readonly APP_ASSETS: "App Assets";
876
+ /** Top-level documents (PDF, DOCX, etc.). */
877
+ readonly DOCUMENTS: "Documents";
878
+ /** PDF-specific drawer. */
879
+ readonly DOCUMENTS_PDFS: "Documents/PDFs";
880
+ /** Top-level code files (scripts, snippets). */
881
+ readonly CODE: "Code";
882
+ /** Code saved via the code-editor window. */
883
+ readonly CODE_EDITOR: "Code/Editor";
884
+ /** Generic AI-generated outputs. */
885
+ readonly GENERATED: "Generated";
886
+ /** Files attached to agent apps, keyed by app id at runtime. */
887
+ readonly AGENT_APPS: "Agent Apps";
888
+ /** Files attached to tasks, keyed by task id at runtime. */
889
+ readonly TASK_ATTACHMENTS: "Task Attachments";
890
+ /** Files attached to War Room tiles, keyed by tile id at runtime. */
891
+ readonly WAR_ROOM: "War Room";
892
+ /** Files dropped into a chat composer (pre-send or history). */
893
+ readonly CHAT_ATTACHMENTS: "Chat Attachments";
894
+ /** Slack-imported files. */
895
+ readonly SLACK_IMPORTS: "Slack Imports";
896
+ /** Web-scrape captures (HTML snapshots, exported markdown). */
897
+ readonly SCRAPED_CONTENT: "Scraped Content";
898
+ /**
899
+ * Org-scoped shared assets — logos, hero images, anything an
900
+ * organization renders on a public surface. Keyed by org id at
901
+ * runtime (e.g. `Shared Assets/orgs/<org-id>`). Always public —
902
+ * the CDN URL is the rendering target.
903
+ */
904
+ readonly SHARED_ASSETS_ORGS: "Shared Assets/orgs";
905
+ /** Public screenshot attachments submitted through the feedback window. */
906
+ readonly FEEDBACK_IMAGES: "Shared Assets/feedback-images";
907
+ /**
908
+ * OG / social-preview covers for HTML pages the user publishes.
909
+ * Lives under Shared Assets so all org/public-surface previews
910
+ * share one CDN-backed bucket.
911
+ */
912
+ readonly HTML_PAGES_OG: "Shared Assets/html-pages";
913
+ /**
914
+ * Favicons rendered by prompt-app / agent-app standalone pages.
915
+ * Always public — favicon requests are anonymous.
916
+ */
917
+ readonly PROMPT_APPS_FAVICONS: "App Assets/prompt-apps/favicons";
918
+ /**
919
+ * Per-block assets attached to agent blocks (icons, illustrations,
920
+ * inline media). Keyed by block id at runtime via
921
+ * `folderForAgentBlock(blockId)`.
922
+ */
923
+ readonly AGENT_BLOCKS: "Agent Apps/blocks";
924
+ /**
925
+ * Cover images rendered on canvas social/share surfaces. Public so
926
+ * the share-card OG image is a stable CDN URL.
927
+ */
928
+ readonly CANVAS_COVERS: "Images/Canvas/Covers";
929
+ /**
930
+ * Source images the user is editing in the image studio. Private —
931
+ * these are the user's working files, NOT public render targets.
932
+ * Outputs that the user explicitly publishes land elsewhere.
933
+ */
934
+ readonly IMAGES_EDITED_SOURCES: "Images/Edited/Sources";
935
+ /** Root for all ephemeral cloud-files work. Hidden in the tree. */
936
+ readonly TMP: ".matrx-tmp";
937
+ /** Short-lived audio files staged for URL-based transcription. */
938
+ readonly TMP_TRANSCRIPTS: ".matrx-tmp/transcripts";
939
+ /** Upload-in-progress staging (future use). */
940
+ readonly TMP_UPLOADS: ".matrx-tmp/uploads";
941
+ /** Root for backend-owned infrastructure files (variants, posters, etc.). */
942
+ readonly SYSTEM_FILES: "system-files";
943
+ /** SOCIAL_BASELINE variant storage: og.jpg / thumb.jpg / tiny.jpg / page1_url.jpg. */
944
+ readonly SYSTEM_VARIANTS: "system-files/variants";
945
+ /**
946
+ * Backend "generations" registry root — every AI generation (image,
947
+ * video, audio/TTS) the server materializes lands under
948
+ * `generations/<images|video|audio>/...`. Backend-owned; mirrors the
949
+ * server's `is_system_path()` (which treats `system-files` AND
950
+ * `generations` as system). Never shown in the user tree.
951
+ */
952
+ readonly GENERATIONS: "generations";
953
+ /**
954
+ * Recorded audio captured by the Transcripts / voice features. As of
955
+ * 2026-06-14 the backend relocates every `origin: "transcripts"` upload
956
+ * here, UNDER the hidden `system-files/` root — one
957
+ * `recording_<iso>_<rand>.webm` per capture (80+ for an active user).
958
+ * Because it's under `system-files/`, `isSystemPath` already hides it
959
+ * from the tree, folder views, AND Recents — no separate predicate
960
+ * needed. Managed exclusively via the Transcripts UI (by `cld_files.id`).
961
+ * See docs/files/transcript-recordings-system-relocation.md.
962
+ */
963
+ readonly TRANSCRIPT_RECORDINGS: "system-files/transcripts/Recordings";
964
+ /**
965
+ * LEGACY user-namespace location for transcript recordings, before the
966
+ * 2026-06-14 backend relocation to `system-files/transcripts/...`. Kept
967
+ * ONLY as a defensive Recents guard: the backend relocates every
968
+ * `origin: "transcripts"` upload, but the presigned/TUS (chunked + large
969
+ * file) relocation path was genuinely missing and is committed but NOT yet
970
+ * in prod as of 2026-06-15.
971
+ *
972
+ * DO NOT DROP THIS GUARD until that backend fix has DEPLOYED to prod —
973
+ * dropping it earlier would let a chunked/large recording slip through
974
+ * unhidden. Sequence: backend deploys → then remove this constant from
975
+ * `isSystemManagedContentPath` (and delete it). See the FE-response /
976
+ * "open items" in docs/files/transcript-recordings-system-relocation.md.
977
+ */
978
+ readonly TRANSCRIPT_RECORDINGS_LEGACY: "Transcripts/Recordings";
979
+ /**
980
+ * Per-tool generated image variants — `tool-images/<id>/v/<name>.jpg`,
981
+ * multiple sizes per source. Machine-produced output, excluded from
982
+ * Recents (see `isSystemManagedContentPath`).
983
+ */
984
+ readonly TOOL_IMAGES: "tool-images";
985
+ /**
986
+ * FastFire voice-drill recordings — full-session + per-card response clips.
987
+ * Live under the hidden `system-files/fastfire/...` root (same class as
988
+ * transcript recordings). Managed via Education / FastFire UI only.
989
+ */
990
+ readonly SYSTEM_FASTFIRE: "system-files/fastfire";
991
+ readonly SYSTEM_FASTFIRE_SESSIONS: "system-files/fastfire/sessions";
992
+ readonly SYSTEM_FASTFIRE_RESPONSES: "system-files/fastfire/responses";
993
+ /**
994
+ * Spoken Practice (oral exam / interview / debate) recordings — full-session
995
+ * clips + per-prompt spoken-answer clips. Same hidden `system-files/` class as
996
+ * FastFire (so `isSystemPath` keeps them out of the Files browser + Recents).
997
+ */
998
+ readonly SYSTEM_SPOKEN_PRACTICE: "system-files/spoken-practice";
999
+ readonly SYSTEM_SPOKEN_PRACTICE_SESSIONS: "system-files/spoken-practice/sessions";
1000
+ readonly SYSTEM_SPOKEN_PRACTICE_RESPONSES: "system-files/spoken-practice/responses";
1001
+ /**
1002
+ * Handwritten / image-answer grading — photos of worked problems a learner
1003
+ * submits to the vision grader (assessment written-item photo answers +
1004
+ * the standalone "Grade my handwritten work" surface). Same hidden
1005
+ * `system-files/` class (kept out of the Files browser + Recents).
1006
+ */
1007
+ readonly SYSTEM_IMAGE_GRADE: "system-files/image-grade";
1008
+ readonly SYSTEM_IMAGE_GRADE_RESPONSES: "system-files/image-grade/responses";
1009
+ /**
1010
+ * LEGACY user-namespace FastFire paths (pre-2026-07 relocation). Kept as a
1011
+ * defensive tree + Recents guard until every row is backfilled under
1012
+ * `system-files/fastfire/`.
1013
+ */
1014
+ readonly FASTFIRE_SESSIONS: "FastFire/sessions";
1015
+ readonly FASTFIRE_RESPONSES: "FastFire/responses";
1016
+ };
1017
+ /**
1018
+ * Files attached to a specific task.
1019
+ * folderForTask("abc-123") → "Task Attachments/abc-123"
1020
+ */
1021
+ declare function folderForTask(taskId: string): string;
1022
+ /**
1023
+ * Files attached to a specific War Room tile.
1024
+ * folderForWarRoomThread("abc-123") → "War Room/abc-123"
1025
+ */
1026
+ declare function folderForWarRoomThread(threadId: string): string;
1027
+ /**
1028
+ * Files attached to a specific chat / conversation.
1029
+ * folderForConversation("xyz") → "Chat Attachments/xyz"
1030
+ */
1031
+ declare function folderForConversation(conversationId: string): string;
1032
+ /**
1033
+ * Assets for a specific agent app (favicon, icons, etc.).
1034
+ * folderForAgentApp("abc") → "Agent Apps/abc"
1035
+ */
1036
+ declare function folderForAgentApp(appId: string): string;
1037
+ /**
1038
+ * Per-podcast episode assets.
1039
+ */
1040
+ declare function folderForPodcast(podcastId: string): string;
1041
+ /**
1042
+ * Per-org shared assets.
1043
+ * folderForOrg("org-abc") → "Shared Assets/orgs/org-abc"
1044
+ */
1045
+ declare function folderForOrg(orgId: string): string;
1046
+ /**
1047
+ * Per-agent-block assets (icons, illustrations, attached media).
1048
+ * folderForAgentBlock("blk_123") → "Agent Apps/blocks/blk_123"
1049
+ */
1050
+ declare function folderForAgentBlock(blockId: string): string;
1051
+ /** Per-org capture subtree. Org id in the path keeps each workspace's
1052
+ * captures a DISTINCT global folder path (the server allows one folder
1053
+ * path per user, pinned to one org — a flat `Captures/Videos` cannot exist
1054
+ * under two orgs). Falls back to the flat path when there is no active org
1055
+ * (personal context).
1056
+ *
1057
+ * Nested output still resolves to `personal` visibility: the
1058
+ * `resolveDefaultVisibility` rule keys on the `Captures` prefix (a
1059
+ * longest-prefix match), so `Captures/<org>/Videos` matches the same rule as
1060
+ * the flat `Captures/Videos`. The org id in the path is filing, not access —
1061
+ * personal-visibility files stay owner-gated regardless of org. */
1062
+ declare function folderForCaptures(kind: "photo" | "video" | "audio", orgId?: string | null): string;
1063
+ /** Per-item product-capture folder: `Product Captures/<orgId>/<leaf>`.
1064
+ * `leaf` is the item's sanitized code (the QR value / SKU) when known at
1065
+ * creation, else the item id — set ONCE at item creation and never renamed.
1066
+ * Org id in the path for the same one-path-one-org server rule as
1067
+ * `folderForCaptures`. Files here upload with explicit `visibility:
1068
+ * "internal"` (org-wide access is the point of the surface), so the
1069
+ * `resolveDefaultVisibility` prefix rules are deliberately not consulted. */
1070
+ declare function folderForProductCaptureItem(orgId: string, leaf: string): string;
1071
+ /**
1072
+ * Commerce-intake artifact folder for one asset (or the batch-level stream in
1073
+ * untracked mode — pass the batch id as `leaf`). Fixed at creation, NEVER
1074
+ * renamed — a QR/serial that arrives later lives on identifier rows only
1075
+ * (PROTOTYPE-CONCEPTS P13: the cloud path is never renamed).
1076
+ */
1077
+ declare function folderForIntakeAsset(orgId: string, batchId: string, leaf: string): string;
1078
+ /** True if the folder is under `.matrx-tmp/` (user-hidden). */
1079
+ declare function isHiddenFolder(folderPath: string): boolean;
1080
+ /**
1081
+ * True if `path` (a `cld_files.file_path` or `cld_folders.folder_path`)
1082
+ * points at a backend-owned system / infrastructure row that must NEVER
1083
+ * surface in the user's tree.
1084
+ *
1085
+ * Today this covers:
1086
+ * - `system-files` / `system-files/...` — backfilled SOCIAL_BASELINE
1087
+ * variant rows (og/thumb/tiny/page1_url) and their per-source-file
1088
+ * folders. ~3–4 rows per uploaded image, ~10k+ for active accounts.
1089
+ * - `generations` / `generations/...` — the backend AI-generation
1090
+ * registry root (image/video/audio output). Mirrors the server's
1091
+ * `is_system_path()`, which treats BOTH `system-files` and
1092
+ * `generations` as system; the tree RPC already drops these, this is
1093
+ * the FE-side parity guard at every other tree boundary.
1094
+ * - `.matrx-tmp` / `.matrx-tmp/...` — ephemeral staging the user
1095
+ * shouldn't see or manage.
1096
+ *
1097
+ * The predicate is intentionally path-based (not `derivation_kind` or
1098
+ * `parent_file_id`) because the Python backfill didn't populate those
1099
+ * fields on variant rows. Once they do, we'll add data-shape signals
1100
+ * here and drop the path heuristic.
1101
+ *
1102
+ * Pass either a file path or a folder path — both follow the same
1103
+ * convention (system paths are prefix-rooted).
1104
+ */
1105
+ declare function isSystemPath(path: string | null | undefined): boolean;
1106
+ /**
1107
+ * THE VISIBILITY RULE IS THE DATABASE'S, NOT THIS FILE'S.
1108
+ *
1109
+ * `isHiddenFromUserTree()` and `isFastFirePath()` used to live here and were a
1110
+ * SECOND copy of the server's rule — the browser hid top-level `FastFire/**`
1111
+ * that the server called user-visible, so the sync daemon wrote those bytes to
1112
+ * disk while the browser refused to show them (folder-sync DECISIONS D16,
1113
+ * SPEC-SERVER §1.4 consumer 2). They are deleted.
1114
+ *
1115
+ * Rows that arrive through `get_user_file_tree` are already filtered by
1116
+ * `files.is_user_visible_path` — never filter them again. Realtime payloads,
1117
+ * which bypass the RPC, use the parity-tested mirror in
1118
+ * `features/files/utils/user-visible.ts`.
1119
+ */
1120
+ /**
1121
+ * RECENTS IS THE DATABASE'S RULE TOO. `isExcludedFromRecents`,
1122
+ * `isGeneratedContentPath` and `isSystemManagedContentPath` lived here as a
1123
+ * client-only Recents rule; they are deleted (2026-09-24). Their roots moved
1124
+ * into the one declaration (`files.is_recent_activity*`, aidream
1125
+ * matrx_files/user_visible.py RECENT_EXCLUDED_ROOTS) and the browser applies
1126
+ * its parity-guarded mirror, `isRecentActivityFile` / `isRecentActivityPath`
1127
+ * in `features/files/utils/user-visible.ts`. Rule + registry: common-docs
1128
+ * systems/files/file-service/USER_FILES_VS_MACHINE_FILES.md.
1129
+ */
1130
+ /**
1131
+ * True if the folder path matches one of our canonical conventions (either
1132
+ * visible or hidden). Useful for UI that wants to show a pretty icon next
1133
+ * to known folders.
1134
+ */
1135
+ declare function isConventionalFolder(folderPath: string): boolean;
1136
+ /**
1137
+ * Maps a CloudFolders constant → human-friendly description for tooltips /
1138
+ * empty states. Extend as needed.
1139
+ */
1140
+ declare const CloudFolderDescriptions: Record<string, string>;
1141
+ /**
1142
+ * Resolve the default visibility for a given folder path. Longest-prefix
1143
+ * match against `DEFAULT_VISIBILITY_RULES`; falls back to `"personal"` for
1144
+ * any folder not in the table. Use at upload sites that don't have an
1145
+ * obvious context-driven choice.
1146
+ */
1147
+ declare function resolveDefaultVisibility(folderPath: string): Visibility;
1148
+
1149
+ /** Stable, narrow video publish-date text for cards, rows, and embeds. */
1150
+ declare function formatVideoPublishDate(value: string | null | undefined): string;
1151
+ /** Accessible detail for the compact video publish-date treatment. */
1152
+ declare function formatVideoPublishDateTitle(value: string | null | undefined): string;
1153
+ /**
1154
+ * Read a provider/schema publish date from the common metadata envelopes used
1155
+ * by research media, crawled site videos, and brand-library assets.
1156
+ */
1157
+ declare function videoPublishDateFromMetadata(value: unknown): string | null;
1158
+
1159
+ /**
1160
+ * Shared bounded registry for ephemeral `URL.createObjectURL` blobs.
1161
+ *
1162
+ * Consumers that create local blob URLs use this primitive so previews cannot
1163
+ * leak memory without bound. Explicit cleanup remains preferred; the global
1164
+ * ceiling is a final safety net for callers that no longer own their URL.
1165
+ */
1166
+ /** Create a tracked object URL; evicts the oldest if over the cap. */
1167
+ declare function createTrackedObjectUrl(blob: Blob): string;
1168
+ /** Explicitly revoke a tracked URL when a consumer knows it is finished. */
1169
+ declare function revokeTrackedObjectUrl(url: string | undefined | null): void;
1170
+
1171
+ export { type AudioBlock, CloudFolderDescriptions, CloudFolders, type DocumentBlock, type ExternalAudioBlock, type ExternalDocumentBlock, ExternalFetchError, type ExternalImageBlock, type ExternalVideoBlock, FileAccessDeniedError, FileDeletedError, FileHandlerError, type FileHandlerErrorCode, type FileIdentityHint, FileNotFoundError, FileUploadError, type ImageBlock, type MatrxAudioBlock, type MatrxDocumentBlock, type MatrxImageBlock, type MatrxVideoBlock, type MediaBlockKindArg, type MediaGenerationKind, type MediaGenerationMetadata, type MediaGenerationMetadataWire, type MediaKind, type MediaOrigin, type MediaRef, type MediaStatus, type MediaVisibility, type PermissionLevel, ShareLinkInvalidError, type UnifiedImageBlock, type UnifiedMediaBlock, UploadCancelledError, type VideoBlock, VideoPublishDate, type Visibility, type WireMediaBlock, type WireMediaBlockData, type YouTubeBlock, blockFromMediaRef, composeUploadFolderPath, createTrackedObjectUrl, deriveViewerUrl, extractFileIdFromUrl, fileIdToMediaRef, folderForAgentApp, folderForAgentBlock, folderForCaptures, folderForConversation, folderForIntakeAsset, folderForOrg, folderForPodcast, folderForProductCaptureItem, folderForTask, folderForWarRoomThread, formatVideoPublishDate, formatVideoPublishDateTitle, fromCxAudioPart, fromCxMediaPart, fromCxVideoPart, fromImageOutputData, fromMediaBlock, fromPartialImageData, fromRenderBlock, imageBlockFromMediaRef, isAudioBlock, isConventionalFolder, isDocumentBlock, isExternalImageBlock, isExternalMediaBlock, isHiddenFolder, isImageBlock, isMatrxImageBlock, isMatrxMediaBlock, isMediaBlockData, isSystemPath, isUnifiedImageBlock, isUnifiedMediaBlock, isUploadCancelledError, isVideoBlock, isYouTubeBlock, parseFilenameFromUrl, parseGenerationMetadata, resolveDefaultVisibility, revokeTrackedObjectUrl, toCxMediaPart, toVisibility, urlToMediaRef, videoBlockFromMediaRef, videoPublishDateFromMetadata };