@mengine/medeo-tool 1.2.1-alpha.0 → 1.2.1-alpha.8

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
@@ -1,15 +1,16 @@
1
1
  # `@mengine/medeo-tool`
2
2
 
3
- Node tool surface for editing a Medeo video document through the deterministic
4
- edit sandbox.
3
+ Node tool surface for editing the authoritative Medeo Entity/Relation graph
4
+ through the deterministic edit sandbox. Timeline editing is entity editing,
5
+ not a legacy document mutation followed by optional entity registration.
5
6
 
6
7
  The package owns the complete tool path:
7
8
 
8
9
  - compact snapshot projection;
9
10
  - trusted worker execution of an agent-authored JavaScript edit script;
10
- - op-journal `ChangePlan` caching;
11
- - versioned or per-op-preflight commit through `MengineDocSession`;
12
- - session cache and shutdown.
11
+ - entity-command `ChangePlan` caching;
12
+ - complete Entity/Relation snapshot reads and revision-CAS commits;
13
+ - per-document serialization, document cache, and shutdown.
13
14
 
14
15
  A host supplies only environment facts: Mengine HTTP origin, optional request
15
16
  identity headers, `fetch`, and a stable agent peer id.
@@ -27,7 +28,8 @@ const snapshot = await medeo.handle({ op: 'snapshot', doc_id: docId });
27
28
  const run = await medeo.handle({
28
29
  op: 'run-edit-script',
29
30
  doc_id: docId,
30
- script: 'await edit.deleteBgm({})',
31
+ script: 'edit.updateClip({ clipEntityId: inputs.clip_id, payload: { volume: -6 } })',
32
+ inputs: { clip_id: existingClipEntityId },
31
33
  });
32
34
  if (!run.ok || run.op !== 'run-edit-script') throw new Error('edit script failed');
33
35
  const commit = await medeo.handle({
@@ -36,9 +38,70 @@ const commit = await medeo.handle({
36
38
  plan_id: run.plan_id,
37
39
  });
38
40
 
41
+ // Generation facts are recalled by the host and passed into the sandbox.
42
+ // This example is text-to-image: there is no invented input-media entity.
43
+ const entityRun = await medeo.handle({
44
+ op: 'run-edit-script',
45
+ doc_id: docId,
46
+ inputs: { output_asset_id: 'asset-from-host', track_entity_id: existingTrackEntityId },
47
+ script: `
48
+ if (typeof inputs.output_asset_id !== 'string' || typeof inputs.track_entity_id !== 'string') {
49
+ throw new Error('missing recalled generation facts');
50
+ }
51
+ const asset = entities.importAsset({ asset_id: inputs.output_asset_id });
52
+ const output = entities.create({
53
+ entity_kind: 'image',
54
+ payload: { extent: { kind: 'unbounded', start: 0 }, sampling: 'constant', coordinateSpace: 'ms' },
55
+ });
56
+ relations.link({ relation_kind: 'physical-asset', endpoint_0_entity_id: output, endpoint_1_entity_id: asset });
57
+ edit.insertClip({
58
+ trackEntityId: inputs.track_entity_id,
59
+ contentEntityId: output,
60
+ sourceRange: { start: 0, end: 1 },
61
+ duration: { mode: 'fixed', value: 5000 },
62
+ targetRange: { start: 0, end: 5000 },
63
+ });
64
+ `,
65
+ });
66
+
39
67
  await medeo.close();
40
68
  ```
41
69
 
42
70
  The sandbox has no network, storage, clock, or generation access. Materialize
43
- speech/media side effects in the host first and pass stable facts through
44
- `inputs`.
71
+ or recall speech/media side effects in the host first and pass stable facts
72
+ through `inputs`. There is no automatic Asset→Entity projection: the model
73
+ selects and imports assets, creates only known typed Entities, and links them
74
+ explicitly. Assets and media Entities are intentionally not one-to-one.
75
+
76
+ The production tool exposes native Clip/Marker editing plus visual placement,
77
+ voiceover, caption, and BGM helpers through its generated sandbox interface.
78
+ They preserve structural and anchor relations in the same plan. Timeline targets
79
+ are Entity IDs, never raw asset IDs or URLs. Asset import, media creation, factual `generated` relations, and Clip
80
+ insertion belong in the **same entity plan**. `generated` retains output at
81
+ endpoint 0 and input at endpoint 1, while lookup can use either endpoint.
82
+
83
+ Entities own their facts: SequenceMarker owns source/target ranges, duration,
84
+ and time remapping; Clip owns volume; Track owns role/visibility. Cross-entity
85
+ references are Relations. There is no separate orientation or timing profile.
86
+
87
+ The server validates and commits the graph and its read-only Loro projection in
88
+ one database transaction. `deleted_entity_ids` and `deleted_relation_ids` make
89
+ deletion explicit; dropped rows without deletion intent are rejected. After
90
+ cutover, raw Loro `/updates` cannot mutate the document. Legacy standalone
91
+ sandbox exports remain library compatibility APIs, not a production-tool mode.
92
+
93
+ Existing nonempty legacy documents require a separate version-CAS migration
94
+ whose projection preserves existing editing facts. `snapshot` identifies that
95
+ requirement and the media IDs needing factual metadata. Call
96
+ `{op:'migrate-legacy',doc_id,asset_facts}` explicitly, then take a fresh snapshot
97
+ before editing. The package reads the canonical document and current Loro
98
+ version itself; it does not accept a caller-supplied snapshot or version, use a
99
+ stale cached document after a failed pull, or combine migration with a new edit.
100
+ Repeating migration on an entity timeline is read-only. An uncertain submission
101
+ must be inspected and retried, never reported as committed.
102
+
103
+ The compatibility reader covers the existing four lanes: Image/Video, Voice,
104
+ Caption, and background Audio. Coordinates are whole milliseconds for this
105
+ reader. It preserves linear visual remapping (`{kind:'linear',rate:2}`), including
106
+ image display speed, and explicit anchor chains. Nonlinear remapping and multiple
107
+ visual overlay tracks are not existing editor features and remain unsupported.
@@ -0,0 +1,220 @@
1
+ //#region src/entity/entity-contract.d.ts
2
+ type JsonPrimitive = string | number | boolean | null;
3
+ type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
4
+ interface JsonObject {
5
+ [key: string]: JsonValue;
6
+ }
7
+ type KnownEntityKind = 'axvideo' | 'timeline' | 'track' | 'clip' | 'asset' | 'video' | 'audio' | 'voice' | 'image' | 'sequence-marker' | 'viewport' | 'audio-script' | 'phonetic-script' | 'caption';
8
+ type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'physical-asset' | 'generated' | 'phonetic-script-provenance' | 'caption-provenance' | 'caption-alignment' | 'clip-anchor' | 'audio-script-render';
9
+ type AuthorableRelationKind = Exclude<KnownRelationKind, 'generated'>;
10
+ interface BoundedNativeSequencePayload extends JsonObject {
11
+ /** Factual coordinates from recalled media metadata; never invent an end/duration. */
12
+ extent: {
13
+ kind: 'bounded';
14
+ start: number;
15
+ end: number;
16
+ };
17
+ sampling: 'native';
18
+ coordinateSpace: JsonValue;
19
+ }
20
+ interface UnboundedConstantSequencePayload extends JsonObject {
21
+ extent: {
22
+ kind: 'unbounded';
23
+ start: number;
24
+ };
25
+ sampling: 'constant';
26
+ coordinateSpace: JsonValue;
27
+ }
28
+ interface BoundedDerivedSequencePayload extends JsonObject {
29
+ extent: {
30
+ kind: 'bounded';
31
+ start: number;
32
+ end: number;
33
+ };
34
+ sampling: 'derived';
35
+ coordinateSpace: JsonValue;
36
+ }
37
+ type ScriptTextSegment = JsonObject & {
38
+ segmentId: string;
39
+ text: string;
40
+ language?: string;
41
+ };
42
+ interface EntityPayloadByKind {
43
+ axvideo: BoundedDerivedSequencePayload;
44
+ timeline: JsonObject;
45
+ track: JsonObject & {
46
+ hidden?: boolean;
47
+ role?: string;
48
+ };
49
+ clip: JsonObject;
50
+ /** Asset-owned metadata. Peer media associations belong in physical-asset Relations. */
51
+ asset: JsonObject;
52
+ video: BoundedNativeSequencePayload;
53
+ audio: BoundedNativeSequencePayload;
54
+ voice: BoundedNativeSequencePayload;
55
+ image: UnboundedConstantSequencePayload;
56
+ 'sequence-marker': JsonObject & {
57
+ sourceRange: {
58
+ start: number;
59
+ end: number;
60
+ };
61
+ targetRange?: {
62
+ start: number;
63
+ end: number;
64
+ };
65
+ duration: {
66
+ mode: 'from-source';
67
+ } | {
68
+ mode: 'fixed';
69
+ value: number;
70
+ };
71
+ timeRemapping?: JsonValue;
72
+ anchorOffset?: number;
73
+ durationPolicy?: 'timeline';
74
+ };
75
+ viewport: JsonObject;
76
+ 'audio-script': JsonObject & {
77
+ segments: ScriptTextSegment[];
78
+ };
79
+ 'phonetic-script': JsonObject & {
80
+ segments: ScriptTextSegment[];
81
+ };
82
+ caption: BoundedNativeSequencePayload;
83
+ }
84
+ interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {
85
+ entity_id: string;
86
+ entity_kind: K;
87
+ payload: EntityPayloadByKind[K];
88
+ }
89
+ interface SandboxRelation {
90
+ relation_id: string;
91
+ relation_kind: KnownRelationKind;
92
+ endpoint_0_entity_id: string;
93
+ endpoint_1_entity_id: string;
94
+ metadata: JsonObject;
95
+ trace: JsonObject;
96
+ }
97
+ interface EntityStoreSnapshot {
98
+ revision: number;
99
+ entities: SandboxEntity[];
100
+ relations: SandboxRelation[];
101
+ }
102
+ type CreateEntityInput = { [K in KnownEntityKind]: {
103
+ entity_id?: string;
104
+ entity_kind: K;
105
+ payload: EntityPayloadByKind[K];
106
+ } }[KnownEntityKind];
107
+ interface UpdateEntityInput {
108
+ entity_id: string;
109
+ payload: JsonObject;
110
+ }
111
+ interface DeleteEntityInput {
112
+ entity_id: string;
113
+ }
114
+ interface ImportAssetInput {
115
+ asset_id: string;
116
+ entity_id?: string;
117
+ payload?: JsonObject;
118
+ }
119
+ type EmptyRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline';
120
+ interface LinkRelationBase {
121
+ relation_id?: string;
122
+ endpoint_0_entity_id: string;
123
+ endpoint_1_entity_id: string;
124
+ trace?: JsonObject;
125
+ }
126
+ type LinkRelationInput = (LinkRelationBase & {
127
+ relation_kind: EmptyRelationKind;
128
+ metadata?: {
129
+ [key: string]: never;
130
+ };
131
+ }) | (LinkRelationBase & {
132
+ relation_kind: 'physical-asset';
133
+ metadata?: JsonObject;
134
+ }) | (LinkRelationBase & {
135
+ relation_kind: 'phonetic-script-provenance' | 'caption-provenance';
136
+ metadata: JsonObject & {
137
+ segmentAlignment: JsonValue;
138
+ };
139
+ }) | (LinkRelationBase & {
140
+ relation_kind: 'caption-alignment';
141
+ metadata: JsonObject & {
142
+ alignment: JsonValue;
143
+ };
144
+ });
145
+ interface LinkGeneratedRelationInput {
146
+ relation_id?: string;
147
+ output_entity_id: string;
148
+ input_entity_id: string;
149
+ trace?: JsonObject;
150
+ }
151
+ interface LinkClipAnchorRelationInput {
152
+ relation_id?: string;
153
+ child_clip_entity_id: string;
154
+ host_clip_entity_id: string;
155
+ trace?: JsonObject;
156
+ }
157
+ interface LinkAudioScriptRenderRelationInput {
158
+ relation_id?: string;
159
+ output_entity_id: string;
160
+ script_entity_id: string;
161
+ trace?: JsonObject;
162
+ }
163
+ interface UnlinkRelationInput {
164
+ relation_id: string;
165
+ }
166
+ type EntityCommand = {
167
+ kind: 'create-entity';
168
+ entity: SandboxEntity;
169
+ } | {
170
+ kind: 'update-entity';
171
+ entity_id: string;
172
+ payload: JsonObject;
173
+ } | {
174
+ kind: 'delete-entity';
175
+ entity_id: string;
176
+ } | {
177
+ kind: 'link-relation';
178
+ relation: SandboxRelation;
179
+ } | {
180
+ kind: 'unlink-relation';
181
+ relation_id: string;
182
+ };
183
+ interface EntityPlanState {
184
+ base_revision: number;
185
+ commands: readonly EntityCommand[];
186
+ rows: EntityStoreSnapshot;
187
+ deleted_entity_ids: readonly string[];
188
+ deleted_relation_ids: readonly string[];
189
+ }
190
+ interface EntityFacade {
191
+ list(): SandboxEntity[];
192
+ get(entityId: string): SandboxEntity | null;
193
+ /** Return every explicitly imported Asset entity for a Memota asset id. */
194
+ findByAssetId(assetId: string): SandboxEntity<'asset'>[];
195
+ create(input: CreateEntityInput): string;
196
+ /** Replace one Entity's owned payload without changing its identity or kind. */
197
+ update(input: UpdateEntityInput): void;
198
+ /** Delete an Entity only after all of its incident Relations have been explicitly unlinked. */
199
+ delete(input: DeleteEntityInput): void;
200
+ /** Import one physical asset without implying a one-to-one media Entity mapping. */
201
+ importAsset(input: ImportAssetInput): string;
202
+ }
203
+ interface RelationFacade {
204
+ list(): SandboxRelation[];
205
+ /** Incident lookup is endpoint-agnostic; persisted endpoint positions stay unchanged. */
206
+ of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];
207
+ /** For physical-asset use sequence media as endpoint 0 and Asset as endpoint 1. */
208
+ link(input: LinkRelationInput): string;
209
+ /** Author ordered generated(output,input); generic link() deliberately rejects this kind. */
210
+ linkGenerated(input: LinkGeneratedRelationInput): string;
211
+ /** Author ordered clip-anchor(child,host) without positional endpoint ambiguity. */
212
+ linkClipAnchor(input: LinkClipAnchorRelationInput): string;
213
+ /** Author ordered audio-script-render(output,script) without positional endpoint ambiguity. */
214
+ linkAudioScriptRender(input: LinkAudioScriptRenderRelationInput): string;
215
+ /** Remove a Relation by identity; endpoint replacement is an explicit unlink plus link. */
216
+ unlink(input: UnlinkRelationInput): void;
217
+ }
218
+ //#endregion
219
+ export { SandboxEntity as _, EntityFacade as a, UpdateEntityInput as b, ImportAssetInput as c, JsonValue as d, KnownEntityKind as f, RelationFacade as g, LinkRelationInput as h, EntityCommand as i, JsonObject as l, LinkGeneratedRelationInput as m, CreateEntityInput as n, EntityPlanState as o, KnownRelationKind as p, DeleteEntityInput as r, EntityStoreSnapshot as s, AuthorableRelationKind as t, JsonPrimitive as u, SandboxRelation as v, UnlinkRelationInput as y };
220
+ //# sourceMappingURL=entity-contract-DycLxdQ5.d.mts.map