@mengine/medeo-tool 1.3.1-alpha.11 → 1.3.1-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +21 -1
- package/dist/index.mjs +135 -49
- package/dist/index.mjs.map +1 -1
- package/dist/sandbox-api.d.mts +34 -44
- package/dist/{script-session-DLBhoA1R.mjs → script-session-IqAIVBCM.mjs} +3 -9
- package/dist/script-session-IqAIVBCM.mjs.map +1 -0
- package/dist/worker-entry.mjs +64 -5
- package/dist/worker-entry.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/script-session-DLBhoA1R.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -211,6 +211,20 @@ type EditScriptResult = {
|
|
|
211
211
|
/** Run `script` against a forked document snapshot; always resolves (never rejects). */
|
|
212
212
|
declare function runEditScript(options: RunEditScriptOptions): Promise<EditScriptResult>;
|
|
213
213
|
//#endregion
|
|
214
|
+
//#region src/entity/caption-asset-assembly.d.ts
|
|
215
|
+
/** Optional physical artifact registered for one immutable Caption identity. */
|
|
216
|
+
interface CaptionAssetFact {
|
|
217
|
+
readonly captionEntityId: string;
|
|
218
|
+
readonly assetId: string;
|
|
219
|
+
readonly storageKey: string;
|
|
220
|
+
}
|
|
221
|
+
/** Host-only lookup. Missing facts mean the Caption has no physical artifact. */
|
|
222
|
+
type CaptionAssetsLoader = (docId: string, captionEntityIds: readonly string[]) => Promise<readonly CaptionAssetFact[]>;
|
|
223
|
+
interface CaptionAssetAssemblyOutcome {
|
|
224
|
+
readonly status: 'applied' | 'current' | 'failed';
|
|
225
|
+
readonly message?: string;
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
214
228
|
//#region src/entity/generation-sync.d.ts
|
|
215
229
|
/** Factual generation lineage for recalled Memota assets, supplied by the host. */
|
|
216
230
|
interface AssetGenerationFact {
|
|
@@ -455,6 +469,8 @@ interface CreateMedeoToolOptions {
|
|
|
455
469
|
* names entities, relations, or endpoints.
|
|
456
470
|
*/
|
|
457
471
|
loadGenerationFacts?: GenerationFactsLoader;
|
|
472
|
+
/** Assemble optional Caption artifacts by immutable entity ID; never exposed to scripts. */
|
|
473
|
+
loadCaptionAssets?: CaptionAssetsLoader;
|
|
458
474
|
fetchImpl?: typeof fetch;
|
|
459
475
|
/** @deprecated ManualSyncDoc has no SSE or reconnect loop. */
|
|
460
476
|
sseReconnectDelayMs?: number;
|
|
@@ -498,6 +514,9 @@ type MedeoToolInput = {
|
|
|
498
514
|
validation?: 'preflight';
|
|
499
515
|
};
|
|
500
516
|
type MedeoToolWarning = {
|
|
517
|
+
kind: 'asset_assembly_failed';
|
|
518
|
+
message: string;
|
|
519
|
+
} | {
|
|
501
520
|
kind: 'pull_failed';
|
|
502
521
|
message: string;
|
|
503
522
|
} | {
|
|
@@ -515,6 +534,7 @@ type EntityCommitResult = {
|
|
|
515
534
|
collaborated: boolean;
|
|
516
535
|
entity_revision: number; /** Present only when the host supplies loadGenerationFacts. */
|
|
517
536
|
generation_sync?: GenerationSyncOutcome;
|
|
537
|
+
asset_assembly?: CaptionAssetAssemblyOutcome;
|
|
518
538
|
warnings?: MedeoToolWarning[];
|
|
519
539
|
} | {
|
|
520
540
|
kind: 'unconfirmed';
|
|
@@ -601,5 +621,5 @@ type MedeoInitialDraft = VideoDraft;
|
|
|
601
621
|
*/
|
|
602
622
|
declare function createMedeoTool(options: CreateMedeoToolOptions): MedeoTool;
|
|
603
623
|
//#endregion
|
|
604
|
-
export { type AssetGenerationFact, type AuthorableRelationKind, type ChangePlan, type CommitPlan, type CommitPlanOptions, type CommitPlanResult, type CompactProjectionOptions, type ConsoleShim, type CreateEntityInput, type CreateMedeoToolOptions, type DeleteEntityInput, type EditFacade, EditSandboxSession, type EditSandboxSessionOptions, type EditScriptResult, type EntityCommand, type EntityCommitResult, type EntityFacade, type EntityPlanState, type EntityStoreSnapshot, type GenerationFactsLoader, type GenerationSyncOutcome, type JsonObject, type JsonPrimitive, type JsonValue, type KnownEntityKind, type KnownRelationKind, type LinkGeneratedRelationInput, type LinkRelationInput, MEDEO_TOOL_DESCRIPTION, MEDEO_TOOL_NAME, MEDEO_TOOL_PARAMETERS, type MedeoCommitResult, type MedeoInitialDraft, type MedeoModelContext, type MedeoModelContextInput, type MedeoTool, type MedeoToolInput, type MedeoToolOp, type MedeoToolResult, type RelationFacade, type ResourceEntityKind, type RunEditScriptOptions, type SandboxCheckpoint, type SandboxEntity, type SandboxRelation, type TimelineClipDescriptor, type TimelineFacade, type TimelinePartDescriptor, type UnlinkRelationInput, type UpdateEntityInput, collectAffectedPartIds, commitPlan, createMedeoTool, renderCompactProjection, renderPreview, runEditScript };
|
|
624
|
+
export { type AssetGenerationFact, type AuthorableRelationKind, type CaptionAssetAssemblyOutcome, type CaptionAssetFact, type CaptionAssetsLoader, type ChangePlan, type CommitPlan, type CommitPlanOptions, type CommitPlanResult, type CompactProjectionOptions, type ConsoleShim, type CreateEntityInput, type CreateMedeoToolOptions, type DeleteEntityInput, type EditFacade, EditSandboxSession, type EditSandboxSessionOptions, type EditScriptResult, type EntityCommand, type EntityCommitResult, type EntityFacade, type EntityPlanState, type EntityStoreSnapshot, type GenerationFactsLoader, type GenerationSyncOutcome, type JsonObject, type JsonPrimitive, type JsonValue, type KnownEntityKind, type KnownRelationKind, type LinkGeneratedRelationInput, type LinkRelationInput, MEDEO_TOOL_DESCRIPTION, MEDEO_TOOL_NAME, MEDEO_TOOL_PARAMETERS, type MedeoCommitResult, type MedeoInitialDraft, type MedeoModelContext, type MedeoModelContextInput, type MedeoTool, type MedeoToolInput, type MedeoToolOp, type MedeoToolResult, type RelationFacade, type ResourceEntityKind, type RunEditScriptOptions, type SandboxCheckpoint, type SandboxEntity, type SandboxRelation, type TimelineClipDescriptor, type TimelineFacade, type TimelinePartDescriptor, type UnlinkRelationInput, type UpdateEntityInput, collectAffectedPartIds, commitPlan, createMedeoTool, renderCompactProjection, renderPreview, runEditScript };
|
|
605
625
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as isMediaAssetVariantKind, c as renderCompactProjection, i as createRelationId, o as collectAffectedPartIds, r as createEntityId, s as renderPreview, t as EditSandboxSession } from "./script-session-
|
|
1
|
+
import { a as isMediaAssetVariantKind, c as renderCompactProjection, i as createRelationId, o as collectAffectedPartIds, r as createEntityId, s as renderPreview, t as EditSandboxSession } from "./script-session-IqAIVBCM.mjs";
|
|
2
2
|
import { LoroEntityDocument, ManualSyncDoc, MengineHttpClient, MengineHttpRequestError, ValidationError, base64ToBytes, bytesToBase64, compileEntityRows, createMirrorVideoDocument, createPlainMemoryAdapter, decodeDocVersionMark, encodeDocVersionMark, ensureEditorFoundation, replayJournal, toVideoDocument } from "@mengine/medeo-client";
|
|
3
3
|
import { Worker } from "node:worker_threads";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
//#region src/sandbox/node-host.ts
|
|
6
6
|
const DEFAULT_TIMEOUT_MS = 2e3;
|
|
7
7
|
const DEFAULT_MEMORY_MB = 256;
|
|
@@ -180,6 +180,78 @@ function runEditScript(options) {
|
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
182
|
//#endregion
|
|
183
|
+
//#region src/entity/caption-asset-assembly.ts
|
|
184
|
+
/** Assemble optional infrastructure after business execution, using exact entity identity. */
|
|
185
|
+
async function assembleCaptionAssets(input) {
|
|
186
|
+
try {
|
|
187
|
+
const state = await input.client.fetchState();
|
|
188
|
+
const candidates = state.entities.filter((entity) => entity.entity_kind === "caption" && !state.relations.some((relation) => relation.relation_kind === "physical-asset" && (relation.endpoint_0_entity_id === entity.entity_id || relation.endpoint_1_entity_id === entity.entity_id)));
|
|
189
|
+
if (!candidates.length) return { status: "current" };
|
|
190
|
+
const ids = new Set(candidates.map((entity) => entity.entity_id));
|
|
191
|
+
const facts = await input.loadAssets(input.docId, [...ids]);
|
|
192
|
+
const entities = [...state.entities];
|
|
193
|
+
const relations = [...state.relations];
|
|
194
|
+
const bound = /* @__PURE__ */ new Map();
|
|
195
|
+
for (const fact of facts) {
|
|
196
|
+
if (!ids.has(fact.captionEntityId) || !trimmed(fact.assetId) || !trimmed(fact.storageKey)) throw new Error("Caption Asset fact must identify a requested Caption and a real Asset locator");
|
|
197
|
+
const previous = bound.get(fact.captionEntityId);
|
|
198
|
+
if (previous !== void 0) {
|
|
199
|
+
if (previous !== fact.assetId) throw new Error(`Conflicting Assets for Caption ${fact.captionEntityId}`);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
bound.set(fact.captionEntityId, fact.assetId);
|
|
203
|
+
const matches = entities.filter((entity) => {
|
|
204
|
+
const external = entity.payload.external;
|
|
205
|
+
return external !== null && typeof external === "object" && !Array.isArray(external) && external.system === "memota" && external.key === fact.assetId;
|
|
206
|
+
});
|
|
207
|
+
if (matches.length > 1 || matches[0] && matches[0].entity_kind !== "asset") throw new Error(`Conflicting resource identity for Caption Asset ${fact.assetId}`);
|
|
208
|
+
let asset = matches[0];
|
|
209
|
+
if (asset && asset.payload.storageKey !== fact.storageKey) throw new Error(`Conflicting storage key for Caption Asset ${fact.assetId}`);
|
|
210
|
+
if (!asset) {
|
|
211
|
+
asset = {
|
|
212
|
+
entity_id: stableId("asset", fact.assetId),
|
|
213
|
+
entity_kind: "asset",
|
|
214
|
+
payload: {
|
|
215
|
+
external: {
|
|
216
|
+
system: "memota",
|
|
217
|
+
key: fact.assetId
|
|
218
|
+
},
|
|
219
|
+
storageKey: fact.storageKey
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
if (entities.some((entity) => entity.entity_id === asset.entity_id)) throw new Error("Caption Asset identity collision");
|
|
223
|
+
entities.push(asset);
|
|
224
|
+
}
|
|
225
|
+
relations.push({
|
|
226
|
+
relation_id: stableId("relation", fact.captionEntityId, asset.entity_id),
|
|
227
|
+
relation_kind: "physical-asset",
|
|
228
|
+
endpoint_0_entity_id: fact.captionEntityId,
|
|
229
|
+
endpoint_1_entity_id: asset.entity_id,
|
|
230
|
+
metadata: {},
|
|
231
|
+
trace: {}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (!bound.size) return { status: "current" };
|
|
235
|
+
await input.client.commit(state.revision, {
|
|
236
|
+
...state,
|
|
237
|
+
entities,
|
|
238
|
+
relations
|
|
239
|
+
});
|
|
240
|
+
return { status: "applied" };
|
|
241
|
+
} catch (error) {
|
|
242
|
+
return {
|
|
243
|
+
status: "failed",
|
|
244
|
+
message: error instanceof Error ? error.message : String(error)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function stableId(prefix, ...parts) {
|
|
249
|
+
return `${prefix}_${createHash("sha256").update(JSON.stringify(parts)).digest("hex")}`;
|
|
250
|
+
}
|
|
251
|
+
function trimmed(value) {
|
|
252
|
+
return typeof value === "string" && value.length > 0 && value.trim() === value;
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
183
255
|
//#region src/entity/entity-contract.ts
|
|
184
256
|
const KNOWN_ENTITY_KINDS = [
|
|
185
257
|
"axvideo",
|
|
@@ -538,6 +610,45 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
538
610
|
" sampling: 'native';",
|
|
539
611
|
" coordinateSpace: JsonValue;",
|
|
540
612
|
"}",
|
|
613
|
+
"/** Business editing surface. Infrastructure Assets are assembled by the host. */",
|
|
614
|
+
"export interface BusinessEntityFacade {",
|
|
615
|
+
" list(): SandboxEntity[];",
|
|
616
|
+
" get(entityId: string): SandboxEntity | null;",
|
|
617
|
+
" readCaptionContent(entityId: string): ComposedScriptContent;",
|
|
618
|
+
" readPhoneticScriptContent(entityId: string): ComposedPhoneticContent;",
|
|
619
|
+
" create(",
|
|
620
|
+
" input: Exclude<",
|
|
621
|
+
" CreateEntityInput,",
|
|
622
|
+
" {",
|
|
623
|
+
" entity_kind: 'asset';",
|
|
624
|
+
" }",
|
|
625
|
+
" >,",
|
|
626
|
+
" ): string;",
|
|
627
|
+
" update(input: UpdateEntityInput): void;",
|
|
628
|
+
" declareFields(input: UpdateEntityInput): void;",
|
|
629
|
+
" delete(input: DeleteEntityInput): void;",
|
|
630
|
+
" ensureMedia(fact: MediaAssetFact): {",
|
|
631
|
+
" contentEntityId: string;",
|
|
632
|
+
" };",
|
|
633
|
+
"}",
|
|
634
|
+
"/** Physical Asset bindings are maintained outside the sandbox. */",
|
|
635
|
+
"export interface BusinessRelationFacade {",
|
|
636
|
+
" list(): SandboxRelation[];",
|
|
637
|
+
" of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];",
|
|
638
|
+
" link(",
|
|
639
|
+
" input: Exclude<",
|
|
640
|
+
" LinkRelationInput,",
|
|
641
|
+
" {",
|
|
642
|
+
" relation_kind: 'physical-asset';",
|
|
643
|
+
" }",
|
|
644
|
+
" >,",
|
|
645
|
+
" ): string;",
|
|
646
|
+
" linkGenerated(input: LinkGeneratedRelationInput): string;",
|
|
647
|
+
" linkClipAnchor(input: LinkClipAnchorRelationInput): string;",
|
|
648
|
+
" linkPhoneticScriptRender(input: LinkPhoneticScriptRenderRelationInput): string;",
|
|
649
|
+
" linkAudioScriptSource(input: LinkAudioScriptSourceRelationInput): string;",
|
|
650
|
+
" unlink(input: UnlinkRelationInput): void;",
|
|
651
|
+
"}",
|
|
541
652
|
"export interface CaptionFontDescriptor {",
|
|
542
653
|
" readonly system: 'font-library';",
|
|
543
654
|
" readonly key: string;",
|
|
@@ -640,28 +751,6 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
640
751
|
" | 'axvideo-marker'",
|
|
641
752
|
" | 'marker-timeline'",
|
|
642
753
|
" | 'audio-script-marker';",
|
|
643
|
-
"export interface EntityFacade {",
|
|
644
|
-
" /** Read complete assembled fields; returned objects are snapshots. Use update to persist edits. */",
|
|
645
|
-
" list(): SandboxEntity[];",
|
|
646
|
-
" get(entityId: string): SandboxEntity | null;",
|
|
647
|
-
" /** Find document resources by external Memota asset id, including directly composed media variants. */",
|
|
648
|
-
" findByAssetId(assetId: string): SandboxEntity<ResourceEntityKind>[];",
|
|
649
|
-
" /** Assemble selected Caption text; missing composition is an error. */",
|
|
650
|
-
" readCaptionContent(entityId: string): ComposedScriptContent;",
|
|
651
|
-
" /** Assemble base text and pronunciation fields before generating Voice. */",
|
|
652
|
-
" readPhoneticScriptContent(entityId: string): ComposedPhoneticContent;",
|
|
653
|
-
" create(input: CreateEntityInput): string;",
|
|
654
|
-
" /** Patch assembled fields, routing inherited fields to their declaring entity. */",
|
|
655
|
-
" update(input: UpdateEntityInput): void;",
|
|
656
|
-
" /** Explicitly declare own fields, overriding unambiguous bases without modifying them. Ordinary edits use update. */",
|
|
657
|
-
" declareFields(input: UpdateEntityInput): void;",
|
|
658
|
-
" /** Delete an Entity only after all of its incident Relations have been explicitly unlinked. */",
|
|
659
|
-
" delete(input: DeleteEntityInput): void;",
|
|
660
|
-
" /** Get or create one typed Asset by factual external id and return its single content identity. Never creates a Clip. */",
|
|
661
|
-
" ensureMedia(fact: MediaAssetFact): {",
|
|
662
|
-
" contentEntityId: string;",
|
|
663
|
-
" };",
|
|
664
|
-
"}",
|
|
665
754
|
"export type EntityId = string;",
|
|
666
755
|
"export interface EntityPayloadByKind {",
|
|
667
756
|
" axvideo: BoundedDerivedSequencePayload;",
|
|
@@ -735,6 +824,8 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
735
824
|
"}",
|
|
736
825
|
"export interface InsertCaptionClipInput {",
|
|
737
826
|
" readonly timelineEntityId: string;",
|
|
827
|
+
" /** Existing generation identity for newly materialized Caption content, distinct from its Clip. */",
|
|
828
|
+
" readonly captionEntityId?: string;",
|
|
738
829
|
" /** Stable placed caption identity, distinct from the Caption content identity. */",
|
|
739
830
|
" readonly captionClipEntityId?: string;",
|
|
740
831
|
" /** Existing bases composed by this variant; includes an AudioScript text owner. */",
|
|
@@ -915,23 +1006,6 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
915
1006
|
" readonly timelineEntityId: string;",
|
|
916
1007
|
" readonly style: CaptionStyleFields;",
|
|
917
1008
|
"}",
|
|
918
|
-
"export interface RelationFacade {",
|
|
919
|
-
" list(): SandboxRelation[];",
|
|
920
|
-
" /** Incident lookup is endpoint-agnostic; persisted endpoint positions stay unchanged. */",
|
|
921
|
-
" of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];",
|
|
922
|
-
" /** Link existing entities through ordinary associations; variant bases are stored directly on the variant. */",
|
|
923
|
-
" link(input: LinkRelationInput): string;",
|
|
924
|
-
" /** Author ordered generated(output,input); generic link() deliberately rejects this kind. */",
|
|
925
|
-
" linkGenerated(input: LinkGeneratedRelationInput): string;",
|
|
926
|
-
" /** Author ordered clip-anchor(child,host) without positional endpoint ambiguity. */",
|
|
927
|
-
" linkClipAnchor(input: LinkClipAnchorRelationInput): string;",
|
|
928
|
-
" /** Author ordered phonetic-script-render(output,script) without positional endpoint ambiguity. */",
|
|
929
|
-
" linkPhoneticScriptRender(input: LinkPhoneticScriptRenderRelationInput): string;",
|
|
930
|
-
" /** Author ordered audio-script-source(script,source) without positional endpoint ambiguity. */",
|
|
931
|
-
" linkAudioScriptSource(input: LinkAudioScriptSourceRelationInput): string;",
|
|
932
|
-
" /** Remove a Relation by identity; endpoint replacement is an explicit unlink plus link. */",
|
|
933
|
-
" unlink(input: UnlinkRelationInput): void;",
|
|
934
|
-
"}",
|
|
935
1009
|
"export interface ReplaceClipContentInput {",
|
|
936
1010
|
" readonly clipEntityId: string;",
|
|
937
1011
|
" /** Existing Sequence media Entity id. Asset ids and URLs are not content ids. */",
|
|
@@ -958,8 +1032,6 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
958
1032
|
" readonly sourceRange: SequenceRange<number>;",
|
|
959
1033
|
" readonly volume?: number;",
|
|
960
1034
|
"}",
|
|
961
|
-
"/** Asset identity, either an old physical-only row or a directly composed media variant. */",
|
|
962
|
-
"export type ResourceEntityKind = 'image' | 'video' | 'audio' | 'voice';",
|
|
963
1035
|
"export interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {",
|
|
964
1036
|
" entity_id: string;",
|
|
965
1037
|
" entity_kind: K;",
|
|
@@ -1163,8 +1235,8 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
|
|
|
1163
1235
|
"}",
|
|
1164
1236
|
"export declare const edit: EditApi;",
|
|
1165
1237
|
"export declare const timeline: TimelineApi;",
|
|
1166
|
-
"export declare const entities:
|
|
1167
|
-
"export declare const relations:
|
|
1238
|
+
"export declare const entities: BusinessEntityFacade;",
|
|
1239
|
+
"export declare const relations: BusinessRelationFacade;",
|
|
1168
1240
|
"export declare function checkpoint(): SandboxCheckpoint;",
|
|
1169
1241
|
"export declare function rollbackTo(cp: SandboxCheckpoint): void;",
|
|
1170
1242
|
"export declare const inputs: Readonly<Record<string, unknown>>;",
|
|
@@ -1188,12 +1260,12 @@ const MEDEO_TOOL_EXECUTION_RULES = `
|
|
|
1188
1260
|
The host supplies the current document. Do not ask for, invent, or pass a document id.
|
|
1189
1261
|
timeline.snapshot() returns the Entity/Relation graph with its revision, not a legacy VideoDraft. Inspect Timeline, Track, Clip, SequenceMarker and their relations to plan edits.
|
|
1190
1262
|
Generation lineage is not a model input: the host program queries it via loadGenerationFacts and syncs generated Relations after each successful commit. Memota asset facts are host-provided through inputs. Never invent an asset id, Entity kind, or peer Entity id.
|
|
1191
|
-
Use entities.ensureMedia(fact) to get or create the canonical typed Asset. Native media placement helpers use the same resolver.
|
|
1263
|
+
Use entities.ensureMedia(fact) to get or create the canonical typed Asset. Native media placement helpers use the same resolver. Every Image/Video/Audio/Voice must own its external identity; separate Asset+media graphs are invalid. A typed Asset's external identity cannot be removed or rewritten: to replace its source, ensureMedia for the new Asset and replace the Clip's content. Conflicting facts fail closed. Asset generation itself still creates no editor Entities.
|
|
1192
1264
|
For recalled video/audio/voice, create a bounded/native payload whose extent end comes from factual media duration/coordinates in inputs; never fabricate a duration. Image uses unbounded/constant semantics and has no invented end. If required facts are absent, do not create the media Entity yet.
|
|
1193
|
-
Caption
|
|
1265
|
+
Caption composes AudioScript text. Asset entities and their physical-asset bindings are optional host infrastructure, assembled by Caption entity ID after execution. Generated media lineage is host-owned; do not author generated Relations yourself. relations.of remains endpoint-agnostic for lookup.
|
|
1194
1266
|
The editor projection supports the existing four Track roles: video_clip (Image/Video), speech (Voice), caption (Caption), and bgm (Audio), one of each. Clip.volume is decibels (-60 to 20, 0 = original). Marker.sourceRange is the selected source interval; Marker.duration is effective display/playback duration. Coordinates and duration are whole milliseconds for this reader, not a global DSL restriction. Placement is exactly one of Clip.order, Marker.targetRange, or clip-anchor(child,host) plus Marker.anchorOffset. Use the native move/delete/voiceover helpers so placement and cascade decisions are in the same entity plan. Reading the graph never rebinds anchors or invents empty clips. Linear timeRemapping is {kind:'linear',rate:2,mode:'constant'} and agrees with rounded source span divided by rate. Image remains unbounded/constant; an explicit linear rate scales its display window, not an invented media extent. Nonlinear speed and multiple visual overlay tracks are unsupported.
|
|
1195
1267
|
Entities own fields; ordinary Relations express associations; variants directly hold baseEntityIds and assemble the referenced entities. These foundations are fixed: implementation must follow them, never redefine them. Any entity may compose multiple bases. Equal field names from multiple bases (even equal values) are errors, even when the variant declares that field itself. After validating all base fields are unambiguous, explicitly declared own fields may override base fields without mutating the bases. Base ordering never resolves conflicts. AudioScript owns segmented text. Caption and PhoneticScript persist baseEntityIds including their AudioScript, plus their own fields; no composition Relation exists. Create the real bases before reading or committing a variant. Inside the DSL sandbox, entities.get/list expose complete assembled fields. Consumers read fields without inspecting base IDs or merging bases. entities.update patches supplied fields and routes inherited fields to their declaring entity; omitted fields remain unchanged. entities.declareFields explicitly declares own overrides and is distinct from an ordinary field edit. Persistence keeps owned fields only. entities.readCaptionContent(id) and entities.readPhoneticScriptContent(id) return assembled text. Missing/cyclic bases and field conflicts fail before persistence.
|
|
1196
|
-
Use edit.insertCaptionClip with baseEntityIds and selections; each voiceover caption also supplies baseEntityIds. A selection names segmentId and may use a half-open Unicode code-point textRange to split a segment for the screen without rewriting AudioScript. Generate Voice from an existing PhoneticScript, then use phoneticScriptEntityId in the voiceover helper or relations.linkPhoneticScriptRender({output_entity_id,phonetic_script_entity_id}) for its render relation. Caption and Voice have their own Clips; display anchoring is explicit and independent of composition/alignment.
|
|
1268
|
+
Use edit.insertCaptionClip with baseEntityIds and selections, plus captionEntityId when generation returned a Caption identity; each voiceover caption also supplies baseEntityIds. A selection names segmentId and may use a half-open Unicode code-point textRange to split a segment for the screen without rewriting AudioScript. Generate Voice from an existing PhoneticScript, then use phoneticScriptEntityId in the voiceover helper or relations.linkPhoneticScriptRender({output_entity_id,phonetic_script_entity_id}) for its render relation. Caption and Voice have their own Clips; display anchoring is explicit and independent of composition/alignment.
|
|
1197
1269
|
Move or stretch only the Clip's display Marker; preserve Caption intrinsic Sequence, AudioScript text and its annotation Markers. AudioScript cannot enter a Clip and has no intrinsic time. audio-script-source links its ASR source Audio/Video/Voice; audio-script-marker attaches annotation Markers with directly assigned segmentRanges:{segmentId,startMs,endMs} in whole milliseconds. Annotation Markers have no Clip/AXVideo/content/Timeline relations and never refer to other Markers for time. BGM keeps factual source duration with durationPolicy:'timeline'. Never introduce a speech entity kind.
|
|
1198
1270
|
Create only the known entity kinds. Project creation initializes one current Timeline, four Tracks and an attached AudioScript with segments:[]. Read the current AudioScript ID from timeline.snapshot(); the panel displays this attachment and preserves its segments. Normal edits operate this script, not an unrelated newly created script.
|
|
1199
1271
|
Immutable updates apply to every entity: editing an owned field creates a new content version ID; changing only a variant's base ID preserves the variant ID. Editing a base through a variant updates the owner, and the compiler advances the affected base links and project attachment. A variant-owned edit creates a new variant version and retains its unchanged bases. Do not manually clone entities or duplicate inherited fields to implement versioning. Versions preserve native text and list editing identities so independent concurrent edits survive. Missing facts, unsupported layouts and composition conflicts are explicit errors; there is no legacy migration or whole-state overwrite path.
|
|
@@ -1678,7 +1750,21 @@ function createMedeoTool(options) {
|
|
|
1678
1750
|
if (plan.entity_rows === void 0) throw new Error("entity plan is missing its authoritative rows");
|
|
1679
1751
|
const client = getEntityClient(docId);
|
|
1680
1752
|
if (options.loadGenerationFacts !== void 0 && baseState === void 0) throw new Error("Generation synchronization is missing the cached causal plan baseline");
|
|
1681
|
-
|
|
1753
|
+
const synced = await attachGenerationSync(docId, plan, await commitEntityPlan(client, plan), baseState);
|
|
1754
|
+
if (synced.kind !== "committed" || options.loadCaptionAssets === void 0) return synced;
|
|
1755
|
+
const assembly = await assembleCaptionAssets({
|
|
1756
|
+
client,
|
|
1757
|
+
docId,
|
|
1758
|
+
loadAssets: options.loadCaptionAssets
|
|
1759
|
+
});
|
|
1760
|
+
return {
|
|
1761
|
+
...synced,
|
|
1762
|
+
asset_assembly: assembly,
|
|
1763
|
+
...assembly.status === "failed" ? { warnings: [...synced.warnings ?? [], {
|
|
1764
|
+
kind: "asset_assembly_failed",
|
|
1765
|
+
message: assembly.message ?? "Caption Asset assembly failed"
|
|
1766
|
+
}] } : {}
|
|
1767
|
+
};
|
|
1682
1768
|
}
|
|
1683
1769
|
/**
|
|
1684
1770
|
* After a confirmed entity commit, connect fact-matched generated Relations
|