@tangle-network/agent-app 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/DesignCanvas-3JEEIT6Y.js +10 -0
  2. package/dist/DesignCanvas-3JEEIT6Y.js.map +1 -0
  3. package/dist/DesignCanvasEditor-37LPJIIR.js +9 -0
  4. package/dist/DesignCanvasEditor-37LPJIIR.js.map +1 -0
  5. package/dist/TimelineEditor-OXPJZDP2.js +12 -0
  6. package/dist/TimelineEditor-OXPJZDP2.js.map +1 -0
  7. package/dist/apply-Cp8c3K9D.d.ts +249 -0
  8. package/dist/chunk-2Q73HGDI.js +1743 -0
  9. package/dist/chunk-2Q73HGDI.js.map +1 -0
  10. package/dist/chunk-6UOE5CTA.js +1647 -0
  11. package/dist/chunk-6UOE5CTA.js.map +1 -0
  12. package/dist/chunk-7QCIYDGC.js +1119 -0
  13. package/dist/chunk-7QCIYDGC.js.map +1 -0
  14. package/dist/chunk-A76ZHWNF.js +194 -0
  15. package/dist/chunk-A76ZHWNF.js.map +1 -0
  16. package/dist/chunk-ABGSFUJQ.js +111 -0
  17. package/dist/chunk-ABGSFUJQ.js.map +1 -0
  18. package/dist/{chunk-4YTWB5MG.js → chunk-ETX4O4BB.js} +98 -1
  19. package/dist/chunk-ETX4O4BB.js.map +1 -0
  20. package/dist/chunk-F5KTWRO7.js +2276 -0
  21. package/dist/chunk-F5KTWRO7.js.map +1 -0
  22. package/dist/chunk-IHR6K3GF.js +2367 -0
  23. package/dist/chunk-IHR6K3GF.js.map +1 -0
  24. package/dist/chunk-JZAJE3JL.js +990 -0
  25. package/dist/chunk-JZAJE3JL.js.map +1 -0
  26. package/dist/chunk-ZYBWGSAZ.js +130 -0
  27. package/dist/chunk-ZYBWGSAZ.js.map +1 -0
  28. package/dist/design-canvas/drizzle.d.ts +569 -0
  29. package/dist/design-canvas/drizzle.js +183 -0
  30. package/dist/design-canvas/drizzle.js.map +1 -0
  31. package/dist/design-canvas/index.d.ts +261 -0
  32. package/dist/design-canvas/index.js +96 -0
  33. package/dist/design-canvas/index.js.map +1 -0
  34. package/dist/design-canvas-react/index.d.ts +916 -0
  35. package/dist/design-canvas-react/index.js +423 -0
  36. package/dist/design-canvas-react/index.js.map +1 -0
  37. package/dist/export-presets-Dl5Aa5xj.d.ts +284 -0
  38. package/dist/index.d.ts +11 -2
  39. package/dist/index.js +224 -6
  40. package/dist/mcp-CIupfjxV.d.ts +112 -0
  41. package/dist/mcp-rpc-DLw_r9PQ.d.ts +55 -0
  42. package/dist/model-BHLN208Z.d.ts +183 -0
  43. package/dist/runtime/index.d.ts +108 -1
  44. package/dist/runtime/index.js +7 -1
  45. package/dist/sequences/drizzle.d.ts +1244 -0
  46. package/dist/sequences/drizzle.js +368 -0
  47. package/dist/sequences/drizzle.js.map +1 -0
  48. package/dist/sequences/index.d.ts +331 -0
  49. package/dist/sequences/index.js +114 -0
  50. package/dist/sequences/index.js.map +1 -0
  51. package/dist/sequences-react/index.d.ts +752 -0
  52. package/dist/sequences-react/index.js +241 -0
  53. package/dist/sequences-react/index.js.map +1 -0
  54. package/dist/store-CUStmtdH.d.ts +64 -0
  55. package/dist/store-gckrNq-g.d.ts +242 -0
  56. package/dist/tools/index.d.ts +25 -108
  57. package/dist/tools/index.js +16 -6
  58. package/package.json +62 -2
  59. package/dist/chunk-4YTWB5MG.js.map +0 -1
  60. package/dist/chunk-OLCVUGGI.js +0 -137
  61. package/dist/chunk-OLCVUGGI.js.map +0 -1
@@ -0,0 +1,241 @@
1
+ import {
2
+ COMMAND_HISTORY_LIMIT,
3
+ DEFAULT_MAX_MEDIA_ELEMENTS,
4
+ PreviewCanvas,
5
+ SEEK_TIMEOUT_MS,
6
+ SEEK_TOLERANCE_SECONDS,
7
+ SEQUENCE_MEDIA_DRAG_TYPE,
8
+ SnapIndicatorLine,
9
+ TimelineClipChip,
10
+ TimelineEditor,
11
+ TimelinePlayhead,
12
+ TimelineRuler,
13
+ TimelineTrackRow,
14
+ ZoomControl,
15
+ addCaptionCommand,
16
+ applySnap,
17
+ captionFontPx,
18
+ chooseMoveSnap,
19
+ classifyMediaUrl,
20
+ clipChipGeometry,
21
+ collectSnapPoints,
22
+ compositeCommand,
23
+ computeWaveform,
24
+ containFitRect,
25
+ createCommandStack,
26
+ createImageFrameProvider,
27
+ createMediaElementPool,
28
+ createPlaybackClock,
29
+ createVideoElementFrameProvider,
30
+ createZoomMath,
31
+ deleteClipCommand,
32
+ drawWaveform,
33
+ frameToPixel,
34
+ framesFromPixelDelta,
35
+ letterboxRect,
36
+ loadWaveform,
37
+ moveClipCommand,
38
+ moveDragStartFrame,
39
+ needsSeek,
40
+ pixelToFrame,
41
+ placeClipCommand,
42
+ selectTickStepSeconds,
43
+ setClipTextCommand,
44
+ snapPixel,
45
+ splitClipCommand,
46
+ toggleClipDisabledCommand,
47
+ trimClipCommand,
48
+ trimEndDrag,
49
+ trimStartDrag
50
+ } from "../chunk-IHR6K3GF.js";
51
+ import "../chunk-ZYBWGSAZ.js";
52
+
53
+ // src/sequences-react/media/transcription.ts
54
+ var DEFAULT_WHISPER_MODEL = "onnx-community/whisper-large-v3-turbo";
55
+ var WHISPER_SAMPLE_RATE = 16e3;
56
+ var TRANSCRIPTION_PEER = "@huggingface/transformers";
57
+ var PEER_MISSING_MESSAGE = "transcription requires optional peer @huggingface/transformers";
58
+ function mixdownToMono(buffer) {
59
+ if (buffer.numberOfChannels < 1) throw new Error("audio buffer has no channels \u2014 cannot mix down");
60
+ if (buffer.numberOfChannels === 1) return buffer.getChannelData(0);
61
+ const mono = new Float32Array(buffer.length);
62
+ for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
63
+ const data = buffer.getChannelData(channel);
64
+ if (data.length !== buffer.length) {
65
+ throw new Error(`channel ${channel} has ${data.length} samples, expected ${buffer.length}`);
66
+ }
67
+ for (let i = 0; i < data.length; i++) {
68
+ mono[i] = mono[i] + data[i];
69
+ }
70
+ }
71
+ const scale = 1 / buffer.numberOfChannels;
72
+ for (let i = 0; i < mono.length; i++) {
73
+ mono[i] = mono[i] * scale;
74
+ }
75
+ return mono;
76
+ }
77
+ function mapWhisperOutput(output, durationSeconds, mediaUrl) {
78
+ const first = Array.isArray(output) ? output[0] : output;
79
+ if (first === void 0) throw new Error(`whisper produced no output for ${mediaUrl}`);
80
+ const chunks = first.chunks;
81
+ if (chunks === void 0 || chunks.length === 0) {
82
+ const text = first.text.trim();
83
+ return text.length === 0 ? [] : [{ text, startSeconds: 0, endSeconds: durationSeconds }];
84
+ }
85
+ const segments = [];
86
+ for (const chunk of chunks) {
87
+ const text = chunk.text.trim();
88
+ if (text.length === 0) continue;
89
+ const [start, end] = chunk.timestamp;
90
+ if (!Number.isFinite(start)) {
91
+ throw new Error(`whisper returned a non-numeric start timestamp for "${text}" in ${mediaUrl}`);
92
+ }
93
+ segments.push({ text, startSeconds: start, endSeconds: end === null ? durationSeconds : end });
94
+ }
95
+ return segments;
96
+ }
97
+ async function loadTransformers() {
98
+ try {
99
+ return await import(
100
+ /* @vite-ignore */
101
+ /* webpackIgnore: true */
102
+ TRANSCRIPTION_PEER
103
+ );
104
+ } catch (error) {
105
+ throw new Error(PEER_MISSING_MESSAGE, { cause: error });
106
+ }
107
+ }
108
+ async function fetchMonoAudio(mediaUrl) {
109
+ const response = await fetch(mediaUrl);
110
+ if (!response.ok) {
111
+ throw new Error(`failed to fetch media for transcription: ${response.status} ${response.statusText} from ${mediaUrl}`);
112
+ }
113
+ const bytes = await response.arrayBuffer();
114
+ if (typeof OfflineAudioContext === "undefined") {
115
+ throw new Error("transcription requires Web Audio (OfflineAudioContext) \u2014 call transcribe() from a browser");
116
+ }
117
+ const context = new OfflineAudioContext(1, 1, WHISPER_SAMPLE_RATE);
118
+ const decoded = await context.decodeAudioData(bytes);
119
+ return { samples: mixdownToMono(decoded), durationSeconds: decoded.duration };
120
+ }
121
+ function createWhisperTranscriptionProvider(opts) {
122
+ const model = opts?.model ?? DEFAULT_WHISPER_MODEL;
123
+ let availability = null;
124
+ let pipelinePromise = null;
125
+ const probeAvailability = () => {
126
+ if (availability !== null) return availability;
127
+ const resolve = import.meta.resolve;
128
+ if (typeof resolve !== "function") {
129
+ availability = false;
130
+ return availability;
131
+ }
132
+ try {
133
+ resolve.call(import.meta, TRANSCRIPTION_PEER);
134
+ availability = true;
135
+ } catch {
136
+ availability = false;
137
+ }
138
+ return availability;
139
+ };
140
+ const getPipeline = (transformers, onProgress) => {
141
+ if (pipelinePromise === null) {
142
+ const device = typeof navigator !== "undefined" && "gpu" in navigator ? "webgpu" : "wasm";
143
+ pipelinePromise = transformers.pipeline("automatic-speech-recognition", model, {
144
+ dtype: "q4",
145
+ device,
146
+ // Model download progress only — it dwarfs inference time on first
147
+ // use, and only the first transcribe ever sees a download.
148
+ progress_callback: (event) => {
149
+ if (event.status === "progress" && typeof event.progress === "number" && onProgress !== void 0) {
150
+ onProgress(Math.min(1, Math.max(0, event.progress / 100)));
151
+ }
152
+ }
153
+ });
154
+ pipelinePromise.catch(() => {
155
+ pipelinePromise = null;
156
+ });
157
+ }
158
+ return pipelinePromise;
159
+ };
160
+ return {
161
+ get available() {
162
+ return probeAvailability();
163
+ },
164
+ async transcribe(mediaUrl, transcribeOpts) {
165
+ const transformers = await loadTransformers();
166
+ availability = true;
167
+ const audio = await fetchMonoAudio(mediaUrl);
168
+ const transcriber = await getPipeline(transformers, transcribeOpts?.onProgress);
169
+ const options = {
170
+ return_timestamps: true,
171
+ chunk_length_s: 30,
172
+ stride_length_s: 5
173
+ };
174
+ if (transcribeOpts?.language !== void 0) options.language = transcribeOpts.language;
175
+ const output = await transcriber(audio.samples, options);
176
+ const segments = mapWhisperOutput(output, audio.durationSeconds, mediaUrl);
177
+ transcribeOpts?.onProgress?.(1);
178
+ return segments;
179
+ }
180
+ };
181
+ }
182
+
183
+ // src/sequences-react/lazy.tsx
184
+ import React from "react";
185
+ var SequenceTimelineEditorLazy = React.lazy(() => import("../TimelineEditor-OXPJZDP2.js"));
186
+ export {
187
+ COMMAND_HISTORY_LIMIT,
188
+ DEFAULT_MAX_MEDIA_ELEMENTS,
189
+ DEFAULT_WHISPER_MODEL,
190
+ PreviewCanvas,
191
+ SEEK_TIMEOUT_MS,
192
+ SEEK_TOLERANCE_SECONDS,
193
+ SEQUENCE_MEDIA_DRAG_TYPE,
194
+ SequenceTimelineEditorLazy,
195
+ SnapIndicatorLine,
196
+ TimelineClipChip,
197
+ TimelineEditor,
198
+ TimelinePlayhead,
199
+ TimelineRuler,
200
+ TimelineTrackRow,
201
+ ZoomControl,
202
+ addCaptionCommand,
203
+ applySnap,
204
+ captionFontPx,
205
+ chooseMoveSnap,
206
+ classifyMediaUrl,
207
+ clipChipGeometry,
208
+ collectSnapPoints,
209
+ compositeCommand,
210
+ computeWaveform,
211
+ containFitRect,
212
+ createCommandStack,
213
+ createImageFrameProvider,
214
+ createMediaElementPool,
215
+ createPlaybackClock,
216
+ createVideoElementFrameProvider,
217
+ createWhisperTranscriptionProvider,
218
+ createZoomMath,
219
+ deleteClipCommand,
220
+ drawWaveform,
221
+ frameToPixel,
222
+ framesFromPixelDelta,
223
+ letterboxRect,
224
+ loadWaveform,
225
+ mapWhisperOutput,
226
+ mixdownToMono,
227
+ moveClipCommand,
228
+ moveDragStartFrame,
229
+ needsSeek,
230
+ pixelToFrame,
231
+ placeClipCommand,
232
+ selectTickStepSeconds,
233
+ setClipTextCommand,
234
+ snapPixel,
235
+ splitClipCommand,
236
+ toggleClipDisabledCommand,
237
+ trimClipCommand,
238
+ trimEndDrag,
239
+ trimStartDrag
240
+ };
241
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/sequences-react/media/transcription.ts","../../src/sequences-react/lazy.tsx"],"sourcesContent":["/**\n * Whisper transcription behind the `TranscriptionProvider` seam, powered by\n * the OPTIONAL peer `@huggingface/transformers`. The peer is loaded with a\n * dynamic variable-specifier import inside `transcribe()` so bundlers leave\n * it external and apps that never transcribe never pay for it. `available`\n * is the sync UI affordance signal; `transcribe()` re-verifies with the real\n * import and is the source of truth.\n */\n\nimport type { TranscriptionProvider, TranscriptionSegment } from '../contracts'\nimport type { AudioBufferLike } from './waveform'\n\nexport const DEFAULT_WHISPER_MODEL = 'onnx-community/whisper-large-v3-turbo'\n\n/** Whisper models are trained on 16 kHz mono — decoding through a 16 kHz\n * OfflineAudioContext resamples any source rate in one step. */\nconst WHISPER_SAMPLE_RATE = 16_000\n\nconst TRANSCRIPTION_PEER = '@huggingface/transformers'\nconst PEER_MISSING_MESSAGE = 'transcription requires optional peer @huggingface/transformers'\n\ninterface WhisperChunk {\n text: string\n timestamp: [number, number | null]\n}\n\nexport interface WhisperOutput {\n text: string\n chunks?: WhisperChunk[]\n}\n\ntype WhisperPipeline = (\n audio: Float32Array,\n options: Record<string, unknown>,\n) => Promise<WhisperOutput | WhisperOutput[]>\n\ninterface TransformersProgressEvent {\n status: string\n progress?: number\n}\n\ninterface TransformersModule {\n pipeline(\n task: 'automatic-speech-recognition',\n model: string,\n options?: Record<string, unknown>,\n ): Promise<WhisperPipeline>\n}\n\n/** Mean-mixdown to mono. Single-channel buffers return the live channel data\n * without copying — callers must treat the result as read-only. */\nexport function mixdownToMono(buffer: AudioBufferLike): Float32Array {\n if (buffer.numberOfChannels < 1) throw new Error('audio buffer has no channels — cannot mix down')\n if (buffer.numberOfChannels === 1) return buffer.getChannelData(0)\n const mono = new Float32Array(buffer.length)\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const data = buffer.getChannelData(channel)\n if (data.length !== buffer.length) {\n throw new Error(`channel ${channel} has ${data.length} samples, expected ${buffer.length}`)\n }\n for (let i = 0; i < data.length; i++) {\n // in range by the length assertion above; casts silence noUncheckedIndexedAccess\n mono[i] = (mono[i] as number) + (data[i] as number)\n }\n }\n const scale = 1 / buffer.numberOfChannels\n for (let i = 0; i < mono.length; i++) {\n mono[i] = (mono[i] as number) * scale\n }\n return mono\n}\n\n/** Map whisper chunk output to contract segments. A `null` end timestamp is\n * whisper's \"ran past the end of the audio\" sentinel on the final chunk and\n * resolves to the audio duration. */\nexport function mapWhisperOutput(\n output: WhisperOutput | WhisperOutput[],\n durationSeconds: number,\n mediaUrl: string,\n): TranscriptionSegment[] {\n const first = Array.isArray(output) ? output[0] : output\n if (first === undefined) throw new Error(`whisper produced no output for ${mediaUrl}`)\n const chunks = first.chunks\n if (chunks === undefined || chunks.length === 0) {\n const text = first.text.trim()\n return text.length === 0 ? [] : [{ text, startSeconds: 0, endSeconds: durationSeconds }]\n }\n const segments: TranscriptionSegment[] = []\n for (const chunk of chunks) {\n const text = chunk.text.trim()\n if (text.length === 0) continue\n const [start, end] = chunk.timestamp\n if (!Number.isFinite(start)) {\n throw new Error(`whisper returned a non-numeric start timestamp for \"${text}\" in ${mediaUrl}`)\n }\n segments.push({ text, startSeconds: start, endSeconds: end === null ? durationSeconds : end })\n }\n return segments\n}\n\nasync function loadTransformers(): Promise<TransformersModule> {\n try {\n // Variable specifier + ignore pragmas keep bundlers from statically\n // resolving an optional peer that may not be installed.\n return (await import(/* @vite-ignore */ /* webpackIgnore: true */ TRANSCRIPTION_PEER)) as TransformersModule\n } catch (error) {\n throw new Error(PEER_MISSING_MESSAGE, { cause: error })\n }\n}\n\nasync function fetchMonoAudio(mediaUrl: string): Promise<{ samples: Float32Array; durationSeconds: number }> {\n const response = await fetch(mediaUrl)\n if (!response.ok) {\n throw new Error(`failed to fetch media for transcription: ${response.status} ${response.statusText} from ${mediaUrl}`)\n }\n const bytes = await response.arrayBuffer()\n if (typeof OfflineAudioContext === 'undefined') {\n throw new Error('transcription requires Web Audio (OfflineAudioContext) — call transcribe() from a browser')\n }\n // decodeAudioData resamples to the context rate per the Web Audio spec, so\n // a 16 kHz context yields whisper-ready samples from any source rate.\n const context = new OfflineAudioContext(1, 1, WHISPER_SAMPLE_RATE)\n const decoded = await context.decodeAudioData(bytes)\n return { samples: mixdownToMono(decoded), durationSeconds: decoded.duration }\n}\n\nexport function createWhisperTranscriptionProvider(opts?: { model?: string }): TranscriptionProvider {\n const model = opts?.model ?? DEFAULT_WHISPER_MODEL\n let availability: boolean | null = null\n let pipelinePromise: Promise<WhisperPipeline> | null = null\n\n const probeAvailability = (): boolean => {\n if (availability !== null) return availability\n const resolve = (import.meta as ImportMeta & { resolve?: (specifier: string) => string }).resolve\n if (typeof resolve !== 'function') {\n // No sync resolver in this runtime — report unavailable rather than\n // guess; transcribe() still attempts the real import either way.\n availability = false\n return availability\n }\n try {\n resolve.call(import.meta, TRANSCRIPTION_PEER)\n availability = true\n } catch {\n availability = false\n }\n return availability\n }\n\n const getPipeline = (\n transformers: TransformersModule,\n onProgress?: (fraction: number) => void,\n ): Promise<WhisperPipeline> => {\n if (pipelinePromise === null) {\n const device = typeof navigator !== 'undefined' && 'gpu' in navigator ? 'webgpu' : 'wasm'\n pipelinePromise = transformers.pipeline('automatic-speech-recognition', model, {\n dtype: 'q4',\n device,\n // Model download progress only — it dwarfs inference time on first\n // use, and only the first transcribe ever sees a download.\n progress_callback: (event: TransformersProgressEvent) => {\n if (event.status === 'progress' && typeof event.progress === 'number' && onProgress !== undefined) {\n onProgress(Math.min(1, Math.max(0, event.progress / 100)))\n }\n },\n })\n // A failed load must not poison the cache — the next transcribe retries.\n pipelinePromise.catch(() => {\n pipelinePromise = null\n })\n }\n return pipelinePromise\n }\n\n return {\n get available() {\n return probeAvailability()\n },\n async transcribe(mediaUrl, transcribeOpts) {\n const transformers = await loadTransformers()\n // A successful import is ground truth and overrides a false probe.\n availability = true\n const audio = await fetchMonoAudio(mediaUrl)\n const transcriber = await getPipeline(transformers, transcribeOpts?.onProgress)\n const options: Record<string, unknown> = {\n return_timestamps: true,\n chunk_length_s: 30,\n stride_length_s: 5,\n }\n if (transcribeOpts?.language !== undefined) options.language = transcribeOpts.language\n const output = await transcriber(audio.samples, options)\n const segments = mapWhisperOutput(output, audio.durationSeconds, mediaUrl)\n transcribeOpts?.onProgress?.(1)\n return segments\n },\n }\n}\n","/**\n * Code-split entry for the timeline editor. The editor pulls in canvas\n * painting, waveform decode, and gesture machinery products only need on the\n * sequence route — `React.lazy` keeps it out of their main bundle. Mount\n * inside a `<Suspense>` boundary.\n */\n\nimport React from 'react'\n\nexport const SequenceTimelineEditorLazy = React.lazy(() => import('./components/TimelineEditor'))\n"],"mappings":"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","names":[]}
@@ -0,0 +1,64 @@
1
+ import { c as SceneDocument } from './model-BHLN208Z.js';
2
+
3
+ /**
4
+ * Storage contract for design-canvas documents. Unlike sequences (row per
5
+ * clip), a scene document persists as ONE JSON value with a monotonic
6
+ * revision counter — saves are atomic and optimistic: a save carrying a
7
+ * stale `expectedRev` throws, the caller refetches and replays. That keeps
8
+ * concurrent editors (human + agent in the same document) from silently
9
+ * clobbering each other without needing row-level merge machinery.
10
+ *
11
+ * The product constructs one store per (workspace, document, actor) request
12
+ * scope — RBAC runs BEFORE construction; the store never re-checks identity.
13
+ * Every method throws on failure; the MCP dispatcher converts throws into
14
+ * structured tool errors.
15
+ */
16
+
17
+ interface SceneDocumentRecord {
18
+ document: SceneDocument;
19
+ /** Monotonic revision; increments on every successful save. */
20
+ rev: number;
21
+ }
22
+ interface SceneDecision {
23
+ id: string;
24
+ kind: 'human_edit' | 'agent_edit' | 'agent_proposal' | 'export' | 'note';
25
+ instruction: string;
26
+ reasoningSummary: string | null;
27
+ metadata: Record<string, unknown>;
28
+ createdAt: Date;
29
+ }
30
+ interface NewSceneDecision {
31
+ kind: SceneDecision['kind'];
32
+ instruction: string;
33
+ reasoningSummary?: string | null;
34
+ metadata?: Record<string, unknown>;
35
+ }
36
+ type SceneExportFormat = 'png' | 'jpeg' | 'json';
37
+ interface SceneExportRecord {
38
+ id: string;
39
+ format: SceneExportFormat;
40
+ status: 'queued' | 'processing' | 'completed' | 'failed';
41
+ resultUrl: string | null;
42
+ metadata: Record<string, unknown>;
43
+ createdAt: Date;
44
+ }
45
+ interface SceneStore {
46
+ /** Current document + revision. */
47
+ getDocument(): Promise<SceneDocumentRecord>;
48
+ /** Atomic full-document save. Throws when `expectedRev` is stale — the
49
+ * caller must refetch, reapply, and retry; never merge silently. */
50
+ saveDocument(document: SceneDocument, expectedRev: number): Promise<SceneDocumentRecord>;
51
+ recordDecision(input: NewSceneDecision): Promise<SceneDecision>;
52
+ createExport(format: SceneExportFormat, metadata?: Record<string, unknown>): Promise<SceneExportRecord>;
53
+ listDecisions(limit?: number): Promise<SceneDecision[]>;
54
+ listExports(limit?: number): Promise<SceneExportRecord[]>;
55
+ }
56
+ /** Per-request scope a product binds at store construction; decision and
57
+ * export rows attribute to the acting user — never trusted from tool args. */
58
+ interface SceneStoreScope {
59
+ workspaceId: string;
60
+ documentId: string;
61
+ userId: string;
62
+ }
63
+
64
+ export type { NewSceneDecision as N, SceneDecision as S, SceneDocumentRecord as a, SceneExportFormat as b, SceneExportRecord as c, SceneStore as d, SceneStoreScope as e };
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Frame-accurate sequence timeline model — the product-agnostic spine of the
3
+ * sequences surface. A sequence is a fixed-fps, fixed-duration timeline of
4
+ * tracks; clips sit on tracks at integer frame positions with non-destructive
5
+ * source in/out points. Products bind this model to their own storage through
6
+ * `SequenceStore` (./store) and surface it to agents through the MCP toolset
7
+ * (./mcp).
8
+ *
9
+ * All positions and durations are integer FRAMES at the sequence's fps.
10
+ * Seconds appear only at the API edge (agent tools speak seconds; the
11
+ * dispatcher converts exactly once). Nothing here touches a database, the DOM,
12
+ * or React.
13
+ */
14
+ declare const MIN_SEQUENCE_CLIP_FRAMES = 1;
15
+ /** Track kinds. `reference` holds non-rendered guide media; `agent` holds the
16
+ * agent-decision lane rendered as markers, never as media. */
17
+ type SequenceTrackKind = 'video' | 'audio' | 'caption' | 'reference' | 'agent';
18
+ type SequenceStatus = 'draft' | 'active' | 'exporting' | 'archived';
19
+ type SequenceExportFormat = 'mp4' | 'otio' | 'xml' | 'edl' | 'vtt' | 'srt' | 'contact_sheet';
20
+ type SequenceExportStatus = 'queued' | 'processing' | 'completed' | 'failed' | 'cancelled';
21
+ type SequenceMediaKind = 'video' | 'image' | 'audio';
22
+ interface SequenceMeta {
23
+ id: string;
24
+ title: string;
25
+ fps: number;
26
+ width: number;
27
+ height: number;
28
+ aspectRatio: string;
29
+ durationFrames: number;
30
+ status: SequenceStatus;
31
+ metadata: Record<string, unknown>;
32
+ }
33
+ interface SequenceTrack {
34
+ id: string;
35
+ kind: SequenceTrackKind;
36
+ name: string;
37
+ sortOrder: number;
38
+ locked: boolean;
39
+ muted: boolean;
40
+ metadata: Record<string, unknown>;
41
+ }
42
+ /** Resolved playable media behind a clip. The store resolves product-specific
43
+ * references (generation rows, asset rows) into this shape; the core model
44
+ * never sees the product's tables. */
45
+ interface SequenceClipMedia {
46
+ url: string;
47
+ kind: SequenceMediaKind;
48
+ /** Natural duration of the source media when known. */
49
+ durationSeconds?: number;
50
+ /** Provider job state for media still rendering upstream. */
51
+ providerStatus?: 'queued' | 'processing' | 'completed' | 'failed';
52
+ }
53
+ interface SequenceClip {
54
+ id: string;
55
+ trackId: string;
56
+ label: string;
57
+ startFrame: number;
58
+ durationFrames: number;
59
+ /** Source-relative in point (frames into the source media). */
60
+ sourceInFrame: number;
61
+ /** Source-relative out point; null = natural end of the source. */
62
+ sourceOutFrame: number | null;
63
+ disabled: boolean;
64
+ /** Caption/text body for clips on caption tracks. */
65
+ text?: string;
66
+ /** BCP-47 language tag for caption clips (e.g. 'en', 'es', 'ja'). */
67
+ language?: string;
68
+ /** Opaque product reference to a generation row, when the clip came from one. */
69
+ generationId?: string;
70
+ /** Opaque product reference to an asset row, when the clip came from one. */
71
+ assetId?: string;
72
+ media?: SequenceClipMedia;
73
+ metadata: Record<string, unknown>;
74
+ }
75
+ /** One entry in the sequence's decision log — human edits, agent proposals,
76
+ * agent edits, exports, and notes all land here so the edit history is a
77
+ * single auditable lane. */
78
+ interface SequenceDecision {
79
+ id: string;
80
+ clipId: string | null;
81
+ kind: 'human_edit' | 'agent_proposal' | 'agent_edit' | 'export' | 'note';
82
+ instruction: string;
83
+ reasoningSummary: string | null;
84
+ accepted: boolean | null;
85
+ metadata: Record<string, unknown>;
86
+ createdAt: Date;
87
+ }
88
+ interface SequenceExportRecord {
89
+ id: string;
90
+ format: SequenceExportFormat;
91
+ status: SequenceExportStatus;
92
+ resultUrl: string | null;
93
+ metadata: Record<string, unknown>;
94
+ createdAt: Date;
95
+ }
96
+ /** The full timeline aggregate — what `get_timeline_state` returns and what
97
+ * every operation validates against. */
98
+ interface SequenceTimeline {
99
+ sequence: SequenceMeta;
100
+ tracks: SequenceTrack[];
101
+ clips: SequenceClip[];
102
+ }
103
+ /** What is on screen/audible at a single frame — the answer shape for
104
+ * "what is happening at 0:34". */
105
+ interface SequenceFrameSnapshot {
106
+ frame: number;
107
+ seconds: number;
108
+ /** Active (enabled, in-range) clips at this frame, with their track. */
109
+ active: Array<{
110
+ track: SequenceTrack;
111
+ clip: SequenceClip;
112
+ }>;
113
+ /** Caption text visible at this frame, in track sort order. */
114
+ captions: Array<{
115
+ text: string;
116
+ language?: string;
117
+ clipId: string;
118
+ }>;
119
+ }
120
+ declare function secondsToFrames(seconds: number, fps: number): number;
121
+ declare function framesToSeconds(frames: number, fps: number): number;
122
+ declare function formatSeconds(seconds: number): string;
123
+ /** `m:ss.ff` timecode for UI and agent-readable frame references. */
124
+ declare function formatTimecode(frames: number, fps: number): string;
125
+ interface TimelineClipBounds {
126
+ startFrame: number;
127
+ durationFrames: number;
128
+ }
129
+ interface TimelineInterval {
130
+ startFrame: number;
131
+ endFrame: number;
132
+ }
133
+ declare function clampClipStart(input: {
134
+ startFrame: number;
135
+ durationFrames: number;
136
+ sequenceDurationFrames: number;
137
+ }): number;
138
+ declare function clampClipDuration(input: {
139
+ startFrame: number;
140
+ durationFrames: number;
141
+ sequenceDurationFrames: number;
142
+ }): number;
143
+ declare function assertClipFitsSequence(input: {
144
+ startFrame: number;
145
+ durationFrames: number;
146
+ sequenceDurationFrames: number;
147
+ label: string;
148
+ }): void;
149
+ /** Place a caption near the playhead inside FREE space only — the caption
150
+ * track never double-books. Prefers fps*3 frames, floors at fps. The gap
151
+ * holding (or first after) the playhead wins; with everything ahead occupied
152
+ * the latest earlier gap is used instead. Throws when no gap can hold the
153
+ * minimum — the caller must supply explicit bounds or clear space. */
154
+ declare function chooseCaptionPlacement(input: {
155
+ playheadFrame: number;
156
+ fps: number;
157
+ sequenceDurationFrames: number;
158
+ occupiedIntervals: TimelineInterval[];
159
+ }): TimelineClipBounds;
160
+ /** Resolve everything active at one frame — the core of `get_frame_at_time`. */
161
+ declare function snapshotFrame(timeline: SequenceTimeline, frame: number): SequenceFrameSnapshot;
162
+ /** Occupied intervals on one track, for placement collision checks. */
163
+ declare function trackIntervals(timeline: SequenceTimeline, trackId: string): TimelineInterval[];
164
+
165
+ /**
166
+ * Storage contract binding the sequence model to a product's database. The
167
+ * product constructs one store per (workspace, sequence, actor) request scope —
168
+ * RBAC and workspace isolation happen BEFORE construction (the product's
169
+ * `requireWorkspaceAccess` equivalent); the store never re-checks identity.
170
+ *
171
+ * Every method throws on failure — no silent nulls, no `{ ok: false }` wrappers
172
+ * at this layer. The MCP dispatcher (./mcp) is the boundary that converts
173
+ * thrown errors into structured tool errors the model can read and react to.
174
+ *
175
+ * Mutations append to the decision log themselves only when the operation
176
+ * dispatcher asks (`recordDecision`); plain CRUD stays log-free so human edits
177
+ * driven by the UI can batch their own decision entries.
178
+ */
179
+
180
+ interface NewSequenceTrack {
181
+ kind: SequenceTrackKind;
182
+ name: string;
183
+ sortOrder?: number;
184
+ }
185
+ interface NewSequenceClip {
186
+ trackId: string;
187
+ label: string;
188
+ startFrame: number;
189
+ durationFrames: number;
190
+ sourceInFrame?: number;
191
+ sourceOutFrame?: number | null;
192
+ text?: string;
193
+ language?: string;
194
+ generationId?: string;
195
+ assetId?: string;
196
+ metadata?: Record<string, unknown>;
197
+ }
198
+ interface SequenceClipPatch {
199
+ trackId?: string;
200
+ label?: string;
201
+ startFrame?: number;
202
+ durationFrames?: number;
203
+ sourceInFrame?: number;
204
+ sourceOutFrame?: number | null;
205
+ disabled?: boolean;
206
+ text?: string;
207
+ language?: string;
208
+ metadata?: Record<string, unknown>;
209
+ }
210
+ interface NewSequenceDecision {
211
+ clipId?: string | null;
212
+ kind: SequenceDecision['kind'];
213
+ instruction: string;
214
+ reasoningSummary?: string | null;
215
+ accepted?: boolean | null;
216
+ metadata?: Record<string, unknown>;
217
+ }
218
+ interface SequenceStore {
219
+ /** Full aggregate: sequence meta + tracks + clips with resolved media. */
220
+ getTimeline(): Promise<SequenceTimeline>;
221
+ getClip(clipId: string): Promise<SequenceClip>;
222
+ createTrack(input: NewSequenceTrack): Promise<SequenceTrack>;
223
+ createClip(input: NewSequenceClip): Promise<SequenceClip>;
224
+ updateClip(clipId: string, patch: SequenceClipPatch): Promise<SequenceClip>;
225
+ deleteClip(clipId: string): Promise<void>;
226
+ /** Grow (or shrink, never below the last clip end) the sequence duration. */
227
+ updateSequenceDuration(durationFrames: number): Promise<SequenceMeta>;
228
+ recordDecision(input: NewSequenceDecision): Promise<SequenceDecision>;
229
+ createExport(format: SequenceExportFormat, metadata?: Record<string, unknown>): Promise<SequenceExportRecord>;
230
+ listDecisions(limit?: number): Promise<SequenceDecision[]>;
231
+ listExports(limit?: number): Promise<SequenceExportRecord[]>;
232
+ }
233
+ /** Per-request scope a product binds when constructing its store. Carried so
234
+ * decision rows and export rows attribute to the acting user; never trusted
235
+ * from tool arguments. */
236
+ interface SequenceStoreScope {
237
+ workspaceId: string;
238
+ sequenceId: string;
239
+ userId: string;
240
+ }
241
+
242
+ export { snapshotFrame as A, trackIntervals as B, MIN_SEQUENCE_CLIP_FRAMES as M, type NewSequenceClip as N, type SequenceClip as S, type TimelineClipBounds as T, type NewSequenceDecision as a, type NewSequenceTrack as b, type SequenceClipMedia as c, type SequenceClipPatch as d, type SequenceDecision as e, type SequenceExportFormat as f, type SequenceExportRecord as g, type SequenceExportStatus as h, type SequenceFrameSnapshot as i, type SequenceMediaKind as j, type SequenceMeta as k, type SequenceStatus as l, type SequenceStore as m, type SequenceStoreScope as n, type SequenceTimeline as o, type SequenceTrack as p, type SequenceTrackKind as q, type TimelineInterval as r, assertClipFitsSequence as s, chooseCaptionPlacement as t, clampClipDuration as u, clampClipStart as v, formatSeconds as w, formatTimecode as x, framesToSeconds as y, secondsToFrames as z };