@mengine/medeo-tool 1.4.1-alpha.1 → 1.4.1-alpha.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/README.md +73 -125
- package/dist/{entity-contract-DHasvrhq.d.mts → entity-contract-DQ56Ihrh.d.mts} +42 -77
- package/dist/{script-session-AukLN7x7.mjs → entity-sandbox-BH-7F5C8.mjs} +130 -505
- package/dist/entity-sandbox-BH-7F5C8.mjs.map +1 -0
- package/dist/index.d.mts +14 -99
- package/dist/index.mjs +275 -344
- package/dist/index.mjs.map +1 -1
- package/dist/sandbox-api.d.mts +79 -295
- package/dist/worker-entry.d.mts +1 -2
- package/dist/worker-entry.mjs +190 -207
- package/dist/worker-entry.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/script-session-AukLN7x7.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,141 @@
|
|
|
1
|
-
import { a as createEntityId,
|
|
2
|
-
import { LoroEntityDocument, ManualSyncDoc, MengineHttpClient, MengineHttpRequestError, ValidationError, base64ToBytes, bytesToBase64, compileEntityRows, createMirrorVideoDocument, createPlainMemoryAdapter, decodeDocVersionMark, encodeDocVersionMark, ensureEditorFoundation, replayJournal, toVideoDocument } from "@mengine/medeo-client";
|
|
1
|
+
import { a as createEntityId, i as businessState, o as createRelationId, s as isMediaAssetVariantKind, t as EntitySandbox } from "./entity-sandbox-BH-7F5C8.mjs";
|
|
2
|
+
import { LoroEntityDocument, ManualSyncDoc, MengineHttpClient, MengineHttpRequestError, ValidationError, base64ToBytes, bytesToBase64, compileEntityRows, createMirrorVideoDocument, createPlainMemoryAdapter, decodeDocVersionMark, effectiveVideoClipDurationMs, encodeDocVersionMark, ensureEditorFoundation, replayJournal, solveVideoDocument, speedOf, toVideoDocument } from "@mengine/medeo-client";
|
|
3
3
|
import { Worker } from "node:worker_threads";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
+
//#region src/document/compact-projection.ts
|
|
6
|
+
const DEFAULT_TEXT_PREVIEW_LENGTH = 24;
|
|
7
|
+
/** Kind tag shown in the first column (`video_clip` → `clip`). */
|
|
8
|
+
function kindTag(kind) {
|
|
9
|
+
return kind === "video_clip" ? "clip" : kind;
|
|
10
|
+
}
|
|
11
|
+
/** Lane label: `video_clip` tracks display as `main`, otherwise `parts_kind`. */
|
|
12
|
+
function laneLabel(partsKind) {
|
|
13
|
+
return partsKind === "video_clip" ? "main" : partsKind;
|
|
14
|
+
}
|
|
15
|
+
/** Speed token: absent → `1`; linear → numeric multiplier; anything else → `custom`. */
|
|
16
|
+
function speedToken(speedShift) {
|
|
17
|
+
if (speedShift == null) return "1";
|
|
18
|
+
if (speedShift.category === "linear") return String(speedOf(speedShift));
|
|
19
|
+
return "custom";
|
|
20
|
+
}
|
|
21
|
+
function effectiveDurationMs(part, timelineDurationMs) {
|
|
22
|
+
if (part.video_clip != null) return effectiveVideoClipDurationMs(part.video_clip);
|
|
23
|
+
if (part.speech != null) return part.speech.media_duration_ms ?? 0;
|
|
24
|
+
if (part.caption != null) return part.caption.initial_duration_ms ?? 0;
|
|
25
|
+
if (part.bgm != null) return timelineDurationMs;
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
function truncateText(text, budget) {
|
|
29
|
+
if (text.length <= budget) return text;
|
|
30
|
+
return `${text.slice(0, budget)}…`;
|
|
31
|
+
}
|
|
32
|
+
function anchorToken(timePosition) {
|
|
33
|
+
if (timePosition.mode === "anchored") return `anchor=${timePosition.anchorPartId}+${timePosition.offsetMs}`;
|
|
34
|
+
if (timePosition.mode === "absolute") return "anchor=abs";
|
|
35
|
+
return "anchor=abs";
|
|
36
|
+
}
|
|
37
|
+
function clipAttrs(clip) {
|
|
38
|
+
const playIn = clip.play_in ?? 0;
|
|
39
|
+
const playOut = clip.play_out ?? 0;
|
|
40
|
+
return `media=${clip.origin_media_id ?? ""} trim=${playIn}-${playOut} speed=${speedToken(clip.speed_shift)} vol=${clip.volume ?? 0}`;
|
|
41
|
+
}
|
|
42
|
+
function partAttrs(part, item, textPreviewLength) {
|
|
43
|
+
if (part.video_clip != null) return clipAttrs(part.video_clip);
|
|
44
|
+
if (part.speech != null) return `${anchorToken(item.time_position)} dur=${part.speech.media_duration_ms ?? 0}`;
|
|
45
|
+
if (part.caption != null) {
|
|
46
|
+
const preview = truncateText(part.caption.text ?? "", textPreviewLength);
|
|
47
|
+
return `${anchorToken(item.time_position)} text="${preview}"`;
|
|
48
|
+
}
|
|
49
|
+
if (part.bgm != null) return `vol=${part.bgm.volume ?? 0}`;
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Render a `VideoDocument` as compact text: one header line plus one row per
|
|
54
|
+
* timeline part (optionally filtered by `onlyPartIds`). Deterministic and
|
|
55
|
+
* side-effect free — same document always yields the same string.
|
|
56
|
+
*/
|
|
57
|
+
function renderCompactProjection(document, options) {
|
|
58
|
+
const onlyPartIds = options?.onlyPartIds;
|
|
59
|
+
const textPreviewLength = options?.textPreviewLength ?? DEFAULT_TEXT_PREVIEW_LENGTH;
|
|
60
|
+
const solved = solveVideoDocument(document);
|
|
61
|
+
const library = document.part_library ?? {};
|
|
62
|
+
const tracks = document.tracks ?? [];
|
|
63
|
+
let totalParts = 0;
|
|
64
|
+
const rows = [];
|
|
65
|
+
for (const track of tracks) {
|
|
66
|
+
const partsKind = track.parts_kind;
|
|
67
|
+
if (partsKind == null) continue;
|
|
68
|
+
const lane = laneLabel(partsKind);
|
|
69
|
+
const tag = kindTag(partsKind);
|
|
70
|
+
for (const item of track.items ?? []) {
|
|
71
|
+
totalParts += 1;
|
|
72
|
+
const partId = item.part_id;
|
|
73
|
+
if (onlyPartIds != null && !onlyPartIds.has(partId)) continue;
|
|
74
|
+
const part = library[partId];
|
|
75
|
+
if (part == null) continue;
|
|
76
|
+
const abs = solved.absByPartId.get(partId) ?? 0;
|
|
77
|
+
const dur = effectiveDurationMs(part, solved.durationMs);
|
|
78
|
+
const attrs = partAttrs(part, item, textPreviewLength);
|
|
79
|
+
rows.push(`${tag} ${partId} ${lane} [${abs},${abs + dur}) ${attrs}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return [`# draft=${document.meta.draft_id ?? ""} v=${document.meta.version ?? 0} duration=${solved.durationMs} parts=${totalParts} shown=${rows.length}`, ...rows].join("\n");
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/sandbox/preview.ts
|
|
86
|
+
/**
|
|
87
|
+
* Collect part ids referenced by a journal for compact preview filtering.
|
|
88
|
+
* Walks known id-shaped payload keys (宁多勿少) and unions `generated_ids`.
|
|
89
|
+
*/
|
|
90
|
+
const PART_ID_KEYS = new Set([
|
|
91
|
+
"clip_id",
|
|
92
|
+
"clip_ids",
|
|
93
|
+
"before_clip_id",
|
|
94
|
+
"after_clip_id",
|
|
95
|
+
"speech_id",
|
|
96
|
+
"speech_ids",
|
|
97
|
+
"speech_part_id",
|
|
98
|
+
"caption_id",
|
|
99
|
+
"caption_ids",
|
|
100
|
+
"bgm_id",
|
|
101
|
+
"anchor_part_id",
|
|
102
|
+
"part_id",
|
|
103
|
+
"body_part_id"
|
|
104
|
+
]);
|
|
105
|
+
/** Extract every part id a journal entry touches (payload refs + minted ids). */
|
|
106
|
+
function collectAffectedPartIds(journal) {
|
|
107
|
+
const ids = /* @__PURE__ */ new Set();
|
|
108
|
+
for (const entry of journal) {
|
|
109
|
+
for (const generated of entry.generated_ids ?? []) if (generated.length > 0) ids.add(generated);
|
|
110
|
+
collectFromValue(entry.payload, ids);
|
|
111
|
+
}
|
|
112
|
+
return ids;
|
|
113
|
+
}
|
|
114
|
+
function collectFromValue(value, ids) {
|
|
115
|
+
if (value == null) return;
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
for (const item of value) collectFromValue(item, ids);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (typeof value !== "object") return;
|
|
121
|
+
for (const [key, child] of Object.entries(value)) {
|
|
122
|
+
if (PART_ID_KEYS.has(key)) {
|
|
123
|
+
if (typeof child === "string" && child.length > 0) ids.add(child);
|
|
124
|
+
else if (Array.isArray(child)) {
|
|
125
|
+
for (const item of child) if (typeof item === "string" && item.length > 0) ids.add(item);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
collectFromValue(child, ids);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Render a ChangePlan preview: header + rows for journal-affected parts only.
|
|
133
|
+
* Empty journal → empty `onlyPartIds` (header alone), matching the T2 contract.
|
|
134
|
+
*/
|
|
135
|
+
function renderPreview(document, journal) {
|
|
136
|
+
return renderCompactProjection(document, { onlyPartIds: journal.length === 0 ? /* @__PURE__ */ new Set() : collectAffectedPartIds(journal) });
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
5
139
|
//#region src/sandbox/node-host.ts
|
|
6
140
|
const DEFAULT_TIMEOUT_MS = 2e3;
|
|
7
141
|
const DEFAULT_MEMORY_MB = 256;
|
|
@@ -35,8 +169,7 @@ function runEditScript(options) {
|
|
|
35
169
|
script: options.script,
|
|
36
170
|
inputs: options.inputs,
|
|
37
171
|
entityState: options.entityState,
|
|
38
|
-
idLabel: options.idLabel
|
|
39
|
-
entityOnly: options.entityOnly
|
|
172
|
+
idLabel: options.idLabel
|
|
40
173
|
},
|
|
41
174
|
execArgv: resolveRegisterUrl.pathname.endsWith(".ts") ? [
|
|
42
175
|
"--experimental-transform-types",
|
|
@@ -81,6 +214,26 @@ function runEditScript(options) {
|
|
|
81
214
|
armTimeout();
|
|
82
215
|
return;
|
|
83
216
|
}
|
|
217
|
+
if (message.t === "entity-asset") {
|
|
218
|
+
(async () => {
|
|
219
|
+
try {
|
|
220
|
+
if (!options.loadEntityAsset) throw new Error("Entity Asset loader is unavailable");
|
|
221
|
+
const result = await options.loadEntityAsset(message.entity);
|
|
222
|
+
if (!settled) worker.postMessage({
|
|
223
|
+
t: "entity-asset-result",
|
|
224
|
+
requestId: message.requestId,
|
|
225
|
+
result
|
|
226
|
+
});
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (!settled) worker.postMessage({
|
|
229
|
+
t: "entity-asset-result",
|
|
230
|
+
requestId: message.requestId,
|
|
231
|
+
error: error instanceof Error ? error.message : String(error)
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
})();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
84
237
|
if (message.t === "entry") {
|
|
85
238
|
ops.push(message.entry);
|
|
86
239
|
return;
|
|
@@ -604,12 +757,10 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
604
757
|
" sampling: 'native';",
|
|
605
758
|
" coordinateSpace: JsonValue;",
|
|
606
759
|
"}",
|
|
607
|
-
"/** Business editing surface.
|
|
760
|
+
"/** Business editing surface. Reads are assembled snapshots; writes preserve native operation intent. */",
|
|
608
761
|
"export interface BusinessEntityFacade {",
|
|
609
762
|
" list(): SandboxEntity[];",
|
|
610
763
|
" get(entityId: string): SandboxEntity | null;",
|
|
611
|
-
" readCaptionContent(entityId: string): ComposedScriptContent;",
|
|
612
|
-
" readPhoneticScriptContent(entityId: string): ComposedPhoneticContent;",
|
|
613
764
|
" create(",
|
|
614
765
|
" input: Exclude<",
|
|
615
766
|
" CreateEntityInput,",
|
|
@@ -618,66 +769,18 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
618
769
|
" }",
|
|
619
770
|
" >,",
|
|
620
771
|
" ): string;",
|
|
621
|
-
" update(input:
|
|
772
|
+
" update(input: EntityUpdateInput): void;",
|
|
622
773
|
" declareFields(input: UpdateEntityInput): void;",
|
|
623
774
|
" delete(input: DeleteEntityInput): void;",
|
|
624
775
|
"}",
|
|
625
|
-
"/**
|
|
776
|
+
"/** Endpoint order is retained; each relation kind defines its endpoint semantics. */",
|
|
626
777
|
"export interface BusinessRelationFacade {",
|
|
627
778
|
" list(): SandboxRelation[];",
|
|
628
779
|
" of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];",
|
|
629
|
-
" link(",
|
|
630
|
-
"
|
|
631
|
-
" LinkRelationInput,",
|
|
632
|
-
" {",
|
|
633
|
-
" relation_kind: 'physical-asset';",
|
|
634
|
-
" }",
|
|
635
|
-
" >,",
|
|
636
|
-
" ): string;",
|
|
637
|
-
" linkGenerated(input: LinkGeneratedRelationInput): string;",
|
|
638
|
-
" linkClipAnchor(input: LinkClipAnchorRelationInput): string;",
|
|
639
|
-
" linkPhoneticScriptRender(input: LinkPhoneticScriptRenderRelationInput): string;",
|
|
640
|
-
" linkAudioScriptSource(input: LinkAudioScriptSourceRelationInput): string;",
|
|
780
|
+
" link(input: LinkRelationInput): string;",
|
|
781
|
+
" update(input: RelationUpdateInput): void;",
|
|
641
782
|
" unlink(input: UnlinkRelationInput): void;",
|
|
642
783
|
"}",
|
|
643
|
-
"export interface CaptionFontDescriptor {",
|
|
644
|
-
" readonly system: 'font-library';",
|
|
645
|
-
" readonly key: string;",
|
|
646
|
-
"}",
|
|
647
|
-
"/**",
|
|
648
|
-
" * One ordered entry of the Caption's segment selection. `segmentId` quotes the",
|
|
649
|
-
" * composed AudioScript's own stable segment identity — a local id quoted by the",
|
|
650
|
-
" * variant, never a peer Entity reference. Text itself is never copied here;",
|
|
651
|
-
" * complete Caption content is assembled through its direct baseEntityIds.",
|
|
652
|
-
" * The optional `textRange` narrows one Segment to an intra-Segment sub-span",
|
|
653
|
-
" * (intra-segment re-segmentation); without it the whole Segment text is selected.",
|
|
654
|
-
" */",
|
|
655
|
-
"export type CaptionSegmentSelection = JsonObject & {",
|
|
656
|
-
" readonly segmentId: string;",
|
|
657
|
-
" readonly textRange?: CaptionTextRange;",
|
|
658
|
-
"};",
|
|
659
|
-
"export interface CaptionStyleFields {",
|
|
660
|
-
" readonly font?: CaptionFontDescriptor;",
|
|
661
|
-
" readonly fontSize?: number;",
|
|
662
|
-
" readonly fontColor?: string;",
|
|
663
|
-
" readonly fontWeight?: number;",
|
|
664
|
-
" readonly entranceAnimation?: string;",
|
|
665
|
-
" readonly entranceAnimationDurationMs?: number;",
|
|
666
|
-
" readonly strokeColor?: string;",
|
|
667
|
-
" readonly strokeWidth?: number;",
|
|
668
|
-
" readonly positionX?: number;",
|
|
669
|
-
" readonly positionY?: number;",
|
|
670
|
-
"}",
|
|
671
|
-
"/**",
|
|
672
|
-
" * Half-open `[start, end)` position window inside one Segment's text, counted",
|
|
673
|
-
" * in Unicode code points (not UTF-16 code units), so a boundary never splits a",
|
|
674
|
-
" * surrogate pair. Positions are non-negative safe integers with `start < end`;",
|
|
675
|
-
" * `end` must not exceed the Segment's code-point length.",
|
|
676
|
-
" */",
|
|
677
|
-
"export interface CaptionTextRange extends JsonObject {",
|
|
678
|
-
" readonly start: number;",
|
|
679
|
-
" readonly end: number;",
|
|
680
|
-
"}",
|
|
681
784
|
"export type CaptionTextSelection = JsonObject & {",
|
|
682
785
|
" segmentId: string;",
|
|
683
786
|
" /** Half-open Unicode code-point range within the selected source segment. */",
|
|
@@ -686,31 +789,6 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
686
789
|
" end: number;",
|
|
687
790
|
" };",
|
|
688
791
|
"};",
|
|
689
|
-
"export type ClipEntityId = EntityId;",
|
|
690
|
-
"export type ClipPlacement =",
|
|
691
|
-
" | {",
|
|
692
|
-
" readonly kind: 'sequential';",
|
|
693
|
-
" readonly order: number;",
|
|
694
|
-
" }",
|
|
695
|
-
" | {",
|
|
696
|
-
" readonly kind: 'absolute';",
|
|
697
|
-
" readonly targetRange: SequenceRange<number>;",
|
|
698
|
-
" }",
|
|
699
|
-
" | {",
|
|
700
|
-
" readonly kind: 'anchored';",
|
|
701
|
-
" readonly hostClipEntityId: string;",
|
|
702
|
-
" readonly anchorOffset: number;",
|
|
703
|
-
" };",
|
|
704
|
-
"export interface ComposedPhoneticContent extends ComposedScriptContent {",
|
|
705
|
-
" phonemeScript?: string;",
|
|
706
|
-
" prosody?: JsonObject;",
|
|
707
|
-
"}",
|
|
708
|
-
"/** Read result only: base text is assembled from the real AudioScript row. */",
|
|
709
|
-
"export interface ComposedScriptContent {",
|
|
710
|
-
" audio_script_entity_id: string;",
|
|
711
|
-
" text: string;",
|
|
712
|
-
" segments: ScriptTextSegment[];",
|
|
713
|
-
"}",
|
|
714
792
|
"export type CreateEntityInput = {",
|
|
715
793
|
" [K in KnownEntityKind]: {",
|
|
716
794
|
" entity_id?: string;",
|
|
@@ -718,31 +796,14 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
718
796
|
" payload: StoredEntityPayload<K>;",
|
|
719
797
|
" };",
|
|
720
798
|
"}[KnownEntityKind];",
|
|
721
|
-
"export interface DeleteBgmInput {",
|
|
722
|
-
" readonly timelineEntityId: string;",
|
|
723
|
-
"}",
|
|
724
|
-
"export interface DeleteClipInput {",
|
|
725
|
-
" readonly clipEntityId: string;",
|
|
726
|
-
"}",
|
|
727
|
-
"export interface DeleteClipTreeInput {",
|
|
728
|
-
" readonly clipEntityIds: readonly string[];",
|
|
729
|
-
" readonly onAnchored: 'cascade' | 'detach';",
|
|
730
|
-
"}",
|
|
731
799
|
"export interface DeleteEntityInput {",
|
|
732
800
|
" entity_id: string;",
|
|
733
801
|
"}",
|
|
734
|
-
"
|
|
735
|
-
"
|
|
802
|
+
"/** Immutable resource content resolved by the host for a document Entity. */",
|
|
803
|
+
"export interface EntityAssetContent {",
|
|
804
|
+
" assetId: string;",
|
|
805
|
+
" content: JsonValue;",
|
|
736
806
|
"}",
|
|
737
|
-
"export type EmptyRelationKind =",
|
|
738
|
-
" | 'timeline-track'",
|
|
739
|
-
" | 'track-clip'",
|
|
740
|
-
" | 'clip-marker'",
|
|
741
|
-
" | 'marker-content'",
|
|
742
|
-
" | 'axvideo-marker'",
|
|
743
|
-
" | 'marker-timeline'",
|
|
744
|
-
" | 'audio-script-marker';",
|
|
745
|
-
"export type EntityId = string;",
|
|
746
807
|
"export interface EntityPayloadByKind {",
|
|
747
808
|
" axvideo: BoundedDerivedSequencePayload;",
|
|
748
809
|
" timeline: JsonObject;",
|
|
@@ -751,10 +812,10 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
751
812
|
" role?: string;",
|
|
752
813
|
" };",
|
|
753
814
|
" clip: JsonObject;",
|
|
754
|
-
" video: BoundedNativeSequencePayload;",
|
|
755
|
-
" audio: BoundedNativeSequencePayload;",
|
|
756
|
-
" voice: BoundedNativeSequencePayload;",
|
|
757
|
-
" image: UnboundedConstantSequencePayload;",
|
|
815
|
+
" video: BoundedNativeSequencePayload & MediaAssetPayload;",
|
|
816
|
+
" audio: BoundedNativeSequencePayload & MediaAssetPayload;",
|
|
817
|
+
" voice: BoundedNativeSequencePayload & MediaAssetPayload;",
|
|
818
|
+
" image: UnboundedConstantSequencePayload & MediaAssetPayload;",
|
|
758
819
|
" 'sequence-marker': JsonObject & {",
|
|
759
820
|
" sourceRange: {",
|
|
760
821
|
" start: number;",
|
|
@@ -795,51 +856,62 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
795
856
|
" baseEntityIds: string[];",
|
|
796
857
|
" selections: CaptionTextSelection[];",
|
|
797
858
|
" style?: JsonObject;",
|
|
859
|
+
" segmentRanges?: {",
|
|
860
|
+
" segmentId: string;",
|
|
861
|
+
" startMs: number;",
|
|
862
|
+
" endMs: number;",
|
|
863
|
+
" }[];",
|
|
798
864
|
" };",
|
|
799
865
|
"}",
|
|
800
|
-
"
|
|
801
|
-
"
|
|
802
|
-
"
|
|
803
|
-
" revision: number;",
|
|
804
|
-
" /** Current AudioScript version attached to the project; initialized projects always attach a script, possibly empty. */",
|
|
805
|
-
" audioScriptEntityId: string | null;",
|
|
806
|
-
" entities: SandboxEntity[];",
|
|
807
|
-
" relations: SandboxRelation[];",
|
|
808
|
-
"}",
|
|
809
|
-
"export interface InsertCaptionClipInput {",
|
|
810
|
-
" readonly timelineEntityId: string;",
|
|
811
|
-
" /** Existing generation identity for newly materialized Caption content, distinct from its Clip. */",
|
|
812
|
-
" readonly captionEntityId?: string;",
|
|
813
|
-
" /** Stable placed caption identity, distinct from the Caption content identity. */",
|
|
814
|
-
" readonly captionClipEntityId?: string;",
|
|
815
|
-
" /** Existing bases composed by this variant; includes an AudioScript text owner. */",
|
|
816
|
-
" readonly baseEntityIds: readonly string[];",
|
|
817
|
-
" /** Ordered selection of the AudioScript segments this Caption displays. */",
|
|
818
|
-
" readonly selections: readonly CaptionSegmentSelection[];",
|
|
819
|
-
" /** Intrinsic cue length of the Caption entity itself; display comes from the placement. */",
|
|
820
|
-
" readonly durationMs: number;",
|
|
821
|
-
" readonly style?: CaptionStyleFields;",
|
|
822
|
-
" readonly placement: ClipPlacement;",
|
|
823
|
-
"}",
|
|
824
|
-
"export interface InsertClipInput {",
|
|
825
|
-
" readonly trackEntityId: string;",
|
|
826
|
-
" /** Existing Sequence media Entity id. Asset ids and URLs are not content ids. */",
|
|
827
|
-
" readonly contentEntityId: string;",
|
|
828
|
-
" readonly sourceRange: SequenceRange<number>;",
|
|
829
|
-
" readonly duration: SequenceDuration<number>;",
|
|
830
|
-
" readonly targetRange?: SequenceRange<number>;",
|
|
831
|
-
" readonly clipPayload?: JsonObject;",
|
|
866
|
+
"/** Tokens have no script-authored data; only the creating session can validate them. */",
|
|
867
|
+
"export interface EntitySandboxCheckpoint {",
|
|
868
|
+
" readonly __checkpoint: unique symbol;",
|
|
832
869
|
"}",
|
|
833
|
-
"export interface
|
|
834
|
-
"
|
|
835
|
-
"
|
|
836
|
-
" readonly sourceRange: SequenceRange<number>;",
|
|
837
|
-
" readonly duration: SequenceDuration<number>;",
|
|
838
|
-
" readonly placement: ClipPlacement;",
|
|
839
|
-
" readonly clipPayload?: JsonObject;",
|
|
840
|
-
" /** Stable caller-owned placement identity, when one already exists outside the graph. */",
|
|
841
|
-
" readonly clipEntityId?: string;",
|
|
870
|
+
"export interface EntityUpdateInput {",
|
|
871
|
+
" entity_id: string;",
|
|
872
|
+
" changes: FieldChange[];",
|
|
842
873
|
"}",
|
|
874
|
+
"export type FieldChange =",
|
|
875
|
+
" | {",
|
|
876
|
+
" op: 'set';",
|
|
877
|
+
" path: FieldPath;",
|
|
878
|
+
" value: JsonValue;",
|
|
879
|
+
" }",
|
|
880
|
+
" | {",
|
|
881
|
+
" op: 'unset';",
|
|
882
|
+
" path: FieldPath;",
|
|
883
|
+
" }",
|
|
884
|
+
" | {",
|
|
885
|
+
" op: 'text.splice';",
|
|
886
|
+
" path: FieldPath;",
|
|
887
|
+
" index: number;",
|
|
888
|
+
" deleteCount: number;",
|
|
889
|
+
" text: string;",
|
|
890
|
+
" }",
|
|
891
|
+
" | {",
|
|
892
|
+
" op: 'list.insert';",
|
|
893
|
+
" path: FieldPath;",
|
|
894
|
+
" value: JsonValue;",
|
|
895
|
+
" beforeElementId: string | null;",
|
|
896
|
+
" }",
|
|
897
|
+
" | {",
|
|
898
|
+
" op: 'list.move';",
|
|
899
|
+
" path: FieldPath;",
|
|
900
|
+
" elementId: string;",
|
|
901
|
+
" beforeElementId: string | null;",
|
|
902
|
+
" }",
|
|
903
|
+
" | {",
|
|
904
|
+
" op: 'list.remove';",
|
|
905
|
+
" path: FieldPath;",
|
|
906
|
+
" elementId: string;",
|
|
907
|
+
" };",
|
|
908
|
+
"/** List members are addressed by identity, never a transient array offset. */",
|
|
909
|
+
"export type FieldPath = readonly (",
|
|
910
|
+
" | string",
|
|
911
|
+
" | {",
|
|
912
|
+
" elementId: string;",
|
|
913
|
+
" }",
|
|
914
|
+
")[];",
|
|
843
915
|
"export interface JsonObject {",
|
|
844
916
|
" [key: string]: JsonValue;",
|
|
845
917
|
"}",
|
|
@@ -872,93 +944,25 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
872
944
|
" | 'phonetic-script-render'",
|
|
873
945
|
" | 'audio-script-source'",
|
|
874
946
|
" | 'audio-script-marker';",
|
|
875
|
-
"export interface LinearClipSpeed {",
|
|
876
|
-
" readonly kind: 'linear';",
|
|
877
|
-
" readonly rate: number;",
|
|
878
|
-
" readonly mode?: string;",
|
|
879
|
-
"}",
|
|
880
|
-
"/** `audio-script-source(script, source)`; the script was transcribed from the source media. */",
|
|
881
|
-
"export interface LinkAudioScriptSourceRelationInput {",
|
|
882
|
-
" relation_id?: string;",
|
|
883
|
-
" script_entity_id: string;",
|
|
884
|
-
" source_entity_id: string;",
|
|
885
|
-
" trace?: JsonObject;",
|
|
886
|
-
"}",
|
|
887
|
-
"export interface LinkClipAnchorRelationInput {",
|
|
888
|
-
" relation_id?: string;",
|
|
889
|
-
" child_clip_entity_id: string;",
|
|
890
|
-
" host_clip_entity_id: string;",
|
|
891
|
-
" trace?: JsonObject;",
|
|
892
|
-
"}",
|
|
893
|
-
"export interface LinkGeneratedRelationInput {",
|
|
894
|
-
" relation_id?: string;",
|
|
895
|
-
" output_entity_id: string;",
|
|
896
|
-
" input_entity_id: string;",
|
|
897
|
-
" trace?: JsonObject;",
|
|
898
|
-
"}",
|
|
899
|
-
"export interface LinkPhoneticScriptRenderRelationInput {",
|
|
900
|
-
" relation_id?: string;",
|
|
901
|
-
" output_entity_id: string;",
|
|
902
|
-
" phonetic_script_entity_id: string;",
|
|
903
|
-
" trace?: JsonObject;",
|
|
904
|
-
"}",
|
|
905
947
|
"interface LinkRelationBase {",
|
|
906
948
|
" relation_id?: string;",
|
|
907
949
|
" endpoint_0_entity_id: string;",
|
|
908
950
|
" endpoint_1_entity_id: string;",
|
|
909
951
|
" trace?: JsonObject;",
|
|
910
952
|
"}",
|
|
911
|
-
"export
|
|
912
|
-
"
|
|
913
|
-
"
|
|
914
|
-
" metadata?: {",
|
|
915
|
-
" [key: string]: never;",
|
|
916
|
-
" };",
|
|
917
|
-
" })",
|
|
918
|
-
" | (LinkRelationBase & {",
|
|
919
|
-
" relation_kind: 'physical-asset';",
|
|
920
|
-
" metadata?: JsonObject;",
|
|
921
|
-
" })",
|
|
922
|
-
" | (LinkRelationBase & {",
|
|
923
|
-
" relation_kind: 'caption-alignment';",
|
|
924
|
-
" metadata: JsonObject & {",
|
|
925
|
-
" alignment: JsonValue;",
|
|
926
|
-
" };",
|
|
927
|
-
" });",
|
|
928
|
-
"export interface MoveClipInput {",
|
|
929
|
-
" readonly clipEntityId: string;",
|
|
930
|
-
" readonly trackEntityId: string;",
|
|
931
|
-
"}",
|
|
932
|
-
"export interface MoveClipsToStartsInput {",
|
|
933
|
-
" readonly moves: readonly {",
|
|
934
|
-
" readonly clipEntityId: string;",
|
|
935
|
-
" readonly newStartMs: number;",
|
|
936
|
-
" }[];",
|
|
937
|
-
" /** Absolute-time drags preserve every voiceover's current visible landing. */",
|
|
938
|
-
" readonly onAnchored: 'keepAbsolute';",
|
|
939
|
-
"}",
|
|
940
|
-
"export interface MoveSequentialClipsInput {",
|
|
941
|
-
" readonly clipEntityIds: readonly string[];",
|
|
942
|
-
" readonly anchor: SequentialClipAnchor;",
|
|
943
|
-
" readonly onAnchored: 'follow' | 'keepAbsolute';",
|
|
944
|
-
"}",
|
|
945
|
-
"export interface MoveVoiceoverInput {",
|
|
946
|
-
" readonly voiceoverClipEntityId: string;",
|
|
947
|
-
" /** Absolute requested timeline start; MEngine resolves and persists the host relation. */",
|
|
948
|
-
" readonly newStartMs: number;",
|
|
949
|
-
"}",
|
|
950
|
-
"export interface PatchCaptionStyleInput {",
|
|
951
|
-
" readonly timelineEntityId: string;",
|
|
952
|
-
" readonly style: CaptionStyleFields;",
|
|
953
|
+
"export interface LinkRelationInput extends LinkRelationBase {",
|
|
954
|
+
" relation_kind: KnownRelationKind;",
|
|
955
|
+
" metadata?: JsonObject;",
|
|
953
956
|
"}",
|
|
954
|
-
"export
|
|
955
|
-
"
|
|
956
|
-
"
|
|
957
|
-
"
|
|
958
|
-
"
|
|
959
|
-
"
|
|
960
|
-
"
|
|
961
|
-
"
|
|
957
|
+
"export type MediaAssetPayload = JsonObject & {",
|
|
958
|
+
" external: {",
|
|
959
|
+
" system: 'memota' | 'memota-speech';",
|
|
960
|
+
" key: string;",
|
|
961
|
+
" };",
|
|
962
|
+
"};",
|
|
963
|
+
"export interface RelationUpdateInput {",
|
|
964
|
+
" relation_id: string;",
|
|
965
|
+
" changes: FieldChange[];",
|
|
962
966
|
"}",
|
|
963
967
|
"export interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {",
|
|
964
968
|
" entity_id: string;",
|
|
@@ -978,54 +982,14 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
978
982
|
" text: string;",
|
|
979
983
|
" language?: string;",
|
|
980
984
|
"};",
|
|
981
|
-
"export type SequenceDuration<Span = unknown> =",
|
|
982
|
-
" | {",
|
|
983
|
-
" readonly mode: 'from-source';",
|
|
984
|
-
" }",
|
|
985
|
-
" | {",
|
|
986
|
-
" readonly mode: 'fixed';",
|
|
987
|
-
" readonly value: Span;",
|
|
988
|
-
" };",
|
|
989
|
-
"export interface SequenceRange<Point = unknown> {",
|
|
990
|
-
" readonly start: Point;",
|
|
991
|
-
" readonly end: Point;",
|
|
992
|
-
"}",
|
|
993
|
-
"export type SequentialClipAnchor =",
|
|
994
|
-
" | {",
|
|
995
|
-
" readonly position: 'before' | 'after';",
|
|
996
|
-
" readonly clipEntityId: string;",
|
|
997
|
-
" }",
|
|
998
|
-
" | {",
|
|
999
|
-
" readonly position: 'trackStart';",
|
|
1000
|
-
" };",
|
|
1001
|
-
"export interface SetCaptionVisibilityInput {",
|
|
1002
|
-
" readonly timelineEntityId: string;",
|
|
1003
|
-
" readonly hidden: boolean;",
|
|
1004
|
-
"}",
|
|
1005
|
-
"export interface SetClipPlacementInput {",
|
|
1006
|
-
" readonly clipEntityId: string;",
|
|
1007
|
-
" readonly placement: ClipPlacement;",
|
|
1008
|
-
"}",
|
|
1009
|
-
"export interface SetClipSpeedInput {",
|
|
1010
|
-
" readonly clipEntityId: string;",
|
|
1011
|
-
" readonly timeRemapping: LinearClipSpeed | null;",
|
|
1012
|
-
"}",
|
|
1013
|
-
"export interface SetClipVolumeInput {",
|
|
1014
|
-
" readonly clipEntityId: string;",
|
|
1015
|
-
" /** Playback gain in decibels. */",
|
|
1016
|
-
" readonly volume: number;",
|
|
1017
|
-
"}",
|
|
1018
985
|
"/** Stored own fields; a variant may obtain required content fields from its declared bases. */",
|
|
1019
986
|
"export type StoredEntityPayload<K extends KnownEntityKind> =",
|
|
1020
987
|
" | EntityPayloadByKind[K]",
|
|
988
|
+
" | (K extends 'caption' ? JsonObject & Pick<MediaAssetPayload, 'external'> : never)",
|
|
1021
989
|
" | (JsonObject &",
|
|
1022
990
|
" Partial<EntityPayloadByKind[K]> & {",
|
|
1023
991
|
" baseEntityIds: string[];",
|
|
1024
992
|
" });",
|
|
1025
|
-
"export interface TrimClipInput {",
|
|
1026
|
-
" readonly clipEntityId: string;",
|
|
1027
|
-
" readonly sourceRange: SequenceRange<number>;",
|
|
1028
|
-
"}",
|
|
1029
993
|
"export interface UnboundedConstantSequencePayload extends JsonObject {",
|
|
1030
994
|
" extent: {",
|
|
1031
995
|
" kind: 'unbounded';",
|
|
@@ -1037,93 +1001,60 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
1037
1001
|
"export interface UnlinkRelationInput {",
|
|
1038
1002
|
" relation_id: string;",
|
|
1039
1003
|
"}",
|
|
1040
|
-
"export interface UpdateClipInput {",
|
|
1041
|
-
" readonly clipEntityId: string;",
|
|
1042
|
-
" /** Complete replacement for the Clip-owned payload. */",
|
|
1043
|
-
" readonly payload: JsonObject;",
|
|
1044
|
-
"}",
|
|
1045
|
-
"export interface UpdateClipMarkerInput {",
|
|
1046
|
-
" readonly clipEntityId: string;",
|
|
1047
|
-
" readonly sourceRange?: SequenceRange<number>;",
|
|
1048
|
-
" /** Passing `undefined` explicitly removes the optional target range. */",
|
|
1049
|
-
" readonly targetRange?: SequenceRange<number> | undefined;",
|
|
1050
|
-
" readonly duration?: SequenceDuration<number>;",
|
|
1051
|
-
" /** Passing `undefined` explicitly removes the optional remapping value. */",
|
|
1052
|
-
" readonly timeRemapping?: JsonValue | undefined;",
|
|
1053
|
-
"}",
|
|
1054
1004
|
"/** Patch supplied fields on the assembled entity; omitted fields remain unchanged. */",
|
|
1055
1005
|
"export interface UpdateEntityInput {",
|
|
1056
1006
|
" entity_id: string;",
|
|
1057
1007
|
" payload: JsonObject;",
|
|
1058
1008
|
"}",
|
|
1059
|
-
"/** Timeline writes accept existing media Entity ids, never Memota asset ids or URLs. */",
|
|
1060
|
-
"export interface EditApi {",
|
|
1061
|
-
" insertClip(input: InsertClipInput): ClipEntityId;",
|
|
1062
|
-
" insertPlacedClip(input: InsertPlacedClipInput): ClipEntityId;",
|
|
1063
|
-
" updateClipMarker(input: UpdateClipMarkerInput): void;",
|
|
1064
|
-
" setClipPlacement(input: SetClipPlacementInput): void;",
|
|
1065
|
-
" moveSequentialClips(input: MoveSequentialClipsInput): void;",
|
|
1066
|
-
" moveClip(input: MoveClipInput): void;",
|
|
1067
|
-
" replaceClipContent(input: ReplaceClipContentInput): void;",
|
|
1068
|
-
" setClipVolume(input: SetClipVolumeInput): void;",
|
|
1069
|
-
" setClipSpeed(input: SetClipSpeedInput): void;",
|
|
1070
|
-
" trimClip(input: TrimClipInput): void;",
|
|
1071
|
-
" deleteClip(input: DeleteClipInput): void;",
|
|
1072
|
-
" deleteClipTree(input: DeleteClipTreeInput): void;",
|
|
1073
|
-
" updateClip(input: UpdateClipInput): void;",
|
|
1074
|
-
" moveVoiceover(input: MoveVoiceoverInput): void;",
|
|
1075
|
-
" moveClipsToStarts(input: MoveClipsToStartsInput): void;",
|
|
1076
|
-
" deleteVoiceover(input: DeleteVoiceoverInput): void;",
|
|
1077
|
-
" deleteBgm(input: DeleteBgmInput): void;",
|
|
1078
|
-
" setCaptionVisibility(input: SetCaptionVisibilityInput): void;",
|
|
1079
|
-
" patchCaptionStyle(input: PatchCaptionStyleInput): void;",
|
|
1080
|
-
" insertCaptionClip(input: InsertCaptionClipInput): ClipEntityId;",
|
|
1081
|
-
"}",
|
|
1082
|
-
"export interface TimelineApi {",
|
|
1083
|
-
" snapshot(): EntityStoreSnapshot & {",
|
|
1084
|
-
" audioScriptEntityId: string;",
|
|
1085
|
-
" };",
|
|
1086
|
-
"}",
|
|
1087
|
-
"export interface SandboxCheckpoint {",
|
|
1088
|
-
" readonly index: number;",
|
|
1089
|
-
"}",
|
|
1090
|
-
"export declare const edit: EditApi;",
|
|
1091
|
-
"export declare const timeline: TimelineApi;",
|
|
1092
1009
|
"export declare const entities: BusinessEntityFacade;",
|
|
1093
1010
|
"export declare const relations: BusinessRelationFacade;",
|
|
1094
|
-
"
|
|
1095
|
-
"export declare function
|
|
1011
|
+
"/** Resolve this Entity's attached resource through the host; await before using an uninitialized Caption. */",
|
|
1012
|
+
"export declare function rgetAssetFromEntity(entityId: string): Promise<EntityAssetContent>;",
|
|
1013
|
+
"export declare function checkpoint(): EntitySandboxCheckpoint;",
|
|
1014
|
+
"export declare function rollbackTo(cp: EntitySandboxCheckpoint): void;",
|
|
1096
1015
|
"export declare const inputs: Readonly<Record<string, unknown>>;",
|
|
1016
|
+
"export declare const console: {",
|
|
1017
|
+
" log(...values: unknown[]): void;",
|
|
1018
|
+
" info(...values: unknown[]): void;",
|
|
1019
|
+
" warn(...values: unknown[]): void;",
|
|
1020
|
+
" error(...values: unknown[]): void;",
|
|
1021
|
+
"};",
|
|
1097
1022
|
""
|
|
1098
1023
|
].join("\n");
|
|
1099
1024
|
//#endregion
|
|
1100
1025
|
//#region src/prompt.ts
|
|
1101
1026
|
const MEDEO_TOOL_DESCRIPTION = `
|
|
1102
|
-
Edit the authoritative Medeo Entity/Relation graph
|
|
1027
|
+
Edit the authoritative Medeo Entity/Relation graph in an isolated JavaScript sandbox.
|
|
1103
1028
|
|
|
1104
1029
|
Operations:
|
|
1105
|
-
- snapshot: read
|
|
1106
|
-
- run-edit-script:
|
|
1107
|
-
- commit-plan: publish the native Loro
|
|
1030
|
+
- snapshot: read assembled entities, relations, project attachments and the current document version. Reading never initializes or changes the document.
|
|
1031
|
+
- run-edit-script: use entities and relations to inspect and edit the causal working view. rgetAssetFromEntity(entityId) delegates an attached-resource read to the host. The script has no network, generation or storage access. A successful run returns preview, logs and plan_id.
|
|
1032
|
+
- commit-plan: publish the original native Loro operations compiled against the plan's causal baseline. Independent concurrent edits merge; failed transport is unconfirmed, so retry the same plan_id to preserve operation identity.
|
|
1108
1033
|
|
|
1109
|
-
Default flow: snapshot → run-edit-script with auto_commit=false → inspect preview → commit-plan. Use auto_commit=true
|
|
1110
|
-
|
|
1111
|
-
Generation tools return Asset references, not domain Entity ids. An Asset id may be passed as an entity resource field: external: { system: "memota", key: assetId }. Create or update the domain Entity using entities.*, then place its Entity id with edit.insertClip. Asset ids are not Entity ids, baseEntityIds or Relation endpoints. The sandbox exposes no Asset creation, lookup, reading or storage API; the host resolves resource references. Each placement has its own Clip and SequenceMarker. Generation lineage is host-synced; relations.of(entityId) is endpoint-agnostic.
|
|
1034
|
+
Default flow: snapshot → run-edit-script with auto_commit=false → inspect preview → commit-plan. Use auto_commit=true for low-risk edits that need no host confirmation. A domain conflict requires inspecting and explicitly resolving the merged state; never overwrite the document or silently replay an old plan on a new snapshot.
|
|
1112
1035
|
`.trim();
|
|
1113
1036
|
const MEDEO_TOOL_EXECUTION_RULES = `
|
|
1114
|
-
The host supplies the current document. Do not
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1037
|
+
The host supplies the current document. Do not invent or request a document id. The sandbox exposes only the API below: entity operations, relation operations, entity-bound resource reading and checkpoints. All business edits are expressed using entity fields and relation kinds.
|
|
1038
|
+
|
|
1039
|
+
Entity reads return assembled snapshots. Mutating returned objects has no persistence effect. Create declares own fields. Update requires a nonempty changes array and atomically applies its ordered operations to assembled fields, routing each to its declaring owner. DeclareFields explicitly establishes own fields or an override; it is not the normal way to edit inherited content. Deletion requires explicitly handling incident relations, variant references and protected project attachments; it never deletes external resources or cascades business entities.
|
|
1040
|
+
|
|
1041
|
+
FieldChange paths start at payload for an entity, and at metadata or trace for a relation. Use strings for fields and {elementId:'...'} for list members, never numeric indices. Segment identity is segmentId; reference identity is the referenced id. set writes scalars or schema-declared atomic values, including complete sourceRange, targetRange, extent, duration, timeRemapping, textRange and external. It cannot replace collaborative text, lists or maps. Edit text with text.splice (Unicode code-point index and deleteCount), and segments/selections with list.insert, list.move or list.remove. beforeElementId:null means append; moving before itself, duplicate identity or an absent anchor is an error. Unset removes optional fields only. Map fields are edited through leaf paths; initialize a new own map via declareFields. baseEntityIds is unordered composition membership: insert/remove are supported, moving bases cannot resolve field conflicts. Independent fields and list-item text edits retain native Loro identities.
|
|
1042
|
+
|
|
1043
|
+
Entities own data. Relations associate two existing entities; endpoint_0 and endpoint_1 retain their declared positions. Each relation kind defines direction and meaning; neither relation listing order nor endpoint storage order implies a global business order. Relations.update changes metadata/trace fields only; kind/endpoints require explicit unlink plus link with a new identity. Generated relations use generated(output,input). Ordinary associations do not implement composition.
|
|
1044
|
+
|
|
1045
|
+
Variants directly hold baseEntityIds. Assembly validates every base before applying explicit own overrides: two bases supplying the same field are an error even if values match or the variant declares that field. Missing/cyclic bases fail. Consumers operate assembled fields without copying inherited content or resolving owners themselves. An owned-field edit creates a new immutable entity version. Base changes automatically advance variant base references and project attachments without versioning an unchanged variant. A variant-owned change versions the variant and retains unchanged bases. Do not manually clone entities for versioning. Use the current plan's ids throughout that execution; refresh after commit.
|
|
1046
|
+
|
|
1047
|
+
Generation tools return resource references. An asset id may be passed in an entity's external:{system:'memota',key:assetId}; it is never an entity id, baseEntityId or relation endpoint. Physical resources are host infrastructure. The sandbox has no direct resource lookup/CRUD by asset id. rgetAssetFromEntity takes only an existing business entity id and returns {assetId,content} for its attached resource. For output_caption, create a Caption with payload:{external:{system:'memota',key:assetId}}, then await rgetAssetFromEntity(captionId). The host initializes Caption intrinsic timing and its composed project AudioScript text in the same causal plan; read the editable fields with entities.get afterwards. Repeated reads preserve edited text and return the original immutable resource content. Unawaited initialization is completed before validation; failure prevents publication. Resource-free Caption composition from AudioScript is also valid.
|
|
1048
|
+
|
|
1049
|
+
Project initialization creates one current Timeline, the four standard Tracks and one attached AudioScript with segments:[]. Locate these with entities.list, and edit the attached script. AudioScript owns independent multi-segment text. Caption and PhoneticScript compose it through baseEntityIds. Caption owns selections ({segmentId,textRange?}), intrinsic timing and style; selection textRange is a half-open code-point range and can split display text without rewriting the script. PhoneticScript owns pronunciation/prosody; Voice is generated from it and related by phonetic-script-render(voice,phoneticScript). Speech and caption occupy separate Clips. Subtitles normally accompany sound; use source media or generate Voice from PhoneticScript when the request requires narration. Do not fabricate a sound resource.
|
|
1050
|
+
|
|
1051
|
+
AudioScript has no intrinsic time and cannot be Clip content. ASR input Audio/Video/Voice associates via audio-script-source(script,source). Its external annotation SequenceMarker associates via audio-script-marker(script,marker) and receives assigned segmentRanges, not references to another marker. Caption intrinsic timing and its Clip's display SequenceMarker are distinct. Move/stretch display by editing only the placement marker.
|
|
1052
|
+
|
|
1053
|
+
To place content, create a Clip and a SequenceMarker, then link track-clip(track,clip), clip-marker(clip,marker) and marker-content(marker,content). Existing track membership uses timeline-track(timeline,track). The editor has one track of each role: video_clip for Image/Video, speech for Voice, caption for Caption, bgm for Audio. Inspect factual media extents; never invent duration. Each placement is exactly one of Clip.order (sequential), Marker.targetRange (absolute) or clip-anchor(childClip,hostClip) plus Marker.anchorOffset. Marker.sourceRange selects source, duration describes playback/display. Whole milliseconds are required by this editor projection. Clip.volume is decibels (-60..20, 0 original). Image is unbounded/constant. Linear timeRemapping is {kind:'linear',rate:2,mode:'constant'}, with duration matching the rounded source span/rate. BGM can use durationPolicy:'timeline'. Nonlinear speed and multiple overlay tracks are unsupported. Caption visibility is the caption Track's hidden field. Caption style is its style map. Handle structural links explicitly when moving/deleting; there is no implicit cascade.
|
|
1054
|
+
|
|
1055
|
+
checkpoint returns an opaque token valid only in this execution. rollbackTo restores that point and invalidates later checkpoints. Returned read snapshots and inputs are not writable document authorities. Use only the declarations below.
|
|
1125
1056
|
`.trim();
|
|
1126
|
-
/**
|
|
1057
|
+
/** MEngine owns API disclosure; Harness only supplies the changing document context. */
|
|
1127
1058
|
function renderMedeoModelContext(input) {
|
|
1128
1059
|
const updated = input.updatedSincePreviousModelCall == null ? "unknown (first model call)" : String(input.updatedSincePreviousModelCall);
|
|
1129
1060
|
return `
|
|
@@ -1131,11 +1062,11 @@ ${MEDEO_TOOL_DESCRIPTION}
|
|
|
1131
1062
|
|
|
1132
1063
|
${MEDEO_TOOL_EXECUTION_RULES}
|
|
1133
1064
|
|
|
1134
|
-
Current MEngine document state (sampled
|
|
1065
|
+
Current MEngine document state (sampled immediately before this model call):
|
|
1135
1066
|
- document_version: ${JSON.stringify(input.documentVersion)}
|
|
1136
1067
|
- updated_since_previous_model_call: ${updated}
|
|
1137
1068
|
|
|
1138
|
-
When updated_since_previous_model_call is true,
|
|
1069
|
+
When updated_since_previous_model_call is true, take a fresh snapshot before planning further edits.
|
|
1139
1070
|
|
|
1140
1071
|
Sandbox TypeScript interface:
|
|
1141
1072
|
\`\`\`ts
|
|
@@ -1724,10 +1655,10 @@ function createMedeoTool(options) {
|
|
|
1724
1655
|
document,
|
|
1725
1656
|
baseVersion,
|
|
1726
1657
|
entityState,
|
|
1727
|
-
|
|
1658
|
+
loadEntityAsset: options.loadEntityAsset ? (entity) => options.loadEntityAsset(input.doc_id, entity) : void 0,
|
|
1728
1659
|
script: input.script,
|
|
1729
1660
|
...input.inputs !== void 0 ? { inputs: input.inputs } : {},
|
|
1730
|
-
timeoutMs: input.timeout_ms ?? options.sandbox?.timeoutMs,
|
|
1661
|
+
timeoutMs: input.timeout_ms ?? options.sandbox?.timeoutMs ?? 3e4,
|
|
1731
1662
|
memoryLimitMb: input.memory_limit_mb ?? options.sandbox?.memoryLimitMb
|
|
1732
1663
|
});
|
|
1733
1664
|
if (!result.ok) return {
|
|
@@ -1987,6 +1918,6 @@ function stableId(prefix, ...parts) {
|
|
|
1987
1918
|
return `${prefix}_${createHash("sha256").update(JSON.stringify(parts)).digest("hex")}`;
|
|
1988
1919
|
}
|
|
1989
1920
|
//#endregion
|
|
1990
|
-
export {
|
|
1921
|
+
export { MEDEO_TOOL_DESCRIPTION, MEDEO_TOOL_NAME, MEDEO_TOOL_PARAMETERS, collectAffectedPartIds, commitPlan, createMedeoTool, materializeResources, renderCompactProjection, renderPreview, runEditScript };
|
|
1991
1922
|
|
|
1992
1923
|
//# sourceMappingURL=index.mjs.map
|