@mengine/medeo-client 1.0.1-alpha.1 → 1.0.1

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.
@@ -1,4 +1,3 @@
1
- import { E as MoveVideoClipsInput, H as AdjustVideoClipVolumeInput, I as DeleteBgmInput, J as AdjustBgmVolumeInput, K as AdjustSpeechVolumeInput, O as MoveSpeechesInput, P as DeleteSpeechesInput, Q as AddSpeechesInput, R as ChangeSpeechScriptInput, S as SetBgmInput, W as AdjustVideoClipDurationInput, X as AddVideoClipsInput, b as SetCaptionStyleInput, g as SetVideoClipSpeedShiftInput, j as DeleteVideoClipsInput, v as SetCaptionVisibilityInput, w as ReplaceVideoClipContentInput, z as ChangeSpeechVoiceInput } from "./index-6e5cbdM3.js";
2
1
  import { InferInputType } from "loro-mirror";
3
2
  import { z } from "zod";
4
3
  import { LoroDoc, PeerID } from "loro-crdt";
@@ -553,19 +552,6 @@ interface Track {
553
552
  is_hidden: boolean | undefined;
554
553
  items: TrackItem[] | undefined;
555
554
  }
556
- /** The linear speed multiplier of a `speed_shift`, defaulting to 1 (original). */
557
- declare function speedOf(speedShift: SpeedShift | undefined): number;
558
- /**
559
- * A video clip's effective timeline duration, derived from authoritative facts
560
- * (RFC 02, `reference/16` §4, reference/17 §5): the trim window
561
- * `play_out - play_in` divided by the speed multiplier, rounded to integer ms.
562
- * `play_in` / `play_out` are optional in the IDL but every write path sets them
563
- * (defaulting to the whole media), and the legacy-ingest projection backfills the
564
- * window from a legacy `duration_ms` — so an authoritative clip always carries a
565
- * trim window and there is no stored `duration_ms` to fall back to. A clip with
566
- * neither bound yields 0. Speeds the clip up (>1× → shorter) or down (<1×).
567
- */
568
- declare function effectiveVideoClipDurationMs(clip: VideoClipPart): number;
569
555
  /**
570
556
  * Authoritative part union: a flat discriminated union over the four part kinds.
571
557
  * Deliberately *not* the IDL's namespace union (no `$unknown` / `visit`): engine
@@ -682,7 +668,7 @@ interface VideoDocument {
682
668
  tracks: Track[] | undefined;
683
669
  part_library: Record<string, PartUnion> | undefined;
684
670
  }
685
- type VideoDocumentValidationIssueCode = 'invalid_schema' | 'duplicate_track_item_identity' | 'unknown_part_kind' | 'part_kind_mismatch' | 'missing_part_reference' | 'track_kind_mismatch' | 'main_track_non_video_clip' | 'invalid_part_value' | 'invalid_speech_caption_reference' | 'invalid_position_anchor';
671
+ type VideoDocumentValidationIssueCode = 'invalid_schema' | 'duplicate_track_item_identity' | 'duplicate_lane_track' | 'unknown_part_kind' | 'part_kind_mismatch' | 'missing_part_reference' | 'track_kind_mismatch' | 'main_track_non_video_clip' | 'invalid_part_value' | 'invalid_speech_caption_reference' | 'invalid_position_anchor';
686
672
  /**
687
673
  * Issue severity (RFC 02 §11.1). `error` is a hard reject — the document is not
688
674
  * a legal `VideoDocument` and must not be projected. `recoverable` marks a
@@ -882,35 +868,611 @@ type TrackDraft = NonNullable<NonNullable<VideoDocumentDraft['tracks']>[number]>
882
868
  /** A single track item in a draft. */
883
869
  type TrackItemDraft = NonNullable<NonNullable<TrackDraft['items']>[number]>;
884
870
  //#endregion
885
- //#region src/editor/id-gen.d.ts
871
+ //#region src/editor/schemas/add-speeches.d.ts
872
+ /**
873
+ * Add speeches (and their captions). TTS runs upstream; the stable speech /
874
+ * caption parts arrive materialized (see `speech-assets.ts`). The op writes the
875
+ * parts and each speech's `{ mode:'anchored', anchorPartId, offsetMs }` fact
876
+ * verbatim — no write-time host-picking, no cascade (RFC 02 §4). The projection
877
+ * derives absolute positions on read.
878
+ */
879
+ declare const addSpeechesInputSchema: z.ZodObject<{
880
+ speeches: z.ZodArray<z.ZodObject<{
881
+ speech_id: z.ZodString;
882
+ anchor_part_id: z.ZodString;
883
+ offset_ms: z.ZodNumber;
884
+ audio_storage_key: z.ZodString;
885
+ duration_ms: z.ZodNumber;
886
+ audio_script: z.ZodString;
887
+ volume: z.ZodNumber;
888
+ voice: z.ZodObject<{
889
+ id: z.ZodString;
890
+ name: z.ZodString;
891
+ }, z.core.$strip>;
892
+ origin_speech_id: z.ZodString;
893
+ caption_ids: z.ZodArray<z.ZodString>;
894
+ }, z.core.$strip>>;
895
+ captions: z.ZodArray<z.ZodObject<{
896
+ caption_id: z.ZodString;
897
+ speech_part_id: z.ZodString;
898
+ text: z.ZodString;
899
+ start_ms: z.ZodNumber;
900
+ duration_ms: z.ZodNumber;
901
+ }, z.core.$strip>>;
902
+ }, z.core.$strip>;
903
+ type AddSpeechesInput = z.infer<typeof addSpeechesInputSchema>;
904
+ //#endregion
905
+ //#region src/editor/schemas/add-video-clips.d.ts
906
+ /**
907
+ * Add video clips to a track. Each clip's duration facts are separated so a
908
+ * single number is never overloaded (RFC 02 / `reference/16` §0b):
909
+ *
910
+ * - `media_duration_ms` is the source media's intrinsic full length (a resource
911
+ * fact, written to the part);
912
+ * - `play_in` / `play_out` are the optional trim window into that media; when
913
+ * omitted the whole media is used (`play_in=0`, `play_out=media_duration_ms`).
914
+ *
915
+ * The clip's effective timeline duration is derived by the projection from the
916
+ * trim window and `speed_shift` — it is never an input here.
917
+ */
918
+ declare const addVideoClipsInputSchema: z.ZodObject<{
919
+ clips: z.ZodArray<z.ZodObject<{
920
+ media_id: z.ZodString;
921
+ start_ms: z.ZodOptional<z.ZodNumber>;
922
+ media_duration_ms: z.ZodNumber;
923
+ play_in: z.ZodOptional<z.ZodNumber>;
924
+ play_out: z.ZodOptional<z.ZodNumber>;
925
+ track_id: z.ZodOptional<z.ZodString>;
926
+ }, z.core.$strip>>;
927
+ before_clip_id: z.ZodOptional<z.ZodString>;
928
+ after_clip_id: z.ZodOptional<z.ZodString>;
929
+ }, z.core.$strip>;
930
+ type AddVideoClipsInput = z.infer<typeof addVideoClipsInputSchema>;
931
+ //#endregion
932
+ //#region src/editor/schemas/adjust-bgm-volume.d.ts
933
+ declare const adjustBgmVolumeInputSchema: z.ZodObject<{
934
+ bgm: z.ZodArray<z.ZodObject<{
935
+ bgm_id: z.ZodString;
936
+ volume: z.ZodNumber;
937
+ }, z.core.$strip>>;
938
+ }, z.core.$strip>;
939
+ type AdjustBgmVolumeInput = z.infer<typeof adjustBgmVolumeInputSchema>;
940
+ //#endregion
941
+ //#region src/editor/schemas/adjust-speech-volume.d.ts
942
+ declare const adjustSpeechVolumeInputSchema: z.ZodObject<{
943
+ speeches: z.ZodArray<z.ZodObject<{
944
+ speech_id: z.ZodString;
945
+ volume: z.ZodNumber;
946
+ }, z.core.$strip>>;
947
+ }, z.core.$strip>;
948
+ type AdjustSpeechVolumeInput = z.infer<typeof adjustSpeechVolumeInputSchema>;
949
+ //#endregion
950
+ //#region src/editor/schemas/adjust-video-clip-duration.d.ts
951
+ /**
952
+ * Re-trim existing video clips (the user-facing "adjust duration" gesture is a
953
+ * trim of the source window). The new `play_in` / `play_out` are the facts; the
954
+ * effective timeline duration is derived from them and the clip's `speed_shift`,
955
+ * and the change reflows downstream clips, speeches, and the timeline inside the
956
+ * op's transaction (no caller-materialized cascade).
957
+ */
958
+ declare const adjustVideoClipDurationInputSchema: z.ZodObject<{
959
+ clips: z.ZodArray<z.ZodObject<{
960
+ clip_id: z.ZodString;
961
+ play_in: z.ZodNumber;
962
+ play_out: z.ZodNumber;
963
+ }, z.core.$strip>>;
964
+ }, z.core.$strip>;
965
+ type AdjustVideoClipDurationInput = z.infer<typeof adjustVideoClipDurationInputSchema>;
966
+ //#endregion
967
+ //#region src/editor/schemas/adjust-video-clip-volume.d.ts
968
+ declare const adjustVideoClipVolumeInputSchema: z.ZodObject<{
969
+ clips: z.ZodArray<z.ZodObject<{
970
+ clip_id: z.ZodString;
971
+ volume: z.ZodNumber;
972
+ }, z.core.$strip>>;
973
+ }, z.core.$strip>;
974
+ type AdjustVideoClipVolumeInput = z.infer<typeof adjustVideoClipVolumeInputSchema>;
975
+ //#endregion
976
+ //#region src/editor/schemas/change-speech.d.ts
977
+ /**
978
+ * Change a speech's script or voice. Both re-run TTS upstream and return the
979
+ * regenerated speech / caption parts in the same materialized shape as
980
+ * `AddSpeeches` (`speech-assets.ts`); the op upserts them by id (the speech part
981
+ * id is preserved across a re-TTS), re-seats at `start_ms`, and reflows. Old
982
+ * caption parts no longer owned by the speech are removed via `caption_ids`.
983
+ */
984
+ declare const changeSpeechScriptInputSchema: z.ZodObject<{
985
+ speeches: z.ZodArray<z.ZodObject<{
986
+ speech_id: z.ZodString;
987
+ anchor_part_id: z.ZodString;
988
+ offset_ms: z.ZodNumber;
989
+ audio_storage_key: z.ZodString;
990
+ duration_ms: z.ZodNumber;
991
+ audio_script: z.ZodString;
992
+ volume: z.ZodNumber;
993
+ voice: z.ZodObject<{
994
+ id: z.ZodString;
995
+ name: z.ZodString;
996
+ }, z.core.$strip>;
997
+ origin_speech_id: z.ZodString;
998
+ caption_ids: z.ZodArray<z.ZodString>;
999
+ }, z.core.$strip>>;
1000
+ captions: z.ZodArray<z.ZodObject<{
1001
+ caption_id: z.ZodString;
1002
+ speech_part_id: z.ZodString;
1003
+ text: z.ZodString;
1004
+ start_ms: z.ZodNumber;
1005
+ duration_ms: z.ZodNumber;
1006
+ }, z.core.$strip>>;
1007
+ }, z.core.$strip>;
1008
+ declare const changeSpeechVoiceInputSchema: z.ZodObject<{
1009
+ speeches: z.ZodArray<z.ZodObject<{
1010
+ speech_id: z.ZodString;
1011
+ anchor_part_id: z.ZodString;
1012
+ offset_ms: z.ZodNumber;
1013
+ audio_storage_key: z.ZodString;
1014
+ duration_ms: z.ZodNumber;
1015
+ audio_script: z.ZodString;
1016
+ volume: z.ZodNumber;
1017
+ voice: z.ZodObject<{
1018
+ id: z.ZodString;
1019
+ name: z.ZodString;
1020
+ }, z.core.$strip>;
1021
+ origin_speech_id: z.ZodString;
1022
+ caption_ids: z.ZodArray<z.ZodString>;
1023
+ }, z.core.$strip>>;
1024
+ captions: z.ZodArray<z.ZodObject<{
1025
+ caption_id: z.ZodString;
1026
+ speech_part_id: z.ZodString;
1027
+ text: z.ZodString;
1028
+ start_ms: z.ZodNumber;
1029
+ duration_ms: z.ZodNumber;
1030
+ }, z.core.$strip>>;
1031
+ }, z.core.$strip>;
1032
+ type ChangeSpeechScriptInput = z.infer<typeof changeSpeechScriptInputSchema>;
1033
+ type ChangeSpeechVoiceInput = z.infer<typeof changeSpeechVoiceInputSchema>;
1034
+ //#endregion
1035
+ //#region src/editor/schemas/delete-bgm.d.ts
1036
+ /**
1037
+ * Remove the document BGM. Pure document edit: clears the bgm lane and removes
1038
+ * the bgm part. Takes no input (a document holds at most one bgm); an empty
1039
+ * object keeps the op signature uniform with the rest.
1040
+ */
1041
+ declare const deleteBgmInputSchema: z.ZodObject<{}, z.core.$strip>;
1042
+ type DeleteBgmInput = z.infer<typeof deleteBgmInputSchema>;
1043
+ //#endregion
1044
+ //#region src/editor/schemas/delete-speeches.d.ts
1045
+ /**
1046
+ * Delete speeches with their captions. Pure document edit (no side effect): the
1047
+ * op removes each speech part, cascade-deletes the captions it owns (via
1048
+ * `caption_ids` / `speech_part_id`), drops their track items, and reflows.
1049
+ */
1050
+ declare const deleteSpeechesInputSchema: z.ZodObject<{
1051
+ speech_ids: z.ZodArray<z.ZodString>;
1052
+ }, z.core.$strip>;
1053
+ type DeleteSpeechesInput = z.infer<typeof deleteSpeechesInputSchema>;
1054
+ //#endregion
1055
+ //#region src/editor/schemas/delete-video-clips.d.ts
1056
+ /**
1057
+ * How a delete handles the anchored subtree (speeches anchored to a deleted clip,
1058
+ * and their captions) — a delete-op policy, not a data-model field (reference/17
1059
+ * §6). `cascade` (default) removes the subtree; `detach` keeps the direct
1060
+ * anchored children, re-pinning them to `absolute` so they stay on the timeline.
1061
+ */
1062
+ declare const anchoredDeletePolicySchema: z.ZodEnum<{
1063
+ cascade: "cascade";
1064
+ detach: "detach";
1065
+ }>;
1066
+ type AnchoredDeletePolicy = z.infer<typeof anchoredDeletePolicySchema>;
1067
+ declare const deleteVideoClipsInputSchema: z.ZodObject<{
1068
+ clip_ids: z.ZodArray<z.ZodString>;
1069
+ on_anchored: z.ZodOptional<z.ZodEnum<{
1070
+ cascade: "cascade";
1071
+ detach: "detach";
1072
+ }>>;
1073
+ }, z.core.$strip>;
1074
+ type DeleteVideoClipsInput = z.infer<typeof deleteVideoClipsInputSchema>;
1075
+ //#endregion
1076
+ //#region src/editor/schemas/move-speeches.d.ts
1077
+ /**
1078
+ * Move speeches in time. Pure document edit: the op re-seats each speech at its
1079
+ * new absolute `start_ms`; the cascade reassigns it to the host video clip,
1080
+ * resolves overlaps, and reflows. Captions follow their speech.
1081
+ */
1082
+ declare const moveSpeechesInputSchema: z.ZodObject<{
1083
+ speeches: z.ZodArray<z.ZodObject<{
1084
+ speech_id: z.ZodString;
1085
+ new_start_ms: z.ZodNumber;
1086
+ }, z.core.$strip>>;
1087
+ }, z.core.$strip>;
1088
+ type MoveSpeechesInput = z.infer<typeof moveSpeechesInputSchema>;
1089
+ //#endregion
1090
+ //#region src/editor/schemas/move-video-clips-by-anchor.d.ts
886
1091
  /**
887
- * Part-id generation, aligned with the online ecosystem.
1092
+ * Where the moved block lands on the main track.
1093
+ *
1094
+ * A discriminated union rather than two optional `before_clip_id` /
1095
+ * `after_clip_id` fields (the shape `addVideoClips` had to use, because there the
1096
+ * two modes share a whole clip description): here the alternatives carry nothing
1097
+ * in common, so making them mutually exclusive *by type* removes three runtime
1098
+ * `superRefine` checks that would otherwise have to be written and tested.
888
1099
  *
889
- * The authoritative online producers agent-harness (`@harness/shared`
890
- * `genObjId`) and director.v2 (`common/obj_id.py` `gen_obj_id`) both mint part
891
- * ids as `` `${prefix}_${ulid()}` ``, and real captured drafts use exactly that
892
- * shape (`clip_…` / `spe_…` / `cap_…` / `bgm_…`, each a 26-char ULID). The engine
893
- * previously emitted `vc_<base36 timestamp><6 random>`, a different prefix AND a
894
- * different encoding — the sole cross-repo id divergence. This module removes it
895
- * by emitting the same `<prefix>_<ULID>` bytes.
1100
+ * `track_start` is an explicit member, not the absence of an anchor. The
1101
+ * agent-harness mutation this maps from treats "neither anchor given" as
1102
+ * "move to the front" (`applyBatchMoveVideoClips` falls back to `insertIndex = 0`),
1103
+ * which is a default buried in a tool description. Requiring the caller to name
1104
+ * that intent keeps a forgotten field from silently reordering the timeline.
1105
+ */
1106
+ declare const moveAnchorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1107
+ position: z.ZodLiteral<"before">;
1108
+ clip_id: z.ZodString;
1109
+ }, z.core.$strip>, z.ZodObject<{
1110
+ position: z.ZodLiteral<"after">;
1111
+ clip_id: z.ZodString;
1112
+ }, z.core.$strip>, z.ZodObject<{
1113
+ position: z.ZodLiteral<"track_start">;
1114
+ }, z.core.$strip>], "position">;
1115
+ /**
1116
+ * What happens to the speeches anchored to the clips being moved.
896
1117
  *
897
- * The ULID is generated inline (Crockford Base32, 48-bit time + 80-bit random)
898
- * rather than pulling the `ulid` npm package: the randomness class matches the
899
- * old generator (both `Math.random`-based) and it keeps `@mengine/medeo-client`
900
- * dependency-free for a purely mechanical id string. Part ids only need to be
901
- * unique and lexicographically time-sortable, which this satisfies.
902
- */
903
- /**
904
- * Online part-id semantic prefixes. `clip` (video clip) is the only value the
905
- * engine currently mints (see `addVideoClips`); the rest are declared so the
906
- * type documents the shared vocabulary and guards against reintroducing the old
907
- * `vc`/`sp`/`cp`/`bg` names. Speech/caption/bgm ids arrive pre-minted in op
908
- * payloads, so the engine never generates them itself.
909
- */
910
- type PartIdPrefix = 'clip' | 'spe' | 'cap' | 'bgm' | 'ti';
911
- declare function generatePartId(prefix: PartIdPrefix): string;
912
- /** Injectable part-id mint; defaults to `generatePartId` on `SemanticEditor`. */
913
- type PartIdFactory = (prefix: PartIdPrefix) => string;
1118
+ * - `follow` keeps each speech anchored where it is, so it travels with its clip
1119
+ * to the new position. In the anchored model this is the *no-op* branch: a
1120
+ * speech's authoritative fact is `{ anchorPartId, offsetMs }` and its absolute
1121
+ * time is derived on read from the host's position, so moving the host moves
1122
+ * the speech with no write to the speech at all.
1123
+ * - `keep_absolute` preserves each speech's current absolute landing instead, then
1124
+ * re-anchors it to whichever clip now covers that time (RFC 02 §9.1/§11.1). This
1125
+ * is the branch that costs an extra pass, and the one the FE timeline uses.
1126
+ *
1127
+ * **Required, with no default**, matching `deleteVideoClips` and
1128
+ * `replaceVideoClipSequence`. The two branches decide which picture the user's
1129
+ * narration ends up over, which is too consequential to infer from a missing
1130
+ * field — and neither branch is "safe enough" to be the implicit one.
1131
+ */
1132
+ declare const movedClipAnchoredPolicySchema: z.ZodEnum<{
1133
+ follow: "follow";
1134
+ keep_absolute: "keep_absolute";
1135
+ }>;
1136
+ /**
1137
+ * Reorder a set of main-track clips relative to a reference clip.
1138
+ *
1139
+ * Distinct from `moveVideoClips`, which positions clips by absolute time
1140
+ * (`new_start_ms`) and is what the FE timeline dispatches after a drag. Main-track
1141
+ * clips are `sequential`-positioned, so absolute time is not authoritative state
1142
+ * there (RFC 02 §4/§7): `moveVideoClips` has to *guess* an index back out of the
1143
+ * time it was handed, whereas an anchor already is the ordinal fact being changed.
1144
+ * Keeping them separate also isolates blast radius — this method can diverge on
1145
+ * speech policy without touching the FE path.
1146
+ *
1147
+ * `clip_ids` need not be contiguous. They move as one block, keeping their
1148
+ * relative order, which is the agent-harness `batch_move_video_clips` contract.
1149
+ */
1150
+ declare const moveVideoClipsByAnchorInputSchema: z.ZodObject<{
1151
+ clip_ids: z.ZodArray<z.ZodString>;
1152
+ anchor: z.ZodDiscriminatedUnion<[z.ZodObject<{
1153
+ position: z.ZodLiteral<"before">;
1154
+ clip_id: z.ZodString;
1155
+ }, z.core.$strip>, z.ZodObject<{
1156
+ position: z.ZodLiteral<"after">;
1157
+ clip_id: z.ZodString;
1158
+ }, z.core.$strip>, z.ZodObject<{
1159
+ position: z.ZodLiteral<"track_start">;
1160
+ }, z.core.$strip>], "position">;
1161
+ on_anchored: z.ZodEnum<{
1162
+ follow: "follow";
1163
+ keep_absolute: "keep_absolute";
1164
+ }>;
1165
+ }, z.core.$strip>;
1166
+ type MoveAnchor = z.infer<typeof moveAnchorSchema>;
1167
+ type MovedClipAnchoredPolicy = z.infer<typeof movedClipAnchoredPolicySchema>;
1168
+ type MoveVideoClipsByAnchorInput = z.infer<typeof moveVideoClipsByAnchorInputSchema>;
1169
+ //#endregion
1170
+ //#region src/editor/schemas/move-video-clips.d.ts
1171
+ declare const moveVideoClipsInputSchema: z.ZodObject<{
1172
+ clips: z.ZodArray<z.ZodObject<{
1173
+ clip_id: z.ZodString;
1174
+ new_start_ms: z.ZodNumber;
1175
+ new_track_id: z.ZodOptional<z.ZodString>;
1176
+ }, z.core.$strip>>;
1177
+ }, z.core.$strip>;
1178
+ type MoveVideoClipsInput = z.infer<typeof moveVideoClipsInputSchema>;
1179
+ //#endregion
1180
+ //#region src/editor/schemas/replace-video-clip-content.d.ts
1181
+ /**
1182
+ * Replace the media backing existing video clips. The media import runs upstream
1183
+ * (Director); its stable result — the new media id, intrinsic length, and the
1184
+ * reset trim window — arrives materialized (see
1185
+ * `results/phase-4-side-effect-payload-contract.md` §4). Director resets
1186
+ * `play_in=0` / `play_out=media_duration_ms` and clears `speed_shift` on
1187
+ * replacement. The clip `part_id`s (hence their track items) are unchanged; the
1188
+ * editor reflows the main track from the new effective durations.
1189
+ */
1190
+ declare const replaceVideoClipContentInputSchema: z.ZodObject<{
1191
+ clips: z.ZodArray<z.ZodObject<{
1192
+ clip_id: z.ZodString;
1193
+ origin_media_id: z.ZodString;
1194
+ media_duration_ms: z.ZodNumber;
1195
+ play_in: z.ZodNumber;
1196
+ play_out: z.ZodNumber;
1197
+ volume: z.ZodNumber;
1198
+ }, z.core.$strip>>;
1199
+ }, z.core.$strip>;
1200
+ type ReplaceVideoClipContentInput = z.infer<typeof replaceVideoClipContentInputSchema>;
1201
+ //#endregion
1202
+ //#region src/editor/schemas/replace-video-clip-sequence.d.ts
1203
+ /**
1204
+ * What happens to the speeches anchored to the clips being replaced.
1205
+ *
1206
+ * - `remap` re-anchors each surviving speech to the new clip in the SAME POSITION
1207
+ * of the sequence, keeping its offset — old[i]'s children become new[i]'s
1208
+ * children. An old clip with no counterpart (fewer new clips than old) has its
1209
+ * subtree deleted, because there is nothing left to anchor to.
1210
+ * - `cascade` deletes every anchored speech (and its captions) outright, like
1211
+ * `deleteVideoClips`.
1212
+ *
1213
+ * **Required, with no default.** The two branches differ in whether the user's
1214
+ * narration survives, and the agent tool that drives this op makes its
1215
+ * `preserve_speeches` flag required for that reason. A default here would let a
1216
+ * caller that forgot the field silently delete speech.
1217
+ */
1218
+ declare const anchoredReplacePolicySchema: z.ZodEnum<{
1219
+ cascade: "cascade";
1220
+ remap: "remap";
1221
+ }>;
1222
+ type AnchoredReplacePolicy = z.infer<typeof anchoredReplacePolicySchema>;
1223
+ /**
1224
+ * Replace a contiguous run of main-track clips with a new run.
1225
+ *
1226
+ * A composite of delete + insert that cannot be expressed as the two ops in
1227
+ * sequence, because the anchored speeches have to survive *across* the swap: with
1228
+ * `remap` they are re-anchored positionally, which needs both the old and the new
1229
+ * ids in the same transaction (ADR 0009 — the cascade stays in the editor, callers
1230
+ * never re-wire anchors themselves).
1231
+ *
1232
+ * Duration facts follow `addVideoClips`: `media_duration_ms` is the source's
1233
+ * intrinsic length and the trim window defaults to the whole media. The effective
1234
+ * timeline duration is derived by the projection, never an input.
1235
+ *
1236
+ * `media_id` is optional: omitting it creates a **deliberate empty placeholder
1237
+ * clip** (`origin_media_id: ''`) — structure with no picture. This is the only op
1238
+ * that can produce one, and it is authoritative state, unlike the gap fillers the
1239
+ * read-side solve mints (which never enter the document).
1240
+ */
1241
+ declare const replaceVideoClipSequenceInputSchema: z.ZodObject<{
1242
+ old_clip_ids: z.ZodArray<z.ZodString>;
1243
+ new_clips: z.ZodArray<z.ZodObject<{
1244
+ media_id: z.ZodOptional<z.ZodString>;
1245
+ media_duration_ms: z.ZodNumber;
1246
+ play_in: z.ZodOptional<z.ZodNumber>;
1247
+ play_out: z.ZodOptional<z.ZodNumber>;
1248
+ }, z.core.$strip>>;
1249
+ on_anchored: z.ZodEnum<{
1250
+ cascade: "cascade";
1251
+ remap: "remap";
1252
+ }>;
1253
+ }, z.core.$strip>;
1254
+ type ReplaceVideoClipSequenceInput = z.infer<typeof replaceVideoClipSequenceInputSchema>;
1255
+ //#endregion
1256
+ //#region src/editor/schemas/set-bgm.d.ts
1257
+ /**
1258
+ * Set the document BGM. The media's stable result (storage key) arrives
1259
+ * materialized from upstream (see
1260
+ * `results/phase-4-side-effect-payload-contract.md` §3). The op upserts the bgm
1261
+ * part and seats it on the bgm lane; its effective length is always the whole
1262
+ * timeline, derived by the projection on read — so there is no `duration_ms`
1263
+ * input or fact (RFC 02 / `reference/16` §0b). A `bgm_id` lets the op replace an
1264
+ * existing bgm part by id.
1265
+ */
1266
+ declare const setBgmInputSchema: z.ZodObject<{
1267
+ bgm_id: z.ZodString;
1268
+ audio_storage_key: z.ZodString;
1269
+ origin_media_id: z.ZodString;
1270
+ volume: z.ZodNumber;
1271
+ }, z.core.$strip>;
1272
+ type SetBgmInput = z.infer<typeof setBgmInputSchema>;
1273
+ //#endregion
1274
+ //#region src/editor/schemas/set-caption-style.d.ts
1275
+ /**
1276
+ * Set the caption visual style. GLOBAL by design: the style applies to every
1277
+ * caption part in the document — it carries NO `caption_id`. This mirrors the FE,
1278
+ * whose caption-style store (`caption-style.ts:persistCaptionStylePatch`) iterates
1279
+ * ALL captions and writes the same normalized style to each; the product has a
1280
+ * single document-wide caption style, not per-caption styling.
1281
+ *
1282
+ * Every field is optional and maps to a `CaptionStyle` attribute (snake_case
1283
+ * IDL). A field present in the input is written to every caption; a field ABSENT
1284
+ * from the input is left untouched on each caption (the editor merges the patch
1285
+ * onto each caption's existing style — this is a value edit, not a full-style
1286
+ * replace, so a partial patch such as "recolor only" does not wipe font size).
1287
+ *
1288
+ * Pure document edit, no cascade — captions keep their positions; only the style
1289
+ * sub-map of each caption part changes.
1290
+ */
1291
+ declare const setCaptionStyleInputSchema: z.ZodObject<{
1292
+ font_id: z.ZodOptional<z.ZodString>;
1293
+ font_size: z.ZodOptional<z.ZodNumber>;
1294
+ font_color: z.ZodOptional<z.ZodString>;
1295
+ font_weight: z.ZodOptional<z.ZodNumber>;
1296
+ entrance_animation: z.ZodOptional<z.ZodString>;
1297
+ entrance_animation_duration_ms: z.ZodOptional<z.ZodNumber>;
1298
+ stroke_color: z.ZodOptional<z.ZodString>;
1299
+ stroke_width: z.ZodOptional<z.ZodNumber>;
1300
+ position_x: z.ZodOptional<z.ZodNumber>;
1301
+ position_y: z.ZodOptional<z.ZodNumber>;
1302
+ }, z.core.$strip>;
1303
+ type SetCaptionStyleInput = z.infer<typeof setCaptionStyleInputSchema>;
1304
+ //#endregion
1305
+ //#region src/editor/schemas/set-caption-visibility.d.ts
1306
+ /**
1307
+ * Toggle caption visibility (the caption track's `is_hidden` flag). Pure
1308
+ * document edit, no cascade — captions keep their positions; only the lane's
1309
+ * hidden flag changes.
1310
+ */
1311
+ declare const setCaptionVisibilityInputSchema: z.ZodObject<{
1312
+ is_hidden: z.ZodBoolean;
1313
+ }, z.core.$strip>;
1314
+ type SetCaptionVisibilityInput = z.infer<typeof setCaptionVisibilityInputSchema>;
1315
+ //#endregion
1316
+ //#region src/editor/schemas/set-video-clip-speed-shift.d.ts
1317
+ /**
1318
+ * Set the playback speed of existing video clips. Per the speed-shift decision
1319
+ * (`reference/16` §0): the op writes only the `speed_shift` fact — it does NOT
1320
+ * store an effective `duration_ms` (projection derives it from the trim window /
1321
+ * speed) and does NOT scale anchored speeches' relative offsets (offsets stay
1322
+ * put; the cascade reflows absolute positions). A `null` speed_shift clears the
1323
+ * speed back to original (1×).
1324
+ */
1325
+ declare const setVideoClipSpeedShiftInputSchema: z.ZodObject<{
1326
+ clips: z.ZodArray<z.ZodObject<{
1327
+ clip_id: z.ZodString;
1328
+ speed_shift: z.ZodNullable<z.ZodObject<{
1329
+ category: z.ZodEnum<{
1330
+ curve: "curve";
1331
+ linear: "linear";
1332
+ }>;
1333
+ mode: z.ZodString;
1334
+ config: z.ZodUnion<readonly [z.ZodObject<{
1335
+ linear: z.ZodObject<{
1336
+ speed: z.ZodNumber;
1337
+ }, z.core.$strip>;
1338
+ }, z.core.$strip>, z.ZodObject<{
1339
+ curve: z.ZodObject<{
1340
+ keyframes: z.ZodArray<z.ZodObject<{
1341
+ position: z.ZodNumber;
1342
+ rate: z.ZodNumber;
1343
+ in_tangent: z.ZodOptional<z.ZodObject<{
1344
+ x: z.ZodNumber;
1345
+ y: z.ZodNumber;
1346
+ }, z.core.$strip>>;
1347
+ out_tangent: z.ZodOptional<z.ZodObject<{
1348
+ x: z.ZodNumber;
1349
+ y: z.ZodNumber;
1350
+ }, z.core.$strip>>;
1351
+ }, z.core.$strip>>;
1352
+ }, z.core.$strip>;
1353
+ }, z.core.$strip>]>;
1354
+ }, z.core.$strip>>;
1355
+ }, z.core.$strip>>;
1356
+ }, z.core.$strip>;
1357
+ type SetVideoClipSpeedShiftInput = z.infer<typeof setVideoClipSpeedShiftInputSchema>;
1358
+ //#endregion
1359
+ //#region src/editor/schemas/shared.d.ts
1360
+ declare const clipIdSchema: z.ZodString;
1361
+ declare const clipIdsSchema: z.ZodArray<z.ZodString>;
1362
+ declare const mediaIdSchema: z.ZodString;
1363
+ declare const speechIdSchema: z.ZodString;
1364
+ declare const timelineMsSchema: z.ZodNumber;
1365
+ declare const positiveMsSchema: z.ZodNumber;
1366
+ declare const volumeSchema: z.ZodNumber;
1367
+ declare const speechIdsSchema: z.ZodArray<z.ZodString>;
1368
+ /**
1369
+ * A clip's playback-speed fact, the only thing `SetVideoClipSpeedShift` writes.
1370
+ * Mirrors the IDL `SpeedShift`: `category` is `linear` | `curve`, and `config`
1371
+ * is a discriminated union — `{ linear: { speed } }` for a constant multiplier
1372
+ * (the multiplier projection reads at `config.linear.speed`) or `{ curve: {
1373
+ * keyframes } }` for a Bezier-controlled variable speed (RFC 02 / `reference/16`
1374
+ * §0). Exactly one of `linear` / `curve` is present.
1375
+ */
1376
+ declare const speedShiftSchema: z.ZodObject<{
1377
+ category: z.ZodEnum<{
1378
+ curve: "curve";
1379
+ linear: "linear";
1380
+ }>;
1381
+ mode: z.ZodString;
1382
+ config: z.ZodUnion<readonly [z.ZodObject<{
1383
+ linear: z.ZodObject<{
1384
+ speed: z.ZodNumber;
1385
+ }, z.core.$strip>;
1386
+ }, z.core.$strip>, z.ZodObject<{
1387
+ curve: z.ZodObject<{
1388
+ keyframes: z.ZodArray<z.ZodObject<{
1389
+ position: z.ZodNumber;
1390
+ rate: z.ZodNumber;
1391
+ in_tangent: z.ZodOptional<z.ZodObject<{
1392
+ x: z.ZodNumber;
1393
+ y: z.ZodNumber;
1394
+ }, z.core.$strip>>;
1395
+ out_tangent: z.ZodOptional<z.ZodObject<{
1396
+ x: z.ZodNumber;
1397
+ y: z.ZodNumber;
1398
+ }, z.core.$strip>>;
1399
+ }, z.core.$strip>>;
1400
+ }, z.core.$strip>;
1401
+ }, z.core.$strip>]>;
1402
+ }, z.core.$strip>;
1403
+ declare const voiceSchema: z.ZodObject<{
1404
+ id: z.ZodString;
1405
+ name: z.ZodString;
1406
+ }, z.core.$strip>;
1407
+ //#endregion
1408
+ //#region src/editor/schemas/speech-assets.d.ts
1409
+ /**
1410
+ * The materialized TTS result shared by `AddSpeeches` / `ChangeSpeechScript` /
1411
+ * `ChangeSpeechVoice` (see `results/phase-4-side-effect-payload-contract.md`
1412
+ * §1/§2). The side effect (TTS/ASR + billing) runs upstream; the op receives the
1413
+ * stable speech + caption parts and writes them as authoritative facts. No
1414
+ * cascade runs on write — the projection derives absolute positions on read.
1415
+ *
1416
+ * Each speech carries the anchoring fact directly (RFC 02 §4): the host video
1417
+ * clip `anchor_part_id` and the `offset_ms` within it. The upstream caller
1418
+ * already knows which clip a speech attaches to, so the op writes
1419
+ * `{ mode:'anchored', anchorPartId, offsetMs }` verbatim — no write-time
1420
+ * host-picking. Captions anchor to their speech via the caption part's
1421
+ * `start_ms` (offset within the speech).
1422
+ */
1423
+ declare const speechAssetSchema: z.ZodObject<{
1424
+ speech_id: z.ZodString;
1425
+ anchor_part_id: z.ZodString;
1426
+ offset_ms: z.ZodNumber;
1427
+ audio_storage_key: z.ZodString;
1428
+ duration_ms: z.ZodNumber;
1429
+ audio_script: z.ZodString;
1430
+ volume: z.ZodNumber;
1431
+ voice: z.ZodObject<{
1432
+ id: z.ZodString;
1433
+ name: z.ZodString;
1434
+ }, z.core.$strip>;
1435
+ origin_speech_id: z.ZodString;
1436
+ caption_ids: z.ZodArray<z.ZodString>;
1437
+ }, z.core.$strip>;
1438
+ declare const captionAssetSchema: z.ZodObject<{
1439
+ caption_id: z.ZodString;
1440
+ speech_part_id: z.ZodString;
1441
+ text: z.ZodString;
1442
+ start_ms: z.ZodNumber;
1443
+ duration_ms: z.ZodNumber;
1444
+ }, z.core.$strip>;
1445
+ /** A materialized speech-subtree write (speeches + their captions). */
1446
+ declare const speechAssetsSchema: z.ZodObject<{
1447
+ speeches: z.ZodArray<z.ZodObject<{
1448
+ speech_id: z.ZodString;
1449
+ anchor_part_id: z.ZodString;
1450
+ offset_ms: z.ZodNumber;
1451
+ audio_storage_key: z.ZodString;
1452
+ duration_ms: z.ZodNumber;
1453
+ audio_script: z.ZodString;
1454
+ volume: z.ZodNumber;
1455
+ voice: z.ZodObject<{
1456
+ id: z.ZodString;
1457
+ name: z.ZodString;
1458
+ }, z.core.$strip>;
1459
+ origin_speech_id: z.ZodString;
1460
+ caption_ids: z.ZodArray<z.ZodString>;
1461
+ }, z.core.$strip>>;
1462
+ captions: z.ZodArray<z.ZodObject<{
1463
+ caption_id: z.ZodString;
1464
+ speech_part_id: z.ZodString;
1465
+ text: z.ZodString;
1466
+ start_ms: z.ZodNumber;
1467
+ duration_ms: z.ZodNumber;
1468
+ }, z.core.$strip>>;
1469
+ }, z.core.$strip>;
1470
+ type SpeechAsset = z.infer<typeof speechAssetSchema>;
1471
+ type CaptionAsset = z.infer<typeof captionAssetSchema>;
1472
+ type SpeechAssets = z.infer<typeof speechAssetsSchema>;
1473
+ declare namespace index_d_exports {
1474
+ export { AddSpeechesInput, AddVideoClipsInput, AdjustBgmVolumeInput, AdjustSpeechVolumeInput, AdjustVideoClipDurationInput, AdjustVideoClipVolumeInput, AnchoredDeletePolicy, AnchoredReplacePolicy, CaptionAsset, ChangeSpeechScriptInput, ChangeSpeechVoiceInput, DeleteBgmInput, DeleteSpeechesInput, DeleteVideoClipsInput, MoveAnchor, MoveSpeechesInput, MoveVideoClipsByAnchorInput, MoveVideoClipsInput, MovedClipAnchoredPolicy, ReplaceVideoClipContentInput, ReplaceVideoClipSequenceInput, SetBgmInput, SetCaptionStyleInput, SetCaptionVisibilityInput, SetVideoClipSpeedShiftInput, SpeechAsset, SpeechAssets, addSpeechesInputSchema, addVideoClipsInputSchema, adjustBgmVolumeInputSchema, adjustSpeechVolumeInputSchema, adjustVideoClipDurationInputSchema, adjustVideoClipVolumeInputSchema, anchoredDeletePolicySchema, anchoredReplacePolicySchema, changeSpeechScriptInputSchema, changeSpeechVoiceInputSchema, clipIdSchema, clipIdsSchema, deleteBgmInputSchema, deleteSpeechesInputSchema, deleteVideoClipsInputSchema, mediaIdSchema, moveAnchorSchema, moveSpeechesInputSchema, moveVideoClipsByAnchorInputSchema, moveVideoClipsInputSchema, movedClipAnchoredPolicySchema, positiveMsSchema, replaceVideoClipContentInputSchema, replaceVideoClipSequenceInputSchema, setBgmInputSchema, setCaptionStyleInputSchema, setCaptionVisibilityInputSchema, setVideoClipSpeedShiftInputSchema, speechAssetsSchema, speechIdSchema, speechIdsSchema, speedShiftSchema, timelineMsSchema, voiceSchema, volumeSchema };
1475
+ }
914
1476
  //#endregion
915
1477
  //#region src/editor/schema-validator.d.ts
916
1478
  interface SnapshotReadable {
@@ -923,11 +1485,33 @@ declare class ValidationError extends Error {
923
1485
  }
924
1486
  declare class SchemaValidator {
925
1487
  validateMoveVideoClips(input: MoveVideoClipsInput, doc: SnapshotReadable): void;
1488
+ /**
1489
+ * The anchor must not be one of the clips being moved. Unlike the schema's
1490
+ * mutual exclusions this needs the input read as a whole, so it stays here.
1491
+ *
1492
+ * Rejected rather than normalized: "put this block before itself" has no
1493
+ * defensible outcome — treating it as a no-op hides a caller bug behind a
1494
+ * success, and picking any surviving neighbour invents an intent. The legacy
1495
+ * `applyBatchMoveVideoClips` omits this check (a self-anchor there silently
1496
+ * lands the block at its own pre-move index); director's original move tool
1497
+ * did enforce it (`_validate_position`: "不是被移动的clips"), and that is the
1498
+ * behaviour worth keeping.
1499
+ */
1500
+ validateMoveVideoClipsByAnchor(input: MoveVideoClipsByAnchorInput, doc: SnapshotReadable): void;
926
1501
  validateDeleteVideoClips(input: DeleteVideoClipsInput, doc: SnapshotReadable): void;
927
1502
  validateAddVideoClips(input: AddVideoClipsInput, doc: SnapshotReadable): void;
928
1503
  validateAdjustVideoClipVolume(input: AdjustVideoClipVolumeInput, doc: SnapshotReadable): void;
929
1504
  validateSetVideoClipSpeedShift(input: SetVideoClipSpeedShiftInput, doc: SnapshotReadable): void;
930
1505
  validateReplaceVideoClipContent(input: ReplaceVideoClipContentInput, doc: SnapshotReadable): void;
1506
+ /**
1507
+ * `old_clip_ids` must name a **contiguous run of the main track, in track
1508
+ * order**. A sparse or reordered selection is rejected rather than normalized:
1509
+ * a sparse selection has no single stretch to swap, so the insert index, the
1510
+ * positional speech remap, and the resulting order would each need a different
1511
+ * arbitrary choice. Mirrors the agent tool's own contract ("must be a
1512
+ * contiguous main-track sequence listed in timeline order").
1513
+ */
1514
+ validateReplaceVideoClipSequence(input: ReplaceVideoClipSequenceInput, doc: SnapshotReadable): void;
931
1515
  validateAdjustVideoClipDuration(input: AdjustVideoClipDurationInput, doc: SnapshotReadable): void;
932
1516
  validateAdjustSpeechVolume(input: AdjustSpeechVolumeInput, doc: SnapshotReadable): void;
933
1517
  validateAdjustBgmVolume(input: AdjustBgmVolumeInput, doc: SnapshotReadable): void;
@@ -987,7 +1571,7 @@ declare class SchemaValidator {
987
1571
  * side-effect payload contract in
988
1572
  * `docs/projects/medeo-integration/results/phase-4-side-effect-payload-contract.md`.
989
1573
  */
990
- type ImplementedSemanticOpKind = 'MoveVideoClips' | 'DeleteVideoClips' | 'AddVideoClips' | 'AdjustVideoClipVolume' | 'SetVideoClipSpeedShift' | 'ReplaceVideoClipContent' | 'AdjustVideoClipDuration' | 'AddSpeeches' | 'DeleteSpeeches' | 'MoveSpeeches' | 'ChangeSpeechScript' | 'ChangeSpeechVoice' | 'AdjustSpeechVolume' | 'SetCaptionVisibility' | 'SetCaptionStyle' | 'SetBgm' | 'DeleteBgm' | 'AdjustBgmVolume';
1574
+ type ImplementedSemanticOpKind = 'MoveVideoClips' | 'MoveVideoClipsByAnchor' | 'DeleteVideoClips' | 'AddVideoClips' | 'AdjustVideoClipVolume' | 'SetVideoClipSpeedShift' | 'ReplaceVideoClipContent' | 'ReplaceVideoClipSequence' | 'AdjustVideoClipDuration' | 'AddSpeeches' | 'DeleteSpeeches' | 'MoveSpeeches' | 'ChangeSpeechScript' | 'ChangeSpeechVoice' | 'AdjustSpeechVolume' | 'SetCaptionVisibility' | 'SetCaptionStyle' | 'SetBgm' | 'DeleteBgm' | 'AdjustBgmVolume';
991
1575
  /**
992
1576
  * Operations on the roadmap but NOT yet implemented by `SemanticEditor`. Empty:
993
1577
  * Phase 4 covers every op with a real entry point. Kept as a named type so the
@@ -1007,24 +1591,56 @@ declare const IMPLEMENTED_SEMANTIC_OP_KINDS: readonly ImplementedSemanticOpKind[
1007
1591
  declare function isImplementedSemanticOpKind(kind: string): kind is ImplementedSemanticOpKind;
1008
1592
  //#endregion
1009
1593
  //#region src/editor/semantic-editor.d.ts
1594
+ /**
1595
+ * Who authored an op, recorded in the commit message alongside `semantic_op`.
1596
+ *
1597
+ * Rides the Loro commit message rather than a wire field or a storage column,
1598
+ * for the same reason the semantic metadata does (`rfc/05 §5`): the message
1599
+ * travels inside the update bytes, so attribution cannot disagree with the ops
1600
+ * it describes, and it survives log truncation — a normal snapshot carries the
1601
+ * full history, so folding and deleting update rows keeps every Change's message
1602
+ * readable. A per-row column would lose the attribution at the first re-snapshot
1603
+ * and could only record one author per push, while one push may carry several
1604
+ * Changes.
1605
+ *
1606
+ * `role` deliberately mirrors the legacy `video_draft_op_records.operator_role`
1607
+ * enum (`'user' | 'agent'`) so the two engines answer "was this a person or the
1608
+ * agent" with the same vocabulary. It is declared rather than inferred from the
1609
+ * Loro peer id: peer ranges separate the writers today, but that is an audit aid
1610
+ * by convention, not an enforced fact, and peer ids are deliberately NOT derived
1611
+ * from identity (`PeerId != userid`, adr/0012).
1612
+ *
1613
+ * A deliberately narrow shape: no client/platform field. That is telemetry, and
1614
+ * document history is permanent and undeletable — the wrong place for it. Adding
1615
+ * one later costs nothing (the message is JSON; older Changes simply lack the
1616
+ * key), so the omission does not close the door.
1617
+ *
1618
+ * **Self-reported.** The client writes this and no server verifies it. The
1619
+ * authorization principal is the `medeo-user-id` header, which mengine-server
1620
+ * owner-checks; this field is the author, which it does not. The two hold the
1621
+ * same value today but answer different questions — see the harness's mengine
1622
+ * repository for why they are passed separately.
1623
+ */
1624
+ interface OpActor {
1625
+ user_id: string;
1626
+ role: 'user' | 'agent';
1627
+ }
1010
1628
  interface CommitOptions {
1011
- /**
1012
- * Caller-supplied intent attached to the op's audit message.
1013
- *
1014
- * Typed as `unknown` to match `TransactAudit.intent`. The wire (real
1015
- * mengine-server and the in-memory test double) only surfaces **string**
1016
- * intents onto the audit log — non-string values stay in the Loro commit
1017
- * message but parse to `null`. Agent callers should pass a string.
1018
- */
1019
- intent?: unknown;
1629
+ intent?: {
1630
+ kind: string;
1631
+ payload: unknown;
1632
+ };
1633
+ /** Author of this op; omitted for writes with no acting user (bootstrap, repair). */
1634
+ actor?: OpActor;
1020
1635
  }
1021
- type SemanticOpInput = MoveVideoClipsInput | DeleteVideoClipsInput | AddVideoClipsInput | AdjustVideoClipVolumeInput | AdjustSpeechVolumeInput | AdjustBgmVolumeInput | SetVideoClipSpeedShiftInput | ReplaceVideoClipContentInput | AdjustVideoClipDurationInput | AddSpeechesInput | DeleteSpeechesInput | MoveSpeechesInput | SetCaptionVisibilityInput | SetCaptionStyleInput | SetBgmInput | DeleteBgmInput;
1636
+ type SemanticOpInput = MoveVideoClipsInput | MoveVideoClipsByAnchorInput | DeleteVideoClipsInput | AddVideoClipsInput | AdjustVideoClipVolumeInput | AdjustSpeechVolumeInput | AdjustBgmVolumeInput | SetVideoClipSpeedShiftInput | ReplaceVideoClipContentInput | ReplaceVideoClipSequenceInput | AdjustVideoClipDurationInput | AddSpeechesInput | DeleteSpeechesInput | MoveSpeechesInput | SetCaptionVisibilityInput | SetCaptionStyleInput | SetBgmInput | DeleteBgmInput;
1022
1637
  type SemanticOpName = ImplementedSemanticOpKind;
1023
1638
  /** Audit metadata committed alongside an op's writes. */
1024
1639
  interface TransactAudit {
1025
1640
  kind: SemanticOpName;
1026
1641
  payload: unknown;
1027
1642
  intent?: unknown;
1643
+ actor?: OpActor;
1028
1644
  }
1029
1645
  /**
1030
1646
  * Narrow write surface the `SemanticEditor` drives (ADR 0008 / 0009). It stays
@@ -1058,11 +1674,46 @@ interface SemanticDocumentAdapter extends SnapshotReadable {
1058
1674
  declare class SemanticEditor {
1059
1675
  private readonly doc;
1060
1676
  private readonly validator;
1061
- private readonly idFactory;
1062
- constructor(doc: SemanticDocumentAdapter, validator?: SchemaValidator, idFactory?: PartIdFactory);
1677
+ constructor(doc: SemanticDocumentAdapter, validator?: SchemaValidator);
1063
1678
  moveVideoClips(input: MoveVideoClipsInput, options?: CommitOptions): Promise<void>;
1679
+ /**
1680
+ * Reorder main-track clips relative to an anchor clip, moving them as one block.
1681
+ *
1682
+ * The ordinal sibling of `moveVideoClips`: main-track clips are
1683
+ * `sequential`-positioned, so their order is the authoritative fact and absolute
1684
+ * time is derived on read (RFC 02 §4/§7). A caller that already knows "put these
1685
+ * after that one" should say so, instead of computing a timeline offset that this
1686
+ * editor would only have to resolve back into an index.
1687
+ *
1688
+ * `on_anchored` decides the speech treatment and is required. Note the asymmetry
1689
+ * in cost: `follow` writes nothing to the speeches (their `{ anchorPartId,
1690
+ * offsetMs }` facts stay valid and the derived absolute time moves with the host),
1691
+ * while `keep_absolute` runs the extra re-parent pass that preserves each
1692
+ * speech's absolute landing. `moveVideoClips` is permanently `keep_absolute`,
1693
+ * matching the FE timeline it serves.
1694
+ */
1695
+ moveVideoClipsByAnchor(input: MoveVideoClipsByAnchorInput, options?: CommitOptions): Promise<void>;
1064
1696
  deleteVideoClips(input: DeleteVideoClipsInput, options?: CommitOptions): Promise<void>;
1065
1697
  addVideoClips(input: AddVideoClipsInput, options?: CommitOptions): Promise<void>;
1698
+ /**
1699
+ * Swap a contiguous run of main-track clips for a new run, in one transaction.
1700
+ *
1701
+ * Composite by necessity, not convenience: with `on_anchored: 'remap'` each
1702
+ * surviving speech is re-anchored to the new clip in the **same position of the
1703
+ * sequence** (old[i] → new[i]), which needs both id sets live at once. Splitting
1704
+ * it into `deleteVideoClips` + `addVideoClips` would leave the caller holding the
1705
+ * anchor re-wiring — the cascade-in-the-caller mistake ADR 0009 retires.
1706
+ *
1707
+ * Positional pairing, not by count: when there are fewer new clips than old, the
1708
+ * unmatched old clips have no counterpart, so their anchored subtrees are deleted
1709
+ * (there is nothing to anchor to). When there are more new clips than old, the
1710
+ * extra ones simply arrive with no children.
1711
+ *
1712
+ * The new clips are inserted where the run started, so surrounding order is
1713
+ * preserved. Every position is derived on read (RFC 02 §7) — this writes only the
1714
+ * facts: track order, the trim windows, and the surviving anchors.
1715
+ */
1716
+ replaceVideoClipSequence(input: ReplaceVideoClipSequenceInput, options?: CommitOptions): Promise<void>;
1066
1717
  adjustVideoClipVolume(input: AdjustVideoClipVolumeInput, options?: CommitOptions): Promise<void>;
1067
1718
  setVideoClipSpeedShift(input: SetVideoClipSpeedShiftInput, options?: CommitOptions): Promise<void>;
1068
1719
  adjustSpeechVolume(input: AdjustSpeechVolumeInput, options?: CommitOptions): Promise<void>;
@@ -1105,12 +1756,23 @@ declare class SemanticEditor {
1105
1756
  /**
1106
1757
  * Set the document BGM. The media's stable result arrives materialized.
1107
1758
  *
1108
- * KNOWN GAP (non-blocking): when the BGM comes from the public library, the
1109
- * Director must also register project-level stock media ownership. That is a
1110
- * separate "register-only, no draft write" side effect Director does not yet
1111
- * expose; until it does, a public-library BGM set here will not auto-appear in
1112
- * the project media library. The document edit itself is complete and correct.
1113
- * See `docs/projects/medeo-integration/results/phase-4-op-side-effect-classification.md`.
1759
+ * KNOWN GAP (non-blocking, FE callers only): a public-library BGM also needs
1760
+ * project-level media ownership registered, or it plays but never appears in
1761
+ * the project's media library. This editor deliberately does not do it — the
1762
+ * document edit is complete and correct, and ownership is a side effect owned
1763
+ * by whoever holds the authoritative media data (memota), not by a CRDT write.
1764
+ *
1765
+ * Who is affected, as of 2026-08-13: the agent harness registers it on BOTH
1766
+ * its legacy and mengine paths (shared `resolveMutation` calls memota
1767
+ * `attachMedia` directly), and FE's legacy REST path gets it from Director.
1768
+ * Only FE's mengine path is missing it. FE cannot call memota directly:
1769
+ * `attachMedia` is exposed on memota's internal contract only, so the fix is a
1770
+ * Director proxy route — NOT a new Director business endpoint, since Director
1771
+ * stopped owning media ownership entirely (`a2951355`, 2026-08-05).
1772
+ *
1773
+ * Tracked as a Phase 7 gate (it must land before "new documents default to
1774
+ * mengine" makes public-library BGM a routine operation). See
1775
+ * `docs/projects/medeo-integration/results/phase-6-m4-legacy-refresh-isolation.md`.
1114
1776
  */
1115
1777
  setBgm(input: SetBgmInput, options?: CommitOptions): Promise<void>;
1116
1778
  /** Remove the document BGM; clears the bgm lane and removes the part. */
@@ -1183,57 +1845,6 @@ declare class MirrorVideoDocumentAdapter implements SemanticDocumentAdapter {
1183
1845
  declare function createMirrorVideoDocument(document: VideoDocument, options?: MirrorVideoDocumentOptions): LoroDoc;
1184
1846
  /** Build a `MirrorVideoDocumentAdapter` over a fresh doc seeded with `document`. */
1185
1847
  declare function createMirrorVideoDocumentAdapter(document: VideoDocument, options?: MirrorVideoDocumentOptions): MirrorVideoDocumentAdapter;
1186
- /** Write a whole `VideoDocument` into a draft (seed a fresh doc / plain-memory state). */
1187
- declare function writeVideoDocumentToDraft(draft: VideoDocumentDraft, document: VideoDocument): void;
1188
- //#endregion
1189
- //#region src/document/plain-memory-adapter.d.ts
1190
- /**
1191
- * A `TransactAudit` widened with the ordered ids minted during that transact.
1192
- * `generated_ids` is empty when the op never called the id factory.
1193
- */
1194
- interface JournalEntry extends TransactAudit {
1195
- /** Ids produced by the adapter's id factory during this transact, in mint order. */
1196
- generated_ids: string[];
1197
- }
1198
- interface PlainMemoryAdapterOptions {
1199
- /** Underlying id mint; wrapped so each call inside a transact is journaled. */
1200
- idFactory?: PartIdFactory;
1201
- }
1202
- /**
1203
- * Pure in-memory `SemanticDocumentAdapter` — no Loro/WASM. Holds a
1204
- * `VideoDocumentDraft` object and applies each `transact` via immer `produce`,
1205
- * journaling every audit that actually mutated state (plus any ids minted
1206
- * during that transact).
1207
- *
1208
- * Known benign difference vs `MirrorVideoDocumentAdapter`: the editor's
1209
- * `setPart` assigns a fresh part object on every call, so a same-value rewrite
1210
- * produces a new immer state and IS journaled here, while the mirror's deep
1211
- * diff emits no commit. Terminal `snapshot()` stays equal; replay is idempotent.
1212
- */
1213
- declare class PlainMemoryAdapter implements SemanticDocumentAdapter {
1214
- private state;
1215
- private readonly _journal;
1216
- private readonly baseIdFactory;
1217
- /** Non-null only while a `transact` edit callback is running. */
1218
- private pendingIds;
1219
- /**
1220
- * Recording wrapper around the underlying factory. Callers (sandbox editor)
1221
- * use this so every minted id is appended to the current transact's list.
1222
- */
1223
- readonly idFactory: PartIdFactory;
1224
- constructor(document: VideoDocument, options?: PlainMemoryAdapterOptions);
1225
- get journal(): readonly JournalEntry[];
1226
- hasContent(): boolean;
1227
- snapshot(): VideoDocument;
1228
- /**
1229
- * Apply one op via immer. A throw in `edit` discards the draft (state and
1230
- * journal unchanged). When `produce` returns the same reference, there was
1231
- * no structural change — skip journal, matching mirror "no change, no commit".
1232
- */
1233
- transact(edit: (draft: VideoDocumentDraft) => void, audit: TransactAudit): void;
1234
- }
1235
- /** Build a `PlainMemoryAdapter` seeded with `document`. */
1236
- declare function createPlainMemoryAdapter(document: VideoDocument, options?: PlainMemoryAdapterOptions): PlainMemoryAdapter;
1237
1848
  //#endregion
1238
1849
  //#region src/document/mirror-read.d.ts
1239
1850
  /**
@@ -1418,4 +2029,4 @@ declare const videoDocumentSchema: z.ZodObject<{
1418
2029
  }, z.core.$loose>]>>>;
1419
2030
  }, z.core.$loose>;
1420
2031
  //#endregion
1421
- export { VideoClipPart as $, TrackItemDraft as A, derivePositionFromAbs as B, isImplementedSemanticOpKind as C, PartIdFactory as D, ValidationError as E, assertValidVideoDocument as F, PartKind as G, toVideoDocument as H, validateVideoDocument as I, Timeline as J, PartUnion as K, DerivedItemPosition as L, VideoDocumentMirrorSchema as M, videoDocumentMirrorSchema as N, generatePartId as O, VideoDocumentValidationError as P, VIDEO_DOCUMENT_SCHEMA_VERSION as Q, SpeechHostMap as R, SemanticOpKind as S, SnapshotReadable as T, BgmPart as U, fromVideoDocument as V, CaptionPart as W, TrackItem as X, Track as Y, TrackItemTimePosition as Z, SemanticOpName as _, PlainMemoryAdapter as a, effectiveVideoClipDurationMs as at, ImplementedSemanticOpKind as b, MirrorVideoDocumentAdapter as c, CaptionPart$1 as ct, createMirrorVideoDocumentAdapter as d, SpeedShift as dt, VideoDocument as et, writeVideoDocumentToDraft as f, Timeline$1 as ft, SemanticOpInput as g, SemanticEditor as h, JournalEntry as i, VideoDraft as it, VideoDocumentDraft as j, TrackDraft as k, MirrorVideoDocumentOptions as l, CaptionStyle as lt, SemanticDocumentAdapter as m, TrackItem$1 as mt, videoDocumentSchema as n, VideoDocumentValidationIssue as nt, PlainMemoryAdapterOptions as o, speedOf as ot, CommitOptions as p, Track$1 as pt, SpeechPart as q, readVideoDocumentFromDraft as r, VideoDocumentValidationIssueCode as rt, createPlainMemoryAdapter as s, Attachment as st, partUnionSchema as t, VideoDocumentSchemaVersion as tt, createMirrorVideoDocument as u, PartAggregation as ut, TransactAudit as v, SchemaValidator as w, PlannedSemanticOpKind as x, IMPLEMENTED_SEMANTIC_OP_KINDS as y, buildSpeechHostMap as z };
2032
+ export { VideoDraft as $, assertValidVideoDocument as A, PartKind as B, index_d_exports as C, VideoDocumentMirrorSchema as D, VideoDocumentDraft as E, derivePositionFromAbs as F, TrackItem as G, SpeechPart as H, fromVideoDocument as I, VideoClipPart as J, TrackItemTimePosition as K, toVideoDocument as L, DerivedItemPosition as M, SpeechHostMap as N, videoDocumentMirrorSchema as O, buildSpeechHostMap as P, VideoDocumentValidationIssueCode as Q, BgmPart as R, ValidationError as S, TrackItemDraft as T, Timeline as U, PartUnion as V, Track as W, VideoDocumentSchemaVersion as X, VideoDocument as Y, VideoDocumentValidationIssue as Z, PlannedSemanticOpKind as _, MirrorVideoDocumentOptions as a, SpeedShift as at, SchemaValidator as b, CommitOptions as c, TrackItem$1 as ct, SemanticEditor as d, VideoDraftPartUnion as et, SemanticOpInput as f, ImplementedSemanticOpKind as g, IMPLEMENTED_SEMANTIC_OP_KINDS as h, MirrorVideoDocumentAdapter as i, PartAggregation as it, validateVideoDocument as j, VideoDocumentValidationError as k, OpActor as l, TransactAudit as m, videoDocumentSchema as n, CaptionPart$1 as nt, createMirrorVideoDocument as o, Timeline$1 as ot, SemanticOpName as p, VIDEO_DOCUMENT_SCHEMA_VERSION as q, readVideoDocumentFromDraft as r, CaptionStyle as rt, createMirrorVideoDocumentAdapter as s, Track$1 as st, partUnionSchema as t, Attachment as tt, SemanticDocumentAdapter as u, SemanticOpKind as v, TrackDraft as w, SnapshotReadable as x, isImplementedSemanticOpKind as y, CaptionPart as z };