@mengine/medeo-tool 1.4.1-alpha.2 → 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 +67 -141
- package/dist/{entity-contract-Cpf3P69H.d.mts → entity-contract-DQ56Ihrh.d.mts} +36 -76
- package/dist/{script-session-lXpqmupK.mjs → entity-sandbox-BH-7F5C8.mjs} +126 -503
- package/dist/entity-sandbox-BH-7F5C8.mjs.map +1 -0
- package/dist/index.d.mts +3 -99
- package/dist/index.mjs +233 -343
- package/dist/index.mjs.map +1 -1
- package/dist/sandbox-api.d.mts +59 -293
- package/dist/worker-entry.d.mts +1 -2
- package/dist/worker-entry.mjs +70 -209
- package/dist/worker-entry.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/script-session-lXpqmupK.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",
|
|
@@ -624,12 +757,10 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
624
757
|
" sampling: 'native';",
|
|
625
758
|
" coordinateSpace: JsonValue;",
|
|
626
759
|
"}",
|
|
627
|
-
"/** Business editing surface.
|
|
760
|
+
"/** Business editing surface. Reads are assembled snapshots; writes preserve native operation intent. */",
|
|
628
761
|
"export interface BusinessEntityFacade {",
|
|
629
762
|
" list(): SandboxEntity[];",
|
|
630
763
|
" get(entityId: string): SandboxEntity | null;",
|
|
631
|
-
" readCaptionContent(entityId: string): ComposedScriptContent;",
|
|
632
|
-
" readPhoneticScriptContent(entityId: string): ComposedPhoneticContent;",
|
|
633
764
|
" create(",
|
|
634
765
|
" input: Exclude<",
|
|
635
766
|
" CreateEntityInput,",
|
|
@@ -638,66 +769,18 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
638
769
|
" }",
|
|
639
770
|
" >,",
|
|
640
771
|
" ): string;",
|
|
641
|
-
" update(input:
|
|
772
|
+
" update(input: EntityUpdateInput): void;",
|
|
642
773
|
" declareFields(input: UpdateEntityInput): void;",
|
|
643
774
|
" delete(input: DeleteEntityInput): void;",
|
|
644
775
|
"}",
|
|
645
|
-
"/**
|
|
776
|
+
"/** Endpoint order is retained; each relation kind defines its endpoint semantics. */",
|
|
646
777
|
"export interface BusinessRelationFacade {",
|
|
647
778
|
" list(): SandboxRelation[];",
|
|
648
779
|
" of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];",
|
|
649
|
-
" link(",
|
|
650
|
-
"
|
|
651
|
-
" LinkRelationInput,",
|
|
652
|
-
" {",
|
|
653
|
-
" relation_kind: 'physical-asset';",
|
|
654
|
-
" }",
|
|
655
|
-
" >,",
|
|
656
|
-
" ): string;",
|
|
657
|
-
" linkGenerated(input: LinkGeneratedRelationInput): string;",
|
|
658
|
-
" linkClipAnchor(input: LinkClipAnchorRelationInput): string;",
|
|
659
|
-
" linkPhoneticScriptRender(input: LinkPhoneticScriptRenderRelationInput): string;",
|
|
660
|
-
" linkAudioScriptSource(input: LinkAudioScriptSourceRelationInput): string;",
|
|
780
|
+
" link(input: LinkRelationInput): string;",
|
|
781
|
+
" update(input: RelationUpdateInput): void;",
|
|
661
782
|
" unlink(input: UnlinkRelationInput): void;",
|
|
662
783
|
"}",
|
|
663
|
-
"export interface CaptionFontDescriptor {",
|
|
664
|
-
" readonly system: 'font-library';",
|
|
665
|
-
" readonly key: string;",
|
|
666
|
-
"}",
|
|
667
|
-
"/**",
|
|
668
|
-
" * One ordered entry of the Caption's segment selection. `segmentId` quotes the",
|
|
669
|
-
" * composed AudioScript's own stable segment identity — a local id quoted by the",
|
|
670
|
-
" * variant, never a peer Entity reference. Text itself is never copied here;",
|
|
671
|
-
" * complete Caption content is assembled through its direct baseEntityIds.",
|
|
672
|
-
" * The optional `textRange` narrows one Segment to an intra-Segment sub-span",
|
|
673
|
-
" * (intra-segment re-segmentation); without it the whole Segment text is selected.",
|
|
674
|
-
" */",
|
|
675
|
-
"export type CaptionSegmentSelection = JsonObject & {",
|
|
676
|
-
" readonly segmentId: string;",
|
|
677
|
-
" readonly textRange?: CaptionTextRange;",
|
|
678
|
-
"};",
|
|
679
|
-
"export interface CaptionStyleFields {",
|
|
680
|
-
" readonly font?: CaptionFontDescriptor;",
|
|
681
|
-
" readonly fontSize?: number;",
|
|
682
|
-
" readonly fontColor?: string;",
|
|
683
|
-
" readonly fontWeight?: number;",
|
|
684
|
-
" readonly entranceAnimation?: string;",
|
|
685
|
-
" readonly entranceAnimationDurationMs?: number;",
|
|
686
|
-
" readonly strokeColor?: string;",
|
|
687
|
-
" readonly strokeWidth?: number;",
|
|
688
|
-
" readonly positionX?: number;",
|
|
689
|
-
" readonly positionY?: number;",
|
|
690
|
-
"}",
|
|
691
|
-
"/**",
|
|
692
|
-
" * Half-open `[start, end)` position window inside one Segment's text, counted",
|
|
693
|
-
" * in Unicode code points (not UTF-16 code units), so a boundary never splits a",
|
|
694
|
-
" * surrogate pair. Positions are non-negative safe integers with `start < end`;",
|
|
695
|
-
" * `end` must not exceed the Segment's code-point length.",
|
|
696
|
-
" */",
|
|
697
|
-
"export interface CaptionTextRange extends JsonObject {",
|
|
698
|
-
" readonly start: number;",
|
|
699
|
-
" readonly end: number;",
|
|
700
|
-
"}",
|
|
701
784
|
"export type CaptionTextSelection = JsonObject & {",
|
|
702
785
|
" segmentId: string;",
|
|
703
786
|
" /** Half-open Unicode code-point range within the selected source segment. */",
|
|
@@ -706,31 +789,6 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
706
789
|
" end: number;",
|
|
707
790
|
" };",
|
|
708
791
|
"};",
|
|
709
|
-
"export type ClipEntityId = EntityId;",
|
|
710
|
-
"export type ClipPlacement =",
|
|
711
|
-
" | {",
|
|
712
|
-
" readonly kind: 'sequential';",
|
|
713
|
-
" readonly order: number;",
|
|
714
|
-
" }",
|
|
715
|
-
" | {",
|
|
716
|
-
" readonly kind: 'absolute';",
|
|
717
|
-
" readonly targetRange: SequenceRange<number>;",
|
|
718
|
-
" }",
|
|
719
|
-
" | {",
|
|
720
|
-
" readonly kind: 'anchored';",
|
|
721
|
-
" readonly hostClipEntityId: string;",
|
|
722
|
-
" readonly anchorOffset: number;",
|
|
723
|
-
" };",
|
|
724
|
-
"export interface ComposedPhoneticContent extends ComposedScriptContent {",
|
|
725
|
-
" phonemeScript?: string;",
|
|
726
|
-
" prosody?: JsonObject;",
|
|
727
|
-
"}",
|
|
728
|
-
"/** Read result only: base text is assembled from the real AudioScript row. */",
|
|
729
|
-
"export interface ComposedScriptContent {",
|
|
730
|
-
" audio_script_entity_id: string;",
|
|
731
|
-
" text: string;",
|
|
732
|
-
" segments: ScriptTextSegment[];",
|
|
733
|
-
"}",
|
|
734
792
|
"export type CreateEntityInput = {",
|
|
735
793
|
" [K in KnownEntityKind]: {",
|
|
736
794
|
" entity_id?: string;",
|
|
@@ -738,36 +796,14 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
738
796
|
" payload: StoredEntityPayload<K>;",
|
|
739
797
|
" };",
|
|
740
798
|
"}[KnownEntityKind];",
|
|
741
|
-
"export interface DeleteBgmInput {",
|
|
742
|
-
" readonly timelineEntityId: string;",
|
|
743
|
-
"}",
|
|
744
|
-
"export interface DeleteClipInput {",
|
|
745
|
-
" readonly clipEntityId: string;",
|
|
746
|
-
"}",
|
|
747
|
-
"export interface DeleteClipTreeInput {",
|
|
748
|
-
" readonly clipEntityIds: readonly string[];",
|
|
749
|
-
" readonly onAnchored: 'cascade' | 'detach';",
|
|
750
|
-
"}",
|
|
751
799
|
"export interface DeleteEntityInput {",
|
|
752
800
|
" entity_id: string;",
|
|
753
801
|
"}",
|
|
754
|
-
"export interface DeleteVoiceoverInput {",
|
|
755
|
-
" readonly voiceoverClipEntityIds: readonly string[];",
|
|
756
|
-
"}",
|
|
757
|
-
"export type EmptyRelationKind =",
|
|
758
|
-
" | 'timeline-track'",
|
|
759
|
-
" | 'track-clip'",
|
|
760
|
-
" | 'clip-marker'",
|
|
761
|
-
" | 'marker-content'",
|
|
762
|
-
" | 'axvideo-marker'",
|
|
763
|
-
" | 'marker-timeline'",
|
|
764
|
-
" | 'audio-script-marker';",
|
|
765
802
|
"/** Immutable resource content resolved by the host for a document Entity. */",
|
|
766
803
|
"export interface EntityAssetContent {",
|
|
767
804
|
" assetId: string;",
|
|
768
805
|
" content: JsonValue;",
|
|
769
806
|
"}",
|
|
770
|
-
"export type EntityId = string;",
|
|
771
807
|
"export interface EntityPayloadByKind {",
|
|
772
808
|
" axvideo: BoundedDerivedSequencePayload;",
|
|
773
809
|
" timeline: JsonObject;",
|
|
@@ -827,49 +863,55 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
827
863
|
" }[];",
|
|
828
864
|
" };",
|
|
829
865
|
"}",
|
|
830
|
-
"
|
|
831
|
-
"
|
|
832
|
-
"
|
|
833
|
-
" revision: number;",
|
|
834
|
-
" /** Current AudioScript version attached to the project; initialized projects always attach a script, possibly empty. */",
|
|
835
|
-
" audioScriptEntityId: string | null;",
|
|
836
|
-
" entities: SandboxEntity[];",
|
|
837
|
-
" relations: SandboxRelation[];",
|
|
838
|
-
"}",
|
|
839
|
-
"export interface InsertCaptionClipInput {",
|
|
840
|
-
" readonly timelineEntityId: string;",
|
|
841
|
-
" /** Existing generation identity for newly materialized Caption content, distinct from its Clip. */",
|
|
842
|
-
" readonly captionEntityId?: string;",
|
|
843
|
-
" /** Stable placed caption identity, distinct from the Caption content identity. */",
|
|
844
|
-
" readonly captionClipEntityId?: string;",
|
|
845
|
-
" /** Existing bases composed by this variant; includes an AudioScript text owner. */",
|
|
846
|
-
" readonly baseEntityIds: readonly string[];",
|
|
847
|
-
" /** Ordered selection of the AudioScript segments this Caption displays. */",
|
|
848
|
-
" readonly selections: readonly CaptionSegmentSelection[];",
|
|
849
|
-
" /** Intrinsic cue length of the Caption entity itself; display comes from the placement. */",
|
|
850
|
-
" readonly durationMs: number;",
|
|
851
|
-
" readonly style?: CaptionStyleFields;",
|
|
852
|
-
" readonly placement: ClipPlacement;",
|
|
853
|
-
"}",
|
|
854
|
-
"export interface InsertClipInput {",
|
|
855
|
-
" readonly trackEntityId: string;",
|
|
856
|
-
" /** Existing Sequence media Entity id. Asset ids and URLs are not content ids. */",
|
|
857
|
-
" readonly contentEntityId: string;",
|
|
858
|
-
" readonly sourceRange: SequenceRange<number>;",
|
|
859
|
-
" readonly duration: SequenceDuration<number>;",
|
|
860
|
-
" readonly targetRange?: SequenceRange<number>;",
|
|
861
|
-
" 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;",
|
|
862
869
|
"}",
|
|
863
|
-
"export interface
|
|
864
|
-
"
|
|
865
|
-
"
|
|
866
|
-
" readonly sourceRange: SequenceRange<number>;",
|
|
867
|
-
" readonly duration: SequenceDuration<number>;",
|
|
868
|
-
" readonly placement: ClipPlacement;",
|
|
869
|
-
" readonly clipPayload?: JsonObject;",
|
|
870
|
-
" /** Stable caller-owned placement identity, when one already exists outside the graph. */",
|
|
871
|
-
" readonly clipEntityId?: string;",
|
|
870
|
+
"export interface EntityUpdateInput {",
|
|
871
|
+
" entity_id: string;",
|
|
872
|
+
" changes: FieldChange[];",
|
|
872
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
|
+
")[];",
|
|
873
915
|
"export interface JsonObject {",
|
|
874
916
|
" [key: string]: JsonValue;",
|
|
875
917
|
"}",
|
|
@@ -902,99 +944,25 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
902
944
|
" | 'phonetic-script-render'",
|
|
903
945
|
" | 'audio-script-source'",
|
|
904
946
|
" | 'audio-script-marker';",
|
|
905
|
-
"export interface LinearClipSpeed {",
|
|
906
|
-
" readonly kind: 'linear';",
|
|
907
|
-
" readonly rate: number;",
|
|
908
|
-
" readonly mode?: string;",
|
|
909
|
-
"}",
|
|
910
|
-
"/** `audio-script-source(script, source)`; the script was transcribed from the source media. */",
|
|
911
|
-
"export interface LinkAudioScriptSourceRelationInput {",
|
|
912
|
-
" relation_id?: string;",
|
|
913
|
-
" script_entity_id: string;",
|
|
914
|
-
" source_entity_id: string;",
|
|
915
|
-
" trace?: JsonObject;",
|
|
916
|
-
"}",
|
|
917
|
-
"export interface LinkClipAnchorRelationInput {",
|
|
918
|
-
" relation_id?: string;",
|
|
919
|
-
" child_clip_entity_id: string;",
|
|
920
|
-
" host_clip_entity_id: string;",
|
|
921
|
-
" trace?: JsonObject;",
|
|
922
|
-
"}",
|
|
923
|
-
"export interface LinkGeneratedRelationInput {",
|
|
924
|
-
" relation_id?: string;",
|
|
925
|
-
" output_entity_id: string;",
|
|
926
|
-
" input_entity_id: string;",
|
|
927
|
-
" trace?: JsonObject;",
|
|
928
|
-
"}",
|
|
929
|
-
"export interface LinkPhoneticScriptRenderRelationInput {",
|
|
930
|
-
" relation_id?: string;",
|
|
931
|
-
" output_entity_id: string;",
|
|
932
|
-
" phonetic_script_entity_id: string;",
|
|
933
|
-
" trace?: JsonObject;",
|
|
934
|
-
"}",
|
|
935
947
|
"interface LinkRelationBase {",
|
|
936
948
|
" relation_id?: string;",
|
|
937
949
|
" endpoint_0_entity_id: string;",
|
|
938
950
|
" endpoint_1_entity_id: string;",
|
|
939
951
|
" trace?: JsonObject;",
|
|
940
952
|
"}",
|
|
941
|
-
"export
|
|
942
|
-
"
|
|
943
|
-
"
|
|
944
|
-
"
|
|
945
|
-
" [key: string]: never;",
|
|
946
|
-
" };",
|
|
947
|
-
" })",
|
|
948
|
-
" | (LinkRelationBase & {",
|
|
949
|
-
" relation_kind: 'physical-asset';",
|
|
950
|
-
" metadata?: JsonObject;",
|
|
951
|
-
" })",
|
|
952
|
-
" | (LinkRelationBase & {",
|
|
953
|
-
" relation_kind: 'caption-alignment';",
|
|
954
|
-
" metadata: JsonObject & {",
|
|
955
|
-
" alignment: JsonValue;",
|
|
956
|
-
" };",
|
|
957
|
-
" });",
|
|
953
|
+
"export interface LinkRelationInput extends LinkRelationBase {",
|
|
954
|
+
" relation_kind: KnownRelationKind;",
|
|
955
|
+
" metadata?: JsonObject;",
|
|
956
|
+
"}",
|
|
958
957
|
"export type MediaAssetPayload = JsonObject & {",
|
|
959
958
|
" external: {",
|
|
960
959
|
" system: 'memota' | 'memota-speech';",
|
|
961
960
|
" key: string;",
|
|
962
961
|
" };",
|
|
963
962
|
"};",
|
|
964
|
-
"export interface
|
|
965
|
-
"
|
|
966
|
-
"
|
|
967
|
-
"}",
|
|
968
|
-
"export interface MoveClipsToStartsInput {",
|
|
969
|
-
" readonly moves: readonly {",
|
|
970
|
-
" readonly clipEntityId: string;",
|
|
971
|
-
" readonly newStartMs: number;",
|
|
972
|
-
" }[];",
|
|
973
|
-
" /** Absolute-time drags preserve every voiceover's current visible landing. */",
|
|
974
|
-
" readonly onAnchored: 'keepAbsolute';",
|
|
975
|
-
"}",
|
|
976
|
-
"export interface MoveSequentialClipsInput {",
|
|
977
|
-
" readonly clipEntityIds: readonly string[];",
|
|
978
|
-
" readonly anchor: SequentialClipAnchor;",
|
|
979
|
-
" readonly onAnchored: 'follow' | 'keepAbsolute';",
|
|
980
|
-
"}",
|
|
981
|
-
"export interface MoveVoiceoverInput {",
|
|
982
|
-
" readonly voiceoverClipEntityId: string;",
|
|
983
|
-
" /** Absolute requested timeline start; MEngine resolves and persists the host relation. */",
|
|
984
|
-
" readonly newStartMs: number;",
|
|
985
|
-
"}",
|
|
986
|
-
"export interface PatchCaptionStyleInput {",
|
|
987
|
-
" readonly timelineEntityId: string;",
|
|
988
|
-
" readonly style: CaptionStyleFields;",
|
|
989
|
-
"}",
|
|
990
|
-
"export interface ReplaceClipContentInput {",
|
|
991
|
-
" readonly clipEntityId: string;",
|
|
992
|
-
" /** Existing Sequence media Entity id. Asset ids and URLs are not content ids. */",
|
|
993
|
-
" readonly contentEntityId: string;",
|
|
994
|
-
" readonly sourceRange: SequenceRange<number>;",
|
|
995
|
-
" readonly duration: SequenceDuration<number>;",
|
|
996
|
-
" readonly targetRange?: SequenceRange<number>;",
|
|
997
|
-
" readonly timeRemapping?: JsonValue;",
|
|
963
|
+
"export interface RelationUpdateInput {",
|
|
964
|
+
" relation_id: string;",
|
|
965
|
+
" changes: FieldChange[];",
|
|
998
966
|
"}",
|
|
999
967
|
"export interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {",
|
|
1000
968
|
" entity_id: string;",
|
|
@@ -1014,43 +982,6 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
1014
982
|
" text: string;",
|
|
1015
983
|
" language?: string;",
|
|
1016
984
|
"};",
|
|
1017
|
-
"export type SequenceDuration<Span = unknown> =",
|
|
1018
|
-
" | {",
|
|
1019
|
-
" readonly mode: 'from-source';",
|
|
1020
|
-
" }",
|
|
1021
|
-
" | {",
|
|
1022
|
-
" readonly mode: 'fixed';",
|
|
1023
|
-
" readonly value: Span;",
|
|
1024
|
-
" };",
|
|
1025
|
-
"export interface SequenceRange<Point = unknown> {",
|
|
1026
|
-
" readonly start: Point;",
|
|
1027
|
-
" readonly end: Point;",
|
|
1028
|
-
"}",
|
|
1029
|
-
"export type SequentialClipAnchor =",
|
|
1030
|
-
" | {",
|
|
1031
|
-
" readonly position: 'before' | 'after';",
|
|
1032
|
-
" readonly clipEntityId: string;",
|
|
1033
|
-
" }",
|
|
1034
|
-
" | {",
|
|
1035
|
-
" readonly position: 'trackStart';",
|
|
1036
|
-
" };",
|
|
1037
|
-
"export interface SetCaptionVisibilityInput {",
|
|
1038
|
-
" readonly timelineEntityId: string;",
|
|
1039
|
-
" readonly hidden: boolean;",
|
|
1040
|
-
"}",
|
|
1041
|
-
"export interface SetClipPlacementInput {",
|
|
1042
|
-
" readonly clipEntityId: string;",
|
|
1043
|
-
" readonly placement: ClipPlacement;",
|
|
1044
|
-
"}",
|
|
1045
|
-
"export interface SetClipSpeedInput {",
|
|
1046
|
-
" readonly clipEntityId: string;",
|
|
1047
|
-
" readonly timeRemapping: LinearClipSpeed | null;",
|
|
1048
|
-
"}",
|
|
1049
|
-
"export interface SetClipVolumeInput {",
|
|
1050
|
-
" readonly clipEntityId: string;",
|
|
1051
|
-
" /** Playback gain in decibels. */",
|
|
1052
|
-
" readonly volume: number;",
|
|
1053
|
-
"}",
|
|
1054
985
|
"/** Stored own fields; a variant may obtain required content fields from its declared bases. */",
|
|
1055
986
|
"export type StoredEntityPayload<K extends KnownEntityKind> =",
|
|
1056
987
|
" | EntityPayloadByKind[K]",
|
|
@@ -1059,10 +990,6 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
1059
990
|
" Partial<EntityPayloadByKind[K]> & {",
|
|
1060
991
|
" baseEntityIds: string[];",
|
|
1061
992
|
" });",
|
|
1062
|
-
"export interface TrimClipInput {",
|
|
1063
|
-
" readonly clipEntityId: string;",
|
|
1064
|
-
" readonly sourceRange: SequenceRange<number>;",
|
|
1065
|
-
"}",
|
|
1066
993
|
"export interface UnboundedConstantSequencePayload extends JsonObject {",
|
|
1067
994
|
" extent: {",
|
|
1068
995
|
" kind: 'unbounded';",
|
|
@@ -1074,96 +1001,60 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
1074
1001
|
"export interface UnlinkRelationInput {",
|
|
1075
1002
|
" relation_id: string;",
|
|
1076
1003
|
"}",
|
|
1077
|
-
"export interface UpdateClipInput {",
|
|
1078
|
-
" readonly clipEntityId: string;",
|
|
1079
|
-
" /** Complete replacement for the Clip-owned payload. */",
|
|
1080
|
-
" readonly payload: JsonObject;",
|
|
1081
|
-
"}",
|
|
1082
|
-
"export interface UpdateClipMarkerInput {",
|
|
1083
|
-
" readonly clipEntityId: string;",
|
|
1084
|
-
" readonly sourceRange?: SequenceRange<number>;",
|
|
1085
|
-
" /** Passing `undefined` explicitly removes the optional target range. */",
|
|
1086
|
-
" readonly targetRange?: SequenceRange<number> | undefined;",
|
|
1087
|
-
" readonly duration?: SequenceDuration<number>;",
|
|
1088
|
-
" /** Passing `undefined` explicitly removes the optional remapping value. */",
|
|
1089
|
-
" readonly timeRemapping?: JsonValue | undefined;",
|
|
1090
|
-
"}",
|
|
1091
1004
|
"/** Patch supplied fields on the assembled entity; omitted fields remain unchanged. */",
|
|
1092
1005
|
"export interface UpdateEntityInput {",
|
|
1093
1006
|
" entity_id: string;",
|
|
1094
1007
|
" payload: JsonObject;",
|
|
1095
1008
|
"}",
|
|
1096
|
-
"/** Timeline writes accept existing media Entity ids, never Memota asset ids or URLs. */",
|
|
1097
|
-
"export interface EditApi {",
|
|
1098
|
-
" insertClip(input: InsertClipInput): ClipEntityId;",
|
|
1099
|
-
" insertPlacedClip(input: InsertPlacedClipInput): ClipEntityId;",
|
|
1100
|
-
" updateClipMarker(input: UpdateClipMarkerInput): void;",
|
|
1101
|
-
" setClipPlacement(input: SetClipPlacementInput): void;",
|
|
1102
|
-
" moveSequentialClips(input: MoveSequentialClipsInput): void;",
|
|
1103
|
-
" moveClip(input: MoveClipInput): void;",
|
|
1104
|
-
" replaceClipContent(input: ReplaceClipContentInput): void;",
|
|
1105
|
-
" setClipVolume(input: SetClipVolumeInput): void;",
|
|
1106
|
-
" setClipSpeed(input: SetClipSpeedInput): void;",
|
|
1107
|
-
" trimClip(input: TrimClipInput): void;",
|
|
1108
|
-
" deleteClip(input: DeleteClipInput): void;",
|
|
1109
|
-
" deleteClipTree(input: DeleteClipTreeInput): void;",
|
|
1110
|
-
" updateClip(input: UpdateClipInput): void;",
|
|
1111
|
-
" moveVoiceover(input: MoveVoiceoverInput): void;",
|
|
1112
|
-
" moveClipsToStarts(input: MoveClipsToStartsInput): void;",
|
|
1113
|
-
" deleteVoiceover(input: DeleteVoiceoverInput): void;",
|
|
1114
|
-
" deleteBgm(input: DeleteBgmInput): void;",
|
|
1115
|
-
" setCaptionVisibility(input: SetCaptionVisibilityInput): void;",
|
|
1116
|
-
" patchCaptionStyle(input: PatchCaptionStyleInput): void;",
|
|
1117
|
-
" insertCaptionClip(input: InsertCaptionClipInput): ClipEntityId;",
|
|
1118
|
-
"}",
|
|
1119
|
-
"export interface TimelineApi {",
|
|
1120
|
-
" snapshot(): EntityStoreSnapshot & {",
|
|
1121
|
-
" audioScriptEntityId: string;",
|
|
1122
|
-
" };",
|
|
1123
|
-
"}",
|
|
1124
|
-
"export interface SandboxCheckpoint {",
|
|
1125
|
-
" readonly index: number;",
|
|
1126
|
-
"}",
|
|
1127
|
-
"export declare const edit: EditApi;",
|
|
1128
|
-
"export declare const timeline: TimelineApi;",
|
|
1129
1009
|
"export declare const entities: BusinessEntityFacade;",
|
|
1130
1010
|
"export declare const relations: BusinessRelationFacade;",
|
|
1131
|
-
"/** Resolve
|
|
1011
|
+
"/** Resolve this Entity's attached resource through the host; await before using an uninitialized Caption. */",
|
|
1132
1012
|
"export declare function rgetAssetFromEntity(entityId: string): Promise<EntityAssetContent>;",
|
|
1133
|
-
"export declare function checkpoint():
|
|
1134
|
-
"export declare function rollbackTo(cp:
|
|
1013
|
+
"export declare function checkpoint(): EntitySandboxCheckpoint;",
|
|
1014
|
+
"export declare function rollbackTo(cp: EntitySandboxCheckpoint): void;",
|
|
1135
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
|
+
"};",
|
|
1136
1022
|
""
|
|
1137
1023
|
].join("\n");
|
|
1138
1024
|
//#endregion
|
|
1139
1025
|
//#region src/prompt.ts
|
|
1140
1026
|
const MEDEO_TOOL_DESCRIPTION = `
|
|
1141
|
-
Edit the authoritative Medeo Entity/Relation graph
|
|
1027
|
+
Edit the authoritative Medeo Entity/Relation graph in an isolated JavaScript sandbox.
|
|
1142
1028
|
|
|
1143
1029
|
Operations:
|
|
1144
|
-
- snapshot: read
|
|
1145
|
-
- run-edit-script:
|
|
1146
|
-
- commit-plan: publish the native Loro
|
|
1147
|
-
|
|
1148
|
-
Default flow: snapshot → run-edit-script with auto_commit=false → inspect preview → commit-plan. Use auto_commit=true only for low-risk edits when the host does not need user confirmation. Concurrent edits do not require replaying the script against a newer snapshot. If a domain conflict is reported, inspect the merged state and resolve it explicitly; never replace the complete document to force the edit through.
|
|
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.
|
|
1149
1033
|
|
|
1150
|
-
|
|
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.
|
|
1151
1035
|
`.trim();
|
|
1152
1036
|
const MEDEO_TOOL_EXECUTION_RULES = `
|
|
1153
|
-
The host supplies the current document. Do not
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
Entities own
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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.
|
|
1165
1056
|
`.trim();
|
|
1166
|
-
/**
|
|
1057
|
+
/** MEngine owns API disclosure; Harness only supplies the changing document context. */
|
|
1167
1058
|
function renderMedeoModelContext(input) {
|
|
1168
1059
|
const updated = input.updatedSincePreviousModelCall == null ? "unknown (first model call)" : String(input.updatedSincePreviousModelCall);
|
|
1169
1060
|
return `
|
|
@@ -1171,11 +1062,11 @@ ${MEDEO_TOOL_DESCRIPTION}
|
|
|
1171
1062
|
|
|
1172
1063
|
${MEDEO_TOOL_EXECUTION_RULES}
|
|
1173
1064
|
|
|
1174
|
-
Current MEngine document state (sampled
|
|
1065
|
+
Current MEngine document state (sampled immediately before this model call):
|
|
1175
1066
|
- document_version: ${JSON.stringify(input.documentVersion)}
|
|
1176
1067
|
- updated_since_previous_model_call: ${updated}
|
|
1177
1068
|
|
|
1178
|
-
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.
|
|
1179
1070
|
|
|
1180
1071
|
Sandbox TypeScript interface:
|
|
1181
1072
|
\`\`\`ts
|
|
@@ -1764,7 +1655,6 @@ function createMedeoTool(options) {
|
|
|
1764
1655
|
document,
|
|
1765
1656
|
baseVersion,
|
|
1766
1657
|
entityState,
|
|
1767
|
-
entityOnly: true,
|
|
1768
1658
|
loadEntityAsset: options.loadEntityAsset ? (entity) => options.loadEntityAsset(input.doc_id, entity) : void 0,
|
|
1769
1659
|
script: input.script,
|
|
1770
1660
|
...input.inputs !== void 0 ? { inputs: input.inputs } : {},
|
|
@@ -2028,6 +1918,6 @@ function stableId(prefix, ...parts) {
|
|
|
2028
1918
|
return `${prefix}_${createHash("sha256").update(JSON.stringify(parts)).digest("hex")}`;
|
|
2029
1919
|
}
|
|
2030
1920
|
//#endregion
|
|
2031
|
-
export {
|
|
1921
|
+
export { MEDEO_TOOL_DESCRIPTION, MEDEO_TOOL_NAME, MEDEO_TOOL_PARAMETERS, collectAffectedPartIds, commitPlan, createMedeoTool, materializeResources, renderCompactProjection, renderPreview, runEditScript };
|
|
2032
1922
|
|
|
2033
1923
|
//# sourceMappingURL=index.mjs.map
|