@cueframe/kernel 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/_members/authoring/agent/errors.d.ts +8 -0
  2. package/dist/_members/authoring/index.d.ts +1 -1
  3. package/dist/_members/authoring/motion/materializeMotionPresetOps.d.ts +4 -0
  4. package/dist/_members/authoring/prompts/index.d.ts +0 -1
  5. package/dist/_members/authoring/types.d.ts +1 -1
  6. package/dist/_members/cloud-import/CloudImportService.d.ts +3 -4
  7. package/dist/_members/cloud-import/ImportProvider.d.ts +3 -7
  8. package/dist/_members/cloud-import/cloudConfig.d.ts +0 -6
  9. package/dist/_members/cloud-import/index.d.ts +2 -4
  10. package/dist/_members/composition/graphics-plan/schemas.d.ts +20 -20
  11. package/dist/_members/composition/primitive-catalog/projection.d.ts +2 -1
  12. package/dist/_members/composition/projection/behind-readiness.d.ts +4 -2
  13. package/dist/_members/composition/validators/brandKit.d.ts +54 -94
  14. package/dist/_members/composition/validators/composePlan.d.ts +52 -52
  15. package/dist/_members/composition/validators/composition.d.ts +213 -253
  16. package/dist/_members/composition/validators/font.d.ts +2 -2
  17. package/dist/_members/composition/validators/index.d.ts +1 -0
  18. package/dist/_members/composition/validators/opaqueId.d.ts +13 -0
  19. package/dist/_members/composition/validators/project.d.ts +11 -15
  20. package/dist/_members/composition/validators/render.d.ts +372 -534
  21. package/dist/_members/composition/validators/session.d.ts +19 -39
  22. package/dist/_members/composition/validators/webhook.d.ts +8 -16
  23. package/dist/_members/composition-ops/index.d.ts +192 -228
  24. package/dist/_members/director/ai-sdk/messages.d.ts +2 -2
  25. package/dist/_members/director/domain/chat.d.ts +4 -4
  26. package/dist/_members/director/testing/contract.d.ts +1 -1
  27. package/dist/_members/errors/registry.d.ts +115 -1
  28. package/dist/_members/mcp/adapter.d.ts +19 -0
  29. package/dist/_members/mcp/core.d.ts +1 -1
  30. package/dist/_members/mcp/curation.d.ts +4 -0
  31. package/dist/_members/mcp/generated/harness.d.ts +1 -1
  32. package/dist/_members/mcp/generated/operations.d.ts +12 -0
  33. package/dist/_members/mcp/wait.d.ts +1 -1
  34. package/dist/_members/media-model/api.d.ts +53 -109
  35. package/dist/_members/media-model/generation.d.ts +2 -2
  36. package/dist/_members/media-model/index.d.ts +1 -0
  37. package/dist/_members/media-model/opaqueId.d.ts +10 -0
  38. package/dist/_members/render-harness/localRender.d.ts +3 -0
  39. package/dist/_members/render-harness/localRender.d.ts.map +1 -1
  40. package/dist/_members/render-harness/remotion-runtime.d.ts +53 -0
  41. package/dist/authoring/build-tools.js +6 -6
  42. package/dist/authoring/context.js +4 -4
  43. package/dist/authoring/prompts.js +1 -5
  44. package/dist/authoring/tools.js +5 -5
  45. package/dist/authoring.js +40 -27
  46. package/dist/chunk-2SNY75AR.js +498 -0
  47. package/dist/{chunk-XQ72DZKJ.js → chunk-7TQCR7AB.js} +1 -1
  48. package/dist/{chunk-QETYXKQQ.js → chunk-7V3F5HO4.js} +97 -14
  49. package/dist/{chunk-BZZTVOGG.js → chunk-D2EGO543.js} +5 -5
  50. package/dist/{chunk-ZPLJ24OV.js → chunk-D6S3ZKIY.js} +1 -1
  51. package/dist/{chunk-K2IUBCND.js → chunk-HFC3MKXA.js} +0 -51
  52. package/dist/{chunk-ICSZF37W.js → chunk-JRIPQ7DN.js} +1 -1
  53. package/dist/{chunk-TFGKUWHY.js → chunk-JTVFIN6H.js} +15 -19
  54. package/dist/{chunk-DCVJWGIA.js → chunk-JY26PIRP.js} +2 -2
  55. package/dist/{chunk-DWLNPNC4.js → chunk-LSP4HN5N.js} +1 -1
  56. package/dist/{chunk-AO744WIK.js → chunk-PJ4R4N4Q.js} +123 -38
  57. package/dist/{chunk-VFMW3JUY.js → chunk-QSDFTYKV.js} +1 -1
  58. package/dist/{chunk-LLUW26BS.js → chunk-R5BMJ2NX.js} +38 -2
  59. package/dist/{chunk-W3SIWT7J.js → chunk-RQ2G4DXI.js} +406 -174
  60. package/dist/{chunk-VHF66LCQ.js → chunk-RXCEPCAQ.js} +102 -41
  61. package/dist/{chunk-LJ5W4ZBU.js → chunk-TLVPDT4R.js} +1 -1
  62. package/dist/{chunk-PIURX6IZ.js → chunk-WTT3GGCA.js} +9 -4
  63. package/dist/{chunk-CAJQ7H5A.js → chunk-YE3F4LNJ.js} +17 -17
  64. package/dist/{chunk-TD347SSX.js → chunk-YXEZ75MB.js} +31 -20
  65. package/dist/{chunk-TPA6PYN2.js → chunk-ZBRPF6GV.js} +25 -10
  66. package/dist/cloud-import.js +20 -116
  67. package/dist/compose/renderer.js +17 -17
  68. package/dist/compose.js +18 -18
  69. package/dist/composition/graphics-plan.js +1 -1
  70. package/dist/composition/ops.js +3 -3
  71. package/dist/composition/primitive-catalog.js +1 -1
  72. package/dist/composition/projection.js +2 -2
  73. package/dist/composition/validators.js +4 -2
  74. package/dist/composition-ops.js +6 -6
  75. package/dist/composition.js +6 -4
  76. package/dist/{context-JLLU3MYD.js → context-XWNT6QZH.js} +4 -4
  77. package/dist/core/render.js +3 -3
  78. package/dist/core/select.js +2 -2
  79. package/dist/core.js +3 -3
  80. package/dist/director/ai-sdk.js +3 -3
  81. package/dist/director/domain.js +2 -2
  82. package/dist/director.js +3 -3
  83. package/dist/errors.js +1 -1
  84. package/dist/mcp/adapter.js +59 -22
  85. package/dist/mcp/core.js +1 -1
  86. package/dist/mcp/curation.js +5 -1
  87. package/dist/mcp/server.js +4 -4
  88. package/dist/mcp.js +4 -4
  89. package/dist/media-model/media-kind.js +98 -9
  90. package/dist/media-model/testing.js +6 -6
  91. package/dist/media-model.js +83 -77
  92. package/dist/nle-export.js +18 -18
  93. package/dist/primitives/backgrounds.js +2 -2
  94. package/dist/primitives/captions.js +3 -3
  95. package/dist/primitives/effects.js +2 -2
  96. package/dist/primitives/hero-text.js +2 -2
  97. package/dist/primitives/layouts.js +2 -2
  98. package/dist/primitives/lib/index.js +12 -12
  99. package/dist/primitives/lib.js +12 -12
  100. package/dist/primitives/scenes.js +2 -2
  101. package/dist/primitives/text.js +2 -2
  102. package/dist/primitives/ui-blocks.js +2 -2
  103. package/dist/primitives.js +19 -19
  104. package/dist/render-harness.js +4 -2
  105. package/package.json +13 -37
  106. package/dist/_members/authoring/prompts/designReviewerPrompt.d.ts +0 -2
  107. package/dist/_members/cloud-import/MediaItemPreprocessor.d.ts +0 -18
  108. package/dist/_members/cloud-import/multipartUpload.d.ts +0 -12
  109. package/dist/_members/convex-api/api.d.ts +0 -2
  110. package/dist/_members/convex-api/dataModel.d.ts +0 -4
  111. package/dist/chunk-6IU2UQI5.js +0 -107
  112. package/dist/chunk-EP2Y6M2J.js +0 -64
  113. package/dist/chunk-WZH57M53.js +0 -494
  114. package/dist/cloud-import/multipart.d.ts +0 -1
  115. package/dist/cloud-import/multipart.js +0 -8
  116. package/dist/convex-api/dataModel.d.ts +0 -1
  117. package/dist/convex-api/dataModel.js +0 -0
  118. package/dist/convex-api.d.ts +0 -1
  119. package/dist/convex-api.js +0 -8
  120. package/dist/{chunk-RP4SXXLJ.js → chunk-3PM6VICZ.js} +3 -3
  121. package/dist/{chunk-UQSOS726.js → chunk-6ABIXGLO.js} +4 -4
  122. package/dist/{chunk-SAMQUUO5.js → chunk-BIBYZJL3.js} +3 -3
  123. package/dist/{chunk-GMD33ZD7.js → chunk-O7GNE4BQ.js} +3 -3
  124. package/dist/{chunk-BKHGL4MB.js → chunk-THJAY7QS.js} +4 -4
  125. package/dist/{chunk-DYZE7JZA.js → chunk-TMIBBD5E.js} +3 -3
  126. package/dist/{chunk-G5BFI32U.js → chunk-XATRRDVG.js} +3 -3
@@ -209,7 +209,10 @@ function formatComposition(json) {
209
209
  ];
210
210
  if (json?.captions) {
211
211
  const segs = json.captions.segments?.length ?? 0;
212
- const words = json.captions.words?.length ?? 0;
212
+ const words = Array.isArray(json.captions.segments) ? json.captions.segments.reduce(
213
+ (total, segment) => total + (Array.isArray(segment?.words) ? segment.words.length : 0),
214
+ 0
215
+ ) : 0;
213
216
  lines.push(`captions: ${segs} segment(s), ${words} word(s) [text elided]`);
214
217
  }
215
218
  if (Array.isArray(json?.markers) && json.markers.length) {
@@ -228,16 +231,71 @@ function formatWarnings(json) {
228
231
 
229
232
  \u26A0 ${warns.join("\n\u26A0 ")}` : "";
230
233
  }
234
+ var CONTEXT_WINDOW_CAP = 6;
235
+ var CONTEXT_FACE_CAP = 3;
236
+ var CONTEXT_SAMPLE_WORDS = 24;
237
+ function formatMediaContext(json) {
238
+ const lines = [];
239
+ const dims = json?.width && json?.height ? `${json.width}\xD7${json.height}` : "unknown dimensions";
240
+ const duration = typeof json?.durationSec === "number" ? fmtClock(json.durationSec) : "unknown duration";
241
+ lines.push(`Media ${json?.mediaId ?? "?"} \u2014 ${dims}, ${duration}`);
242
+ if (json?.thumbnail?.url) lines.push(`thumbnail: ${json.thumbnail.url}`);
243
+ const windows = Array.isArray(json?.faces?.windows) ? json.faces.windows : [];
244
+ lines.push(`faces: ${json?.faces?.status ?? "unknown"} (${windows.length} source window(s))`);
245
+ for (const window of windows.slice(0, CONTEXT_WINDOW_CAP)) {
246
+ const faces = Array.isArray(window?.faces) ? window.faces : [];
247
+ const roster = faces.slice(0, CONTEXT_FACE_CAP).map((face) => {
248
+ const bbox = face?.bbox ?? {};
249
+ const box = [bbox.x, bbox.y, bbox.width, bbox.height].every((value) => typeof value === "number") ? ` bbox:${[bbox.x, bbox.y, bbox.width, bbox.height].map((n) => n.toFixed(3)).join(",")}` : "";
250
+ const speaking = typeof face?.speakingShare === "number" ? ` speaking:${Math.round(face.speakingShare * 100)}%` : "";
251
+ return `${face?.faceId ?? "?"}${box}${speaking}`;
252
+ });
253
+ lines.push(`- ${window?.window?.startSec ?? "?"}\u2192${window?.window?.endSec ?? "?"}s: ${roster.join(" | ") || "no usable subjects"}`);
254
+ }
255
+ if (windows.length > CONTEXT_WINDOW_CAP) lines.push(`- \u2026 ${windows.length - CONTEXT_WINDOW_CAP} more face windows elided`);
256
+ const transcript = json?.transcript;
257
+ const utterances = Array.isArray(transcript?.data?.utterances) ? transcript.data.utterances : Array.isArray(transcript?.data?.data) ? transcript.data.data : [];
258
+ const words = utterances.flatMap((utterance) => Array.isArray(utterance?.words) ? utterance.words : []);
259
+ const sample = words.slice(0, CONTEXT_SAMPLE_WORDS).map((word) => {
260
+ const start = typeof word?.start === "number" ? `@${word.start.toFixed(2)}s` : "";
261
+ return `${word?.word ?? word?.text ?? ""}${start}`;
262
+ }).filter(Boolean);
263
+ lines.push(`transcript: ${transcript?.status ?? "unknown"} (${utterances.length} utterance(s), ${words.length} word(s))`);
264
+ if (sample.length) lines.push(`sample: ${sample.join(" ")}`);
265
+ if (transcript?.status === "ready") {
266
+ lines.push("Use get_transcript(mediaItemId, limit, after/before) for exact paginated word timings.");
267
+ }
268
+ if (json?.beatGrid) {
269
+ const beats = Array.isArray(json.beatGrid.beatTimesMs) ? json.beatGrid.beatTimesMs : [];
270
+ lines.push(`beatGrid: ${json.beatGrid.bpm ?? "?"} bpm, ${beats.length} beats, ${json.beatGrid.method ?? "?"}${beats.length ? `; first ms: ${beats.slice(0, 12).join(",")}` : ""}`);
271
+ } else lines.push("beatGrid: none");
272
+ const wordTimes = Array.isArray(json?.wordGrid?.wordTimesMs) ? json.wordGrid.wordTimesMs : [];
273
+ lines.push(`wordGrid: ${wordTimes.length} onsets${wordTimes.length ? `; first ms: ${wordTimes.slice(0, 12).join(",")}` : ""}`);
274
+ lines.push(`audio: role=${json?.audioRole ?? "none"} envelope=${json?.envelope?.envelopeClass ?? "none"}`);
275
+ const facts = Array.isArray(json?.facts) ? json.facts : [];
276
+ const offers = Array.isArray(json?.offers) ? json.offers : [];
277
+ lines.push(`facts: ${facts.map((fact) => `${fact?.kind ?? "?"}:${fact?.status ?? "?"}`).join(", ") || "none"}`);
278
+ lines.push(`offers: ${offers.map((offer) => `${offer?.kind ?? "?"}${offer?.quotedUsd != null ? `($${offer.quotedUsd})` : ""}`).join(", ") || "none"}`);
279
+ return lines.join("\n");
280
+ }
281
+ function formatTranscriptPage(json) {
282
+ const utterances = Array.isArray(json?.data) ? json.data : Array.isArray(json?.utterances) ? json.utterances : [];
283
+ return JSON.stringify({
284
+ mediaItemId: json?.mediaItemId,
285
+ language: json?.language,
286
+ utterances,
287
+ ...json?.pagination ? { pagination: json.pagination } : {}
288
+ }, null, 2);
289
+ }
231
290
  var CURATED_TOOLS = [
232
291
  {
233
292
  name: "compose",
234
293
  operationId: "compose",
235
294
  pathArgs: { projectId: "id" },
236
- description: 'Start the async Director compose workflow: it authors an ensemble of full composition candidates, verifies each, and saves the winner to the project. Body must set EXACTLY ONE source (mutually exclusive): `briefId` \u2014 author FROM a persisted Brief (the PLANNED path: create_brief first; every Brief field is honored or the compose is refused loudly naming the field); `fromComposition: true` \u2014 author from the project\'s CURRENT composition (the GENERAL path: seed it with apply_composition first \u2014 your media clips are the intent, and any graphics you authored are CARRIED VERBATIM as locked clips the Director authors around, never re-authors or drops); or `suggestionId` \u2014 author from an AI clip suggestion (the CLIPPING path, paired with suggest_briefs). Returns { composeJobId, workflowId, status, streamUrl }. Then call wait_job(kind="compose", id=composeJobId, projectId=...) to block until the winner is selected.',
237
- okFormat: (j, a) => `Compose workflow started.
295
+ description: 'Start an async Director compose job: it authors an ensemble of full composition candidates, verifies each, and saves the winner to the project. Body must set EXACTLY ONE source (mutually exclusive): `briefId` \u2014 author FROM a persisted Brief (the PLANNED path: create_brief first; every Brief field is honored or the compose is refused loudly naming the field); `fromComposition: true` \u2014 author from the project\'s CURRENT composition (the GENERAL path: seed it with apply_composition first \u2014 your media clips are the intent, and any graphics you authored are CARRIED VERBATIM as locked clips the Director authors around, never re-authors or drops); or `suggestionId` \u2014 author from an AI clip suggestion (the CLIPPING path, paired with suggest_briefs). Returns { composeJobId, status, streamUrl }. Then call wait_job(kind="compose", id=composeJobId, projectId=...) to block until the winner is selected.',
296
+ okFormat: (j, a) => `Compose job started.
238
297
 
239
298
  composeJobId: ${j.composeJobId}
240
- workflowId: ${j.workflowId}
241
299
  status: ${j.status}${j.streamUrl ? `
242
300
  streamUrl: ${j.streamUrl}` : ""}
243
301
 
@@ -294,43 +352,28 @@ Call wait_job(kind="render", id="${j.id}", projectId="${a.projectId}") to block
294
352
  },
295
353
  {
296
354
  name: "preview_frame",
297
- operationId: "previewFrame",
355
+ operationId: "previewFrameJob",
298
356
  pathArgs: { projectId: "id" },
299
- description: "Render presigned PNG still(s) of the composition at the given timestamps \u2014 the live preview for layout/copy judgment. STATELESS: nothing to open, warm, or close; concurrent previews don't contend. Defaults to the PERSISTED state: absent `body.composition` \u21D2 the project's ACTIVE composition \u2014 exactly what create_render will encode; pass `body.compositionId` to target a specific persisted composition (stale/foreign id \u21D2 404, never a silent fallback). The response's `previewed` field names what was rendered ({source: 'persisted'|'inline'|'scratch', compositionId?, etag?}) \u2014 trust a preview as render-truth ONLY when source is 'persisted'. `body.timestamps` = seconds to sample. A still cannot show motion \u2014 judge motion with preview_clip (a window of the composition) or preview_component (one graphic).",
300
- preview: {
301
- urls: (j) => (Array.isArray(j?.stills) ? j.stills : []).map((st) => st?.url),
302
- kind: "image",
303
- cap: 12
304
- },
305
- okFormat: (j, a) => {
306
- const stills = Array.isArray(j?.stills) ? j.stills : [];
307
- const p = j?.previewed ?? {};
308
- const head = `Previewed ${stills.length} still(s) of project ${a.projectId}.
309
- source: ${p.source ?? "unknown"}${p.compositionId ? ` \xB7 compositionId: ${p.compositionId}` : ""}${p.etag ? ` \xB7 etag: ${p.etag}` : ""}${p.source !== "persisted" ? ' \u2014 NOT render-truth (only source "persisted" is what create_render will encode)' : ""}`;
310
- const lines = stills.map((st, i) => {
311
- const secs = typeof st?.beatSec === "number" ? st.beatSec : typeof st?.t === "number" ? st.t : typeof st?.timestamp === "number" ? st.timestamp : null;
312
- const t = secs === null ? `#${i}` : `${secs.toFixed(2)}s`;
313
- const url = typeof st?.url === "string" && !st.url.startsWith("data:") ? ` ${st.url}` : "";
314
- return ` ${t}${url}`;
315
- });
316
- return `${head}
317
- ${lines.join("\n")}
357
+ description: `Queue durable still-frame evidence for layout/copy judgment. Returns immediately with {jobId, kind:'preview_frame', status}; then call wait_job(kind="preview_frame", id=jobId) to receive every presigned frame as an image. The immutable snapshot records geometry, layers, warnings, and whether the target was persisted, inline, or session scratch. A still cannot prove motion \u2014 use preview_clip or preview_component for animation.`,
358
+ okFormat: (j) => `Preview-frame job queued.
318
359
 
319
- The frames ride with this result as images \u2014 judge the layout from the pixels, not from this text.`;
320
- },
321
- annotations: { readOnlyHint: true }
360
+ jobId: ${j.jobId}
361
+ status: ${j.status}
362
+
363
+ Call wait_job(kind="preview_frame", id="${j.jobId}") for the rendered images.`,
364
+ annotations: { idempotentHint: true }
322
365
  },
323
366
  {
324
367
  name: "preview_component",
325
368
  operationId: "previewComponent",
326
369
  pathArgs: { projectId: "id" },
327
- description: 'Queue a MOTION-clip render of ONE org-installed component \u2014 ASYNC: returns a jobId immediately; then wait_job(kind="preview", id=jobId) for a presigned short clip (VP9-alpha webm, ~3s \u2014 the SAME alpha bake the production render composites, so what you preview is what ships). Motion is the point of components; a still is one frame and lies about animation (A8). The author\u2192preview\u2192fix loop for a graphic. SESSIONLESS: the warm box is a server detail (a cold first call returns 409 box_warming \u2014 retry in a few seconds). `body.componentId` selects the component; pass `body.params` to render it with props. Failures land on the job terminal with typed codes: component_does_not_compile / component_runtime_error carry the sanitized author-fixable message \u2014 read it, fix the tsxSource via create_component, and re-preview. Idempotent per (componentId, params, dims).',
370
+ description: 'Queue a typed preview artifact for ONE org-installed component \u2014 ASYNC: returns a jobId immediately; then wait_job(kind="preview", id=jobId). Live components return artifactKind="image" with image/png or image/jpeg still evidence; baked components return artifactKind="motion" with the SAME content-addressed video/webm VP9-alpha object the production render composites. `body.componentId` selects the component; pass `body.params` to render it with props. Compile/runtime failures land on the job terminal with sanitized author-fixable messages. Idempotent per (componentId, params, dims).',
328
371
  okFormat: (j) => `Preview job queued.
329
372
 
330
373
  jobId: ${j.jobId}
331
374
  status: ${j.status}
332
375
 
333
- Call wait_job(kind="preview", id="${j.jobId}") for the presigned motion clip (re-call it if it returns still-running).`,
376
+ Call wait_job(kind="preview", id="${j.jobId}") for the typed preview artifact (live=image, baked=motion; re-call it if it returns still-running).`,
334
377
  annotations: { idempotentHint: true }
335
378
  },
336
379
  {
@@ -349,7 +392,7 @@ wait_job(kind="render", id="${j.renderId ?? j.id}", projectId=...) for the watch
349
392
  name: "score_composition",
350
393
  operationId: "scoreComposition",
351
394
  pathArgs: { projectId: "id" },
352
- description: `Queue the server-side judge \u2014 the standalone sighted check. ASYNC: returns a jobId (the judge takes minutes: it samples eval beats, renders them in a server-managed warm box, and grades editorial/spatial/brand/caption). Then wait_job(kind="verify", id=jobId) for per-criterion scores 0\u201310, a weighted composite, a worst-first critique, AND a checkpointId \u2014 the verdicts mint a durable kind:'score' Checkpoint, the same evidence noun the Director's gates produce (get_checkpoint re-reads it later). SESSIONLESS: a cold first call returns 409 box_warming \u2014 retry in a few seconds. \`body.composition\` optional (absent \u21D2 the ACTIVE composition). Fix the lowest criterion via apply_composition and re-score. Score answers "how good IS this" (grounded numbers); consult answers "what SHOULD I change" (advice + a fix candidate). Idempotent per composition state; the compose_verify meter fires on the job's success terminal.`,
395
+ description: 'Queue the durable server-side judge \u2014 it snapshots the exact authored target, samples eval beats, captures frames asynchronously, and grades editorial/spatial/brand/caption. Then wait_job(kind="verify", id=jobId) for per-criterion scores, weighted composite, worst-first critique, and checkpointId. `body.composition` is optional (absent \u21D2 ACTIVE). Fix the lowest criterion via apply_composition and re-score. Idempotent per immutable snapshot; compose_verify bills only on the guarded success terminal.',
353
396
  okFormat: (j) => `Score job queued.
354
397
 
355
398
  jobId: ${j.jobId}
@@ -466,9 +509,49 @@ ${j.tsxSource}`,
466
509
  name: "get_media_context",
467
510
  operationId: "getMediaContext",
468
511
  pathArgs: { mediaItemId: "id" },
469
- description: "Fetch the authoring context for a media item: detected faces (subject boxes per frame) and the transcript. Use this before composing/applying so your crop intents, captions, and timing line up with what is actually in the footage.",
512
+ description: "Fetch bounded authoring context for a media item: dimensions/thumbnail, compact face roster, transcript counts/sample, temporal grids, and fact readiness. The full transcript body is deliberately elided so this read cannot overflow MCP; follow with get_transcript for exact paginated word timings.",
513
+ okFormat: (j) => formatMediaContext(j),
470
514
  annotations: { readOnlyHint: true }
471
515
  },
516
+ {
517
+ name: "get_transcript",
518
+ operationId: "getMediaTranscript",
519
+ pathArgs: { mediaItemId: "id" },
520
+ queryArgs: {
521
+ after: "Zero-based utterance cursor returned as pagination.nextCursor.",
522
+ before: "Zero-based utterance cursor for a backward page; do not combine with after.",
523
+ limit: "Utterances per page (1\u2013100). Use 20 by default."
524
+ },
525
+ description: "Read exact source-time transcript words in bounded utterance pages. Word start/end values are seconds from the source media start. Pass limit (20 recommended), then pagination.nextCursor as after; the formatter omits the unbounded fullText duplicate.",
526
+ okFormat: (j) => formatTranscriptPage(j),
527
+ annotations: { readOnlyHint: true }
528
+ },
529
+ {
530
+ name: "prepare_media",
531
+ operationId: "purchaseMediaFact",
532
+ pathArgs: { mediaItemId: "id" },
533
+ description: 'Prepare the perception artifact an authored edit needs BEFORE sighted review. Pass body.kind="subjectTrack" for face/active-speaker reframing, or body.kind="matte" for a true zPlane:"behind-subject" graphic. Pass body.intent {startSec,endSec} using SOURCE time (the media trim, not timeline time); it is required for mattes and strongly recommended for subject tracks so the prepared fact exactly matches the clip and quote. This is the supported product path \u2014 never accept a preview that silently flattens depth or ignores speaker tracking. Returns a fact receipt; poll get_media_facts with the same kind/window until exact.state is ready (failed is terminal). Existing pending/ready facts are reused without another charge.',
534
+ okFormat: (j, a) => `Media preparation ${j.alreadyExisted ? "reused" : "started"}.
535
+
536
+ factId: ${j.factId}
537
+ status: ${j.status}
538
+ quotedUsd: ${j.quotedUsd}
539
+
540
+ Poll get_media_facts(mediaItemId="${a.mediaItemId}", kind="${a.body?.kind}", startSec="${a.body?.intent?.startSec}", endSec="${a.body?.intent?.endSec}") until exact.state is ready before preview_frame or score_composition.`,
541
+ annotations: { idempotentHint: true }
542
+ },
543
+ {
544
+ name: "get_media_facts",
545
+ operationId: "listMediaFacts",
546
+ pathArgs: { mediaItemId: "id" },
547
+ queryArgs: {
548
+ kind: "Optional exact fact kind: subjectTrack | matte. Requires startSec and endSec.",
549
+ startSec: "Source-window start in seconds. Send together with kind and endSec.",
550
+ endSec: "Source-window end in seconds. Send together with kind and startSec."
551
+ },
552
+ description: "Read prepared subject tracks and behind-subject mattes for one media item. For an exact readiness check, pass kind + startSec + endSec together using the same SOURCE window sent to prepare_media; exact.state is pending | ready | failed | available | unavailable. Use this after prepare_media and do not call preview_frame / score_composition until the required exact fact is ready.",
553
+ annotations: { readOnlyHint: true, idempotentHint: true }
554
+ },
472
555
  {
473
556
  name: "list_media",
474
557
  operationId: "listMedia",
@@ -540,28 +623,28 @@ Render it with create_render(projectId="${a.projectId}", body={ compositionId: "
540
623
  {
541
624
  name: "import_media",
542
625
  operationId: "importMedia",
543
- description: 'Import a media item into your library from a public URL \u2014 the agent-friendly ingest path (single call, no upload protocol). Pass url (https) + filename (+ optional contentType). Returns { id, status:"importing" }. Processing is async: poll list_media for processingStatus, or register a webhook (create_webhook on media.completed \u2014 status: complete|failed rides the payload). Use the returned id with get_media_context, suggest_briefs, or as a composition source.'
626
+ description: 'Import a media item into your library from a public URL \u2014 the agent-friendly ingest path (single call, no upload protocol). Pass url (https) + filename (+ optional contentType). Returns { id, status:"importing" }. Processing is async: call wait_job(kind="media", id=mediaItemId), or register a webhook (create_webhook on media.completed \u2014 status: complete|failed rides the payload). Use the returned id with get_media_context, suggest_briefs, or as a composition source.'
544
627
  },
545
628
  {
546
629
  name: "generate_media",
547
630
  operationId: "generateMedia",
548
- description: 'Generate one first-class Library media item from a prompt. Choose generator: text-to-video | text-to-image | image-to-video | text-to-music | text-to-speech | text-to-sfx | auto. Put aspect/duration/voice/model controls in body.style and first/last/image references in body.refs. Returns { id, status:"generating", workflowId?, estimateUsd? }; returns 402 if the estimate exceeds the per-call ceiling. Async \u2014 poll list_media or use a webhook (media.completed \u2014 status rides the payload). Use the returned id like any other media item.'
631
+ description: 'Generate one first-class Library media item from a prompt. Choose generator: text-to-video | text-to-image | image-to-video | text-to-music | text-to-speech | text-to-sfx | auto. Put supported presentation controls in body.style and first/last/image references in body.refs. Returns { id, status:"generating", estimateUsd? }; returns 402 if the estimate exceeds the per-call ceiling. Async \u2014 call wait_job(kind="media", id=mediaItemId) or use a webhook (media.completed \u2014 status rides the payload). Use the returned id like any other media item.'
549
632
  },
550
633
  {
551
634
  name: "create_upload",
552
635
  operationId: "createMediaUpload",
553
- description: "Ingest a LOCAL file \u2014 bytes you hold, when there is no public URL to hand import_media (prefer import_media whenever the bytes already sit at an https URL). TWO steps: (1) this tool reserves a media row and returns { id, uploadUrl, expiresAt } \u2014 a short-lived presigned PUT URL. PUT the raw file bytes straight to uploadUrl over HTTPS with your declared Content-Type (the bytes go direct to R2, never through MCP; \u2264 ~5 GB per PUT). (2) call finalize_upload({ mediaItemId: id }) to start processing. Body: filename, contentType (allowed enum), size (bytes). Thereafter it is async exactly like import_media: poll list_media for processingStatus, or register a webhook (create_webhook on media.completed). Use the returned id with get_media_context, suggest_briefs, or as a composition source."
636
+ description: "Ingest a LOCAL file \u2014 bytes you hold, when there is no public URL to hand import_media (prefer import_media whenever the bytes already sit at an https URL). TWO steps: (1) this tool reserves a media item and returns { id, uploadUrl, expiresAt } \u2014 a short-lived PUT URL. PUT the raw file bytes to uploadUrl over HTTPS with your declared Content-Type (\u2264 ~5 GB per PUT). (2) call finalize_upload({ mediaItemId: id }) to start processing. Body: filename, contentType (allowed enum), size (bytes). Thereafter it is async exactly like import_media: poll list_media for processingStatus, or register a webhook (create_webhook on media.completed). Use the returned id with get_media_context, suggest_briefs, or as a composition source."
554
637
  },
555
638
  {
556
639
  name: "finalize_upload",
557
640
  operationId: "finalizeMedia",
558
641
  pathArgs: { mediaItemId: "id" },
559
- description: 'Finalize a direct upload once you have PUT the bytes to the uploadUrl from create_upload. Confirms the bytes landed and starts ingest: image/asset become ready immediately (status "complete"); audio/video enter processing (status "processing"). Returns { id, status, processingPhase }. 400 if no bytes were uploaded (PUT first) or the item is not awaiting finalize; 404 if the id is not yours. Then poll list_media / get_media_context, or use a media.completed webhook.'
642
+ description: 'Finalize a direct upload once you have PUT the bytes to the uploadUrl from create_upload. Confirms the bytes landed and starts ingest: image/asset become ready immediately (status "complete"); audio/video enter processing (status "processing"). Returns { id, status, processingPhase }. 400 if no bytes were uploaded (PUT first) or the item is not yours. Then call wait_job(kind="media", id=mediaItemId), or use a media.completed webhook.'
560
643
  },
561
644
  {
562
645
  name: "search_resources",
563
646
  operationId: "searchResources",
564
- description: "Search licensed STOCK media to compose with \u2014 video/photo (Pexels + Pixabay fallback), sfx (Freesound, CC0-only), gif (Klipy). Pass query for ONE search, or queries[] (\u226410) to run them all in a SINGLE call \u2014 strongly prefer the batch when you know several things you need (a 64-search session spent 5m10s on serial round-trips). A batch returns results[], one per query in order, each with its own candidates; a query that fails carries an error and never discards the others. Optional kind [default video], provider pin, limit \u226450 apply to every query in the batch. Returns ranked candidates, each with an opaque candidateId + attribution (author, assetUrl) + license, and a preview URL in the text. FREE + rate-limited + 7-day cached \u2014 reach for this before generate_media when real footage beats synthesis. TO SEE the candidates rather than read about them, pass limit \u22652: thumbnails/audio are attached only when you asked for options to compare, because a preview of a single forced choice is pure cost. Import a chosen candidate into a project with import_resource. Candidates expire after 7 days (re-search to refresh a candidateId).",
647
+ description: "Search licensed stock media to compose with across video, photo, sound effects, and GIFs. Pass query for ONE search, or queries[] (\u226410) to run them all in a SINGLE call \u2014 strongly prefer the batch when you know several things you need. A batch returns results[], one per query in order, each with its own candidates; a query that fails carries an error and never discards the others. Optional kind [default video], provider pin, and limit \u226450 apply to every query in the batch. Returns ranked candidates, each with an opaque candidateId + attribution (author, assetUrl) + license, and a preview URL in the text. FREE + rate-limited + cached \u2014 reach for this before generate_media when real footage beats synthesis. TO SEE the candidates rather than read about them, pass limit \u22652: thumbnails/audio are attached only when you asked for options to compare. Import a chosen candidate into a project with import_resource. Candidate handles expire; re-search to refresh one.",
565
648
  okFormat: (j) => formatResourceCandidates(j),
566
649
  preview: {
567
650
  urls: (j) => (Array.isArray(j?.results) ? j.results.flatMap((r) => Array.isArray(r?.candidates) ? r.candidates : []) : Array.isArray(j?.candidates) ? j.candidates : []).map((c) => c?.previewUrl),
@@ -581,13 +664,13 @@ Render it with create_render(projectId="${a.projectId}", body={ compositionId: "
581
664
  {
582
665
  name: "import_resource",
583
666
  operationId: "importResource",
584
- description: 'Import a stock candidate (candidateId from search_resources) into a project as an ORG-OWNED media item \u2014 provider/externalId/license provenance preserved, bytes deduped across orgs. Pass projectId + candidateId. Returns { id, status:"importing"|"ready" }; processing is async \u2014 poll list_media / get_media_context, then use the returned id as a composition source. Gated on the resource_import plan feature (402 billing_required if your plan lacks it). 422 candidate_mismatch if the candidate expired \u2014 re-run search_resources.'
667
+ description: 'Import a stock candidate (candidateId from search_resources) into a project as an ORG-OWNED media item \u2014 provider/externalId/license provenance preserved, bytes deduped across orgs. Pass projectId + candidateId. Returns { id, status:"importing"|"ready" }; processing is async \u2014 call wait_job(kind="media", id=mediaItemId), then use the returned id as a composition source. Gated on the resource_import plan feature (402 billing_required if your plan lacks it). 422 candidate_mismatch if the candidate expired \u2014 re-run search_resources.'
585
668
  },
586
669
  {
587
670
  name: "suggest_briefs",
588
671
  operationId: "suggestBriefs",
589
672
  pathArgs: { mediaItemId: "id" },
590
- description: `Run AI clip-suggestion analysis on a long-form media item \u2014 the CLIPPING path (find the best short-form moments). Returns { workflowId, status }; then wait_job(kind="suggestions", id=mediaItemId) \u2014 its terminal lists each suggestion WITH its briefId. When the media belongs to a PROJECT, each finalized suggestion is promoted to a kind-'clip' BRIEF (its window pinned as a beat) \u2014 list_briefs {kind:"clip"} shows them and compose({ briefId }) runs the flagship path from one. Project-less media: pass the suggestion id to compose({ suggestionId }) directly. Clipping is a vertical, NOT the general path.`
673
+ description: `Run AI clip-suggestion analysis on a long-form media item \u2014 the CLIPPING path (find the best short-form moments). Returns { status, alreadyRunning? }; then wait_job(kind="suggestions", id=mediaItemId) \u2014 its terminal lists each suggestion WITH its briefId. When the media belongs to a PROJECT, each finalized suggestion is promoted to a kind-'clip' BRIEF (its window pinned as a beat) \u2014 list_briefs {kind:"clip"} shows them and compose({ briefId }) runs the flagship path from one. Project-less media: pass the suggestion id to compose({ suggestionId }) directly. Clipping is a vertical, NOT the general path.`
591
674
  },
592
675
  {
593
676
  name: "create_webhook",
@@ -905,6 +988,8 @@ export {
905
988
  formatResourceCandidates,
906
989
  formatComposition,
907
990
  formatWarnings,
991
+ formatMediaContext,
992
+ formatTranscriptPage,
908
993
  CURATED_TOOLS,
909
994
  INTERNAL_EDITOR_TOOLS,
910
995
  KNOWN_BACKEND_GAPS,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveBookendTimeline
3
- } from "./chunk-VHF66LCQ.js";
3
+ } from "./chunk-RXCEPCAQ.js";
4
4
 
5
5
  // ../execution/compose/src/tokenResolver.ts
6
6
  var DEFAULTS = {
@@ -86,7 +86,7 @@ var ERROR_CODES = {
86
86
  selector_speaking_double_count: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
87
87
  selector_identity_detector_version_mismatch: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
88
88
  variant_not_baked: { stage: "render", category: "internal", retryable: false, httpStatus: 422 },
89
- behind_matte_unresolved: { stage: "render", category: "internal", retryable: false, httpStatus: 422 },
89
+ behind_matte_unresolved: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
90
90
  render_output_invalid: { stage: "render", category: "internal", retryable: false, httpStatus: 422 },
91
91
  audio_sync_invalid: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
92
92
  generation_output_invalid: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
@@ -96,16 +96,35 @@ var ERROR_CODES = {
96
96
  brand_kit_unresolved: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
97
97
  unknown_format: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
98
98
  render_not_cancellable: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
99
+ render_not_found: { stage: "render", category: "authoring", retryable: false, httpStatus: 404 },
99
100
  compose_not_cancellable: { stage: "system", category: "authoring", retryable: false, httpStatus: 422 },
100
101
  job_not_cancellable: { stage: "system", category: "authoring", retryable: false, httpStatus: 422 },
101
102
  export_not_cancellable: { stage: "export", category: "authoring", retryable: false, httpStatus: 422 },
102
103
  render_not_complete: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
103
104
  lambda_infra_failed: { stage: "render", category: "transient", retryable: true, httpStatus: 503 },
105
+ render_provider_unreachable: { stage: "render", category: "transient", retryable: true, httpStatus: 503 },
106
+ render_provider_start_failed: { stage: "render", category: "transient", retryable: true, httpStatus: 503 },
107
+ behind_split_unsupported: { stage: "validate", category: "authoring", retryable: false, httpStatus: 422 },
108
+ render_pipeline_unavailable: { stage: "render", category: "transient", retryable: true, httpStatus: 503 },
109
+ render_route_missing: { stage: "render", category: "internal", retryable: true, httpStatus: 503 },
110
+ render_pipeline_failed: { stage: "render", category: "transient", retryable: true, httpStatus: 503 },
111
+ render_stage_timeout: { stage: "render", category: "transient", retryable: true, httpStatus: 504 },
112
+ render_promotion_failed: { stage: "render", category: "internal", retryable: true, httpStatus: 500 },
113
+ render_execution_failed: { stage: "render", category: "internal", retryable: false, httpStatus: 500 },
114
+ invalid_render_terminal: { stage: "render", category: "internal", retryable: false, httpStatus: 500 },
115
+ sighted_dispatch_failed: { stage: "system", category: "transient", retryable: true, httpStatus: 503 },
116
+ sighted_stage_timeout: { stage: "render", category: "transient", retryable: true, httpStatus: 504 },
117
+ sighted_provider_unreachable: { stage: "render", category: "transient", retryable: true, httpStatus: 503 },
118
+ sighted_artifact_missing: { stage: "render", category: "internal", retryable: true, httpStatus: 500 },
119
+ sighted_execution_failed: { stage: "render", category: "internal", retryable: false, httpStatus: 500 },
120
+ sighted_snapshot_missing: { stage: "system", category: "internal", retryable: false, httpStatus: 500 },
104
121
  render_stalled: { stage: "render", category: "transient", retryable: true, httpStatus: 500 },
105
122
  render_unknown_primitive: { stage: "render", category: "authoring", retryable: false, httpStatus: 422 },
106
123
  session_in_use: { stage: "export", category: "authoring", retryable: false, httpStatus: 422 },
107
124
  transcript_unavailable: { stage: "export", category: "transient", retryable: true, httpStatus: 503 },
108
125
  service_unavailable: { stage: "system", category: "transient", retryable: true, httpStatus: 503 },
126
+ operation_moved_async: { stage: "system", category: "authoring", retryable: false, httpStatus: 410 },
127
+ active_composition_invariant: { stage: "system", category: "internal", retryable: false, httpStatus: 500 },
109
128
  internal_error: { stage: "system", category: "internal", retryable: false, httpStatus: 500 }
110
129
  };
111
130
  var CueframeError = class extends Error {
@@ -191,7 +210,24 @@ var FIX_ALLOW = [
191
210
  "typeMismatches",
192
211
  "constraintViolations"
193
212
  ];
194
- var LOCATOR_ALLOW = ["projectId", "renderId", "requestId", "clipId", "trackId", "compositionPath"];
213
+ var LOCATOR_ALLOW = [
214
+ "projectId",
215
+ "compositionId",
216
+ "renderId",
217
+ "jobId",
218
+ "requestId",
219
+ "workflowId",
220
+ "activityId",
221
+ "stage",
222
+ "clipId",
223
+ "trackId",
224
+ "mediaId",
225
+ "componentId",
226
+ "outputFrame",
227
+ "timecodeMs",
228
+ "providerRequestId",
229
+ "compositionPath"
230
+ ];
195
231
  var GENERIC = {
196
232
  transient: "A temporary error occurred; retry.",
197
233
  internal: "An unexpected error occurred."