@contenthero/sdk 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -125,6 +125,14 @@ export interface GenerateRequest {
125
125
  * also know the id up front and can poll `getGeneration` immediately.
126
126
  */
127
127
  outputId?: string;
128
+ /** Optional: place the generated asset onto this editor project's timeline in the same call. Omit for a
129
+ * standalone library output. Sync models (audio) place immediately; async models (image/video) place when
130
+ * the output finalizes. */
131
+ projectId?: string;
132
+ /** Optional placement intent; omitted = playhead when `playheadFrame` is given, else append at the end. */
133
+ placement?: PlacementIntent;
134
+ /** Optional interactive-fallback playhead frame (echo one from get_context for playhead-relative placement). */
135
+ playheadFrame?: number;
128
136
  }
129
137
  /** The nine Reference Board types. */
130
138
  export type BoardType = 'character' | 'pose' | 'mascot' | 'creature' | 'weapon' | 'vehicle' | 'object' | 'location' | 'shot';
@@ -174,6 +182,26 @@ export interface GenerateResult {
174
182
  outputUrls?: string[];
175
183
  /** True when a client-supplied `outputId` matched an existing job (no new work was started). */
176
184
  idempotentReplay?: boolean;
185
+ /** Where the asset is being placed (present only when `projectId` was supplied). Lets a caller chain further
186
+ * ops onto the placed clip/layer without a get_context hop, and see any placement warnings. */
187
+ placement?: PlacementResult;
188
+ }
189
+ /** The outcome of a one-call placement (present on a generate result when `projectId` was supplied). The ids are
190
+ * known at submit time (deterministic), so they are usable for chaining before the async asset finishes. */
191
+ export interface PlacementResult {
192
+ /** True while the async placement completes at finalize (image/video); absent for a synchronous audio place. */
193
+ pending?: boolean;
194
+ projectId: string;
195
+ /** The placed clip / layer id (deterministic). Chain further ops (animate, reposition, reorder) onto it. */
196
+ itemId?: string;
197
+ /** 'canvas' when placed as a layer on a slide, 'editor' when placed as a clip on a track. Mirrors
198
+ * `project.kind` exactly, rather than renaming it. */
199
+ surface?: 'canvas' | 'editor';
200
+ /** Canvas only: the placed layer id (same as itemId) and the resolved target slide. */
201
+ layerId?: string;
202
+ slideId?: string;
203
+ /** Non-fatal placement notes (e.g. an ambiguous slide fallback). */
204
+ warnings?: string[];
177
205
  }
178
206
  /**
179
207
  * Result of a get_cost preflight (`estimateCost` / `estimateBoardCost`): the credit
@@ -187,6 +215,131 @@ export interface CostEstimate {
187
215
  modelId?: string;
188
216
  contentType?: 'image' | 'video' | 'audio';
189
217
  }
218
+ /**
219
+ * Request for `editAudio`: transform an existing audio file into a new one with
220
+ * an audio-processing model (audio input -> audio output), e.g. voice isolation.
221
+ * The sibling of `generate` for the existing-audio -> audio shape.
222
+ */
223
+ /**
224
+ * Where a generated/processed clip lands on an editor project's timeline. All positional fields are in
225
+ * SECONDS (resolved to frames server-side via the project fps). Omitted intent = the playhead when a
226
+ * `playheadFrame` is supplied, else append at the end of the timeline.
227
+ */
228
+ export type PlacementIntent = TimelinePlacementIntent | CanvasPlacementIntent;
229
+ /** Placement onto a VIDEO timeline: a clip on a track at a time. */
230
+ export type TimelinePlacementIntent =
231
+ /** Land after the last clip on the best track of the clip's kind, or on a freshly spawned track. */
232
+ {
233
+ mode: 'append';
234
+ }
235
+ /** Land at an explicit time. `track` selects the track: a track id, 'overlay' (a non-primary track, reused
236
+ * or spawned), or 'primary' (the main track, media only); omitted uses the default track. `durationSeconds`
237
+ * sets an IMAGE point clip's length (ignored for video/audio). */
238
+ | {
239
+ mode: 'at';
240
+ startSeconds?: number;
241
+ track?: string;
242
+ durationSeconds?: number;
243
+ }
244
+ /** Land at the current playhead (the interactive fallback). `durationSeconds` sets an IMAGE point clip's length. */
245
+ | {
246
+ mode: 'atPlayhead';
247
+ durationSeconds?: number;
248
+ }
249
+ /** Swap in place for an existing clip; the new clip inherits its track + start. `duration` keeps the new
250
+ * clip's own length and ripples ('natural', default) or trims it to the replaced slot ('match'). */
251
+ | {
252
+ mode: 'replace';
253
+ itemId: string;
254
+ duration?: 'natural' | 'match';
255
+ }
256
+ /** Fill or cover a time span. `track` selects the track (id / 'overlay' / 'primary'); omitted uses the default. */
257
+ | {
258
+ mode: 'range';
259
+ startSeconds?: number;
260
+ endSeconds?: number;
261
+ track?: string;
262
+ fit?: 'cover' | 'trim' | 'overwrite';
263
+ };
264
+ /** Placement onto a CANVAS design: a layer on a slide at a position + size (6A A8). Flat (no `mode`); all fields
265
+ * optional. Positions/sizes are design pixels. Omitting `slideId`/`slideIndex` targets the focused slide. */
266
+ export interface CanvasPlacementIntent {
267
+ slideId?: string;
268
+ slideIndex?: number;
269
+ fit?: 'contain' | 'cover' | 'none';
270
+ anchor?: 'center' | 'top-left' | 'top' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right';
271
+ x?: number;
272
+ y?: number;
273
+ width?: number;
274
+ height?: number;
275
+ /** Make the generated asset the slide's background rather than a free layer (placed full-bleed, promoted into
276
+ * the background slot once it lands). Ignores anchor / x / y / width / height. */
277
+ asBackground?: boolean;
278
+ }
279
+ export interface EditAudioRequest {
280
+ /** The audio-processing model to run. */
281
+ modelId: string;
282
+ /**
283
+ * Public URL (or a previous output id) of the source audio to process.
284
+ *
285
+ * Required in FILE mode and must be OMITTED in in-place mode, where the sources come from the clips
286
+ * themselves. The two are mutually exclusive: one produces a new library asset, the other edits a timeline.
287
+ */
288
+ sourceUrl?: string;
289
+ /** Source audio length in seconds. Required for a cost estimate; on a real run
290
+ * it refines the estimate (the authoritative charge is the processed duration). */
291
+ durationSeconds?: number;
292
+ /** Optional: place the processed audio onto this editor project's timeline in the same call. Omit for a
293
+ * standalone library output (today's behavior). */
294
+ projectId?: string;
295
+ /** Optional placement intent; omitted = playhead when `playheadFrame` is given, else append at the end. */
296
+ placement?: PlacementIntent;
297
+ /** Optional interactive-fallback playhead frame (echo one from get_context for playhead-relative placement). */
298
+ playheadFrame?: number;
299
+ /**
300
+ * IN-PLACE mode: enhance the audio OF EXISTING CLIPS on `projectId`, rather than processing a standalone file.
301
+ *
302
+ * Requires `projectId` and `modelId: 'auphonic-enhance'`. Omitting `clipIds` while passing
303
+ * `enhanceClips: true` means every audible clip on the timeline, the same whole-timeline default the sibling
304
+ * timeline ops use. Silenced clips are excluded automatically; enhancing audio nobody can hear would spend
305
+ * credits and then overwrite something the user deliberately muted.
306
+ *
307
+ * ⚠️ RETURNS A LIST on `outputs`. Auphonic estimates a noise profile and picks a loudness target per
308
+ * PRODUCTION, so one source's clips are concatenated and enhanced as a SINGLE job. Grouping stops at the
309
+ * source because a profile spanning two recordings is an average of two rooms and fits neither, so a
310
+ * selection covering three recordings is three jobs and three outputIds.
311
+ */
312
+ clipIds?: string[];
313
+ /** Opt in to in-place mode without naming clips (whole timeline). Implied when `clipIds` is present. */
314
+ enhanceClips?: boolean;
315
+ }
316
+ /** One in-place enhancement job: the clips of a single source, concatenated and enhanced together. */
317
+ export interface EnhanceClipsJob {
318
+ outputId: string;
319
+ creditsEstimate?: number;
320
+ /** Every clip this job's pieces will be applied to. */
321
+ clipIds: string[];
322
+ /** How many distinct windows were concatenated into this job. */
323
+ windows: number;
324
+ }
325
+ /**
326
+ * The result of `editAudio`, which serves two shapes.
327
+ *
328
+ * FILE mode returns the `GenerateResult` fields. IN-PLACE mode returns `outputs`, one per source, and echoes
329
+ * the first on `outputId` so a single-source project can be awaited without unpacking the list. `status` is
330
+ * `'noop'` when the selection contained no audible audio, which is deliberately distinguishable from a failure.
331
+ */
332
+ export interface EditAudioResult extends Omit<GenerateResult, 'status'> {
333
+ status: 'processing' | 'completed' | 'noop';
334
+ /** In-place mode only: one job per source. */
335
+ outputs?: EnhanceClipsJob[];
336
+ /** In-place mode only: the project the pieces are applied to. */
337
+ projectId?: string;
338
+ /** In-place mode only: selected clips skipped because they are silenced. */
339
+ silencedClipsExcluded?: number;
340
+ /** Present with `status: 'noop'`, explaining why nothing ran. */
341
+ note?: string;
342
+ }
190
343
  /** A generation record as returned by `getGeneration` and `generateAndWait`. */
191
344
  export interface Generation {
192
345
  outputId: string;
@@ -199,6 +352,16 @@ export interface Generation {
199
352
  error: string | null;
200
353
  createdAt: string;
201
354
  completedAt: string | null;
355
+ /** The terminal signal to await when a generation carries a project PLACEMENT (a fresh asset placed on a canvas
356
+ * / timeline, or an in-place background removal): true once the output exists AND its placement side-effect
357
+ * (the placeholder->asset swap, the cutout, a background promotion) has been applied to the project. `status`
358
+ * flips to 'completed' when the asset exists and billing settles, which can precede the swap; `waitForGeneration`
359
+ * keys on `settled` so "done" always implies the visible composition change is in. Absent (older servers) is
360
+ * treated as settled. Outputs with no placement are settled as soon as they complete. */
361
+ settled?: boolean;
362
+ /** Where the asset was placed (present only when `projectId` was supplied to generate). Carried from the submit
363
+ * response through `generateAndWait` so a caller gets the placement outcome alongside the finished asset. */
364
+ placement?: PlacementResult;
202
365
  }
203
366
  /** Subscription tiers the API normalizes balances against. */
204
367
  export type SubscriptionTier = 'mortal' | 'hero' | 'champion' | 'legend';
@@ -234,6 +397,11 @@ export interface Transcription {
234
397
  wordCount: number;
235
398
  /** Source audio length in seconds, when known. */
236
399
  durationSeconds: number | null;
400
+ /**
401
+ * ContentHero credits charged for the run. Zero only when the user's own ElevenLabs
402
+ * key covered it, in which case the provider billed them directly.
403
+ */
404
+ creditsUsed: number;
237
405
  }
238
406
  /** An avatar as returned by `listAvatars` (the list projection). */
239
407
  export interface AvatarSummary {
@@ -252,6 +420,8 @@ export interface AvatarLook {
252
420
  imageUrl: string | null;
253
421
  lookType: string | null;
254
422
  isDefault: boolean;
423
+ isFavorited: boolean;
424
+ isArchived: boolean;
255
425
  }
256
426
  /** Full avatar detail as returned by `getAvatar`. */
257
427
  export interface Avatar extends AvatarSummary {
@@ -281,6 +451,11 @@ export interface Voice extends VoiceSummary {
281
451
  description: string | null;
282
452
  useCase: string | null;
283
453
  }
454
+ /** Options for `listVoices`. */
455
+ export interface ListVoicesOptions {
456
+ /** When true, return only favorited voices. */
457
+ favorited?: boolean;
458
+ }
284
459
  /** A brand kit as returned by `listBrandKits` (the list projection). */
285
460
  export interface BrandKitSummary {
286
461
  id: string;
@@ -293,6 +468,13 @@ export interface BrandKitSummary {
293
468
  isArchived: boolean;
294
469
  createdAt: string | null;
295
470
  }
471
+ /** Options for `listBrandKits`. */
472
+ export interface ListBrandKitsOptions {
473
+ /** When true, return only favorited brand kits. */
474
+ favorited?: boolean;
475
+ /** When true, return only archived brand kits (default excludes archived). */
476
+ archived?: boolean;
477
+ }
296
478
  /** A brand/inspiration account linked to a brand kit. */
297
479
  export interface BrandKitAccount {
298
480
  /** The tracked-account id; feeds getInspirationAccount / getBrandAccountPerformance. */
@@ -440,6 +622,13 @@ export interface UpdateBrandKitSectionInput {
440
622
  }
441
623
  /** A studio output's media kind. */
442
624
  export type MediaType = 'image' | 'video' | 'audio' | 'transcript';
625
+ /**
626
+ * Which library a media read targets. 'creations' = studio generations (with variations);
627
+ * 'uploads' = the editor Uploads tab (the user-level upload library); 'stock' = stock media
628
+ * the user has used (cached and reusable); 'all' = every library merged newest-first, each item
629
+ * self-describing via its `source` (list only; a `get` uses the item's specific source).
630
+ */
631
+ export type MediaSource = 'creations' | 'uploads' | 'stock' | 'all';
443
632
  /** One variation (slot) of a studio output. */
444
633
  export interface MediaVariation {
445
634
  /** 1-based variation number (matches the UI and a share link's ?v=N). */
@@ -447,8 +636,29 @@ export interface MediaVariation {
447
636
  url: string | null;
448
637
  status: string;
449
638
  isFavorited: boolean;
639
+ isArchived: boolean;
640
+ /**
641
+ * MEASURED PIXEL GEOMETRY of this slot's image. `content` is the ARTWORK's bounds within the file (its
642
+ * alpha bounds), so a padded logo can be placed and aligned by what is VISIBLE rather than by its file
643
+ * rectangle. Absent until the asset has been measured. The same field appears on a media-batch item, so
644
+ * the CLI and the MCP report identical facts.
645
+ */
646
+ geometry?: {
647
+ width: number;
648
+ height: number;
649
+ content?: {
650
+ x: number;
651
+ y: number;
652
+ width: number;
653
+ height: number;
654
+ };
655
+ };
450
656
  }
451
- /** A studio output as returned by `listMedia` (the list projection). */
657
+ /**
658
+ * One atomic library asset as returned by `listMedia`. The grain is a VARIATION (an individual media file),
659
+ * not a whole generation: a studio generation with N variations lists as N items sharing one `id` but with
660
+ * distinct `variant`. The universal identity is (id, variant).
661
+ */
452
662
  export interface MediaSummary {
453
663
  id: string;
454
664
  type: MediaType;
@@ -456,13 +666,27 @@ export interface MediaSummary {
456
666
  prompt: string | null;
457
667
  status: string;
458
668
  createdAt: string | null;
459
- variationCount: number;
460
- /** Resolved (non-null) variation URLs. */
461
- urls: string[];
669
+ /**
670
+ * 0-based canonical slot of this variation (matches a share link's ?v=N as variant+1, and getMedia's
671
+ * `<id>-<N>` token as N = variant+1). Single-asset sources (uploads, stock) are always 0.
672
+ */
673
+ variant: number;
674
+ /** This variation's resolved URL (null only in a detail view whose slots have no media yet). */
675
+ url: string | null;
676
+ /** Total variations in the parent generation this variation belongs to (1 for uploads/stock). */
677
+ generationSize: number;
678
+ /** Whether THIS variation is favorited (studio per-slot; uploads/stock are go-forward, false). */
679
+ isFavorited: boolean;
462
680
  /** Asset class: 'creation' (default), 'board' (a reference board), or 'look'. */
463
681
  kind: string | null;
464
682
  /** Board type when kind is 'board' (character, weapon, location, etc.); else null. */
465
683
  boardType: string | null;
684
+ /** Which library this item came from ('creations' | 'uploads' | 'stock'); self-describing. */
685
+ source: MediaSource;
686
+ /** Original file name (uploads); null for studio outputs. */
687
+ fileName: string | null;
688
+ /** Duration in seconds for a single-file item (uploads video/audio); null otherwise. */
689
+ durationSeconds: number | null;
466
690
  }
467
691
  /** Full studio output detail as returned by `getMedia`. */
468
692
  export interface MediaItem extends MediaSummary {
@@ -474,16 +698,209 @@ export interface MediaItem extends MediaSummary {
474
698
  variations: MediaVariation[];
475
699
  /** Set when the requested token addressed a single variation; else null. */
476
700
  selectedVariation: number | null;
701
+ /** Output-level representative still (video poster / optimized image preview), or null. */
702
+ thumbnailUrl: string | null;
703
+ }
704
+ /**
705
+ * VIDEO keyframe watch (opt-in): set any of these on a video item to get low-res keyframes across the
706
+ * [fromSec, toSec] source-time window (whole clip by default), so you can SEE the footage — the fix for
707
+ * "video returns no frame yet". Frame-based, so cost is decoupled from clip length.
708
+ */
709
+ export interface MediaClipWindow {
710
+ fromSec?: number;
711
+ toSec?: number;
712
+ /** How many keyframes (proportional default; service-capped). */
713
+ frames?: number;
714
+ }
715
+ /** One requested item for `getMediaBatch`: a raw URL, or an output id (+ variation); videos accept a window. */
716
+ export type MediaBatchItem = ({
717
+ url: string;
718
+ } | {
719
+ mediaId: string;
720
+ variation?: number;
721
+ }) & MediaClipWindow;
722
+ /**
723
+ * One resolved item from `getMediaBatch`, uniform across the url and mediaId
724
+ * paths. `url` is the single thing to fetch (the MCP turns it into an image
725
+ * block); `ok` is false with an `error` when the item could not be resolved.
726
+ */
727
+ export interface ResolvedMediaBatchItem {
728
+ ok: boolean;
729
+ /** Echo of the requested item, to correlate results with inputs. */
730
+ input: MediaBatchItem;
731
+ /** The media itself (image master, or video master). */
732
+ url: string | null;
733
+ /**
734
+ * The still image to view for this item: the image itself for images, the
735
+ * poster still for a video's primary variation, null when no still is available
736
+ * (audio, transcript, non-primary video variation, raw video url). The MCP turns
737
+ * this into an image block; SDK/CLI just surface the URL.
738
+ */
739
+ imageUrl: string | null;
740
+ type: MediaType | null;
741
+ model: string | null;
742
+ prompt: string | null;
743
+ /** The output id when resolved from a mediaId; null for a raw url. */
744
+ mediaId: string | null;
745
+ /** The 1-based variation this url represents (mediaId path), else null. */
746
+ variation: number | null;
747
+ /** Sibling variation numbers not returned here (mediaId-without-variation path). */
748
+ otherVariations: number[];
749
+ /**
750
+ * VIDEO keyframes (present only when the item requested a window on a video): low-res frames across the
751
+ * source-time window, each an inline `data:image/jpeg;base64,...`. The MCP turns each into an image block.
752
+ */
753
+ keyframes?: {
754
+ atSec: number;
755
+ dataUrl: string;
756
+ }[];
757
+ /**
758
+ * MEASURED PIXEL GEOMETRY from the storage spine. Absent for an asset that is not ours or not yet measured.
759
+ *
760
+ * `content` is the ARTWORK's bounds within the file (its alpha bounds). For a padded logo these differ
761
+ * sharply from the file rectangle, and using the file rectangle is what makes a placed logo look wrong:
762
+ * the true aspect is the artwork's, and aligning the file edge leaves a visible gap the width of the
763
+ * transparent margin. This is the same measurement the editor's selection, snapping and align-to-page use.
764
+ */
765
+ geometry?: {
766
+ width: number;
767
+ height: number;
768
+ /** Alpha bounds in SOURCE pixels. Equals the full frame for an image with no transparent margin. */
769
+ content?: {
770
+ x: number;
771
+ y: number;
772
+ width: number;
773
+ height: number;
774
+ };
775
+ };
776
+ /**
777
+ * MEASURED LENGTH in seconds, for anything time-based: video, audio, and whatever comes later.
778
+ *
779
+ * Absent for an asset that is not ours or not yet measured. This was missing entirely for audio, which made
780
+ * `editAudio` uncallable without downloading the file first, because it REQUIRES a `durationSeconds` to
781
+ * price the job and no read returned one.
782
+ */
783
+ durationSeconds?: number;
784
+ error?: string;
785
+ }
786
+ /** Result of `getMediaBatch`: one resolved entry per requested item, in order. */
787
+ export interface MediaBatchResult {
788
+ items: ResolvedMediaBatchItem[];
477
789
  }
478
790
  /** Options for `listMedia`. */
479
791
  export interface ListMediaOptions {
792
+ /** Which library to read; defaults to 'creations' (studio outputs). 'uploads' = the editor Uploads tab. */
793
+ source?: MediaSource;
480
794
  contentType?: MediaType | MediaType[];
481
795
  status?: string;
482
796
  /** Filter by asset class: 'creation', 'board', 'look', or 'upload'. */
483
797
  kind?: 'creation' | 'board' | 'look' | 'upload';
798
+ /** When true, return only outputs that have a favorited (non-archived) variation. */
799
+ favorited?: boolean;
800
+ /** When true, return only outputs that have an archived variation. */
801
+ archived?: boolean;
484
802
  limit?: number;
485
803
  offset?: number;
486
804
  }
805
+ /** Media kinds that semantic library search can return / filter by. */
806
+ export type MediaKind = 'image' | 'video' | 'audio';
807
+ /** A scene within a video asset that matched a media search, with its own relevance. */
808
+ export interface SearchMediaScene {
809
+ /** Scene start within the asset, in milliseconds. */
810
+ startMs: number;
811
+ /** Scene end within the asset, in milliseconds. */
812
+ endMs: number;
813
+ /** Cosine relevance of this scene to the query (0..1). */
814
+ relevance: number;
815
+ }
816
+ /** One asset (a single variation) returned by semantic library search. */
817
+ export interface SearchMediaResult {
818
+ /** The asset's record id (source_record_id). With `variant`, uniquely identifies the atomic asset. */
819
+ id: string;
820
+ /** The variation index within the record (studio generation output index; 0 for single-asset sources). */
821
+ variant: number;
822
+ /** The asset's origin table (studio_outputs, editor_uploads, stock_assets, brand_kits). */
823
+ sourceTable: string;
824
+ kind: MediaKind | null;
825
+ /** A resolved, usable URL for the asset (a cached edit proxy for stock). */
826
+ url: string | null;
827
+ /** The vision description of the asset. */
828
+ summary: string | null;
829
+ tags: string[];
830
+ /** Best cosine relevance of this asset to the query (0..1). */
831
+ relevance: number;
832
+ /** For videos, the specific scenes that matched, best first (empty for image/audio). */
833
+ scenes: SearchMediaScene[];
834
+ }
835
+ /** Options for searchMedia. */
836
+ export interface SearchMediaOptions {
837
+ /** Restrict results to these media kinds. Omit to search all kinds. */
838
+ kinds?: MediaKind[];
839
+ /** Maximum number of assets to return (default 12, max 50). */
840
+ limit?: number;
841
+ }
842
+ export type FolderType = 'manual' | 'smart';
843
+ /** The smart-folder query spec = the same filters the library search bar produces. */
844
+ export interface SmartFolderQuery {
845
+ text?: string;
846
+ kinds?: MediaKind[];
847
+ sources?: Array<'creations' | 'uploads' | 'stock'>;
848
+ facets?: Record<string, string>;
849
+ tags?: string[];
850
+ favoritedOnly?: boolean;
851
+ sort?: 'relevance' | 'recent' | 'name';
852
+ }
853
+ export interface Folder {
854
+ id: string;
855
+ name: string;
856
+ type: FolderType;
857
+ query: SmartFolderQuery | null;
858
+ parentId: string | null;
859
+ icon: string | null;
860
+ color: string | null;
861
+ position: number;
862
+ createdAt: string;
863
+ updatedAt: string;
864
+ }
865
+ /** A built-in derived folder (recents, favorites, edits, canvas, posts). */
866
+ export interface DerivedFolder {
867
+ key: string;
868
+ name: string;
869
+ }
870
+ /** One item inside a folder: media (variation-atomic) or an entity (project/post, manual folders only). */
871
+ export type FolderItem = {
872
+ type: 'media';
873
+ kind: MediaKind | null;
874
+ sourceTable: string;
875
+ sourceRecordId: string;
876
+ variant: number;
877
+ url: string | null;
878
+ summary: string | null;
879
+ isFavorited: boolean;
880
+ relevance?: number;
881
+ } | {
882
+ type: 'project' | 'post';
883
+ id: string;
884
+ name: string;
885
+ subtype: string | null;
886
+ };
887
+ export interface CreateFolderInput {
888
+ name: string;
889
+ type?: FolderType;
890
+ query?: SmartFolderQuery;
891
+ parentId?: string | null;
892
+ }
893
+ export interface UpdateFolderInput {
894
+ name?: string;
895
+ parentId?: string | null;
896
+ query?: SmartFolderQuery | null;
897
+ }
898
+ /** A pointer into a manual folder, by the universal variation-atomic identity. */
899
+ export interface FolderItemRef {
900
+ sourceTable: string;
901
+ sourceRecordId: string;
902
+ variant?: number;
903
+ }
487
904
  /** Fields to start a presigned media upload (phase 1 of uploadMedia). */
488
905
  export interface CreateMediaUploadInput {
489
906
  fileName: string;
@@ -494,6 +911,16 @@ export interface CreateMediaUploadInput {
494
911
  export interface CreateMediaUploadResult {
495
912
  outputId: string;
496
913
  uploadUrl: string;
914
+ /**
915
+ * Headers to send with the PUT, exactly as given.
916
+ *
917
+ * Optional because an older API deployment does not return it; when absent, send
918
+ * `Content-Type` alone. Once storage is R2 the presigned URL signs the owner in as
919
+ * `x-amz-meta-user_id` and a PUT without it is refused, so a client that hardcodes
920
+ * `Content-Type` breaks. Taking the headers from the server keeps this client
921
+ * store-agnostic and lets the API and the SDK deploy in either order.
922
+ */
923
+ uploadHeaders?: Record<string, string>;
497
924
  storagePath: string;
498
925
  expiresAt: string;
499
926
  }
@@ -810,6 +1237,8 @@ export interface ListOutliersOptions {
810
1237
  sortBy?: 'score' | 'date' | 'views';
811
1238
  /** Scope to the inspiration accounts linked to this brand kit. */
812
1239
  brandKitId?: string;
1240
+ /** When true, return only content the caller has favorited. */
1241
+ favorited?: boolean;
813
1242
  limit?: number;
814
1243
  offset?: number;
815
1244
  }
@@ -894,4 +1323,498 @@ export interface PlatformSchema {
894
1323
  /** Per-format field template: field names + default values (File handles stripped). */
895
1324
  fieldTemplatesByFormat: Record<string, Record<string, unknown>>;
896
1325
  }
1326
+ /** The asset types that can be favorited. */
1327
+ export type FavoriteAssetType = 'post' | 'voice' | 'brand_kit' | 'project' | 'inspiration_content' | 'gallery' | 'transition';
1328
+ /** The asset types that can be archived. */
1329
+ export type ArchiveAssetType = 'post' | 'brand_kit' | 'brand_kit_section' | 'project';
1330
+ /**
1331
+ * The target of a favorite / unfavorite call.
1332
+ *
1333
+ * Provide `assetType` + `id` for a top-level asset, OR `id` + `variationIndex`
1334
+ * (1-based) to target a single studio output variation slot, in which case the
1335
+ * id is a studio output id and `assetType` is ignored.
1336
+ */
1337
+ export interface FavoriteInput {
1338
+ assetType?: FavoriteAssetType;
1339
+ id: string;
1340
+ /** 1-based variation slot; when set, `id` is a studio output id. */
1341
+ variationIndex?: number;
1342
+ }
1343
+ /**
1344
+ * The target of an archive / unarchive call.
1345
+ *
1346
+ * Provide `assetType` + `id` for a top-level asset, OR `id` + `variationIndex`
1347
+ * (1-based) to target a single studio output variation slot, in which case the
1348
+ * id is a studio output id and `assetType` is ignored.
1349
+ */
1350
+ export interface ArchiveInput {
1351
+ assetType?: ArchiveAssetType;
1352
+ id: string;
1353
+ /** 1-based variation slot; when set, `id` is a studio output id. */
1354
+ variationIndex?: number;
1355
+ }
1356
+ /** Which write model a project's composition uses: 2D canvas layers or a 1D editor timeline. */
1357
+ export type EditorSurface = 'canvas' | 'editor';
1358
+ /** One op in the shared editor/canvas op vocabulary. Opaque here: shaped by the reducer for the
1359
+ * project's surface (canvas layer/slide ops, or timeline clip ops). Every op has an `op` name. */
1360
+ export interface EditorOp {
1361
+ op: string;
1362
+ /**
1363
+ * Optional client-generated stable id (uuid) for this op. When omitted, `applyEditorOps` generates one
1364
+ * for you before sending. It is the op's identity across its whole lifecycle: the server persists it, the
1365
+ * unique-per-project constraint makes a retried op idempotent, and a live editor client uses it to ignore
1366
+ * the broadcast echo of its own edit.
1367
+ */
1368
+ op_id?: string;
1369
+ [key: string]: unknown;
1370
+ }
1371
+ /** Input to `applyEditorOps`. */
1372
+ export interface ApplyEditorOpsInput {
1373
+ /** The project to edit. Its `surface` selects the op vocabulary (canvas layers vs timeline clips). */
1374
+ projectId: string;
1375
+ /** The ops to apply, in order. */
1376
+ ops: EditorOp[];
1377
+ /** Optimistic-concurrency token from a prior read. When omitted, the server uses the current revision. */
1378
+ expectedRevision?: number;
1379
+ /** A short human intent for the edit (attribution + observability). */
1380
+ userIntent?: string;
1381
+ /** When true, the result includes a fingerprint-validated preview still URL of the post-edit state. */
1382
+ includeRenderUrl?: boolean;
1383
+ }
1384
+ /** The per-op outcome (surface-agnostic; created ids normalized across surfaces). */
1385
+ export interface EditorOpResult {
1386
+ op: string;
1387
+ /** The op's stable id (the one you sent, or the one generated for you), echoed back for every op. */
1388
+ opId: string;
1389
+ ok: boolean;
1390
+ error?: string;
1391
+ warnings?: string[];
1392
+ createdIds?: string[];
1393
+ /** For an async effect op (remove_background): the studio_outputs id of the dispatched job, so the caller can
1394
+ * wait_for_generation on it. Present only on a successfully-dispatched async op. */
1395
+ generatingOutputId?: string;
1396
+ }
1397
+ /** Result of `applyEditorOps`: the new revision + per-op results. */
1398
+ export interface ApplyEditorOpsResult {
1399
+ revision: number;
1400
+ results: EditorOpResult[];
1401
+ /** Present only when `includeRenderUrl` was set: a preview still URL of the resulting composition. */
1402
+ renderUrl?: string | null;
1403
+ }
1404
+ /** Which surface a project is: an editor (video timeline) or a canvas (slides/layers). */
1405
+ /**
1406
+ * Which surface a project lives on.
1407
+ *
1408
+ * Named `ProjectKind` for source compatibility; the field it describes is now `surface`. The API column was
1409
+ * renamed because `canvas` and `editor` are two of the product's eleven surfaces, while `kind` now means the
1410
+ * document shape (`tracks` | `slides`) on a project version.
1411
+ */
1412
+ export type ProjectKind = 'editor' | 'canvas';
1413
+ export type ProjectSurface = ProjectKind;
1414
+ /** Lightweight project list item (spans both surfaces), from `listProjects`. */
1415
+ export interface ProjectSummary {
1416
+ id: string;
1417
+ surface: string;
1418
+ /** @deprecated Alias for `surface`, still emitted for one release window. Prefer `surface`. */
1419
+ kind: string;
1420
+ title: string;
1421
+ orientation: string;
1422
+ width: number;
1423
+ height: number;
1424
+ thumbnailUrl: string | null;
1425
+ isArchived: boolean;
1426
+ isFavorited: boolean;
1427
+ createdAt: string | null;
1428
+ updatedAt: string | null;
1429
+ }
1430
+ /**
1431
+ * A project's full detail, from `getProject` (read-before-write) and returned by `createProject`. Extends
1432
+ * the summary with the composition `state` + `revision` (pass the revision back as `applyEditorOps`'s
1433
+ * `expectedRevision`) plus the link fields.
1434
+ */
1435
+ /** One clip group on the timeline: its members share a groupId, and the ordinal/name are stamped on each.
1436
+ * Rolled up by get_project so you can list groups + resolve members without scanning every clip. */
1437
+ export interface GroupSummary {
1438
+ id: string;
1439
+ /** Stable "Group N" number, assigned at creation and never renumbered. */
1440
+ ordinal: number | null;
1441
+ /** Optional user/agent-set name (via update_group); null means show "Group {ordinal}". */
1442
+ name: string | null;
1443
+ memberClipIds: string[];
1444
+ }
1445
+ export interface ProjectDetail extends ProjectSummary {
1446
+ revision: number;
1447
+ /** The full composition state (`{ slides }` for canvas, `{ tracks }` for the editor timeline). */
1448
+ state: unknown;
1449
+ /** Timeline clip groups rolled up from the clips. Empty for canvas. Use to list/target groups
1450
+ * (update_group to rename, update_clips with `groupId` to bulk-edit a whole group). */
1451
+ groups: GroupSummary[];
1452
+ assetReferences: unknown;
1453
+ brandKitId: string | null;
1454
+ exportedPostId: string | null;
1455
+ exportedUrl: string | null;
1456
+ shareId: string | null;
1457
+ favoritedAt: string | null;
1458
+ archivedAt: string | null;
1459
+ /** A fingerprint-validated preview still URL. Present only when getProject is called with
1460
+ * `includeRenderUrl` (opt-in, since it may render). */
1461
+ renderUrl?: string | null;
1462
+ /**
1463
+ * The coordinate space LAYER GEOMETRY is expressed in. This is NOT `width`/`height`.
1464
+ *
1465
+ * `width`/`height` are the project's OUTPUT resolution (what a render produces). Layer boxes and
1466
+ * positions are in PREVIEW dims, the project scaled to a 960px longest edge, so a 2168x1152 project has
1467
+ * a 960x510 layer space. The two differ by 2.26x there, and nothing in the response previously said so,
1468
+ * which meant an external caller sizing a layer from `width`/`height` was silently wrong: an oversized
1469
+ * box is valid input, so no error was ever raised.
1470
+ *
1471
+ * Use it for any absolute geometry, and pass it as layerWidth/layerHeight for a FULL-FRAME layer.
1472
+ */
1473
+ compositionSpace?: {
1474
+ width: number;
1475
+ height: number;
1476
+ };
1477
+ }
1478
+ /** One live participant in `getContext`: who is present and on what surface/scope. */
1479
+ export interface LiveContextParticipant {
1480
+ userId: string;
1481
+ sessionId: string;
1482
+ /** The surface they are on: 'canvas' | 'editor' | 'studio' | 'content' | future surfaces. */
1483
+ surface: string;
1484
+ projectId: string | null;
1485
+ postId: string | null;
1486
+ /** ISO timestamp of their last activity. */
1487
+ updatedAt: string;
1488
+ }
1489
+ /** The result of `getContext`: the most-recent-active session's live context + the full participant set. */
1490
+ export interface LiveContextResult {
1491
+ /**
1492
+ * The most-recent-active session's context: a discriminated `{ surface, ...surfaceState }` object carrying
1493
+ * the focus (e.g. `focusedSlideId`, `playheadFrame`) and current selection. A short-lived `snapshotUrl` of
1494
+ * the live viewport is included ONLY when the read was made with `capture: true`. Null when no session is live.
1495
+ */
1496
+ context: Record<string, unknown> | null;
1497
+ /** Metadata for that default participant, or null when no one is live. */
1498
+ participant: LiveContextParticipant | null;
1499
+ /** Every currently-live participant, most-recent first, for multi-human callers. */
1500
+ participants: LiveContextParticipant[];
1501
+ }
1502
+ /** Options for `getContext`. */
1503
+ export interface GetContextInput {
1504
+ /** Scope to a specific project's presence (editor/canvas). Omit for the caller's most-recent surface anywhere. */
1505
+ projectId?: string;
1506
+ /**
1507
+ * Opt in to vision: also ping the live tab for a fresh viewport screenshot at read time, returned as a
1508
+ * short-lived `snapshotUrl`. Default false = structured-only (fast, never touches the live page). To see the
1509
+ * COMPOSED OUTPUT (not the user's screen) use `render` instead.
1510
+ */
1511
+ capture?: boolean;
1512
+ /**
1513
+ * Opt in to an inline render (never persisted), returned as image(s), so you can visually verify work while
1514
+ * iterating. `true` renders the current focus point as a still; use `mode` + the params below for a filmstrip.
1515
+ * Ephemeral (counts against no quota) and does not need a live tab. To watch a RAW source clip use `getMedia`
1516
+ * with a video item; for a composed VIDEO of a range use `createPreview` / `getPreview` (a job).
1517
+ */
1518
+ render?: boolean;
1519
+ /**
1520
+ * Render tier (inferred from the params when omitted): 'still' (one composed frame/slide) or 'filmstrip' (N
1521
+ * composed frames across an editor range).
1522
+ */
1523
+ mode?: 'still' | 'filmstrip';
1524
+ /** still (editor): which timeline frame. Omit to render the current playhead frame. */
1525
+ frame?: number;
1526
+ /** still (canvas): which slide (id). Omit to render the focused slide. */
1527
+ slideId?: string;
1528
+ /** still (canvas): which slide (1-based index; alternative to `slideId`). */
1529
+ slideIndex?: number;
1530
+ /** filmstrip: start timeline frame of the range (edit space). Omit to start at the beginning. */
1531
+ fromFrame?: number;
1532
+ /** filmstrip: end timeline frame of the range. Omit to run to the end. */
1533
+ toFrame?: number;
1534
+ /** filmstrip: how many frames to return. Omit for a proportional default. */
1535
+ count?: number;
1536
+ /**
1537
+ * still: render at an explicit DISPLAY width in pixels, so you can judge legibility at the size the output
1538
+ * will actually be seen (a classroom tile, a thumbnail, a feed card) rather than at full resolution, where
1539
+ * small type always looks fine. Height is derived from the composition's aspect ratio and is deliberately
1540
+ * not a parameter. Clamped to a sane range; the size actually produced comes back on `rendered`.
1541
+ */
1542
+ width?: number;
1543
+ }
1544
+ /** Input to `createPreview`: currently a short COMPOSED video of an editor range (ephemeral, job-based). */
1545
+ export interface PreviewInput {
1546
+ projectId: string;
1547
+ /** Start timeline frame of the range (edit space). Omit to start at the beginning. */
1548
+ fromFrame?: number;
1549
+ /** End timeline frame. Omit to run to the end (capped to a short preview length). */
1550
+ toFrame?: number;
1551
+ }
1552
+ /** The handle returned by `createPreview`; feed `renderId` + `bucketName` to `getPreview`. */
1553
+ export interface PreviewJob {
1554
+ renderId: string;
1555
+ bucketName: string;
1556
+ fromFrame: number;
1557
+ toFrame: number;
1558
+ durationSeconds: number;
1559
+ }
1560
+ /** The poll result for a preview render. */
1561
+ export interface PreviewStatus {
1562
+ status: 'rendering' | 'done' | 'failed';
1563
+ /** 0..1 while rendering. */
1564
+ progress?: number;
1565
+ /** Short-lived signed URL to the ephemeral preview output (present when status = 'done'). */
1566
+ url?: string;
1567
+ /** Estimated Lambda cost for this render (telemetry). */
1568
+ estimatedCostUsd?: number;
1569
+ error?: string;
1570
+ }
1571
+ /** A resolved selected editor timeline clip, threaded so you see the selection without a `getProject` hop. */
1572
+ export interface EditorSelectedItem {
1573
+ id: string;
1574
+ type: string;
1575
+ trackId?: string;
1576
+ /** Timeline start frame (edit space). */
1577
+ from: number;
1578
+ durationInFrames: number;
1579
+ /** Resolved media URL for image/video/audio clips, so you can fetch the raw clip directly. */
1580
+ mediaUrl?: string;
1581
+ }
1582
+ /** The inline render returned when `getContext` is called with `render`. Shape depends on the tier. */
1583
+ export interface LiveContextRender {
1584
+ /** The tier that produced this: 'still' (default) | 'filmstrip'. */
1585
+ mode?: 'still' | 'filmstrip';
1586
+ surface?: 'editor' | 'canvas';
1587
+ frame?: number;
1588
+ slideId?: string;
1589
+ slideIndex?: number;
1590
+ /** still: the pixel size of the returned image. Confirms what an explicit `width` request actually produced
1591
+ * (it is clamped), and reports the derived height. */
1592
+ width?: number;
1593
+ height?: number;
1594
+ /** still: `data:image/webp;base64,...` of the composed frame/slide. */
1595
+ dataUrl?: string;
1596
+ fromFrame?: number;
1597
+ toFrame?: number;
1598
+ frames?: Array<{
1599
+ frame?: number;
1600
+ dataUrl: string;
1601
+ }>;
1602
+ }
1603
+ /** Filters for `listProjects`. */
1604
+ export interface ListProjectsInput {
1605
+ /** 'archived' -> only archived; 'favorited' -> favorited + not archived; omitted -> not archived. */
1606
+ filter?: 'archived' | 'favorited';
1607
+ /** Restrict to one surface. Omitted returns both. */
1608
+ surface?: ProjectSurface;
1609
+ /** @deprecated Alias for `surface`, accepted for one release window. `surface` wins if both are set. */
1610
+ kind?: ProjectKind;
1611
+ /** Case-insensitive title search. */
1612
+ search?: string;
1613
+ }
1614
+ /** Input to `createProject`. All optional; the server applies the same defaults as the in-app new-project
1615
+ * flow (16:9 landscape, `editor` kind, an empty composition the app lazy-inits). */
1616
+ export interface CreateProjectInput {
1617
+ surface?: ProjectSurface;
1618
+ /** @deprecated Alias for `surface`, accepted for one release window. */
1619
+ kind?: ProjectKind;
1620
+ title?: string;
1621
+ orientation?: string;
1622
+ width?: number;
1623
+ height?: number;
1624
+ brandKitId?: string;
1625
+ }
1626
+ /** A source for `importProject`: a PowerPoint / Google Slides file URL, or a Canva design id. */
1627
+ export type ImportProjectSource = {
1628
+ type: 'pptx';
1629
+ fileUrl: string;
1630
+ } | {
1631
+ type: 'canva';
1632
+ designId: string;
1633
+ };
1634
+ /** Input to `importProject`. Creates a new canvas project from the imported deck. */
1635
+ export interface ImportProjectInput {
1636
+ source: ImportProjectSource;
1637
+ /** Title for the created project. Defaults to 'Imported deck'. */
1638
+ title?: string;
1639
+ /** Optional slide count for an early page-cap check. */
1640
+ pageCount?: number;
1641
+ }
1642
+ /** Options for starting a project export. All optional; defaults: format 'mp4', 720p, watermark on. */
1643
+ export interface StartExportInput {
1644
+ /** 'mp4' (both surfaces) or 'png'/'jpg' (both surfaces) or 'pdf'/'pptx' (canvas only). */
1645
+ format?: string;
1646
+ /** Video resolution (mp4): '480p'|'720p'|'1080p'|'2k'|'4k'. 1080p+ is plan-gated. */
1647
+ resolution?: string;
1648
+ /** Video quality (mp4): 'low'|'recommended'|'high'. */
1649
+ quality?: string;
1650
+ /** Keep the watermark. Removing it is plan-gated. Defaults true. */
1651
+ watermark?: boolean;
1652
+ /** Editor still (png/jpg) only: the timeline frame to render. Clamped to the composition length. Defaults 0. */
1653
+ frame?: number;
1654
+ }
1655
+ /** An export job. `mp4` starts as 'rendering' (poll it); canvas still/doc formats return 'completed'. */
1656
+ export interface ExportJob {
1657
+ exportId: string;
1658
+ /** 'pending' | 'rendering' | 'transferring' | 'completed' | 'failed'. */
1659
+ status: string;
1660
+ /** The final file URL, present when status is 'completed'. */
1661
+ outputUrl?: string | null;
1662
+ errorMessage?: string | null;
1663
+ /** 0..1 render progress. */
1664
+ progress?: number;
1665
+ }
1666
+ /** One format in the export catalog. */
1667
+ export interface ExportFormatSpec {
1668
+ format: string;
1669
+ surfaces: string[];
1670
+ /** true = async render job (poll it); false = returned completed immediately. */
1671
+ async: boolean;
1672
+ description: string;
1673
+ options: string[];
1674
+ }
1675
+ /** The export-format catalog, from `getExportFormats`. */
1676
+ export interface ExportFormatCatalog {
1677
+ formats: ExportFormatSpec[];
1678
+ resolutions: string[];
1679
+ qualities: string[];
1680
+ }
1681
+ /** One editable field on a layer/clip type (from the type-discovery catalogs). */
1682
+ export interface EditorTypeProp {
1683
+ name: string;
1684
+ /** A human-readable type hint (e.g. 'string', 'number', "'left' | 'center' | 'right'"). */
1685
+ type: string;
1686
+ description?: string;
1687
+ }
1688
+ /** A layer/clip type and its editable props. */
1689
+ export interface EditorTypeSpec {
1690
+ type: string;
1691
+ description: string;
1692
+ props: EditorTypeProp[];
1693
+ /** Which shared prop groups this type also accepts (keys of `sharedProps`). */
1694
+ supports: string[];
1695
+ /** A copy-pasteable minimal item skeleton for creating this type via add_item / insert_prebuilt_track. */
1696
+ example?: Record<string, unknown>;
1697
+ }
1698
+ /** How to CREATE clips + tracks (the creation ops of update_timeline), documented alongside the edit ops. */
1699
+ export interface EditorCreationSpec {
1700
+ description: string;
1701
+ ops: {
1702
+ op: string;
1703
+ shape: string;
1704
+ description: string;
1705
+ }[];
1706
+ }
1707
+ /** A timeline track type and the clip types it holds. */
1708
+ export interface EditorTrackSpec {
1709
+ trackType: string;
1710
+ description: string;
1711
+ holds: string[];
1712
+ }
1713
+ /** Shared prop groups reused across visual types (referenced by each type's `supports`). */
1714
+ export interface EditorSharedProps {
1715
+ base: EditorTypeProp[];
1716
+ transform: EditorTypeProp[];
1717
+ decoration: EditorTypeProp[];
1718
+ adjust: EditorTypeProp[];
1719
+ }
1720
+ /** The canvas layer-type catalog, from `getLayerTypes`. Makes `update_canvas` self-describing. */
1721
+ export interface LayerTypeCatalog {
1722
+ surface: 'canvas';
1723
+ description: string;
1724
+ sharedProps: EditorSharedProps;
1725
+ layerTypes: EditorTypeSpec[];
1726
+ /** The update_canvas op vocabulary: every layer + slide op an agent can emit, with its field signature. */
1727
+ ops?: EditorCreationSpec;
1728
+ }
1729
+ /** The editor timeline clip + track-type catalog, from `getTimelineTypes`. Makes `update_timeline`
1730
+ * self-describing. */
1731
+ export interface TimelineTypeCatalog {
1732
+ surface: 'editor';
1733
+ description: string;
1734
+ sharedProps: EditorSharedProps;
1735
+ clipTypes: EditorTypeSpec[];
1736
+ trackTypes: EditorTrackSpec[];
1737
+ /** How to CREATE clips + tracks (add_item, insert_track, insert_prebuilt_track), using each clipType `example`. */
1738
+ creation?: EditorCreationSpec;
1739
+ /** The EDIT ops of update_timeline (move_clip, trim_clip, disable_ranges, ...), with each field signature. */
1740
+ editOps?: EditorCreationSpec;
1741
+ }
1742
+ /** A spoken word with source-media timing, ABSOLUTE timeline frames, and per-word metadata (granularity 'word'). */
1743
+ export interface WordTiming {
1744
+ text: string;
1745
+ /** Start within the SOURCE media, in ms. */
1746
+ startMs: number;
1747
+ /** End within the SOURCE media, in ms (exclusive). */
1748
+ endMs: number;
1749
+ /** Absolute timeline frame this word starts on (feed straight into update_timeline split / range ops). */
1750
+ startFrame: number;
1751
+ /** Absolute timeline frame this word ends on. */
1752
+ endFrame: number;
1753
+ /** Diarized speaker, when known. */
1754
+ speakerId: string | null;
1755
+ /** Normalized 0-1 confidence, when known. */
1756
+ confidence: number | null;
1757
+ }
1758
+ /** A derived dead-air gap between words within a clip: source-media range + timeline frames + duration. */
1759
+ export interface Silence {
1760
+ startMs: number;
1761
+ endMs: number;
1762
+ durationMs: number;
1763
+ startFrame: number;
1764
+ endFrame: number;
1765
+ }
1766
+ /** A non-speech audio event (e.g. "[chuckles]", "[sighs]") with source-media timing + timeline frames. */
1767
+ export interface AudioEvent {
1768
+ text: string;
1769
+ startMs: number;
1770
+ endMs: number;
1771
+ startFrame: number;
1772
+ endFrame: number;
1773
+ speakerId: string | null;
1774
+ }
1775
+ /** One timeline clip's transcript segment: the words spoken within it plus its current enabled/disabled state. */
1776
+ export interface TranscriptSegment {
1777
+ /** The timeline clip id, targetable by update_timeline disable_ranges / set_disabled / delete_ranges. */
1778
+ clipId: string;
1779
+ /** Whether the clip is currently excluded from the render (disabled). */
1780
+ disabled: boolean;
1781
+ /** Why it was disabled ('silence' | 'manual' | 'agent'), when known. */
1782
+ disabledReason: string | null;
1783
+ /** The freeform note an agent left explaining this cut, when present. */
1784
+ disabledNote: string | null;
1785
+ /** Start of this clip's slice within its SOURCE media, in milliseconds. */
1786
+ sourceStartMs: number;
1787
+ /** End of this clip's slice within its SOURCE media, in milliseconds (exclusive). */
1788
+ sourceEndMs: number;
1789
+ /** The clip's start position on the TIMELINE, in frames (source range maps onto [fromFrame, fromFrame+durationFrames)). */
1790
+ fromFrame: number;
1791
+ /** The clip's length on the TIMELINE, in frames. */
1792
+ durationFrames: number;
1793
+ /** The words spoken within this clip, space-joined. Empty for a silence gap or untranscribed clip. */
1794
+ text: string;
1795
+ /** Word-level timing (granularity 'word' only), scoped to the requested window when given. */
1796
+ words?: WordTiming[];
1797
+ /** Derived dead-air gaps within this clip (granularity 'word' only). */
1798
+ silences?: Silence[];
1799
+ /** Non-speech audio events within this clip (granularity 'word' only; empty for pre-token-store media). */
1800
+ audioEvents?: AudioEvent[];
1801
+ }
1802
+ export interface TranscriptResult {
1803
+ projectId: string;
1804
+ /**
1805
+ * The project's current optimistic-concurrency revision. Pass it straight back as `applyEditorOps` /
1806
+ * update_timeline's `expectedRevision` to make a follow-up edit fail on a concurrent change instead of
1807
+ * clobbering it. `expectedRevision` is optional, so omit it to just apply to the current revision.
1808
+ */
1809
+ revision: number;
1810
+ fps: number;
1811
+ /** true when at least one clip's source media had a stored transcript. */
1812
+ mediaTranscribed: boolean;
1813
+ segmentCount: number;
1814
+ segments: TranscriptSegment[];
1815
+ /** The distinct diarized speakers present across the returned transcript, sorted. */
1816
+ speakers?: string[];
1817
+ /** Present only when nothing has been transcribed yet, explaining the empty result. */
1818
+ note?: string;
1819
+ }
897
1820
  //# sourceMappingURL=types.d.ts.map