@mengine/medeo-tool 1.4.1-alpha.0 → 1.4.1-alpha.2

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 CHANGED
@@ -57,11 +57,35 @@ const entityRun = await medeo.handle({
57
57
  await medeo.close();
58
58
  ```
59
59
 
60
- The model has only business Entity/Relation operations. No Asset lookup,
61
- read, create, update, delete, import, binding or locator API is exposed in the
62
- sandbox or through another model tool. `ensureMedia`, `findByAssetId` and
63
- Asset-based editor shortcuts are host-only. Generic entity writes reject
64
- `external` and `storageKey`, and cannot access physical Asset rows or bindings.
60
+ The sandbox exposes only business Entity/Relation operations. It has no Asset
61
+ lookup, read, create, update, delete, import or binding API. `ensureMedia`,
62
+ `findByAssetId` and Asset-based editor shortcuts remain host-only.
63
+ Generation tools may return Asset IDs. Entity operations accept these IDs in
64
+ `external: { system: 'memota', key: assetId }`; assembled entity reads preserve
65
+ that reference. An Asset ID is not an Entity ID, a composition base or a Relation
66
+ endpoint. Physical `storageKey` access and Asset rows remain host-only.
67
+
68
+ `await rgetAssetFromEntity(entityId)` reads the immutable resource attached to an
69
+ Entity through host I/O and returns `{ assetId, content }`. It does not accept an
70
+ Asset ID. For a new Caption, pass `payload.external` to `entities.create`, then
71
+ await this function before placing it. The host reads the resource and the
72
+ session initializes Caption intrinsic timing, the composed project AudioScript
73
+ text and its annotation Marker. No Asset row or physical-asset relation is
74
+ created. Pending Caption initialization also completes before final validation.
75
+ Failure publishes no partial plan. Repeated reads preserve edited text; raw
76
+ resource content remains immutable. Native Loro updates retain the original
77
+ causal baseline and merge independent collaborator edits.
78
+
79
+ ```js
80
+ const caption = entities.create({
81
+ entity_kind: 'caption',
82
+ payload: {
83
+ external: { system: 'memota', key: inputs.captionAssetId },
84
+ },
85
+ });
86
+ const asset = await rgetAssetFromEntity(caption);
87
+ console.log(entities.readCaptionContent(caption));
88
+ ```
65
89
 
66
90
  `materializeResources` is a host API. It creates media entities from factual
67
91
  resources, or Caption with composed AudioScript text, ASR annotation markers
@@ -118,10 +118,15 @@ interface EntityPayloadByKind {
118
118
  baseEntityIds: string[];
119
119
  selections: CaptionTextSelection[];
120
120
  style?: JsonObject;
121
+ segmentRanges?: {
122
+ segmentId: string;
123
+ startMs: number;
124
+ endMs: number;
125
+ }[];
121
126
  };
122
127
  }
123
128
  /** Stored own fields; a variant may obtain required content fields from its declared bases. */
124
- type StoredEntityPayload<K extends KnownEntityKind> = EntityPayloadByKind[K] | (JsonObject & Partial<EntityPayloadByKind[K]> & {
129
+ type StoredEntityPayload<K extends KnownEntityKind> = EntityPayloadByKind[K] | (K extends 'caption' ? JsonObject & Pick<MediaAssetPayload, 'external'> : never) | (JsonObject & Partial<EntityPayloadByKind[K]> & {
125
130
  baseEntityIds: string[];
126
131
  });
127
132
  interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {
@@ -273,4 +278,4 @@ interface RelationFacade {
273
278
  }
274
279
  //#endregion
275
280
  export { SandboxEntity as _, EntityFacade as a, UpdateEntityInput as b, JsonObject as c, KnownEntityKind as d, KnownRelationKind as f, ResourceEntityKind as g, RelationFacade as h, EntityCommand as i, JsonPrimitive as l, LinkRelationInput as m, CreateEntityInput as n, EntityPlanState as o, LinkGeneratedRelationInput as p, DeleteEntityInput as r, EntityStoreSnapshot as s, AuthorableRelationKind as t, JsonValue as u, SandboxRelation as v, UnlinkRelationInput as y };
276
- //# sourceMappingURL=entity-contract-DHasvrhq.d.mts.map
281
+ //# sourceMappingURL=entity-contract-Cpf3P69H.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as SandboxEntity, a as EntityFacade, b as UpdateEntityInput, c as JsonObject, d as KnownEntityKind, f as KnownRelationKind, g as ResourceEntityKind, h as RelationFacade, i as EntityCommand, l as JsonPrimitive, m as LinkRelationInput, n as CreateEntityInput, o as EntityPlanState, p as LinkGeneratedRelationInput, r as DeleteEntityInput, s as EntityStoreSnapshot, t as AuthorableRelationKind, u as JsonValue, v as SandboxRelation, y as UnlinkRelationInput } from "./entity-contract-DHasvrhq.mjs";
1
+ import { _ as SandboxEntity, a as EntityFacade, b as UpdateEntityInput, c as JsonObject, d as KnownEntityKind, f as KnownRelationKind, g as ResourceEntityKind, h as RelationFacade, i as EntityCommand, l as JsonPrimitive, m as LinkRelationInput, n as CreateEntityInput, o as EntityPlanState, p as LinkGeneratedRelationInput, r as DeleteEntityInput, s as EntityStoreSnapshot, t as AuthorableRelationKind, u as JsonValue, v as SandboxRelation, y as UnlinkRelationInput } from "./entity-contract-Cpf3P69H.mjs";
2
2
  import { JournalEntry, ManualSyncDoc, MediaAssetFact, PartIdFactory, SemanticOpName, VideoDocument, VideoDraft, fromVideoDocument } from "@mengine/medeo-client";
3
3
  import { AddSpeechesInput, AddVideoClipsInput, AdjustBgmVolumeInput, AdjustSpeechVolumeInput, AdjustVideoClipDurationInput, AdjustVideoClipVolumeInput, ChangeSpeechScriptInput, ChangeSpeechVoiceInput, DeleteBgmInput, DeleteSpeechesInput, DeleteVideoClipsInput, MoveSpeechesInput, MoveVideoClipsByAnchorInput, MoveVideoClipsInput, ReplaceVideoClipContentInput, ReplaceVideoClipSequenceInput, SetBgmInput, SetCaptionStyleInput, SetCaptionVisibilityInput, SetVideoClipSpeedShiftInput } from "@mengine/medeo-client/schemas";
4
4
 
@@ -33,6 +33,15 @@ declare function collectAffectedPartIds(journal: readonly JournalEntry[]): Set<s
33
33
  */
34
34
  declare function renderPreview(document: VideoDocument, journal: readonly JournalEntry[]): string;
35
35
  //#endregion
36
+ //#region src/entity/entity-asset.d.ts
37
+ /** Immutable resource content resolved by the host for a document Entity. */
38
+ interface EntityAssetContent {
39
+ assetId: string;
40
+ content: JsonValue;
41
+ }
42
+ /** Host-only I/O. The sandbox supplies an Entity, never an arbitrary Asset query. */
43
+ type EntityAssetLoader = (docId: string, entity: SandboxEntity) => Promise<EntityAssetContent>;
44
+ //#endregion
36
45
  //#region src/entity/entity-sandbox.d.ts
37
46
  type DomainIdFactory = (prefix: 'entity' | 'relation') => string;
38
47
  //#endregion
@@ -172,6 +181,7 @@ interface ConsoleShim {
172
181
  * injects loaders that break worker boot.
173
182
  */
174
183
  interface RunEditScriptOptions {
184
+ loadEntityAsset?: (entity: SandboxEntity) => Promise<EntityAssetContent>;
175
185
  document: VideoDocument;
176
186
  baseVersion: string;
177
187
  script: string;
@@ -480,6 +490,7 @@ interface CreateMedeoToolOptions {
480
490
  loadGenerationFacts?: GenerationFactsLoader;
481
491
  /** Assemble optional Caption artifacts by immutable entity ID; never exposed to scripts. */
482
492
  loadCaptionAssets?: CaptionAssetsLoader;
493
+ loadEntityAsset?: EntityAssetLoader;
483
494
  fetchImpl?: typeof fetch;
484
495
  /** @deprecated ManualSyncDoc has no SSE or reconnect loop. */
485
496
  sseReconnectDelayMs?: number;
@@ -652,5 +663,5 @@ declare function materializeResources(options: EntityHttpClientOptions & {
652
663
  loadGenerationFacts?: GenerationFactsLoader;
653
664
  }, resources: readonly GeneratedResource[]): Promise<readonly string[]>;
654
665
  //#endregion
655
- 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 GeneratedResource, 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, materializeResources, renderCompactProjection, renderPreview, runEditScript };
666
+ 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 EntityAssetContent, type EntityAssetLoader, type EntityCommand, type EntityCommitResult, type EntityFacade, type EntityPlanState, type EntityStoreSnapshot, type GeneratedResource, 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, materializeResources, renderCompactProjection, renderPreview, runEditScript };
656
667
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as createEntityId, c as collectAffectedPartIds, i as businessState, l as renderPreview, n as EntitySandbox, o as createRelationId, s as isMediaAssetVariantKind, t as EditSandboxSession, u as renderCompactProjection } from "./script-session-DTq_VPEA.mjs";
1
+ import { a as createEntityId, c as collectAffectedPartIds, i as businessState, l as renderPreview, n as EntitySandbox, o as createRelationId, s as isMediaAssetVariantKind, t as EditSandboxSession, u as renderCompactProjection } from "./script-session-lXpqmupK.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
4
  import { createHash, randomUUID } from "node:crypto";
@@ -81,6 +81,26 @@ function runEditScript(options) {
81
81
  armTimeout();
82
82
  return;
83
83
  }
84
+ if (message.t === "entity-asset") {
85
+ (async () => {
86
+ try {
87
+ if (!options.loadEntityAsset) throw new Error("Entity Asset loader is unavailable");
88
+ const result = await options.loadEntityAsset(message.entity);
89
+ if (!settled) worker.postMessage({
90
+ t: "entity-asset-result",
91
+ requestId: message.requestId,
92
+ result
93
+ });
94
+ } catch (error) {
95
+ if (!settled) worker.postMessage({
96
+ t: "entity-asset-result",
97
+ requestId: message.requestId,
98
+ error: error instanceof Error ? error.message : String(error)
99
+ });
100
+ }
101
+ })();
102
+ return;
103
+ }
84
104
  if (message.t === "entry") {
85
105
  ops.push(message.entry);
86
106
  return;
@@ -742,6 +762,11 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
742
762
  " | 'axvideo-marker'",
743
763
  " | 'marker-timeline'",
744
764
  " | 'audio-script-marker';",
765
+ "/** Immutable resource content resolved by the host for a document Entity. */",
766
+ "export interface EntityAssetContent {",
767
+ " assetId: string;",
768
+ " content: JsonValue;",
769
+ "}",
745
770
  "export type EntityId = string;",
746
771
  "export interface EntityPayloadByKind {",
747
772
  " axvideo: BoundedDerivedSequencePayload;",
@@ -751,10 +776,10 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
751
776
  " role?: string;",
752
777
  " };",
753
778
  " clip: JsonObject;",
754
- " video: BoundedNativeSequencePayload;",
755
- " audio: BoundedNativeSequencePayload;",
756
- " voice: BoundedNativeSequencePayload;",
757
- " image: UnboundedConstantSequencePayload;",
779
+ " video: BoundedNativeSequencePayload & MediaAssetPayload;",
780
+ " audio: BoundedNativeSequencePayload & MediaAssetPayload;",
781
+ " voice: BoundedNativeSequencePayload & MediaAssetPayload;",
782
+ " image: UnboundedConstantSequencePayload & MediaAssetPayload;",
758
783
  " 'sequence-marker': JsonObject & {",
759
784
  " sourceRange: {",
760
785
  " start: number;",
@@ -795,6 +820,11 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
795
820
  " baseEntityIds: string[];",
796
821
  " selections: CaptionTextSelection[];",
797
822
  " style?: JsonObject;",
823
+ " segmentRanges?: {",
824
+ " segmentId: string;",
825
+ " startMs: number;",
826
+ " endMs: number;",
827
+ " }[];",
798
828
  " };",
799
829
  "}",
800
830
  "export interface EntityStoreSnapshot {",
@@ -925,6 +955,12 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
925
955
  " alignment: JsonValue;",
926
956
  " };",
927
957
  " });",
958
+ "export type MediaAssetPayload = JsonObject & {",
959
+ " external: {",
960
+ " system: 'memota' | 'memota-speech';",
961
+ " key: string;",
962
+ " };",
963
+ "};",
928
964
  "export interface MoveClipInput {",
929
965
  " readonly clipEntityId: string;",
930
966
  " readonly trackEntityId: string;",
@@ -1018,6 +1054,7 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
1018
1054
  "/** Stored own fields; a variant may obtain required content fields from its declared bases. */",
1019
1055
  "export type StoredEntityPayload<K extends KnownEntityKind> =",
1020
1056
  " | EntityPayloadByKind[K]",
1057
+ " | (K extends 'caption' ? JsonObject & Pick<MediaAssetPayload, 'external'> : never)",
1021
1058
  " | (JsonObject &",
1022
1059
  " Partial<EntityPayloadByKind[K]> & {",
1023
1060
  " baseEntityIds: string[];",
@@ -1091,6 +1128,8 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
1091
1128
  "export declare const timeline: TimelineApi;",
1092
1129
  "export declare const entities: BusinessEntityFacade;",
1093
1130
  "export declare const relations: BusinessRelationFacade;",
1131
+ "/** Resolve the resource attached to an Entity through the host; await before using a new Caption. */",
1132
+ "export declare function rgetAssetFromEntity(entityId: string): Promise<EntityAssetContent>;",
1094
1133
  "export declare function checkpoint(): SandboxCheckpoint;",
1095
1134
  "export declare function rollbackTo(cp: SandboxCheckpoint): void;",
1096
1135
  "export declare const inputs: Readonly<Record<string, unknown>>;",
@@ -1103,18 +1142,19 @@ Edit the authoritative Medeo Entity/Relation graph through a deterministic, side
1103
1142
 
1104
1143
  Operations:
1105
1144
  - snapshot: read the current Entity/Relation view, project attachments and causal Loro baseline. Reading does not initialize or mutate domain data.
1106
- - run-edit-script: inspect timeline.snapshot(), entities.*, and relations.*; edit.* operates existing Entity ids and creates the required Clip/SequenceMarker structural graph. The sandbox has no network, storage or generation access. Use assembled entity fields; the host manages resource storage and generation provenance. A successful run returns preview, logs, base revision and plan_id.
1145
+ - run-edit-script: inspect timeline.snapshot(), entities.*, and relations.*; edit.* operates existing Entity ids and creates the required Clip/SequenceMarker structural graph. The sandbox has no direct network, storage or generation access. rgetAssetFromEntity(entityId) delegates an attached-resource read to the host. Use assembled entity fields; the host manages resource storage and generation provenance. A successful run returns preview, logs, base revision and plan_id.
1107
1146
  - commit-plan: publish the native Loro update compiled against the plan’s causal baseline. Concurrent independent edits merge through Loro. The timeline and AudioScript panel read the merged entity state. A failed transport is unconfirmed; retry the same plan_id so operation identities are preserved.
1108
1147
 
1109
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.
1110
1149
 
1111
- Generated resources are materialized into domain Entities by the host. Use the returned Entity ids and assembled fields to edit or place content with edit.insertClip. Asset creation, lookup, reading, binding, resource locators and storage are host infrastructure, unavailable to the model through any tool or sandbox API. Each placement has its own Clip and SequenceMarker. Generation lineage is host-synced; relations.of(entityId) is endpoint-agnostic.
1150
+ 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.
1112
1151
  `.trim();
1113
1152
  const MEDEO_TOOL_EXECUTION_RULES = `
1114
1153
  The host supplies the current document. Do not ask for, invent, or pass a document id.
1115
1154
  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.
1116
- Inspect existing Image/Video/Audio/Voice Entities and their factual extents before placing them. The host materializes generated content and resolves its physical resource. Replace a Clip's content using another content Entity id. Never fabricate a duration.
1117
- Caption composes AudioScript text. Its optional physical resource binding is host-owned. A Caption created from AudioScript may have no physical resource. Read and edit assembled fields through entities; do not author infrastructure bindings or generated Relations.
1155
+ Inspect existing Image/Video/Audio/Voice Entities and their factual extents before placing them. For an uploaded or generated resource, create its media Entity with the returned Asset id in external.key and the factual media extent. The host resolves its physical resource. Replace a Clip's content using another content Entity id. Never fabricate a duration.
1156
+ For a Caption Asset, create a Caption with payload:{external:{system:'memota',key:assetId}}, then await rgetAssetFromEntity(captionEntityId). This initializes its intrinsic segmentRanges/extent and the project AudioScript text in the same plan. The result {assetId,content} is the original resource content. entities.get/readCaptionContent then expose editable assembled text. Call the getter before placing a newly resource-backed Caption. Caption resource initialization also runs before final plan validation; failure aborts the plan. Do not transcribe repeatedly or infer speech absence from visual descriptions when an output_caption Asset already exists. Existing text edits are preserved on repeated reads. No Asset ID can be used as the getter argument.
1157
+ Caption composes AudioScript text. Its optional external field may carry the Caption Asset id; the host resolves that resource. A Caption created from AudioScript may have no physical resource. Read and edit assembled fields through entities; do not create Asset entities or physical-asset Relations.
1118
1158
  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.
1119
1159
  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.
1120
1160
  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.
@@ -1725,9 +1765,10 @@ function createMedeoTool(options) {
1725
1765
  baseVersion,
1726
1766
  entityState,
1727
1767
  entityOnly: true,
1768
+ loadEntityAsset: options.loadEntityAsset ? (entity) => options.loadEntityAsset(input.doc_id, entity) : void 0,
1728
1769
  script: input.script,
1729
1770
  ...input.inputs !== void 0 ? { inputs: input.inputs } : {},
1730
- timeoutMs: input.timeout_ms ?? options.sandbox?.timeoutMs,
1771
+ timeoutMs: input.timeout_ms ?? options.sandbox?.timeoutMs ?? 3e4,
1731
1772
  memoryLimitMb: input.memory_limit_mb ?? options.sandbox?.memoryLimitMb
1732
1773
  });
1733
1774
  if (!result.ok) return {