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

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 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/sandbox/node-host.ts","../src/prompt.ts","../src/schema.ts","../src/session/commit-plan.ts","../src/host-tool.ts"],"sourcesContent":["/// <reference types=\"node\" />\n\nimport { Worker } from 'node:worker_threads';\n\nimport type { JournalEntry, VideoDocument } from '@mengine/medeo-client';\n\nimport type { ChangePlan } from './script-session.ts';\n\n/**\n * Host API for running an agent edit script in an isolated Node worker.\n *\n * Requires Node.js >= 24.15 (engines) so the worker can load TypeScript via\n * `--experimental-transform-types`. Do not inherit `process.execArgv` — vitest\n * injects loaders that break worker boot.\n */\n\nexport interface RunEditScriptOptions {\n document: VideoDocument;\n baseVersion: string;\n script: string;\n inputs?: Record<string, unknown>;\n /** Deterministic id mint label for tests; omit to use the default ULID factory. */\n idLabel?: string;\n /** Hard wall-clock timeout; default 2000 ms. */\n timeoutMs?: number;\n /** V8 old-generation ceiling for the worker; default 256 MB. */\n memoryLimitMb?: number;\n /** Override worker module URL (defaults to sibling `worker-entry.ts`). */\n workerEntryUrl?: URL;\n}\n\nexport type EditScriptResult =\n | {\n ok: true;\n plan: ChangePlan;\n /** Script execution time after worker readiness; excludes cold start. */\n durationMs: number;\n }\n | {\n ok: false;\n phase: 'parse' | 'runtime' | 'timeout' | 'memory';\n error: { message: string; line?: number; column?: number; stack?: string };\n partial: { ops: readonly JournalEntry[]; logs: string[] };\n };\n\ntype WorkerMessage =\n | { t: 'ready' }\n | { t: 'entry'; entry: JournalEntry }\n | { t: 'log'; line: string }\n | { t: 'truncate'; index: number }\n | { t: 'done'; preview: string; opsCount: number }\n | {\n t: 'fail';\n phase: 'parse' | 'runtime';\n error: { message: string; line?: number; column?: number; stack?: string };\n };\n\nconst DEFAULT_TIMEOUT_MS = 2000;\nconst DEFAULT_MEMORY_MB = 256;\n\n/**\n * Source runs load the checked-in TypeScript worker; packed runs load the\n * sibling JavaScript chunk emitted as a second package entry. Keeping this\n * branch explicit avoids shipping a `dist/*.ts` URL in the npm artifact.\n */\nfunction sourceSibling(fileName: string): URL {\n const selfUrl = new URL(import.meta.url);\n const extension = selfUrl.pathname.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./${fileName}.${extension}`, selfUrl);\n}\n\n/** Run `script` against a forked document snapshot; always resolves (never rejects). */\nexport function runEditScript(options: RunEditScriptOptions): Promise<EditScriptResult> {\n // Set when the worker has loaded its bundle and is about to invoke the script.\n // Keep this clock separate from worker boot so success timing matches the\n // timeout boundary and excludes cold-start/module-loading cost.\n let scriptStartedAt = performance.now();\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const memoryLimitMb = options.memoryLimitMb ?? DEFAULT_MEMORY_MB;\n const workerEntryUrl = options.workerEntryUrl ?? sourceSibling('worker-entry');\n const resolveRegisterUrl = sourceSibling('node-esm-resolve-register');\n\n const ops: JournalEntry[] = [];\n const logs: string[] = [];\n\n return new Promise<EditScriptResult>((resolve) => {\n let settled = false;\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const worker = new Worker(workerEntryUrl, {\n workerData: {\n document: options.document,\n script: options.script,\n inputs: options.inputs,\n idLabel: options.idLabel,\n },\n // Explicit argv only — never inherit process.execArgv (vitest loaders).\n // Source workers need TypeScript transform plus the resolver hook for\n // workspace packages that still use extensionless directory imports.\n // Packed JavaScript workers already contain those dependencies.\n execArgv: resolveRegisterUrl.pathname.endsWith('.ts')\n ? [\n '--experimental-transform-types',\n '--disable-warning=ExperimentalWarning',\n `--import=${resolveRegisterUrl.href}`,\n ]\n : [],\n resourceLimits: { maxOldGenerationSizeMb: memoryLimitMb },\n });\n\n /** Start wall-clock timeout only after worker signals script is about to run. */\n const armTimeout = (): void => {\n if (settled || timer != null) return;\n timer = setTimeout(() => {\n timedOut = true;\n void worker.terminate();\n finish({\n ok: false,\n phase: 'timeout',\n error: { message: `edit script exceeded timeout of ${timeoutMs}ms` },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n }, timeoutMs);\n };\n\n const finish = (result: EditScriptResult): void => {\n if (settled) return;\n settled = true;\n if (timer != null) clearTimeout(timer);\n void worker.terminate();\n if (result.ok) {\n resolve({ ...result, durationMs: performance.now() - scriptStartedAt });\n } else {\n resolve(result);\n }\n };\n\n worker.on('message', (message: WorkerMessage) => {\n if (settled) return;\n if (message.t === 'ready') {\n scriptStartedAt = performance.now();\n armTimeout();\n return;\n }\n if (message.t === 'entry') {\n ops.push(message.entry);\n return;\n }\n if (message.t === 'log') {\n logs.push(message.line);\n return;\n }\n if (message.t === 'truncate') {\n ops.length = Math.max(0, Math.min(message.index, ops.length));\n return;\n }\n if (message.t === 'done') {\n if (message.opsCount !== ops.length) {\n finish({\n ok: false,\n phase: 'runtime',\n error: {\n message: `opsCount mismatch: worker reported ${message.opsCount}, host collected ${ops.length}`,\n },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n return;\n }\n finish({\n ok: true,\n plan: {\n doc_id: options.document.meta.draft_id ?? '',\n base_version: options.baseVersion,\n ops: ops.slice(),\n preview: message.preview,\n logs: logs.slice(),\n },\n durationMs: 0,\n });\n return;\n }\n if (message.t === 'fail') {\n finish({\n ok: false,\n phase: message.phase,\n error: message.error,\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n }\n });\n\n worker.on('error', (error: Error) => {\n if (settled) return;\n const text = error.message ?? String(error);\n const phase = /memory limit/i.test(text) ? 'memory' : 'runtime';\n finish({\n ok: false,\n phase,\n error: { message: text, stack: error.stack },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n });\n\n worker.on('exit', (code: number) => {\n if (settled) return;\n if (timedOut) return;\n finish({\n ok: false,\n phase: 'runtime',\n error: { message: `worker exited with code ${code ?? 'null'} before completion` },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n });\n });\n}\n","export const MEDEO_TOOL_DESCRIPTION = `\nEdit a Medeo video document through a deterministic, side-effect-free JavaScript sandbox.\n\nOperations:\n- snapshot: return the compact timeline projection and opaque base version.\n- run-edit-script: execute JavaScript against a forked snapshot. Inspect timeline.*, compute coordinates, and call edit.* methods in one script. The sandbox has no network, storage, clock, or generation access. Pass materialized asset/speech facts through inputs. A successful run returns preview, logs, base_version, and plan_id — not the full op journal.\n- commit-plan: replay a cached plan_id into the live MengineDocSession through SemanticEditor. Use validation=version for all-or-nothing commit, or preflight to localize an op conflict after concurrent edits.\n\nDefault 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. On version mismatch, rerun snapshot and the script; never try to patch a rejected journal by hand.\n`.trim();\n","export const MEDEO_TOOL_NAME = 'medeo';\n\nexport type MedeoToolOp = 'snapshot' | 'run-edit-script' | 'commit-plan';\n\n/**\n * JSON Schema for the host-facing three-op `medeo` tool surface.\n *\n * The schema intentionally does not return or accept the full op journal:\n * journals stay in the tool process and are referenced by `plan_id`. This keeps\n * large intermediate products out of model context while preserving the exact\n * journal used for commit.\n */\nexport const MEDEO_TOOL_PARAMETERS = {\n type: 'object',\n required: ['op', 'doc_id'],\n additionalProperties: false,\n properties: {\n op: {\n type: 'string',\n enum: ['snapshot', 'run-edit-script', 'commit-plan'],\n description: 'Which Medeo document operation to run.',\n },\n doc_id: {\n type: 'string',\n minLength: 1,\n description: 'Medeo document id. Copy it from the host context; never invent it.',\n },\n script: {\n type: 'string',\n minLength: 1,\n description:\n 'JavaScript body for run-edit-script. It receives edit, timeline, checkpoint, rollbackTo, inputs, and console; perform all calculations in the script.',\n },\n inputs: {\n type: 'object',\n description:\n 'Pre-materialized, side-effect-free values passed into the script. Generation and network IO must happen in the host before this call.',\n },\n timeout_ms: {\n type: 'integer',\n minimum: 1,\n description: 'Maximum script wall-clock time after worker startup (default 2000).',\n },\n memory_limit_mb: {\n type: 'integer',\n minimum: 16,\n description: 'Worker old-generation memory ceiling in MB (default 256).',\n },\n auto_commit: {\n type: 'boolean',\n description:\n 'Commit the returned plan immediately after the sandbox succeeds. Default false: return preview plus plan_id for explicit commit.',\n },\n plan_id: {\n type: 'string',\n minLength: 1,\n description: 'Plan id returned by run-edit-script; required by commit-plan.',\n },\n validation: {\n type: 'string',\n enum: ['version', 'preflight'],\n description:\n 'commit-plan mode: version rejects any concurrent change; preflight revalidates each op against the current snapshot.',\n },\n },\n oneOf: [\n {\n required: ['op', 'doc_id'],\n properties: {\n op: { const: 'snapshot' },\n doc_id: { $ref: '#/properties/doc_id' },\n },\n additionalProperties: false,\n },\n {\n required: ['op', 'doc_id', 'script'],\n properties: {\n op: { const: 'run-edit-script' },\n doc_id: { $ref: '#/properties/doc_id' },\n script: { $ref: '#/properties/script' },\n inputs: { $ref: '#/properties/inputs' },\n timeout_ms: { $ref: '#/properties/timeout_ms' },\n memory_limit_mb: { $ref: '#/properties/memory_limit_mb' },\n auto_commit: { $ref: '#/properties/auto_commit' },\n },\n additionalProperties: false,\n },\n {\n required: ['op', 'doc_id', 'plan_id'],\n properties: {\n op: { const: 'commit-plan' },\n doc_id: { $ref: '#/properties/doc_id' },\n plan_id: { $ref: '#/properties/plan_id' },\n validation: { $ref: '#/properties/validation' },\n },\n additionalProperties: false,\n },\n ],\n} as const;\n","import {\n createPlainMemoryAdapter,\n replayJournal,\n ValidationError,\n type JournalEntry,\n type MengineDocSession,\n type SemanticOpName,\n} from '@mengine/medeo-client';\n\n/**\n * A sandbox journal plus the opaque version token taken at fork time.\n * `commitPlan` rejects the whole plan when the live session has moved on\n * (phase-1 version gate), or localizes a business conflict to a journal\n * entry under `{ validation: 'preflight' }`.\n */\nexport interface CommitPlan {\n /** `session.version()` at the moment the sandbox was forked. */\n base_version: string;\n ops: readonly JournalEntry[];\n}\n\nexport type CommitPlanResult =\n | { kind: 'committed'; ops_applied: number }\n | { kind: 'rejected'; reason: 'version_mismatch'; expected: string; actual: string }\n | {\n kind: 'rejected';\n reason: 'op_conflict';\n /** Failing entry index in the journal — agent rerun anchor. */\n index: number;\n op_kind: SemanticOpName;\n /** Validator message, passed through verbatim (never a raw Error). */\n message: string;\n };\n\nexport interface CommitPlanOptions {\n /** `'version'` (default, phase-1 hard gate) | `'preflight'` (phase-2 per-op revalidation). */\n validation?: 'version' | 'preflight';\n}\n\n/**\n * Replay a sandbox journal into a live session through its document adapter\n * (SemanticEditor → Loro → mengine-server).\n *\n * - Default / `{ validation: 'version' }`: if `session.version()` ≠\n * `plan.base_version`, reject with zero writes.\n * - `{ validation: 'preflight' }`: skip the version gate; revalidate each op\n * against a PlainMemoryAdapter seeded from the current live snapshot, then\n * replay for real. A SchemaValidator failure becomes `op_conflict` with the\n * failing entry's index. Journal integrity errors (unrecorded/unconsumed\n * ids) still propagate as throws in both modes.\n */\nexport async function commitPlan(\n session: MengineDocSession,\n plan: CommitPlan,\n options?: CommitPlanOptions,\n): Promise<CommitPlanResult> {\n if (options?.validation === 'preflight') {\n return commitPlanPreflight(session, plan);\n }\n\n const actual = session.version();\n if (actual !== plan.base_version) {\n return {\n kind: 'rejected',\n reason: 'version_mismatch',\n expected: plan.base_version,\n actual,\n };\n }\n\n await replayJournal(session.documentAdapter, plan.ops);\n return { kind: 'committed', ops_applied: plan.ops.length };\n}\n\n/**\n * Phase-2 path: scratch revalidation then real replay. Each entry is driven\n * through `replayJournal` alone so a ValidationError maps to a stable index;\n * integrity throws are not wrapped.\n */\nasync function commitPlanPreflight(session: MengineDocSession, plan: CommitPlan): Promise<CommitPlanResult> {\n const scratch = createPlainMemoryAdapter(session.snapshot());\n\n for (let index = 0; index < plan.ops.length; index++) {\n const entry = plan.ops[index];\n if (entry == null) continue;\n try {\n await replayJournal(scratch, [entry]);\n } catch (error) {\n if (error instanceof ValidationError) {\n return opConflict(index, entry.kind, error.message);\n }\n throw error;\n }\n }\n\n // Real replay: optimistic window may still collide; wrap ValidationError the\n // same way. Prior entries in this loop have already been written.\n for (let index = 0; index < plan.ops.length; index++) {\n const entry = plan.ops[index];\n if (entry == null) continue;\n try {\n await replayJournal(session.documentAdapter, [entry]);\n } catch (error) {\n if (error instanceof ValidationError) {\n return opConflict(index, entry.kind, `real replay: ${error.message}`);\n }\n throw error;\n }\n }\n\n return { kind: 'committed', ops_applied: plan.ops.length };\n}\n\nfunction opConflict(index: number, op_kind: SemanticOpName, message: string): CommitPlanResult {\n return { kind: 'rejected', reason: 'op_conflict', index, op_kind, message };\n}\n","import { randomUUID } from 'node:crypto';\n\nimport {\n MengineDocSession,\n MengineHttpClient,\n type MengineDocSessionOptions,\n type VideoDocument,\n} from '@mengine/medeo-client';\n\nimport { renderCompactProjection } from './document/compact-projection.ts';\nimport { MEDEO_TOOL_DESCRIPTION } from './prompt.ts';\nimport { runEditScript } from './sandbox/node-host.ts';\nimport type { ChangePlan } from './sandbox/script-session.ts';\nimport { MEDEO_TOOL_NAME, MEDEO_TOOL_PARAMETERS, type MedeoToolOp } from './schema.ts';\nimport { commitPlan, type CommitPlanOptions, type CommitPlanResult } from './session/commit-plan.ts';\n\ntype ContextualValue<T> = T | ((docId: string) => T | undefined);\n\nexport interface CreateMedeoToolOptions {\n /**\n * Mengine HTTP origin for a document. The host owns environment routing\n * (local/stg/prd/lane) and may return a different origin per document.\n * Sessions cache by doc id, so the origin must remain stable for that doc.\n */\n httpOrigin: ContextualValue<string>;\n /** Optional bearer token, evaluated for each HTTP request. */\n authToken?: ContextualValue<string>;\n /** Optional end-user id header, evaluated for each HTTP request. */\n userId?: ContextualValue<string>;\n /** Stable agent peer id. Supply a host-scoped value so audit provenance is durable. */\n peerId?: ContextualValue<string>;\n fetchImpl?: typeof fetch;\n sseReconnectDelayMs?: number;\n /** Defaults passed to runEditScript; each call may override them. */\n sandbox?: { timeoutMs?: number; memoryLimitMb?: number };\n /** Maximum cached plans; oldest plans are evicted (default 16). */\n maxPlans?: number;\n}\n\nexport type MedeoToolInput =\n | { op: 'snapshot'; doc_id: string }\n | {\n op: 'run-edit-script';\n doc_id: string;\n script: string;\n inputs?: Record<string, unknown>;\n timeout_ms?: number;\n memory_limit_mb?: number;\n auto_commit?: boolean;\n }\n | {\n op: 'commit-plan';\n doc_id: string;\n plan_id: string;\n validation?: 'version' | 'preflight';\n };\n\nexport type MedeoToolResult =\n | { ok: true; op: 'snapshot'; doc_id: string; version: string; preview: string }\n | {\n ok: true;\n op: 'run-edit-script';\n doc_id: string;\n plan_id: string;\n base_version: string;\n ops_count: number;\n preview: string;\n logs: string[];\n duration_ms: number;\n committed?: boolean;\n commit_result?: CommitPlanResult;\n }\n | {\n ok: false;\n op: 'run-edit-script';\n doc_id: string;\n phase: 'parse' | 'runtime' | 'timeout' | 'memory';\n error: { message: string; line?: number; column?: number; stack?: string };\n partial: { ops_count: number; logs: string[] };\n }\n | {\n ok: true;\n op: 'commit-plan';\n doc_id: string;\n plan_id: string;\n committed: boolean;\n result: CommitPlanResult;\n }\n | { ok: false; op: MedeoToolOp; error: string };\n\nexport interface MedeoTool {\n name: typeof MEDEO_TOOL_NAME;\n description: typeof MEDEO_TOOL_DESCRIPTION;\n parameters: typeof MEDEO_TOOL_PARAMETERS;\n handle(input: unknown): Promise<MedeoToolResult>;\n close(): Promise<void>;\n}\n\ninterface CachedPlan {\n docId: string;\n plan: ChangePlan;\n}\n\nconst DEFAULT_MAX_PLANS = 16;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction optionalContext<T>(value: ContextualValue<T> | undefined, docId: string): T | undefined {\n if (value === undefined) return undefined;\n return typeof value === 'function' ? (value as (id: string) => T | undefined)(docId) : value;\n}\n\nfunction requiredContext(value: ContextualValue<string>, docId: string, field: string): string {\n const resolved = optionalContext(value, docId)?.trim();\n if (resolved == null || resolved.length === 0) {\n throw new Error(`${field} must resolve to a non-empty string for doc ${docId}`);\n }\n return resolved;\n}\n\nfunction parseInput(value: unknown): MedeoToolInput {\n if (!isRecord(value)) throw new Error('input must be an object');\n const op = value.op;\n const docId = value.doc_id;\n if (typeof op !== 'string') throw new Error('op must be a string');\n if (typeof docId !== 'string' || docId.trim().length === 0) throw new Error('doc_id must be a non-empty string');\n\n if (op === 'snapshot') return { op, doc_id: docId };\n\n if (op === 'run-edit-script') {\n if (typeof value.script !== 'string' || value.script.length === 0) {\n throw new Error('script must be a non-empty string');\n }\n if (value.inputs !== undefined && !isRecord(value.inputs)) {\n throw new Error('inputs must be an object');\n }\n const timeoutMs = value.timeout_ms;\n if (timeoutMs !== undefined && (typeof timeoutMs !== 'number' || !Number.isInteger(timeoutMs) || timeoutMs <= 0)) {\n throw new Error('timeout_ms must be a positive integer');\n }\n const memoryLimitMb = value.memory_limit_mb;\n if (\n memoryLimitMb !== undefined &&\n (typeof memoryLimitMb !== 'number' || !Number.isInteger(memoryLimitMb) || memoryLimitMb < 16)\n ) {\n throw new Error('memory_limit_mb must be an integer >= 16');\n }\n if (value.auto_commit !== undefined && typeof value.auto_commit !== 'boolean') {\n throw new Error('auto_commit must be a boolean');\n }\n return {\n op,\n doc_id: docId,\n script: value.script,\n ...(value.inputs !== undefined ? { inputs: value.inputs } : {}),\n ...(timeoutMs !== undefined ? { timeout_ms: timeoutMs } : {}),\n ...(memoryLimitMb !== undefined ? { memory_limit_mb: memoryLimitMb } : {}),\n ...(value.auto_commit !== undefined ? { auto_commit: value.auto_commit } : {}),\n };\n }\n\n if (op === 'commit-plan') {\n if (typeof value.plan_id !== 'string' || value.plan_id.length === 0) {\n throw new Error('plan_id must be a non-empty string');\n }\n if (value.validation !== undefined && value.validation !== 'version' && value.validation !== 'preflight') {\n throw new Error('validation must be \"version\" or \"preflight\"');\n }\n return {\n op,\n doc_id: docId,\n plan_id: value.plan_id,\n ...(value.validation !== undefined ? { validation: value.validation } : {}),\n };\n }\n\n throw new Error(`unknown op: ${op}`);\n}\n\n/**\n * Create the self-contained Medeo LLM tool.\n *\n * The package owns session construction, compact projection, sandbox execution,\n * plan caching, commit, and shutdown. The host supplies only environment facts:\n * HTTP origin, credentials, fetch implementation, and a stable peer id.\n */\nexport function createMedeoTool(options: CreateMedeoToolOptions): MedeoTool {\n const sessions = new Map<string, Promise<MengineDocSession>>();\n const plans = new Map<string, CachedPlan>();\n const maxPlans = options.maxPlans ?? DEFAULT_MAX_PLANS;\n let closed = false;\n\n async function getSession(docId: string): Promise<MengineDocSession> {\n if (closed) throw new Error('medeo tool is closed');\n const existing = sessions.get(docId);\n if (existing != null) return await existing;\n\n const created = (async () => {\n const client = new MengineHttpClient({\n docId,\n httpOrigin: requiredContext(options.httpOrigin, docId, 'httpOrigin'),\n ...(options.authToken !== undefined ? { authToken: () => optionalContext(options.authToken, docId) } : {}),\n ...(options.userId !== undefined ? { userId: () => optionalContext(options.userId, docId) } : {}),\n ...(options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}),\n });\n const peerId = optionalContext(options.peerId, docId);\n const session = new MengineDocSession({\n docId,\n client,\n ...(peerId !== undefined ? { peerId: peerId as MengineDocSessionOptions['peerId'] } : {}),\n ...(options.sseReconnectDelayMs !== undefined ? { sseReconnectDelayMs: options.sseReconnectDelayMs } : {}),\n });\n try {\n await session.start();\n return session;\n } catch (error) {\n session.destroy();\n throw error;\n }\n })();\n\n sessions.set(docId, created);\n try {\n return await created;\n } catch (error) {\n if (sessions.get(docId) === created) sessions.delete(docId);\n throw error;\n }\n }\n\n function rememberPlan(docId: string, plan: ChangePlan): string {\n const planId = randomUUID();\n plans.set(planId, { docId, plan });\n while (plans.size > maxPlans) {\n const oldest = plans.keys().next().value;\n if (oldest === undefined) break;\n plans.delete(oldest);\n }\n return planId;\n }\n\n async function snapshot(input: Extract<MedeoToolInput, { op: 'snapshot' }>): Promise<MedeoToolResult> {\n const session = await getSession(input.doc_id);\n const document = session.snapshot();\n return {\n ok: true,\n op: 'snapshot',\n doc_id: input.doc_id,\n version: session.version(),\n preview: renderCompactProjection(document),\n };\n }\n\n async function run(input: Extract<MedeoToolInput, { op: 'run-edit-script' }>): Promise<MedeoToolResult> {\n const session = await getSession(input.doc_id);\n const document: VideoDocument = session.snapshot();\n const baseVersion = session.version();\n const result = await runEditScript({\n document,\n baseVersion,\n script: input.script,\n ...(input.inputs !== undefined ? { inputs: input.inputs } : {}),\n timeoutMs: input.timeout_ms ?? options.sandbox?.timeoutMs,\n memoryLimitMb: input.memory_limit_mb ?? options.sandbox?.memoryLimitMb,\n });\n\n if (!result.ok) {\n return {\n ok: false,\n op: 'run-edit-script',\n doc_id: input.doc_id,\n phase: result.phase,\n error: result.error,\n partial: { ops_count: result.partial.ops.length, logs: result.partial.logs },\n };\n }\n\n const planId = rememberPlan(input.doc_id, result.plan);\n const base = {\n ok: true as const,\n op: 'run-edit-script' as const,\n doc_id: input.doc_id,\n plan_id: planId,\n base_version: baseVersion,\n ops_count: result.plan.ops.length,\n preview: result.plan.preview,\n logs: result.plan.logs,\n duration_ms: result.durationMs,\n };\n if (input.auto_commit !== true) return base;\n\n const commit = await commitPlan(session, result.plan);\n return { ...base, committed: commit.kind === 'committed', commit_result: commit };\n }\n\n async function commit(input: Extract<MedeoToolInput, { op: 'commit-plan' }>): Promise<MedeoToolResult> {\n const cached = plans.get(input.plan_id);\n if (cached == null || cached.docId !== input.doc_id) {\n throw new Error(`plan_id ${input.plan_id} is not available for doc ${input.doc_id}`);\n }\n const session = await getSession(input.doc_id);\n const commitOptions: CommitPlanOptions | undefined =\n input.validation === undefined ? undefined : { validation: input.validation };\n const result = await commitPlan(session, cached.plan, commitOptions);\n return {\n ok: true,\n op: 'commit-plan',\n doc_id: input.doc_id,\n plan_id: input.plan_id,\n committed: result.kind === 'committed',\n result,\n };\n }\n\n return {\n name: MEDEO_TOOL_NAME,\n description: MEDEO_TOOL_DESCRIPTION,\n parameters: MEDEO_TOOL_PARAMETERS,\n async handle(input: unknown): Promise<MedeoToolResult> {\n try {\n const parsed = parseInput(input);\n if (parsed.op === 'snapshot') return await snapshot(parsed);\n if (parsed.op === 'run-edit-script') return await run(parsed);\n return await commit(parsed);\n } catch (error) {\n const op = isRecord(input) && typeof input.op === 'string' ? (input.op as MedeoToolOp) : 'snapshot';\n return {\n ok: false,\n op,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n },\n async close(): Promise<void> {\n closed = true;\n const opening = [...sessions.values()];\n sessions.clear();\n plans.clear();\n const errors: unknown[] = [];\n for (const sessionPromise of opening) {\n try {\n const session = await sessionPromise;\n session.destroy();\n } catch (error) {\n errors.push(error);\n }\n }\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) throw new AggregateError(errors, 'failed to close medeo tool sessions');\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/sandbox/node-host.ts","../src/sandbox/generated/edit-sandbox-model-context.ts","../src/prompt.ts","../src/schema.ts","../src/session/commit-plan.ts","../src/host-tool.ts"],"sourcesContent":["/// <reference types=\"node\" />\n\nimport { Worker } from 'node:worker_threads';\n\nimport type { JournalEntry, VideoDocument } from '@mengine/medeo-client';\n\nimport type { ChangePlan } from './script-session.ts';\n\n/**\n * Host API for running an agent edit script in an isolated Node worker.\n *\n * Requires Node.js >= 24.15 (engines) so the worker can load TypeScript via\n * `--experimental-transform-types`. Do not inherit `process.execArgv` — vitest\n * injects loaders that break worker boot.\n */\n\nexport interface RunEditScriptOptions {\n document: VideoDocument;\n baseVersion: string;\n script: string;\n inputs?: Record<string, unknown>;\n /** Deterministic id mint label for tests; omit to use the default ULID factory. */\n idLabel?: string;\n /** Hard wall-clock timeout; default 2000 ms. */\n timeoutMs?: number;\n /** V8 old-generation ceiling for the worker; default 256 MB. */\n memoryLimitMb?: number;\n /** Override worker module URL (defaults to sibling `worker-entry.ts`). */\n workerEntryUrl?: URL;\n}\n\nexport type EditScriptResult =\n | {\n ok: true;\n plan: ChangePlan;\n /** Script execution time after worker readiness; excludes cold start. */\n durationMs: number;\n }\n | {\n ok: false;\n phase: 'parse' | 'runtime' | 'timeout' | 'memory';\n error: { message: string; line?: number; column?: number; stack?: string };\n partial: { ops: readonly JournalEntry[]; logs: string[] };\n };\n\ntype WorkerMessage =\n | { t: 'ready' }\n | { t: 'entry'; entry: JournalEntry }\n | { t: 'log'; line: string }\n | { t: 'truncate'; index: number }\n | { t: 'done'; preview: string; opsCount: number }\n | {\n t: 'fail';\n phase: 'parse' | 'runtime';\n error: { message: string; line?: number; column?: number; stack?: string };\n };\n\nconst DEFAULT_TIMEOUT_MS = 2000;\nconst DEFAULT_MEMORY_MB = 256;\n\n/**\n * Source runs load the checked-in TypeScript worker; packed runs load the\n * sibling JavaScript chunk emitted as a second package entry. Keeping this\n * branch explicit avoids shipping a `dist/*.ts` URL in the npm artifact.\n */\nfunction sourceSibling(fileName: string): URL {\n const selfUrl = new URL(import.meta.url);\n const extension = selfUrl.pathname.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./${fileName}.${extension}`, selfUrl);\n}\n\n/** Run `script` against a forked document snapshot; always resolves (never rejects). */\nexport function runEditScript(options: RunEditScriptOptions): Promise<EditScriptResult> {\n // Set when the worker has loaded its bundle and is about to invoke the script.\n // Keep this clock separate from worker boot so success timing matches the\n // timeout boundary and excludes cold-start/module-loading cost.\n let scriptStartedAt = performance.now();\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const memoryLimitMb = options.memoryLimitMb ?? DEFAULT_MEMORY_MB;\n const workerEntryUrl = options.workerEntryUrl ?? sourceSibling('worker-entry');\n const resolveRegisterUrl = sourceSibling('node-esm-resolve-register');\n\n const ops: JournalEntry[] = [];\n const logs: string[] = [];\n\n return new Promise<EditScriptResult>((resolve) => {\n let settled = false;\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const worker = new Worker(workerEntryUrl, {\n workerData: {\n document: options.document,\n script: options.script,\n inputs: options.inputs,\n idLabel: options.idLabel,\n },\n // Explicit argv only — never inherit process.execArgv (vitest loaders).\n // Source workers need TypeScript transform plus the resolver hook for\n // workspace packages that still use extensionless directory imports.\n // Packed JavaScript workers already contain those dependencies.\n execArgv: resolveRegisterUrl.pathname.endsWith('.ts')\n ? [\n '--experimental-transform-types',\n '--disable-warning=ExperimentalWarning',\n `--import=${resolveRegisterUrl.href}`,\n ]\n : [],\n resourceLimits: { maxOldGenerationSizeMb: memoryLimitMb },\n });\n\n /** Start wall-clock timeout only after worker signals script is about to run. */\n const armTimeout = (): void => {\n if (settled || timer != null) return;\n timer = setTimeout(() => {\n timedOut = true;\n void worker.terminate();\n finish({\n ok: false,\n phase: 'timeout',\n error: { message: `edit script exceeded timeout of ${timeoutMs}ms` },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n }, timeoutMs);\n };\n\n const finish = (result: EditScriptResult): void => {\n if (settled) return;\n settled = true;\n if (timer != null) clearTimeout(timer);\n void worker.terminate();\n if (result.ok) {\n resolve({ ...result, durationMs: performance.now() - scriptStartedAt });\n } else {\n resolve(result);\n }\n };\n\n worker.on('message', (message: WorkerMessage) => {\n if (settled) return;\n if (message.t === 'ready') {\n scriptStartedAt = performance.now();\n armTimeout();\n return;\n }\n if (message.t === 'entry') {\n ops.push(message.entry);\n return;\n }\n if (message.t === 'log') {\n logs.push(message.line);\n return;\n }\n if (message.t === 'truncate') {\n ops.length = Math.max(0, Math.min(message.index, ops.length));\n return;\n }\n if (message.t === 'done') {\n if (message.opsCount !== ops.length) {\n finish({\n ok: false,\n phase: 'runtime',\n error: {\n message: `opsCount mismatch: worker reported ${message.opsCount}, host collected ${ops.length}`,\n },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n return;\n }\n finish({\n ok: true,\n plan: {\n doc_id: options.document.meta.draft_id ?? '',\n base_version: options.baseVersion,\n ops: ops.slice(),\n preview: message.preview,\n logs: logs.slice(),\n },\n durationMs: 0,\n });\n return;\n }\n if (message.t === 'fail') {\n finish({\n ok: false,\n phase: message.phase,\n error: message.error,\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n }\n });\n\n worker.on('error', (error: Error) => {\n if (settled) return;\n const text = error.message ?? String(error);\n const phase = /memory limit/i.test(text) ? 'memory' : 'runtime';\n finish({\n ok: false,\n phase,\n error: { message: text, stack: error.stack },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n });\n\n worker.on('exit', (code: number) => {\n if (settled) return;\n if (timedOut) return;\n finish({\n ok: false,\n phase: 'runtime',\n error: { message: `worker exited with code ${code ?? 'null'} before completion` },\n partial: { ops: ops.slice(), logs: logs.slice() },\n });\n });\n });\n}\n","/**\n * @generated by gen:sandbox-dts — DO NOT EDIT MANUALLY\n *\n * Runtime copy of the sandbox TypeScript disclosure. The model prompt imports\n * this value so its interface and the checked-in declaration cannot drift.\n */\nexport const EDIT_SANDBOX_API_DTS = [\n '/**',\n ' * @generated by gen:sandbox-dts — DO NOT EDIT MANUALLY',\n ' *',\n ' * Schema version: video-document/v0',\n ' * Semantic ops: 20',\n ' *',\n ' * Boundary: zod `superRefine` / custom refine rules are NOT introspectable and',\n ' * do not appear here. Business mutual-exclusion rules surface via runtime',\n ' * validation errors (L3 feedback channel).',\n ' *',\n ' * @example 读取→计算→批量写',\n ' * ```ts',\n ' * const clips = timeline.clipsInRange(0, 10_000);',\n ' * await edit.setVideoClipSpeedShift({',\n \" * clips: clips.map((c) => ({ clip_id: c.id, speed_shift: { category: 'linear', mode: 'constant', config: { linear: { speed: 1.5 } } } })),\",\n ' * });',\n ' * ```',\n ' *',\n ' * @example anchored 删除',\n ' * ```ts',\n \" * await edit.deleteVideoClips({ clip_ids: ['clip_a'], on_anchored: 'detach' });\",\n ' * ```',\n ' */',\n '',\n '/**',\n \" * A clip's playback-speed fact, the only thing `SetVideoClipSpeedShift` writes.\",\n ' */',\n 'export interface SpeedShift {',\n \" category: 'linear' | 'curve';\",\n ' mode: string;',\n ' config:',\n ' | {',\n ' linear: {',\n ' /**',\n ' * @constraint positive',\n ' */',\n ' speed: number;',\n ' };',\n ' }',\n ' | {',\n ' curve: {',\n ' /**',\n ' * @constraint minLength(2)',\n ' */',\n ' keyframes: {',\n ' /**',\n ' * @constraint min(0)',\n ' * @constraint max(1)',\n ' */',\n ' position: number;',\n ' /**',\n ' * @constraint min(0)',\n ' */',\n ' rate: number;',\n ' /**',\n ' * Bezier tangent handle (x, y)',\n ' */',\n ' in_tangent?: { x: number; y: number };',\n ' /**',\n ' * Bezier tangent handle (x, y)',\n ' */',\n ' out_tangent?: { x: number; y: number };',\n ' }[];',\n ' };',\n ' };',\n '}',\n '',\n '/**',\n ' * TTS voice summary attached to a speech',\n ' */',\n 'export interface Voice {',\n ' /**',\n ' * @constraint minLength(1)',\n ' */',\n ' id: string;',\n ' name: string;',\n '}',\n '',\n '/**',\n ' * A materialized speech-subtree write (speeches + their captions).',\n ' */',\n 'export interface SpeechAssets {',\n ' /**',\n ' * Materialized speech parts to write',\n ' * @constraint minLength(1)',\n ' */',\n ' speeches: {',\n ' /**',\n ' * The speech part ID (= side-effect speech_parts[].id)',\n ' * @constraint minLength(1)',\n ' */',\n ' speech_id: string;',\n ' /**',\n ' * Host video clip part ID the speech anchors to (RFC 02 §4)',\n ' * @constraint minLength(1)',\n ' */',\n ' anchor_part_id: string;',\n ' /**',\n ' * Offset within the host clip (speech.abs = host.abs + offset_ms)',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' offset_ms: number;',\n ' /**',\n ' * @constraint minLength(1)',\n ' */',\n ' audio_storage_key: string;',\n ' /**',\n ' * Duration in milliseconds (> 0)',\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' duration_ms: number;',\n ' audio_script: string;',\n ' /**',\n ' * Volume in decibels (-60.0 to 20.0; 0.0 = original, -60 = mute, +20 = max)',\n ' * @constraint min(-60)',\n ' * @constraint max(20)',\n ' */',\n ' volume: number;',\n ' /**',\n ' * TTS voice summary attached to a speech',\n ' */',\n ' voice: {',\n ' /**',\n ' * @constraint minLength(1)',\n ' */',\n ' id: string;',\n ' name: string;',\n ' };',\n ' /**',\n ' * @constraint minLength(1)',\n ' */',\n ' origin_speech_id: string;',\n ' /**',\n ' * Caption part IDs owned by this speech',\n ' */',\n ' caption_ids: string[];',\n ' }[];',\n ' /**',\n ' * Materialized caption parts owned by the speeches',\n ' */',\n ' captions: {',\n ' /**',\n ' * The caption part ID (= side-effect created_caption_parts[].id)',\n ' * @constraint minLength(1)',\n ' */',\n ' caption_id: string;',\n ' /**',\n ' * The owning speech part ID',\n ' * @constraint minLength(1)',\n ' */',\n ' speech_part_id: string;',\n ' text: string;',\n ' /**',\n ' * Offset within the host speech (caption.abs = speech.abs + start_ms)',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' start_ms: number;',\n ' /**',\n ' * Duration in milliseconds (> 0)',\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' duration_ms: number;',\n ' }[];',\n '}',\n '',\n 'export interface MoveVideoClipsInput {',\n ' /**',\n ' * List of video clips to move to new positions',\n ' * @constraint minLength(1)',\n ' */',\n ' clips: {',\n ' /**',\n ' * The video clip part ID to move',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_id: string;',\n ' /**',\n ' * New absolute start time in milliseconds on the timeline',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' new_start_ms: number;',\n ' /**',\n ' * Target track ID to move the clip to (optional)',\n ' * @constraint minLength(1)',\n ' */',\n ' new_track_id?: string;',\n ' }[];',\n '}',\n '',\n 'export interface MoveVideoClipsByAnchorInput {',\n ' /**',\n ' * Clips to move as one block, keeping their relative order. Need not be contiguous on the track.',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_ids: string[];',\n ' /**',\n ' * Where the moved block lands: before/after a reference clip, or at the head of the track',\n ' */',\n ' anchor:',\n ' | {',\n \" position: 'before';\",\n ' /**',\n ' * The moved block lands immediately before this clip',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_id: string;',\n ' }',\n ' | {',\n \" position: 'after';\",\n ' /**',\n ' * The moved block lands immediately after this clip',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_id: string;',\n ' }',\n \" | { position: 'track_start' };\",\n ' /**',\n ' * What happens to speeches anchored to the moved clips (required — see the policy doc)',\n ' */',\n \" on_anchored: 'follow' | 'keep_absolute';\",\n '}',\n '',\n 'export interface DeleteVideoClipsInput {',\n ' /**',\n ' * List of video clip part IDs to delete from the main track',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_ids: string[];',\n ' /**',\n ' * How to treat anchored children (default cascade)',\n ' */',\n \" on_anchored?: 'cascade' | 'detach';\",\n '}',\n '',\n '/**',\n ' * Add video clips to a track.',\n ' */',\n 'export interface AddVideoClipsInput {',\n ' /**',\n ' * List of video clips to create',\n ' * @constraint minLength(1)',\n ' */',\n ' clips: {',\n ' /**',\n ' * The media asset ID for the video clip',\n ' * @constraint minLength(1)',\n ' */',\n ' media_id: string;',\n ' /**',\n ' * Absolute start time in milliseconds on the timeline',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' start_ms?: number;',\n ' /**',\n \" * The source media's intrinsic full length in ms\",\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' media_duration_ms: number;',\n ' /**',\n ' * Trim window start in the media (default 0)',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' play_in?: number;',\n ' /**',\n ' * Trim window end in the media (default media_duration_ms)',\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' play_out?: number;',\n ' /**',\n ' * Target track ID (optional, defaults to main track)',\n ' * @constraint minLength(1)',\n ' */',\n ' track_id?: string;',\n ' }[];',\n ' /**',\n ' * Insert new clips before this clip ID',\n ' * @constraint minLength(1)',\n ' */',\n ' before_clip_id?: string;',\n ' /**',\n ' * Insert new clips after this clip ID',\n ' * @constraint minLength(1)',\n ' */',\n ' after_clip_id?: string;',\n '}',\n '',\n 'export interface AdjustVideoClipVolumeInput {',\n ' /**',\n ' * List of video clips with their new volume settings',\n ' * @constraint minLength(1)',\n ' */',\n ' clips: {',\n ' /**',\n ' * The video clip part ID to adjust volume for',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_id: string;',\n ' /**',\n ' * Volume in decibels (-60.0 to 20.0; 0.0 = original)',\n ' * @constraint min(-60)',\n ' * @constraint max(20)',\n ' */',\n ' volume: number;',\n ' }[];',\n '}',\n '',\n '/**',\n ' * Set the playback speed of existing video clips.',\n ' */',\n 'export interface SetVideoClipSpeedShiftInput {',\n ' /**',\n ' * Video clips with their new speed settings',\n ' * @constraint minLength(1)',\n ' */',\n ' clips: {',\n ' /**',\n ' * The video clip part ID to set speed for',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_id: string;',\n ' /**',\n ' * The new speed setting, or null to reset to 1×',\n ' */',\n ' speed_shift: {',\n \" category: 'linear' | 'curve';\",\n ' mode: string;',\n ' config:',\n ' | {',\n ' linear: {',\n ' /**',\n ' * @constraint positive',\n ' */',\n ' speed: number;',\n ' };',\n ' }',\n ' | {',\n ' curve: {',\n ' /**',\n ' * @constraint minLength(2)',\n ' */',\n ' keyframes: {',\n ' /**',\n ' * @constraint min(0)',\n ' * @constraint max(1)',\n ' */',\n ' position: number;',\n ' /**',\n ' * @constraint min(0)',\n ' */',\n ' rate: number;',\n ' /**',\n ' * Bezier tangent handle (x, y)',\n ' */',\n ' in_tangent?: { x: number; y: number };',\n ' /**',\n ' * Bezier tangent handle (x, y)',\n ' */',\n ' out_tangent?: { x: number; y: number };',\n ' }[];',\n ' };',\n ' };',\n ' } | null;',\n ' }[];',\n '}',\n '',\n '/**',\n ' * Replace the media backing existing video clips.',\n ' */',\n 'export interface ReplaceVideoClipContentInput {',\n ' /**',\n ' * Video clips whose media is being replaced',\n ' * @constraint minLength(1)',\n ' */',\n ' clips: {',\n ' /**',\n ' * Existing video clip part ID to re-point',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_id: string;',\n ' /**',\n ' * The new media asset ID',\n ' * @constraint minLength(1)',\n ' */',\n ' origin_media_id: string;',\n ' /**',\n \" * The new media's intrinsic full length\",\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' media_duration_ms: number;',\n ' /**',\n ' * Trim window start in the new media (usually 0)',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' play_in: number;',\n ' /**',\n ' * Trim window end in the new media (usually = media_duration_ms)',\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' play_out: number;',\n ' /**',\n ' * Volume in decibels (-60.0 to 20.0; 0.0 = original, -60 = mute, +20 = max)',\n ' * @constraint min(-60)',\n ' * @constraint max(20)',\n ' */',\n ' volume: number;',\n ' }[];',\n '}',\n '',\n 'export interface ReplaceVideoClipSequenceInput {',\n ' /**',\n ' * The clips being replaced: a contiguous main-track run, listed in timeline order',\n ' * @constraint minLength(1)',\n ' */',\n ' old_clip_ids: string[];',\n ' /**',\n ' * The replacement clips, in the order they take on the track',\n ' * @constraint minLength(1)',\n ' */',\n ' new_clips: {',\n ' /**',\n ' * The replacement media asset ID. Omit to create an empty placeholder clip.',\n ' * @constraint minLength(1)',\n ' */',\n ' media_id?: string;',\n ' /**',\n \" * The source media's intrinsic full length in ms\",\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' media_duration_ms: number;',\n ' /**',\n ' * Trim window start in the media (default 0)',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' play_in?: number;',\n ' /**',\n ' * Trim window end in the media (default media_duration_ms)',\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' play_out?: number;',\n ' }[];',\n ' /**',\n ' * What happens to speeches anchored to the replaced clips (required — see the policy doc)',\n ' */',\n \" on_anchored: 'remap' | 'cascade';\",\n '}',\n '',\n '/**',\n ' * Re-trim existing video clips (the user-facing \"adjust duration\" gesture is a trim of the source window).',\n ' */',\n 'export interface AdjustVideoClipDurationInput {',\n ' /**',\n ' * Video clips with their new trim windows',\n ' * @constraint minLength(1)',\n ' */',\n ' clips: {',\n ' /**',\n ' * The video clip part ID to re-trim',\n ' * @constraint minLength(1)',\n ' */',\n ' clip_id: string;',\n ' /**',\n ' * New trim window start in the source media',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' play_in: number;',\n ' /**',\n ' * New trim window end in the source media',\n ' * @constraint int',\n ' * @constraint positive',\n ' */',\n ' play_out: number;',\n ' }[];',\n '}',\n '',\n '/**',\n ' * Add speeches (and their captions).',\n ' */',\n 'export interface AddSpeechesInput extends SpeechAssets {}',\n '',\n '/**',\n ' * Delete speeches with their captions.',\n ' */',\n 'export interface DeleteSpeechesInput {',\n ' /**',\n ' * Speech part IDs to delete (their captions cascade-delete)',\n ' * @constraint minLength(1)',\n ' */',\n ' speech_ids: string[];',\n '}',\n '',\n '/**',\n ' * Move speeches in time.',\n ' */',\n 'export interface MoveSpeechesInput {',\n ' /**',\n ' * Speeches to move to new positions',\n ' * @constraint minLength(1)',\n ' */',\n ' speeches: {',\n ' /**',\n ' * The speech part ID to move',\n ' * @constraint minLength(1)',\n ' */',\n ' speech_id: string;',\n ' /**',\n ' * New absolute start time on the timeline',\n ' * @constraint int',\n ' * @constraint min(0)',\n ' */',\n ' new_start_ms: number;',\n ' }[];',\n '}',\n '',\n '/**',\n \" * Change a speech's script or voice.\",\n ' */',\n 'export interface ChangeSpeechScriptInput extends SpeechAssets {}',\n '',\n 'export interface ChangeSpeechVoiceInput extends SpeechAssets {}',\n '',\n 'export interface AdjustSpeechVolumeInput {',\n ' /**',\n ' * List of speeches with their new volume settings',\n ' * @constraint minLength(1)',\n ' */',\n ' speeches: {',\n ' /**',\n ' * The speech part ID to adjust volume for',\n ' * @constraint minLength(1)',\n ' */',\n ' speech_id: string;',\n ' /**',\n ' * Volume in decibels (-60.0 to 20.0; 0.0 = original)',\n ' * @constraint min(-60)',\n ' * @constraint max(20)',\n ' */',\n ' volume: number;',\n ' }[];',\n '}',\n '',\n '/**',\n \" * Toggle caption visibility (the caption track's `is_hidden` flag).\",\n ' */',\n 'export interface SetCaptionVisibilityInput {',\n ' /**',\n ' * Whether the caption track is hidden',\n ' */',\n ' is_hidden: boolean;',\n '}',\n '',\n '/**',\n ' * Set the caption visual style.',\n ' */',\n 'export interface SetCaptionStyleInput {',\n ' /**',\n ' * Font ID referencing a font from the font library',\n ' * @constraint minLength(1)',\n ' */',\n ' font_id?: string;',\n ' /**',\n ' * Font size in points',\n ' * @constraint positive',\n ' */',\n ' font_size?: number;',\n ' /**',\n ' * Font color as hex string, e.g. \"#FFFFFF\"',\n ' * @constraint minLength(1)',\n ' */',\n ' font_color?: string;',\n ' /**',\n ' * Numeric font weight, e.g. 400 or 700',\n ' * @constraint int',\n ' */',\n ' font_weight?: number;',\n ' /**',\n ' * Entrance animation preset ID, e.g. \"fade\" or \"none\"',\n ' */',\n ' entrance_animation?: string;',\n ' /**',\n ' * Entrance animation duration in ms',\n ' * @constraint min(0)',\n ' */',\n ' entrance_animation_duration_ms?: number;',\n ' /**',\n ' * Outline/stroke color as hex string, e.g. \"#000000\"',\n ' * @constraint minLength(1)',\n ' */',\n ' stroke_color?: string;',\n ' /**',\n ' * Outline/stroke width in pixels',\n ' * @constraint min(0)',\n ' */',\n ' stroke_width?: number;',\n ' /**',\n ' * Caption center X as a fraction (0.0 to 1.0)',\n ' */',\n ' position_x?: number;',\n ' /**',\n ' * Caption center Y as a fraction (0.0 to 1.0)',\n ' */',\n ' position_y?: number;',\n '}',\n '',\n '/**',\n ' * Set the document BGM.',\n ' */',\n 'export interface SetBgmInput {',\n ' /**',\n ' * The bgm part ID to write',\n ' * @constraint minLength(1)',\n ' */',\n ' bgm_id: string;',\n ' /**',\n ' * @constraint minLength(1)',\n ' */',\n ' audio_storage_key: string;',\n ' /**',\n ' * The media asset ID',\n ' * @constraint minLength(1)',\n ' */',\n ' origin_media_id: string;',\n ' /**',\n ' * Volume in decibels (-60.0 to 20.0; 0.0 = original, -60 = mute, +20 = max)',\n ' * @constraint min(-60)',\n ' * @constraint max(20)',\n ' */',\n ' volume: number;',\n '}',\n '',\n '/**',\n ' * Remove the document BGM.',\n ' */',\n 'export interface DeleteBgmInput {',\n ' [key: string]: never;',\n '}',\n '',\n 'export interface AdjustBgmVolumeInput {',\n ' /**',\n ' * List of bgm parts with their new volume settings',\n ' * @constraint minLength(1)',\n ' */',\n ' bgm: {',\n ' /**',\n ' * The bgm part ID to adjust volume for',\n ' * @constraint minLength(1)',\n ' */',\n ' bgm_id: string;',\n ' /**',\n ' * Volume in decibels (-60.0 to 20.0; 0.0 = original)',\n ' * @constraint min(-60)',\n ' * @constraint max(20)',\n ' */',\n ' volume: number;',\n ' }[];',\n '}',\n '',\n '/** Agent write surface — one method per SemanticOp kind. */',\n 'export interface EditApi {',\n ' moveVideoClips(input: MoveVideoClipsInput): Promise<void>;',\n ' moveVideoClipsByAnchor(input: MoveVideoClipsByAnchorInput): Promise<void>;',\n ' deleteVideoClips(input: DeleteVideoClipsInput): Promise<void>;',\n ' /** Add video clips to a track. */',\n ' addVideoClips(input: AddVideoClipsInput): Promise<void>;',\n ' adjustVideoClipVolume(input: AdjustVideoClipVolumeInput): Promise<void>;',\n ' /** Set the playback speed of existing video clips. */',\n ' setVideoClipSpeedShift(input: SetVideoClipSpeedShiftInput): Promise<void>;',\n ' /** Replace the media backing existing video clips. */',\n ' replaceVideoClipContent(input: ReplaceVideoClipContentInput): Promise<void>;',\n ' replaceVideoClipSequence(input: ReplaceVideoClipSequenceInput): Promise<void>;',\n ' /** Re-trim existing video clips (the user-facing \"adjust duration\" gesture is a trim of the source window). */',\n ' adjustVideoClipDuration(input: AdjustVideoClipDurationInput): Promise<void>;',\n ' /** Add speeches (and their captions). */',\n ' addSpeeches(input: AddSpeechesInput): Promise<void>;',\n ' /** Delete speeches with their captions. */',\n ' deleteSpeeches(input: DeleteSpeechesInput): Promise<void>;',\n ' /** Move speeches in time. */',\n ' moveSpeeches(input: MoveSpeechesInput): Promise<void>;',\n \" /** Change a speech's script or voice. */\",\n ' changeSpeechScript(input: ChangeSpeechScriptInput): Promise<void>;',\n ' changeSpeechVoice(input: ChangeSpeechVoiceInput): Promise<void>;',\n ' adjustSpeechVolume(input: AdjustSpeechVolumeInput): Promise<void>;',\n \" /** Toggle caption visibility (the caption track's `is_hidden` flag). */\",\n ' setCaptionVisibility(input: SetCaptionVisibilityInput): Promise<void>;',\n ' /** Set the caption visual style. */',\n ' setCaptionStyle(input: SetCaptionStyleInput): Promise<void>;',\n ' /** Set the document BGM. */',\n ' setBgm(input: SetBgmInput): Promise<void>;',\n ' /** Remove the document BGM. */',\n ' deleteBgm(input: DeleteBgmInput): Promise<void>;',\n ' adjustBgmVolume(input: AdjustBgmVolumeInput): Promise<void>;',\n '}',\n '',\n '/** Clip hit from `clipsInRange`. */',\n 'export interface TimelineClipDescriptor {',\n ' id: string;',\n ' start_ms: number;',\n ' end_ms: number;',\n ' duration_ms: number;',\n ' speed_shift: unknown;',\n ' volume: number | undefined;',\n ' media_id: string | undefined;',\n '}',\n '',\n '/** Part descriptor from `part(id)`. */',\n 'export interface TimelinePartDescriptor {',\n ' id: string;',\n ' kind: string;',\n ' lane: string;',\n ' start_ms: number;',\n ' end_ms: number;',\n ' duration_ms: number;',\n ' part: unknown;',\n '}',\n '',\n '/** Opaque VideoDraft projection (full IDL lives in host document types). */',\n 'export type VideoDraftProjection = {',\n ' readonly timeline?: { readonly duration_ms?: number };',\n ' readonly [key: string]: unknown;',\n '};',\n '',\n '/** Agent read surface over the forked document. */',\n 'export interface TimelineApi {',\n ' /** Snapshot the current VideoDraft projection. */',\n ' snapshot(): VideoDraftProjection;',\n ' /** Clips whose midpoint falls in `[startMs, endMs)`. */',\n ' clipsInRange(startMs: number, endMs: number): TimelineClipDescriptor[];',\n ' /** Look up a part by id, or null if missing. */',\n ' part(id: string): TimelinePartDescriptor | null;',\n '}',\n '',\n '/** Opaque checkpoint handle for rollback. */',\n 'export interface SandboxCheckpoint {',\n ' readonly index: number;',\n '}',\n '',\n 'export declare const edit: EditApi;',\n 'export declare const timeline: TimelineApi;',\n '',\n '/** Capture a rollback point. */',\n 'export declare function checkpoint(): SandboxCheckpoint;',\n '/** Roll the sandbox document back to a prior checkpoint. */',\n 'export declare function rollbackTo(cp: SandboxCheckpoint): void;',\n '/** Host-injected script arguments (opaque). */',\n 'export declare const inputs: unknown;',\n '',\n].join('\\n');\n","import { EDIT_SANDBOX_API_DTS } from './sandbox/generated/edit-sandbox-model-context.ts';\n\nexport const MEDEO_TOOL_DESCRIPTION = `\nEdit a Medeo video document through a deterministic, side-effect-free JavaScript sandbox.\n\nOperations:\n- snapshot: return the compact timeline projection and opaque base version.\n- run-edit-script: execute JavaScript against a forked snapshot. Inspect timeline.*, compute coordinates, and call edit.* methods in one script. The sandbox has no network, storage, clock, or generation access. Pass materialized asset/speech facts through inputs. A successful run returns preview, logs, base_version, and plan_id — not the full op journal.\n- commit-plan: replay a cached plan_id into the live MengineDocSession through SemanticEditor. Use validation=version for all-or-nothing commit, or preflight to localize an op conflict after concurrent edits.\n\nDefault 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. On version mismatch, rerun snapshot and the script; never try to patch a rejected journal by hand.\n`.trim();\n\nconst MEDEO_TOOL_EXECUTION_RULES = `\nThe host supplies the current document. Do not ask for, invent, or pass a document id.\nUse timeline.snapshot() for the whole draft projection. Its duration is timeline.snapshot().timeline?.duration_ms; there is no top-level duration_ms.\nUse only the globals and methods declared by the following TypeScript interface. Values not declared here are unavailable.\n`.trim();\n\nexport interface RenderMedeoModelContextInput {\n documentVersion: string;\n updatedSincePreviousModelCall: boolean | null;\n}\n\n/** Render the complete MEngine-owned context injected before one model call. */\nexport function renderMedeoModelContext(input: RenderMedeoModelContextInput): string {\n const updated =\n input.updatedSincePreviousModelCall == null\n ? 'unknown (first model call)'\n : String(input.updatedSincePreviousModelCall);\n return `\n${MEDEO_TOOL_DESCRIPTION}\n\n${MEDEO_TOOL_EXECUTION_RULES}\n\nCurrent MEngine document state (sampled dynamically immediately before this model call):\n- document_version: ${JSON.stringify(input.documentVersion)}\n- updated_since_previous_model_call: ${updated}\n\nWhen updated_since_previous_model_call is true, the document changed after the previous model call. The change may have come from this tool or another editor, so take a fresh snapshot before planning further edits.\n\nSandbox TypeScript interface:\n\\`\\`\\`ts\n${EDIT_SANDBOX_API_DTS}\n\\`\\`\\`\n`.trim();\n}\n","export const MEDEO_TOOL_NAME = 'medeo';\n\nexport type MedeoToolOp = 'snapshot' | 'run-edit-script' | 'commit-plan';\n\n/**\n * JSON Schema for the host-facing three-op `medeo` tool surface.\n *\n * The schema intentionally does not return or accept the full op journal:\n * journals stay in the tool process and are referenced by `plan_id`. This keeps\n * large intermediate products out of model context while preserving the exact\n * journal used for commit.\n */\nexport const MEDEO_TOOL_PARAMETERS = {\n type: 'object',\n required: ['op', 'doc_id'],\n additionalProperties: false,\n properties: {\n op: {\n type: 'string',\n enum: ['snapshot', 'run-edit-script', 'commit-plan'],\n description: 'Which Medeo document operation to run.',\n },\n doc_id: {\n type: 'string',\n minLength: 1,\n description: 'Medeo document id. Copy it from the host context; never invent it.',\n },\n script: {\n type: 'string',\n minLength: 1,\n description:\n 'JavaScript body for run-edit-script. It receives edit, timeline, checkpoint, rollbackTo, inputs, and console; perform all calculations in the script.',\n },\n inputs: {\n type: 'object',\n description:\n 'Pre-materialized, side-effect-free values passed into the script. Generation and network IO must happen in the host before this call.',\n },\n timeout_ms: {\n type: 'integer',\n minimum: 1,\n description: 'Maximum script wall-clock time after worker startup (default 2000).',\n },\n memory_limit_mb: {\n type: 'integer',\n minimum: 16,\n description: 'Worker old-generation memory ceiling in MB (default 256).',\n },\n auto_commit: {\n type: 'boolean',\n description:\n 'Commit the returned plan immediately after the sandbox succeeds. Default false: return preview plus plan_id for explicit commit.',\n },\n plan_id: {\n type: 'string',\n minLength: 1,\n description: 'Plan id returned by run-edit-script; required by commit-plan.',\n },\n validation: {\n type: 'string',\n enum: ['version', 'preflight'],\n description:\n 'commit-plan mode: version rejects any concurrent change; preflight revalidates each op against the current snapshot.',\n },\n },\n oneOf: [\n {\n required: ['op', 'doc_id'],\n properties: {\n op: { const: 'snapshot' },\n doc_id: { $ref: '#/properties/doc_id' },\n },\n additionalProperties: false,\n },\n {\n required: ['op', 'doc_id', 'script'],\n properties: {\n op: { const: 'run-edit-script' },\n doc_id: { $ref: '#/properties/doc_id' },\n script: { $ref: '#/properties/script' },\n inputs: { $ref: '#/properties/inputs' },\n timeout_ms: { $ref: '#/properties/timeout_ms' },\n memory_limit_mb: { $ref: '#/properties/memory_limit_mb' },\n auto_commit: { $ref: '#/properties/auto_commit' },\n },\n additionalProperties: false,\n },\n {\n required: ['op', 'doc_id', 'plan_id'],\n properties: {\n op: { const: 'commit-plan' },\n doc_id: { $ref: '#/properties/doc_id' },\n plan_id: { $ref: '#/properties/plan_id' },\n validation: { $ref: '#/properties/validation' },\n },\n additionalProperties: false,\n },\n ],\n} as const;\n","import {\n createPlainMemoryAdapter,\n replayJournal,\n ValidationError,\n type JournalEntry,\n type MengineDocSession,\n type SemanticOpName,\n} from '@mengine/medeo-client';\n\n/**\n * A sandbox journal plus the opaque version token taken at fork time.\n * `commitPlan` rejects the whole plan when the live session has moved on\n * (phase-1 version gate), or localizes a business conflict to a journal\n * entry under `{ validation: 'preflight' }`.\n */\nexport interface CommitPlan {\n /** `session.version()` at the moment the sandbox was forked. */\n base_version: string;\n ops: readonly JournalEntry[];\n}\n\nexport type CommitPlanResult =\n | { kind: 'committed'; ops_applied: number }\n | { kind: 'rejected'; reason: 'version_mismatch'; expected: string; actual: string }\n | {\n kind: 'rejected';\n reason: 'op_conflict';\n /** Failing entry index in the journal — agent rerun anchor. */\n index: number;\n op_kind: SemanticOpName;\n /** Validator message, passed through verbatim (never a raw Error). */\n message: string;\n };\n\nexport interface CommitPlanOptions {\n /** `'version'` (default, phase-1 hard gate) | `'preflight'` (phase-2 per-op revalidation). */\n validation?: 'version' | 'preflight';\n}\n\n/**\n * Replay a sandbox journal into a live session through its document adapter\n * (SemanticEditor → Loro → mengine-server).\n *\n * - Default / `{ validation: 'version' }`: if `session.version()` ≠\n * `plan.base_version`, reject with zero writes.\n * - `{ validation: 'preflight' }`: skip the version gate; revalidate each op\n * against a PlainMemoryAdapter seeded from the current live snapshot, then\n * replay for real. A SchemaValidator failure becomes `op_conflict` with the\n * failing entry's index. Journal integrity errors (unrecorded/unconsumed\n * ids) still propagate as throws in both modes.\n */\nexport async function commitPlan(\n session: MengineDocSession,\n plan: CommitPlan,\n options?: CommitPlanOptions,\n): Promise<CommitPlanResult> {\n if (options?.validation === 'preflight') {\n return commitPlanPreflight(session, plan);\n }\n\n const actual = session.version();\n if (actual !== plan.base_version) {\n return {\n kind: 'rejected',\n reason: 'version_mismatch',\n expected: plan.base_version,\n actual,\n };\n }\n\n await replayJournal(session.documentAdapter, plan.ops);\n return { kind: 'committed', ops_applied: plan.ops.length };\n}\n\n/**\n * Phase-2 path: scratch revalidation then real replay. Each entry is driven\n * through `replayJournal` alone so a ValidationError maps to a stable index;\n * integrity throws are not wrapped.\n */\nasync function commitPlanPreflight(session: MengineDocSession, plan: CommitPlan): Promise<CommitPlanResult> {\n const scratch = createPlainMemoryAdapter(session.snapshot());\n\n for (let index = 0; index < plan.ops.length; index++) {\n const entry = plan.ops[index];\n if (entry == null) continue;\n try {\n await replayJournal(scratch, [entry]);\n } catch (error) {\n if (error instanceof ValidationError) {\n return opConflict(index, entry.kind, error.message);\n }\n throw error;\n }\n }\n\n // Real replay: optimistic window may still collide; wrap ValidationError the\n // same way. Prior entries in this loop have already been written.\n for (let index = 0; index < plan.ops.length; index++) {\n const entry = plan.ops[index];\n if (entry == null) continue;\n try {\n await replayJournal(session.documentAdapter, [entry]);\n } catch (error) {\n if (error instanceof ValidationError) {\n return opConflict(index, entry.kind, `real replay: ${error.message}`);\n }\n throw error;\n }\n }\n\n return { kind: 'committed', ops_applied: plan.ops.length };\n}\n\nfunction opConflict(index: number, op_kind: SemanticOpName, message: string): CommitPlanResult {\n return { kind: 'rejected', reason: 'op_conflict', index, op_kind, message };\n}\n","import { randomUUID } from 'node:crypto';\n\nimport {\n createMirrorVideoDocument,\n MengineDocSession,\n MengineHttpClient,\n MengineHttpRequestError,\n toVideoDocument,\n type MengineDocSessionOptions,\n type VideoDocument,\n type VideoDraft,\n} from '@mengine/medeo-client';\n\nimport { renderCompactProjection } from './document/compact-projection.ts';\nimport { MEDEO_TOOL_DESCRIPTION, renderMedeoModelContext } from './prompt.ts';\nimport { runEditScript } from './sandbox/node-host.ts';\nimport type { ChangePlan } from './sandbox/script-session.ts';\nimport { MEDEO_TOOL_NAME, MEDEO_TOOL_PARAMETERS, type MedeoToolOp } from './schema.ts';\nimport { commitPlan, type CommitPlanOptions, type CommitPlanResult } from './session/commit-plan.ts';\n\ntype ContextualValue<T> = T | ((docId: string) => T | undefined);\n\nexport interface CreateMedeoToolOptions {\n /**\n * Mengine HTTP origin for a document. The host owns environment routing\n * (local/stg/prd/lane) and may return a different origin per document.\n * Sessions cache by doc id, so the origin must remain stable for that doc.\n */\n httpOrigin: ContextualValue<string>;\n /** Optional bearer token, evaluated for each HTTP request. */\n authToken?: ContextualValue<string>;\n /** Optional end-user id header, evaluated for each HTTP request. */\n userId?: ContextualValue<string>;\n /** Stable agent peer id. Supply a host-scoped value so audit provenance is durable. */\n peerId?: ContextualValue<string>;\n /**\n * Load the authoritative legacy draft used to create a missing Mengine\n * document. The tool owns the get-or-create flow: it first probes Mengine,\n * converts this draft into a VideoDocument only on a 404, bootstraps the\n * snapshot, and tolerates a concurrent creator winning the race.\n */\n loadInitialDraft?: (docId: string) => Promise<VideoDraft>;\n fetchImpl?: typeof fetch;\n sseReconnectDelayMs?: number;\n /** Defaults passed to runEditScript; each call may override them. */\n sandbox?: { timeoutMs?: number; memoryLimitMb?: number };\n /** Maximum cached plans; oldest plans are evicted (default 16). */\n maxPlans?: number;\n /** Maximum model-call version baselines retained across host contexts (default 128). */\n maxModelContexts?: number;\n}\n\nexport interface MedeoModelContextInput {\n /** Internal MEngine document id. This is host-supplied and never model-facing. */\n doc_id: string;\n /** Stable host conversation/session key used to compare consecutive model calls. */\n context_id: string;\n}\n\nexport interface MedeoModelContext {\n /** Complete MEngine-owned prompt: workflow, runtime state, and sandbox TypeScript interface. */\n prompt: string;\n document_version: string;\n updated_since_previous_model_call: boolean | null;\n}\n\nexport type MedeoToolInput =\n | { op: 'snapshot'; doc_id: string }\n | {\n op: 'run-edit-script';\n doc_id: string;\n script: string;\n inputs?: Record<string, unknown>;\n timeout_ms?: number;\n memory_limit_mb?: number;\n auto_commit?: boolean;\n }\n | {\n op: 'commit-plan';\n doc_id: string;\n plan_id: string;\n validation?: 'version' | 'preflight';\n };\n\nexport type MedeoToolResult =\n | { ok: true; op: 'snapshot'; doc_id: string; version: string; preview: string }\n | {\n ok: true;\n op: 'run-edit-script';\n doc_id: string;\n plan_id: string;\n base_version: string;\n ops_count: number;\n preview: string;\n logs: string[];\n duration_ms: number;\n committed?: boolean;\n commit_result?: CommitPlanResult;\n }\n | {\n ok: false;\n op: 'run-edit-script';\n doc_id: string;\n phase: 'parse' | 'runtime' | 'timeout' | 'memory';\n error: { message: string; line?: number; column?: number; stack?: string };\n partial: { ops_count: number; logs: string[] };\n }\n | {\n ok: true;\n op: 'commit-plan';\n doc_id: string;\n plan_id: string;\n committed: boolean;\n result: CommitPlanResult;\n }\n | { ok: false; op: MedeoToolOp; error: string };\n\nexport interface MedeoTool {\n name: typeof MEDEO_TOOL_NAME;\n description: typeof MEDEO_TOOL_DESCRIPTION;\n parameters: typeof MEDEO_TOOL_PARAMETERS;\n getModelContext(input: MedeoModelContextInput): Promise<MedeoModelContext>;\n handle(input: unknown): Promise<MedeoToolResult>;\n close(): Promise<void>;\n}\n\nexport type MedeoInitialDraft = VideoDraft;\n\ninterface CachedPlan {\n docId: string;\n plan: ChangePlan;\n}\n\nconst DEFAULT_MAX_PLANS = 16;\nconst DEFAULT_MAX_MODEL_CONTEXTS = 128;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction optionalContext<T>(value: ContextualValue<T> | undefined, docId: string): T | undefined {\n if (value === undefined) return undefined;\n return typeof value === 'function' ? (value as (id: string) => T | undefined)(docId) : value;\n}\n\nfunction requiredContext(value: ContextualValue<string>, docId: string, field: string): string {\n const resolved = optionalContext(value, docId)?.trim();\n if (resolved == null || resolved.length === 0) {\n throw new Error(`${field} must resolve to a non-empty string for doc ${docId}`);\n }\n return resolved;\n}\n\nfunction parseInput(value: unknown): MedeoToolInput {\n if (!isRecord(value)) throw new Error('input must be an object');\n const op = value.op;\n const docId = value.doc_id;\n if (typeof op !== 'string') throw new Error('op must be a string');\n if (typeof docId !== 'string' || docId.trim().length === 0) throw new Error('doc_id must be a non-empty string');\n\n if (op === 'snapshot') return { op, doc_id: docId };\n\n if (op === 'run-edit-script') {\n if (typeof value.script !== 'string' || value.script.length === 0) {\n throw new Error('script must be a non-empty string');\n }\n if (value.inputs !== undefined && !isRecord(value.inputs)) {\n throw new Error('inputs must be an object');\n }\n const timeoutMs = value.timeout_ms;\n if (timeoutMs !== undefined && (typeof timeoutMs !== 'number' || !Number.isInteger(timeoutMs) || timeoutMs <= 0)) {\n throw new Error('timeout_ms must be a positive integer');\n }\n const memoryLimitMb = value.memory_limit_mb;\n if (\n memoryLimitMb !== undefined &&\n (typeof memoryLimitMb !== 'number' || !Number.isInteger(memoryLimitMb) || memoryLimitMb < 16)\n ) {\n throw new Error('memory_limit_mb must be an integer >= 16');\n }\n if (value.auto_commit !== undefined && typeof value.auto_commit !== 'boolean') {\n throw new Error('auto_commit must be a boolean');\n }\n return {\n op,\n doc_id: docId,\n script: value.script,\n ...(value.inputs !== undefined ? { inputs: value.inputs } : {}),\n ...(timeoutMs !== undefined ? { timeout_ms: timeoutMs } : {}),\n ...(memoryLimitMb !== undefined ? { memory_limit_mb: memoryLimitMb } : {}),\n ...(value.auto_commit !== undefined ? { auto_commit: value.auto_commit } : {}),\n };\n }\n\n if (op === 'commit-plan') {\n if (typeof value.plan_id !== 'string' || value.plan_id.length === 0) {\n throw new Error('plan_id must be a non-empty string');\n }\n if (value.validation !== undefined && value.validation !== 'version' && value.validation !== 'preflight') {\n throw new Error('validation must be \"version\" or \"preflight\"');\n }\n return {\n op,\n doc_id: docId,\n plan_id: value.plan_id,\n ...(value.validation !== undefined ? { validation: value.validation } : {}),\n };\n }\n\n throw new Error(`unknown op: ${op}`);\n}\n\n/**\n * Create the self-contained Medeo LLM tool.\n *\n * The package owns session construction, compact projection, sandbox execution,\n * plan caching, commit, document get-or-create, and shutdown. The host supplies\n * environment facts plus the authoritative legacy draft loader used only when\n * Mengine has no document yet.\n */\nexport function createMedeoTool(options: CreateMedeoToolOptions): MedeoTool {\n const sessions = new Map<string, Promise<MengineDocSession>>();\n const plans = new Map<string, CachedPlan>();\n const modelContextVersions = new Map<string, string>();\n const maxPlans = options.maxPlans ?? DEFAULT_MAX_PLANS;\n const maxModelContexts = options.maxModelContexts ?? DEFAULT_MAX_MODEL_CONTEXTS;\n let closed = false;\n\n async function getSession(docId: string): Promise<MengineDocSession> {\n if (closed) throw new Error('medeo tool is closed');\n const existing = sessions.get(docId);\n if (existing != null) return await existing;\n\n const created = (async () => {\n const client = new MengineHttpClient({\n docId,\n httpOrigin: requiredContext(options.httpOrigin, docId, 'httpOrigin'),\n ...(options.authToken !== undefined ? { authToken: () => optionalContext(options.authToken, docId) } : {}),\n ...(options.userId !== undefined ? { userId: () => optionalContext(options.userId, docId) } : {}),\n ...(options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}),\n });\n const peerId = optionalContext(options.peerId, docId) as MengineDocSessionOptions['peerId'] | undefined;\n await getOrCreateDocument(client, docId, peerId);\n const session = new MengineDocSession({\n docId,\n client,\n ...(peerId !== undefined ? { peerId } : {}),\n ...(options.sseReconnectDelayMs !== undefined ? { sseReconnectDelayMs: options.sseReconnectDelayMs } : {}),\n });\n try {\n await session.start();\n return session;\n } catch (error) {\n session.destroy();\n throw error;\n }\n })();\n\n sessions.set(docId, created);\n try {\n return await created;\n } catch (error) {\n if (sessions.get(docId) === created) sessions.delete(docId);\n throw error;\n }\n }\n\n async function getOrCreateDocument(\n client: MengineHttpClient,\n docId: string,\n peerId: MengineDocSessionOptions['peerId'] | undefined,\n ): Promise<void> {\n try {\n await client.fetchSnapshot();\n return;\n } catch (error) {\n if (!(error instanceof MengineHttpRequestError) || error.status !== 404) throw error;\n if (options.loadInitialDraft === undefined) throw error;\n }\n\n const draft = await options.loadInitialDraft(docId);\n const document = toVideoDocument(draft);\n const seed = createMirrorVideoDocument(document, {\n ...(peerId !== undefined ? { peerId } : {}),\n origin: 'mengine.medeo_tool.bootstrap',\n });\n\n try {\n await client.bootstrapSnapshot(seed.export({ mode: 'snapshot' }));\n } catch (error) {\n // Bootstrap is create-only. If another worker created the same document\n // after our 404 probe, accept that winner only after an authenticated\n // snapshot read proves the document now exists and is accessible.\n if (!(error instanceof MengineHttpRequestError) || error.status !== 400) throw error;\n await client.fetchSnapshot();\n }\n }\n\n function rememberPlan(docId: string, plan: ChangePlan): string {\n const planId = randomUUID();\n plans.set(planId, { docId, plan });\n while (plans.size > maxPlans) {\n const oldest = plans.keys().next().value;\n if (oldest === undefined) break;\n plans.delete(oldest);\n }\n return planId;\n }\n\n async function getModelContext(input: MedeoModelContextInput): Promise<MedeoModelContext> {\n const docId = input.doc_id.trim();\n const contextId = input.context_id.trim();\n if (docId.length === 0) throw new Error('doc_id must be a non-empty string');\n if (contextId.length === 0) throw new Error('context_id must be a non-empty string');\n\n const session = await getSession(docId);\n const documentVersion = session.version();\n const baselineKey = `${contextId}\\u0000${docId}`;\n const previousVersion = modelContextVersions.get(baselineKey);\n const updatedSincePreviousModelCall = previousVersion == null ? null : previousVersion !== documentVersion;\n\n // Refresh insertion order so the bounded map behaves as an LRU.\n modelContextVersions.delete(baselineKey);\n modelContextVersions.set(baselineKey, documentVersion);\n while (modelContextVersions.size > maxModelContexts) {\n const oldest = modelContextVersions.keys().next().value;\n if (oldest === undefined) break;\n modelContextVersions.delete(oldest);\n }\n\n return {\n prompt: renderMedeoModelContext({ documentVersion, updatedSincePreviousModelCall }),\n document_version: documentVersion,\n updated_since_previous_model_call: updatedSincePreviousModelCall,\n };\n }\n\n async function confirmCommitted(session: MengineDocSession, result: CommitPlanResult): Promise<CommitPlanResult> {\n if (result.kind === 'committed' && result.ops_applied > 0) {\n await session.waitForServerAck();\n }\n return result;\n }\n\n async function snapshot(input: Extract<MedeoToolInput, { op: 'snapshot' }>): Promise<MedeoToolResult> {\n const session = await getSession(input.doc_id);\n const document = session.snapshot();\n return {\n ok: true,\n op: 'snapshot',\n doc_id: input.doc_id,\n version: session.version(),\n preview: renderCompactProjection(document),\n };\n }\n\n async function run(input: Extract<MedeoToolInput, { op: 'run-edit-script' }>): Promise<MedeoToolResult> {\n const session = await getSession(input.doc_id);\n const document: VideoDocument = session.snapshot();\n const baseVersion = session.version();\n const result = await runEditScript({\n document,\n baseVersion,\n script: input.script,\n ...(input.inputs !== undefined ? { inputs: input.inputs } : {}),\n timeoutMs: input.timeout_ms ?? options.sandbox?.timeoutMs,\n memoryLimitMb: input.memory_limit_mb ?? options.sandbox?.memoryLimitMb,\n });\n\n if (!result.ok) {\n return {\n ok: false,\n op: 'run-edit-script',\n doc_id: input.doc_id,\n phase: result.phase,\n error: result.error,\n partial: { ops_count: result.partial.ops.length, logs: result.partial.logs },\n };\n }\n\n const planId = rememberPlan(input.doc_id, result.plan);\n const base = {\n ok: true as const,\n op: 'run-edit-script' as const,\n doc_id: input.doc_id,\n plan_id: planId,\n base_version: baseVersion,\n ops_count: result.plan.ops.length,\n preview: result.plan.preview,\n logs: result.plan.logs,\n duration_ms: result.durationMs,\n };\n if (input.auto_commit !== true) return base;\n\n const commit = await confirmCommitted(session, await commitPlan(session, result.plan));\n return { ...base, committed: commit.kind === 'committed', commit_result: commit };\n }\n\n async function commit(input: Extract<MedeoToolInput, { op: 'commit-plan' }>): Promise<MedeoToolResult> {\n const cached = plans.get(input.plan_id);\n if (cached == null || cached.docId !== input.doc_id) {\n throw new Error(`plan_id ${input.plan_id} is not available for doc ${input.doc_id}`);\n }\n const session = await getSession(input.doc_id);\n const commitOptions: CommitPlanOptions | undefined =\n input.validation === undefined ? undefined : { validation: input.validation };\n const result = await confirmCommitted(session, await commitPlan(session, cached.plan, commitOptions));\n return {\n ok: true,\n op: 'commit-plan',\n doc_id: input.doc_id,\n plan_id: input.plan_id,\n committed: result.kind === 'committed',\n result,\n };\n }\n\n return {\n name: MEDEO_TOOL_NAME,\n description: MEDEO_TOOL_DESCRIPTION,\n parameters: MEDEO_TOOL_PARAMETERS,\n getModelContext,\n async handle(input: unknown): Promise<MedeoToolResult> {\n try {\n const parsed = parseInput(input);\n if (parsed.op === 'snapshot') return await snapshot(parsed);\n if (parsed.op === 'run-edit-script') return await run(parsed);\n return await commit(parsed);\n } catch (error) {\n const op = isRecord(input) && typeof input.op === 'string' ? (input.op as MedeoToolOp) : 'snapshot';\n return {\n ok: false,\n op,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n },\n async close(): Promise<void> {\n closed = true;\n const opening = [...sessions.values()];\n sessions.clear();\n plans.clear();\n modelContextVersions.clear();\n const errors: unknown[] = [];\n for (const sessionPromise of opening) {\n try {\n const session = await sessionPromise;\n session.destroy();\n } catch (error) {\n errors.push(error);\n }\n }\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) throw new AggregateError(errors, 'failed to close medeo tool sessions');\n },\n };\n}\n"],"mappings":"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Q,OAAO,oBAAoB;IAChF,SAAS;KAAE,KAAK,IAAI,MAAM;KAAG,MAAM,KAAK,MAAM;IAAE;GAClD,CAAC;EACH,CAAC;CACH,CAAC;AACH;;;;;;;;;ACjNA,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,EAAE,KAAK,IAAI;;;AC9vBX,MAAa,yBAAyB;;;;;;;;;EASpC,KAAK;AAEP,MAAM,6BAA6B;;;;EAIjC,KAAK;;AAQP,SAAgB,wBAAwB,OAA6C;CACnF,MAAM,UACJ,MAAM,iCAAiC,OACnC,+BACA,OAAO,MAAM,6BAA6B;CAChD,OAAO;EACP,uBAAuB;;EAEvB,2BAA2B;;;sBAGP,KAAK,UAAU,MAAM,eAAe,EAAE;uCACrB,QAAQ;;;;;;EAM7C,qBAAqB;;EAErB,KAAK;AACP;;;AC9CA,MAAa,kBAAkB;;;;;;;;;AAY/B,MAAa,wBAAwB;CACnC,MAAM;CACN,UAAU,CAAC,MAAM,QAAQ;CACzB,sBAAsB;CACtB,YAAY;EACV,IAAI;GACF,MAAM;GACN,MAAM;IAAC;IAAY;IAAmB;GAAa;GACnD,aAAa;EACf;EACA,QAAQ;GACN,MAAM;GACN,WAAW;GACX,aAAa;EACf;EACA,QAAQ;GACN,MAAM;GACN,WAAW;GACX,aACE;EACJ;EACA,QAAQ;GACN,MAAM;GACN,aACE;EACJ;EACA,YAAY;GACV,MAAM;GACN,SAAS;GACT,aAAa;EACf;EACA,iBAAiB;GACf,MAAM;GACN,SAAS;GACT,aAAa;EACf;EACA,aAAa;GACX,MAAM;GACN,aACE;EACJ;EACA,SAAS;GACP,MAAM;GACN,WAAW;GACX,aAAa;EACf;EACA,YAAY;GACV,MAAM;GACN,MAAM,CAAC,WAAW,WAAW;GAC7B,aACE;EACJ;CACF;CACA,OAAO;EACL;GACE,UAAU,CAAC,MAAM,QAAQ;GACzB,YAAY;IACV,IAAI,EAAE,OAAO,WAAW;IACxB,QAAQ,EAAE,MAAM,sBAAsB;GACxC;GACA,sBAAsB;EACxB;EACA;GACE,UAAU;IAAC;IAAM;IAAU;GAAQ;GACnC,YAAY;IACV,IAAI,EAAE,OAAO,kBAAkB;IAC/B,QAAQ,EAAE,MAAM,sBAAsB;IACtC,QAAQ,EAAE,MAAM,sBAAsB;IACtC,QAAQ,EAAE,MAAM,sBAAsB;IACtC,YAAY,EAAE,MAAM,0BAA0B;IAC9C,iBAAiB,EAAE,MAAM,+BAA+B;IACxD,aAAa,EAAE,MAAM,2BAA2B;GAClD;GACA,sBAAsB;EACxB;EACA;GACE,UAAU;IAAC;IAAM;IAAU;GAAS;GACpC,YAAY;IACV,IAAI,EAAE,OAAO,cAAc;IAC3B,QAAQ,EAAE,MAAM,sBAAsB;IACtC,SAAS,EAAE,MAAM,uBAAuB;IACxC,YAAY,EAAE,MAAM,0BAA0B;GAChD;GACA,sBAAsB;EACxB;CACF;AACF;;;;;;;;;;;;;;;AC/CA,eAAsB,WACpB,SACA,MACA,SAC2B;CAC3B,IAAI,SAAS,eAAe,aAC1B,OAAO,oBAAoB,SAAS,IAAI;CAG1C,MAAM,SAAS,QAAQ,QAAQ;CAC/B,IAAI,WAAW,KAAK,cAClB,OAAO;EACL,MAAM;EACN,QAAQ;EACR,UAAU,KAAK;EACf;CACF;CAGF,MAAM,cAAc,QAAQ,iBAAiB,KAAK,GAAG;CACrD,OAAO;EAAE,MAAM;EAAa,aAAa,KAAK,IAAI;CAAO;AAC3D;;;;;;AAOA,eAAe,oBAAoB,SAA4B,MAA6C;CAC1G,MAAM,UAAU,yBAAyB,QAAQ,SAAS,CAAC;CAE3D,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,IAAI,QAAQ,SAAS;EACpD,MAAM,QAAQ,KAAK,IAAI;EACvB,IAAI,SAAS,MAAM;EACnB,IAAI;GACF,MAAM,cAAc,SAAS,CAAC,KAAK,CAAC;EACtC,SAAS,OAAO;GACd,IAAI,iBAAiB,iBACnB,OAAO,WAAW,OAAO,MAAM,MAAM,MAAM,OAAO;GAEpD,MAAM;EACR;CACF;CAIA,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,IAAI,QAAQ,SAAS;EACpD,MAAM,QAAQ,KAAK,IAAI;EACvB,IAAI,SAAS,MAAM;EACnB,IAAI;GACF,MAAM,cAAc,QAAQ,iBAAiB,CAAC,KAAK,CAAC;EACtD,SAAS,OAAO;GACd,IAAI,iBAAiB,iBACnB,OAAO,WAAW,OAAO,MAAM,MAAM,gBAAgB,MAAM,SAAS;GAEtE,MAAM;EACR;CACF;CAEA,OAAO;EAAE,MAAM;EAAa,aAAa,KAAK,IAAI;CAAO;AAC3D;AAEA,SAAS,WAAW,OAAe,SAAyB,SAAmC;CAC7F,OAAO;EAAE,MAAM;EAAY,QAAQ;EAAe;EAAO;EAAS;CAAQ;AAC5E;;;ACkBA,MAAM,oBAAoB;AAC1B,MAAM,6BAA6B;AAEnC,SAAS,SAAS,OAAkD;CAClE,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,gBAAmB,OAAuC,OAA8B;CAC/F,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,OAAO,UAAU,aAAc,MAAwC,KAAK,IAAI;AACzF;AAEA,SAAS,gBAAgB,OAAgC,OAAe,OAAuB;CAC7F,MAAM,WAAW,gBAAgB,OAAO,KAAK,GAAG,KAAK;CACrD,IAAI,YAAY,QAAQ,SAAS,WAAW,GAC1C,MAAM,IAAI,MAAM,GAAG,MAAM,8CAA8C,OAAO;CAEhF,OAAO;AACT;AAEA,SAAS,WAAW,OAAgC;CAClD,IAAI,CAAC,SAAS,KAAK,GAAG,MAAM,IAAI,MAAM,yBAAyB;CAC/D,MAAM,KAAK,MAAM;CACjB,MAAM,QAAQ,MAAM;CACpB,IAAI,OAAO,OAAO,UAAU,MAAM,IAAI,MAAM,qBAAqB;CACjE,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,GAAG,MAAM,IAAI,MAAM,mCAAmC;CAE/G,IAAI,OAAO,YAAY,OAAO;EAAE;EAAI,QAAQ;CAAM;CAElD,IAAI,OAAO,mBAAmB;EAC5B,IAAI,OAAO,MAAM,WAAW,YAAY,MAAM,OAAO,WAAW,GAC9D,MAAM,IAAI,MAAM,mCAAmC;EAErD,IAAI,MAAM,WAAW,KAAA,KAAa,CAAC,SAAS,MAAM,MAAM,GACtD,MAAM,IAAI,MAAM,0BAA0B;EAE5C,MAAM,YAAY,MAAM;EACxB,IAAI,cAAc,KAAA,MAAc,OAAO,cAAc,YAAY,CAAC,OAAO,UAAU,SAAS,KAAK,aAAa,IAC5G,MAAM,IAAI,MAAM,uCAAuC;EAEzD,MAAM,gBAAgB,MAAM;EAC5B,IACE,kBAAkB,KAAA,MACjB,OAAO,kBAAkB,YAAY,CAAC,OAAO,UAAU,aAAa,KAAK,gBAAgB,KAE1F,MAAM,IAAI,MAAM,0CAA0C;EAE5D,IAAI,MAAM,gBAAgB,KAAA,KAAa,OAAO,MAAM,gBAAgB,WAClE,MAAM,IAAI,MAAM,+BAA+B;EAEjD,OAAO;GACL;GACA,QAAQ;GACR,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;GAC7D,GAAI,cAAc,KAAA,IAAY,EAAE,YAAY,UAAU,IAAI,CAAC;GAC3D,GAAI,kBAAkB,KAAA,IAAY,EAAE,iBAAiB,cAAc,IAAI,CAAC;GACxE,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC9E;CACF;CAEA,IAAI,OAAO,eAAe;EACxB,IAAI,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,WAAW,GAChE,MAAM,IAAI,MAAM,oCAAoC;EAEtD,IAAI,MAAM,eAAe,KAAA,KAAa,MAAM,eAAe,aAAa,MAAM,eAAe,aAC3F,MAAM,IAAI,MAAM,iDAA6C;EAE/D,OAAO;GACL;GACA,QAAQ;GACR,SAAS,MAAM;GACf,GAAI,MAAM,eAAe,KAAA,IAAY,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;EAC3E;CACF;CAEA,MAAM,IAAI,MAAM,eAAe,IAAI;AACrC;;;;;;;;;AAUA,SAAgB,gBAAgB,SAA4C;CAC1E,MAAM,2BAAW,IAAI,IAAwC;CAC7D,MAAM,wBAAQ,IAAI,IAAwB;CAC1C,MAAM,uCAAuB,IAAI,IAAoB;CACrD,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,mBAAmB,QAAQ,oBAAoB;CACrD,IAAI,SAAS;CAEb,eAAe,WAAW,OAA2C;EACnE,IAAI,QAAQ,MAAM,IAAI,MAAM,sBAAsB;EAClD,MAAM,WAAW,SAAS,IAAI,KAAK;EACnC,IAAI,YAAY,MAAM,OAAO,MAAM;EAEnC,MAAM,WAAW,YAAY;GAC3B,MAAM,SAAS,IAAI,kBAAkB;IACnC;IACA,YAAY,gBAAgB,QAAQ,YAAY,OAAO,YAAY;IACnE,GAAI,QAAQ,cAAc,KAAA,IAAY,EAAE,iBAAiB,gBAAgB,QAAQ,WAAW,KAAK,EAAE,IAAI,CAAC;IACxG,GAAI,QAAQ,WAAW,KAAA,IAAY,EAAE,cAAc,gBAAgB,QAAQ,QAAQ,KAAK,EAAE,IAAI,CAAC;IAC/F,GAAI,QAAQ,cAAc,KAAA,IAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;GAC5E,CAAC;GACD,MAAM,SAAS,gBAAgB,QAAQ,QAAQ,KAAK;GACpD,MAAM,oBAAoB,QAAQ,OAAO,MAAM;GAC/C,MAAM,UAAU,IAAI,kBAAkB;IACpC;IACA;IACA,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;IACzC,GAAI,QAAQ,wBAAwB,KAAA,IAAY,EAAE,qBAAqB,QAAQ,oBAAoB,IAAI,CAAC;GAC1G,CAAC;GACD,IAAI;IACF,MAAM,QAAQ,MAAM;IACpB,OAAO;GACT,SAAS,OAAO;IACd,QAAQ,QAAQ;IAChB,MAAM;GACR;EACF,GAAG;EAEH,SAAS,IAAI,OAAO,OAAO;EAC3B,IAAI;GACF,OAAO,MAAM;EACf,SAAS,OAAO;GACd,IAAI,SAAS,IAAI,KAAK,MAAM,SAAS,SAAS,OAAO,KAAK;GAC1D,MAAM;EACR;CACF;CAEA,eAAe,oBACb,QACA,OACA,QACe;EACf,IAAI;GACF,MAAM,OAAO,cAAc;GAC3B;EACF,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,4BAA4B,MAAM,WAAW,KAAK,MAAM;GAC/E,IAAI,QAAQ,qBAAqB,KAAA,GAAW,MAAM;EACpD;EAIA,MAAM,OAAO,0BADI,gBAAgB,MADb,QAAQ,iBAAiB,KAAK,CAEJ,GAAG;GAC/C,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;GACzC,QAAQ;EACV,CAAC;EAED,IAAI;GACF,MAAM,OAAO,kBAAkB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC,CAAC;EAClE,SAAS,OAAO;GAId,IAAI,EAAE,iBAAiB,4BAA4B,MAAM,WAAW,KAAK,MAAM;GAC/E,MAAM,OAAO,cAAc;EAC7B;CACF;CAEA,SAAS,aAAa,OAAe,MAA0B;EAC7D,MAAM,SAAS,WAAW;EAC1B,MAAM,IAAI,QAAQ;GAAE;GAAO;EAAK,CAAC;EACjC,OAAO,MAAM,OAAO,UAAU;GAC5B,MAAM,SAAS,MAAM,KAAK,EAAE,KAAK,EAAE;GACnC,IAAI,WAAW,KAAA,GAAW;GAC1B,MAAM,OAAO,MAAM;EACrB;EACA,OAAO;CACT;CAEA,eAAe,gBAAgB,OAA2D;EACxF,MAAM,QAAQ,MAAM,OAAO,KAAK;EAChC,MAAM,YAAY,MAAM,WAAW,KAAK;EACxC,IAAI,MAAM,WAAW,GAAG,MAAM,IAAI,MAAM,mCAAmC;EAC3E,IAAI,UAAU,WAAW,GAAG,MAAM,IAAI,MAAM,uCAAuC;EAGnF,MAAM,mBAAkB,MADF,WAAW,KAAK,GACN,QAAQ;EACxC,MAAM,cAAc,GAAG,UAAU,QAAQ;EACzC,MAAM,kBAAkB,qBAAqB,IAAI,WAAW;EAC5D,MAAM,gCAAgC,mBAAmB,OAAO,OAAO,oBAAoB;EAG3F,qBAAqB,OAAO,WAAW;EACvC,qBAAqB,IAAI,aAAa,eAAe;EACrD,OAAO,qBAAqB,OAAO,kBAAkB;GACnD,MAAM,SAAS,qBAAqB,KAAK,EAAE,KAAK,EAAE;GAClD,IAAI,WAAW,KAAA,GAAW;GAC1B,qBAAqB,OAAO,MAAM;EACpC;EAEA,OAAO;GACL,QAAQ,wBAAwB;IAAE;IAAiB;GAA8B,CAAC;GAClF,kBAAkB;GAClB,mCAAmC;EACrC;CACF;CAEA,eAAe,iBAAiB,SAA4B,QAAqD;EAC/G,IAAI,OAAO,SAAS,eAAe,OAAO,cAAc,GACtD,MAAM,QAAQ,iBAAiB;EAEjC,OAAO;CACT;CAEA,eAAe,SAAS,OAA8E;EACpG,MAAM,UAAU,MAAM,WAAW,MAAM,MAAM;EAC7C,MAAM,WAAW,QAAQ,SAAS;EAClC,OAAO;GACL,IAAI;GACJ,IAAI;GACJ,QAAQ,MAAM;GACd,SAAS,QAAQ,QAAQ;GACzB,SAAS,wBAAwB,QAAQ;EAC3C;CACF;CAEA,eAAe,IAAI,OAAqF;EACtG,MAAM,UAAU,MAAM,WAAW,MAAM,MAAM;EAC7C,MAAM,WAA0B,QAAQ,SAAS;EACjD,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,SAAS,MAAM,cAAc;GACjC;GACA;GACA,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;GAC7D,WAAW,MAAM,cAAc,QAAQ,SAAS;GAChD,eAAe,MAAM,mBAAmB,QAAQ,SAAS;EAC3D,CAAC;EAED,IAAI,CAAC,OAAO,IACV,OAAO;GACL,IAAI;GACJ,IAAI;GACJ,QAAQ,MAAM;GACd,OAAO,OAAO;GACd,OAAO,OAAO;GACd,SAAS;IAAE,WAAW,OAAO,QAAQ,IAAI;IAAQ,MAAM,OAAO,QAAQ;GAAK;EAC7E;EAGF,MAAM,SAAS,aAAa,MAAM,QAAQ,OAAO,IAAI;EACrD,MAAM,OAAO;GACX,IAAI;GACJ,IAAI;GACJ,QAAQ,MAAM;GACd,SAAS;GACT,cAAc;GACd,WAAW,OAAO,KAAK,IAAI;GAC3B,SAAS,OAAO,KAAK;GACrB,MAAM,OAAO,KAAK;GAClB,aAAa,OAAO;EACtB;EACA,IAAI,MAAM,gBAAgB,MAAM,OAAO;EAEvC,MAAM,SAAS,MAAM,iBAAiB,SAAS,MAAM,WAAW,SAAS,OAAO,IAAI,CAAC;EACrF,OAAO;GAAE,GAAG;GAAM,WAAW,OAAO,SAAS;GAAa,eAAe;EAAO;CAClF;CAEA,eAAe,OAAO,OAAiF;EACrG,MAAM,SAAS,MAAM,IAAI,MAAM,OAAO;EACtC,IAAI,UAAU,QAAQ,OAAO,UAAU,MAAM,QAC3C,MAAM,IAAI,MAAM,WAAW,MAAM,QAAQ,4BAA4B,MAAM,QAAQ;EAErF,MAAM,UAAU,MAAM,WAAW,MAAM,MAAM;EAC7C,MAAM,gBACJ,MAAM,eAAe,KAAA,IAAY,KAAA,IAAY,EAAE,YAAY,MAAM,WAAW;EAC9E,MAAM,SAAS,MAAM,iBAAiB,SAAS,MAAM,WAAW,SAAS,OAAO,MAAM,aAAa,CAAC;EACpG,OAAO;GACL,IAAI;GACJ,IAAI;GACJ,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,WAAW,OAAO,SAAS;GAC3B;EACF;CACF;CAEA,OAAO;EACL,MAAM;EACN,aAAa;EACb,YAAY;EACZ;EACA,MAAM,OAAO,OAA0C;GACrD,IAAI;IACF,MAAM,SAAS,WAAW,KAAK;IAC/B,IAAI,OAAO,OAAO,YAAY,OAAO,MAAM,SAAS,MAAM;IAC1D,IAAI,OAAO,OAAO,mBAAmB,OAAO,MAAM,IAAI,MAAM;IAC5D,OAAO,MAAM,OAAO,MAAM;GAC5B,SAAS,OAAO;IAEd,OAAO;KACL,IAAI;KACJ,IAHS,SAAS,KAAK,KAAK,OAAO,MAAM,OAAO,WAAY,MAAM,KAAqB;KAIvF,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAC9D;GACF;EACF;EACA,MAAM,QAAuB;GAC3B,SAAS;GACT,MAAM,UAAU,CAAC,GAAG,SAAS,OAAO,CAAC;GACrC,SAAS,MAAM;GACf,MAAM,MAAM;GACZ,qBAAqB,MAAM;GAC3B,MAAM,SAAoB,CAAC;GAC3B,KAAK,MAAM,kBAAkB,SAC3B,IAAI;IAEF,CAAA,MADsB,gBACd,QAAQ;GAClB,SAAS,OAAO;IACd,OAAO,KAAK,KAAK;GACnB;GAEF,IAAI,OAAO,WAAW,GAAG,MAAM,OAAO;GACtC,IAAI,OAAO,SAAS,GAAG,MAAM,IAAI,eAAe,QAAQ,qCAAqC;EAC/F;CACF;AACF"}
@@ -3,7 +3,7 @@
3
3
  * @generated by gen:sandbox-dts — DO NOT EDIT MANUALLY
4
4
  *
5
5
  * Schema version: video-document/v0
6
- * Semantic ops: 18
6
+ * Semantic ops: 20
7
7
  *
8
8
  * Boundary: zod `superRefine` / custom refine rules are NOT introspectable and
9
9
  * do not appear here. Business mutual-exclusion rules surface via runtime
@@ -192,6 +192,37 @@ interface MoveVideoClipsInput {
192
192
  new_track_id?: string;
193
193
  }[];
194
194
  }
195
+ interface MoveVideoClipsByAnchorInput {
196
+ /**
197
+ * Clips to move as one block, keeping their relative order. Need not be contiguous on the track.
198
+ * @constraint minLength(1)
199
+ */
200
+ clip_ids: string[];
201
+ /**
202
+ * Where the moved block lands: before/after a reference clip, or at the head of the track
203
+ */
204
+ anchor: {
205
+ position: 'before';
206
+ /**
207
+ * The moved block lands immediately before this clip
208
+ * @constraint minLength(1)
209
+ */
210
+ clip_id: string;
211
+ } | {
212
+ position: 'after';
213
+ /**
214
+ * The moved block lands immediately after this clip
215
+ * @constraint minLength(1)
216
+ */
217
+ clip_id: string;
218
+ } | {
219
+ position: 'track_start';
220
+ };
221
+ /**
222
+ * What happens to speeches anchored to the moved clips (required — see the policy doc)
223
+ */
224
+ on_anchored: 'follow' | 'keep_absolute';
225
+ }
195
226
  interface DeleteVideoClipsInput {
196
227
  /**
197
228
  * List of video clip part IDs to delete from the main track
@@ -384,6 +415,46 @@ interface ReplaceVideoClipContentInput {
384
415
  volume: number;
385
416
  }[];
386
417
  }
418
+ interface ReplaceVideoClipSequenceInput {
419
+ /**
420
+ * The clips being replaced: a contiguous main-track run, listed in timeline order
421
+ * @constraint minLength(1)
422
+ */
423
+ old_clip_ids: string[];
424
+ /**
425
+ * The replacement clips, in the order they take on the track
426
+ * @constraint minLength(1)
427
+ */
428
+ new_clips: {
429
+ /**
430
+ * The replacement media asset ID. Omit to create an empty placeholder clip.
431
+ * @constraint minLength(1)
432
+ */
433
+ media_id?: string;
434
+ /**
435
+ * The source media's intrinsic full length in ms
436
+ * @constraint int
437
+ * @constraint positive
438
+ */
439
+ media_duration_ms: number;
440
+ /**
441
+ * Trim window start in the media (default 0)
442
+ * @constraint int
443
+ * @constraint min(0)
444
+ */
445
+ play_in?: number;
446
+ /**
447
+ * Trim window end in the media (default media_duration_ms)
448
+ * @constraint int
449
+ * @constraint positive
450
+ */
451
+ play_out?: number;
452
+ }[];
453
+ /**
454
+ * What happens to speeches anchored to the replaced clips (required — see the policy doc)
455
+ */
456
+ on_anchored: 'remap' | 'cascade';
457
+ }
387
458
  /**
388
459
  * Re-trim existing video clips (the user-facing "adjust duration" gesture is a trim of the source window).
389
460
  */
@@ -586,6 +657,7 @@ interface AdjustBgmVolumeInput {
586
657
  /** Agent write surface — one method per SemanticOp kind. */
587
658
  interface EditApi {
588
659
  moveVideoClips(input: MoveVideoClipsInput): Promise<void>;
660
+ moveVideoClipsByAnchor(input: MoveVideoClipsByAnchorInput): Promise<void>;
589
661
  deleteVideoClips(input: DeleteVideoClipsInput): Promise<void>;
590
662
  /** Add video clips to a track. */
591
663
  addVideoClips(input: AddVideoClipsInput): Promise<void>;
@@ -594,6 +666,7 @@ interface EditApi {
594
666
  setVideoClipSpeedShift(input: SetVideoClipSpeedShiftInput): Promise<void>;
595
667
  /** Replace the media backing existing video clips. */
596
668
  replaceVideoClipContent(input: ReplaceVideoClipContentInput): Promise<void>;
669
+ replaceVideoClipSequence(input: ReplaceVideoClipSequenceInput): Promise<void>;
597
670
  /** Re-trim existing video clips (the user-facing "adjust duration" gesture is a trim of the source window). */
598
671
  adjustVideoClipDuration(input: AdjustVideoClipDurationInput): Promise<void>;
599
672
  /** Add speeches (and their captions). */
@@ -665,5 +738,5 @@ declare function rollbackTo(cp: SandboxCheckpoint): void;
665
738
  /** Host-injected script arguments (opaque). */
666
739
  declare const inputs: unknown;
667
740
  //#endregion
668
- export type { AddSpeechesInput, AddVideoClipsInput, AdjustBgmVolumeInput, AdjustSpeechVolumeInput, AdjustVideoClipDurationInput, AdjustVideoClipVolumeInput, ChangeSpeechScriptInput, ChangeSpeechVoiceInput, DeleteBgmInput, DeleteSpeechesInput, DeleteVideoClipsInput, EditApi, MoveSpeechesInput, MoveVideoClipsInput, ReplaceVideoClipContentInput, SandboxCheckpoint, SetBgmInput, SetCaptionStyleInput, SetCaptionVisibilityInput, SetVideoClipSpeedShiftInput, SpeechAssets, SpeedShift, TimelineApi, TimelineClipDescriptor, TimelinePartDescriptor, VideoDraftProjection, Voice, checkpoint, edit, inputs, rollbackTo, timeline };
741
+ export type { AddSpeechesInput, AddVideoClipsInput, AdjustBgmVolumeInput, AdjustSpeechVolumeInput, AdjustVideoClipDurationInput, AdjustVideoClipVolumeInput, ChangeSpeechScriptInput, ChangeSpeechVoiceInput, DeleteBgmInput, DeleteSpeechesInput, DeleteVideoClipsInput, EditApi, MoveSpeechesInput, MoveVideoClipsByAnchorInput, MoveVideoClipsInput, ReplaceVideoClipContentInput, ReplaceVideoClipSequenceInput, SandboxCheckpoint, SetBgmInput, SetCaptionStyleInput, SetCaptionVisibilityInput, SetVideoClipSpeedShiftInput, SpeechAssets, SpeedShift, TimelineApi, TimelineClipDescriptor, TimelinePartDescriptor, VideoDraftProjection, Voice, checkpoint, edit, inputs, rollbackTo, timeline };
669
742
  //# sourceMappingURL=sandbox-api.d.mts.map
@@ -271,8 +271,10 @@ var EditSandboxSession = class {
271
271
  deleteSpeeches: wrap((e, i) => e.deleteSpeeches(i)),
272
272
  deleteVideoClips: wrap((e, i) => e.deleteVideoClips(i)),
273
273
  moveSpeeches: wrap((e, i) => e.moveSpeeches(i)),
274
+ moveVideoClipsByAnchor: wrap((e, i) => e.moveVideoClipsByAnchor(i)),
274
275
  moveVideoClips: wrap((e, i) => e.moveVideoClips(i)),
275
276
  replaceVideoClipContent: wrap((e, i) => e.replaceVideoClipContent(i)),
277
+ replaceVideoClipSequence: wrap((e, i) => e.replaceVideoClipSequence(i)),
276
278
  setBgm: wrap((e, i) => e.setBgm(i)),
277
279
  setCaptionStyle: wrap((e, i) => e.setCaptionStyle(i)),
278
280
  setCaptionVisibility: wrap((e, i) => e.setCaptionVisibility(i)),
@@ -374,6 +376,9 @@ function replayJournalSync(adapter, journal) {
374
376
  case "MoveVideoClips":
375
377
  editor.moveVideoClips(payload);
376
378
  break;
379
+ case "MoveVideoClipsByAnchor":
380
+ editor.moveVideoClipsByAnchor(payload);
381
+ break;
377
382
  case "DeleteVideoClips":
378
383
  editor.deleteVideoClips(payload);
379
384
  break;
@@ -389,6 +394,9 @@ function replayJournalSync(adapter, journal) {
389
394
  case "ReplaceVideoClipContent":
390
395
  editor.replaceVideoClipContent(payload);
391
396
  break;
397
+ case "ReplaceVideoClipSequence":
398
+ editor.replaceVideoClipSequence(payload);
399
+ break;
392
400
  case "AdjustVideoClipDuration":
393
401
  editor.adjustVideoClipDuration(payload);
394
402
  break;
@@ -436,4 +444,4 @@ function replayJournalSync(adapter, journal) {
436
444
  //#endregion
437
445
  export { renderCompactProjection as i, collectAffectedPartIds as n, renderPreview as r, EditSandboxSession as t };
438
446
 
439
- //# sourceMappingURL=script-session-B8fc9Ccb.mjs.map
447
+ //# sourceMappingURL=script-session-DdPA4tTf.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-session-DdPA4tTf.mjs","names":[],"sources":["../src/document/compact-projection.ts","../src/sandbox/preview.ts","../src/sandbox/script-session.ts"],"sourcesContent":["import {\n effectiveVideoClipDurationMs,\n solveVideoDocument,\n speedOf,\n type PartKind,\n type PartUnion,\n type SpeedShift,\n type TrackItem,\n type TrackItemTimePosition,\n type VideoClipPart,\n type VideoDocument,\n} from '@mengine/medeo-client';\n\n/**\n * Compact text projection of a `VideoDocument` for sandbox ChangePlan\n * `preview` (and pipeline dry-run preview). Pure, runtime-neutral: solves\n * via `solveVideoDocument`, emits one header line plus one row per timeline\n * item (library orphans are never rendered). Row intervals and per-kind\n * effective durations align with `toReadViewPartLibrary` /\n * `fromVideoDocument`.\n */\n\nexport interface CompactProjectionOptions {\n /** Only render these parts (header still reports the full timeline total). Default = all. */\n onlyPartIds?: ReadonlySet<string>;\n /** Caption text preview truncation length. Default 24. */\n textPreviewLength?: number;\n}\n\nconst DEFAULT_TEXT_PREVIEW_LENGTH = 24;\n\n/** Kind tag shown in the first column (`video_clip` → `clip`). */\nfunction kindTag(kind: PartKind): string {\n return kind === 'video_clip' ? 'clip' : kind;\n}\n\n/** Lane label: `video_clip` tracks display as `main`, otherwise `parts_kind`. */\nfunction laneLabel(partsKind: PartKind): string {\n return partsKind === 'video_clip' ? 'main' : partsKind;\n}\n\n/** Speed token: absent → `1`; linear → numeric multiplier; anything else → `custom`. */\nfunction speedToken(speedShift: SpeedShift | undefined): string {\n if (speedShift == null) return '1';\n if (speedShift.category === 'linear') return String(speedOf(speedShift));\n return 'custom';\n}\n\nfunction effectiveDurationMs(part: PartUnion, timelineDurationMs: number): number {\n if (part.video_clip != null) return effectiveVideoClipDurationMs(part.video_clip);\n if (part.speech != null) return part.speech.media_duration_ms ?? 0;\n if (part.caption != null) return part.caption.initial_duration_ms ?? 0;\n if (part.bgm != null) return timelineDurationMs;\n return 0;\n}\n\nfunction truncateText(text: string, budget: number): string {\n if (text.length <= budget) return text;\n return `${text.slice(0, budget)}…`;\n}\n\nfunction anchorToken(timePosition: TrackItemTimePosition): string {\n if (timePosition.mode === 'anchored') {\n return `anchor=${timePosition.anchorPartId}+${timePosition.offsetMs}`;\n }\n if (timePosition.mode === 'absolute') return 'anchor=abs';\n return 'anchor=abs';\n}\n\nfunction clipAttrs(clip: VideoClipPart): string {\n const playIn = clip.play_in ?? 0;\n const playOut = clip.play_out ?? 0;\n return `media=${clip.origin_media_id ?? ''} trim=${playIn}-${playOut} speed=${speedToken(clip.speed_shift)} vol=${clip.volume ?? 0}`;\n}\n\nfunction partAttrs(part: PartUnion, item: TrackItem, textPreviewLength: number): string {\n if (part.video_clip != null) return clipAttrs(part.video_clip);\n if (part.speech != null) {\n return `${anchorToken(item.time_position)} dur=${part.speech.media_duration_ms ?? 0}`;\n }\n if (part.caption != null) {\n const preview = truncateText(part.caption.text ?? '', textPreviewLength);\n return `${anchorToken(item.time_position)} text=\"${preview}\"`;\n }\n if (part.bgm != null) return `vol=${part.bgm.volume ?? 0}`;\n return '';\n}\n\n/**\n * Render a `VideoDocument` as compact text: one header line plus one row per\n * timeline part (optionally filtered by `onlyPartIds`). Deterministic and\n * side-effect free — same document always yields the same string.\n */\nexport function renderCompactProjection(document: VideoDocument, options?: CompactProjectionOptions): string {\n const onlyPartIds = options?.onlyPartIds;\n const textPreviewLength = options?.textPreviewLength ?? DEFAULT_TEXT_PREVIEW_LENGTH;\n\n const solved = solveVideoDocument(document);\n const library = document.part_library ?? {};\n const tracks = document.tracks ?? [];\n\n let totalParts = 0;\n const rows: string[] = [];\n\n for (const track of tracks) {\n const partsKind = track.parts_kind;\n if (partsKind == null) continue;\n const lane = laneLabel(partsKind);\n const tag = kindTag(partsKind);\n\n for (const item of track.items ?? []) {\n totalParts += 1;\n const partId = item.part_id;\n if (onlyPartIds != null && !onlyPartIds.has(partId)) continue;\n\n const part = library[partId];\n if (part == null) continue;\n\n const abs = solved.absByPartId.get(partId) ?? 0;\n const dur = effectiveDurationMs(part, solved.durationMs);\n const attrs = partAttrs(part, item, textPreviewLength);\n rows.push(`${tag} ${partId} ${lane} [${abs},${abs + dur}) ${attrs}`);\n }\n }\n\n const header = `# draft=${document.meta.draft_id ?? ''} v=${document.meta.version ?? 0} duration=${solved.durationMs} parts=${totalParts} shown=${rows.length}`;\n return [header, ...rows].join('\\n');\n}\n","import type { JournalEntry, VideoDocument } from '@mengine/medeo-client';\n\nimport { renderCompactProjection } from '../document/compact-projection.ts';\n\n/**\n * Collect part ids referenced by a journal for compact preview filtering.\n * Walks known id-shaped payload keys (宁多勿少) and unions `generated_ids`.\n */\nconst PART_ID_KEYS = new Set([\n 'clip_id',\n 'clip_ids',\n 'before_clip_id',\n 'after_clip_id',\n 'speech_id',\n 'speech_ids',\n 'speech_part_id',\n 'caption_id',\n 'caption_ids',\n 'bgm_id',\n 'anchor_part_id',\n 'part_id',\n 'body_part_id',\n]);\n\n/** Extract every part id a journal entry touches (payload refs + minted ids). */\nexport function collectAffectedPartIds(journal: readonly JournalEntry[]): Set<string> {\n const ids = new Set<string>();\n for (const entry of journal) {\n for (const generated of entry.generated_ids ?? []) {\n if (generated.length > 0) ids.add(generated);\n }\n collectFromValue(entry.payload, ids);\n }\n return ids;\n}\n\nfunction collectFromValue(value: unknown, ids: Set<string>): void {\n if (value == null) return;\n if (Array.isArray(value)) {\n for (const item of value) collectFromValue(item, ids);\n return;\n }\n if (typeof value !== 'object') return;\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (PART_ID_KEYS.has(key)) {\n if (typeof child === 'string' && child.length > 0) ids.add(child);\n else if (Array.isArray(child)) {\n for (const item of child) {\n if (typeof item === 'string' && item.length > 0) ids.add(item);\n }\n }\n }\n collectFromValue(child, ids);\n }\n}\n\n/**\n * Render a ChangePlan preview: header + rows for journal-affected parts only.\n * Empty journal → empty `onlyPartIds` (header alone), matching the T2 contract.\n */\nexport function renderPreview(document: VideoDocument, journal: readonly JournalEntry[]): string {\n const onlyPartIds = journal.length === 0 ? new Set<string>() : collectAffectedPartIds(journal);\n return renderCompactProjection(document, { onlyPartIds });\n}\n","import {\n createEditSandbox,\n effectiveVideoClipDurationMs,\n fromVideoDocument,\n SchemaValidator,\n SemanticEditor,\n solveVideoDocument,\n type JournalEntry,\n type PartIdFactory,\n type PlainMemoryAdapter,\n type VideoDocument,\n} from '@mengine/medeo-client';\nimport type {\n AddSpeechesInput,\n AddVideoClipsInput,\n AdjustBgmVolumeInput,\n AdjustSpeechVolumeInput,\n AdjustVideoClipDurationInput,\n AdjustVideoClipVolumeInput,\n ChangeSpeechScriptInput,\n ChangeSpeechVoiceInput,\n DeleteBgmInput,\n DeleteSpeechesInput,\n DeleteVideoClipsInput,\n MoveSpeechesInput,\n MoveVideoClipsByAnchorInput,\n MoveVideoClipsInput,\n ReplaceVideoClipContentInput,\n ReplaceVideoClipSequenceInput,\n SetBgmInput,\n SetCaptionStyleInput,\n SetCaptionVisibilityInput,\n SetVideoClipSpeedShiftInput,\n} from '@mengine/medeo-client/schemas';\n\nimport { renderPreview } from './preview.ts';\n\n/**\n * Runtime-neutral edit-sandbox session: `edit.*` / `timeline.*` / checkpoint\n * facade over a forked `VideoDocument`, self-maintained journal, and console\n * log buffer. No `node:*` imports — host/worker layers inject this into vm.\n *\n * Known gap (not solved here): `Math.random` / `Date.now` remain reachable in\n * the vm; purity is by convention.\n */\n\nexport interface SandboxCheckpoint {\n readonly index: number;\n}\n\nexport interface ChangePlan {\n doc_id: string;\n base_version: string;\n ops: readonly JournalEntry[];\n preview: string;\n logs: string[];\n}\n\nexport interface EditSandboxSessionOptions {\n idFactory?: PartIdFactory;\n onEntry?: (entry: JournalEntry) => void;\n onLog?: (line: string) => void;\n /** Notify host that the streamed journal was truncated to `index` (rollback). */\n onTruncate?: (index: number) => void;\n}\n\nexport interface TimelineClipDescriptor {\n id: string;\n start_ms: number;\n end_ms: number;\n duration_ms: number;\n speed_shift: unknown;\n volume: number | undefined;\n media_id: string | undefined;\n}\n\nexport interface TimelinePartDescriptor {\n id: string;\n kind: string;\n lane: string;\n start_ms: number;\n end_ms: number;\n duration_ms: number;\n part: unknown;\n}\n\nconst LOG_LINE_MAX = 2000;\nconst LOG_LINE_CAP = 1000;\nconst LOG_BYTE_CAP = 64 * 1024;\nconst TRUNCATE_MARK = '[truncated]';\nconst LOG_TRUNCATED = '[log truncated]';\n\ninterface SandboxRef {\n adapter: PlainMemoryAdapter;\n editor: SemanticEditor;\n}\n\n/** Session core for one forked document; globals stay identity-stable across rollback. */\nexport class EditSandboxSession {\n private readonly original: VideoDocument;\n private readonly idFactory: PartIdFactory | undefined;\n private readonly onEntry: ((entry: JournalEntry) => void) | undefined;\n private readonly onLog: ((line: string) => void) | undefined;\n private readonly onTruncate: ((index: number) => void) | undefined;\n\n private current: SandboxRef;\n /** Adapter journal length already accounted for — new slices are real commits. */\n private adapterJournalSeen = 0;\n private readonly entries: JournalEntry[] = [];\n private readonly logs: string[] = [];\n private logBytes = 0;\n private logCapped = false;\n\n readonly edit: EditFacade;\n readonly timeline: TimelineFacade;\n readonly console: ConsoleShim;\n readonly checkpoint: () => SandboxCheckpoint;\n readonly rollbackTo: (cp: SandboxCheckpoint) => void;\n\n constructor(document: VideoDocument, options?: EditSandboxSessionOptions) {\n this.original = structuredClone(document);\n this.idFactory = options?.idFactory;\n this.onEntry = options?.onEntry;\n this.onLog = options?.onLog;\n this.onTruncate = options?.onTruncate;\n\n this.current = this.boot(structuredClone(this.original));\n this.adapterJournalSeen = this.current.adapter.journal.length;\n\n this.edit = this.buildEditFacade();\n this.timeline = this.buildTimelineFacade();\n this.console = this.buildConsoleShim();\n this.checkpoint = () => ({ index: this.entries.length });\n this.rollbackTo = (cp) => this.doRollbackTo(cp);\n }\n\n /** Assemble a ChangePlan from the self-maintained journal + current preview. */\n buildPlan(baseVersion: string): ChangePlan {\n return {\n doc_id: this.original.meta.draft_id ?? '',\n base_version: baseVersion,\n ops: this.entries.slice(),\n preview: renderPreview(this.current.adapter.snapshot(), this.entries),\n logs: this.logs.slice(),\n };\n }\n\n getEntries(): readonly JournalEntry[] {\n return this.entries;\n }\n\n getLogs(): readonly string[] {\n return this.logs;\n }\n\n private boot(document: VideoDocument): SandboxRef {\n const sandbox = createEditSandbox(document, this.idFactory != null ? { idFactory: this.idFactory } : undefined);\n return { adapter: sandbox.adapter, editor: sandbox.editor };\n }\n\n private doRollbackTo(cp: SandboxCheckpoint): void {\n if (cp.index > this.entries.length) {\n throw new Error(`rollbackTo: checkpoint index ${cp.index} is past journal length ${this.entries.length}`);\n }\n const prefix = this.entries.slice(0, cp.index);\n const next = this.boot(structuredClone(this.original));\n // Sync replay: editor methods finish mutations before returning a Promise.\n // Must not use async `replayJournal` — agent scripts call rollbackTo without await.\n replayJournalSync(next.adapter, prefix);\n this.entries.length = 0;\n this.entries.push(...prefix);\n this.current = next;\n this.adapterJournalSeen = next.adapter.journal.length;\n // Host streams entries eagerly; tell it to drop the rolled-back suffix.\n this.onTruncate?.(prefix.length);\n }\n\n private captureNewEntries(): void {\n const journal = this.current.adapter.journal;\n if (journal.length <= this.adapterJournalSeen) return;\n const fresh = journal.slice(this.adapterJournalSeen);\n this.adapterJournalSeen = journal.length;\n for (const entry of fresh) {\n this.entries.push(entry);\n this.onEntry?.(entry);\n }\n }\n\n private appendLog(line: string): void {\n if (this.logCapped) return;\n if (this.logs.length >= LOG_LINE_CAP || this.logBytes >= LOG_BYTE_CAP) {\n this.logs.push(LOG_TRUNCATED);\n this.logCapped = true;\n this.onLog?.(LOG_TRUNCATED);\n return;\n }\n let out = line;\n if (out.length > LOG_LINE_MAX) {\n out = `${out.slice(0, LOG_LINE_MAX - TRUNCATE_MARK.length)}${TRUNCATE_MARK}`;\n }\n this.logs.push(out);\n this.logBytes += out.length;\n this.onLog?.(out);\n }\n\n private buildConsoleShim(): ConsoleShim {\n const write = (...args: unknown[]) => {\n this.appendLog(args.map(formatLogArg).join(' '));\n };\n return {\n log: write,\n info: write,\n warn: write,\n error: write,\n };\n }\n\n private buildEditFacade(): EditFacade {\n const wrap =\n <I>(method: (editor: SemanticEditor, input: I) => Promise<void>) =>\n async (input: I): Promise<void> => {\n await method(this.current.editor, input);\n this.captureNewEntries();\n };\n\n return {\n addSpeeches: wrap((e, i: AddSpeechesInput) => e.addSpeeches(i)),\n addVideoClips: async (input: AddVideoClipsInput): Promise<void> => {\n // Schema requires start_ms without before/after, but SemanticEditor\n // treats a missing start_ms as \"append\". Fill duration so agent scripts\n // that omit it (and the host-spec id-factory case) still validate.\n const needsAppend =\n input.before_clip_id == null &&\n input.after_clip_id == null &&\n input.clips.some((clip) => clip.start_ms == null);\n const appendAt = this.timeline.snapshot().timeline?.duration_ms ?? 0;\n const normalized: AddVideoClipsInput = needsAppend\n ? {\n ...input,\n clips: input.clips.map((clip) => (clip.start_ms == null ? { ...clip, start_ms: appendAt } : clip)),\n }\n : input;\n await this.current.editor.addVideoClips(normalized);\n this.captureNewEntries();\n },\n adjustBgmVolume: wrap((e, i: AdjustBgmVolumeInput) => e.adjustBgmVolume(i)),\n adjustSpeechVolume: wrap((e, i: AdjustSpeechVolumeInput) => e.adjustSpeechVolume(i)),\n adjustVideoClipDuration: wrap((e, i: AdjustVideoClipDurationInput) => e.adjustVideoClipDuration(i)),\n adjustVideoClipVolume: wrap((e, i: AdjustVideoClipVolumeInput) => e.adjustVideoClipVolume(i)),\n changeSpeechScript: wrap((e, i: ChangeSpeechScriptInput) => e.changeSpeechScript(i)),\n changeSpeechVoice: wrap((e, i: ChangeSpeechVoiceInput) => e.changeSpeechVoice(i)),\n deleteBgm: wrap((e, i: DeleteBgmInput) => e.deleteBgm(i)),\n deleteSpeeches: wrap((e, i: DeleteSpeechesInput) => e.deleteSpeeches(i)),\n deleteVideoClips: wrap((e, i: DeleteVideoClipsInput) => e.deleteVideoClips(i)),\n moveSpeeches: wrap((e, i: MoveSpeechesInput) => e.moveSpeeches(i)),\n moveVideoClipsByAnchor: wrap((e, i: MoveVideoClipsByAnchorInput) => e.moveVideoClipsByAnchor(i)),\n moveVideoClips: wrap((e, i: MoveVideoClipsInput) => e.moveVideoClips(i)),\n replaceVideoClipContent: wrap((e, i: ReplaceVideoClipContentInput) => e.replaceVideoClipContent(i)),\n replaceVideoClipSequence: wrap((e, i: ReplaceVideoClipSequenceInput) => e.replaceVideoClipSequence(i)),\n setBgm: wrap((e, i: SetBgmInput) => e.setBgm(i)),\n setCaptionStyle: wrap((e, i: SetCaptionStyleInput) => e.setCaptionStyle(i)),\n setCaptionVisibility: wrap((e, i: SetCaptionVisibilityInput) => e.setCaptionVisibility(i)),\n setVideoClipSpeedShift: wrap((e, i: SetVideoClipSpeedShiftInput) => e.setVideoClipSpeedShift(i)),\n };\n }\n\n private buildTimelineFacade(): TimelineFacade {\n return {\n snapshot: () => fromVideoDocument(this.current.adapter.snapshot()),\n clipsInRange: (startMs, endMs) => this.clipsInRange(startMs, endMs),\n part: (id) => this.part(id),\n };\n }\n\n private clipsInRange(startMs: number, endMs: number): TimelineClipDescriptor[] {\n const document = this.current.adapter.snapshot();\n const solved = solveVideoDocument(document);\n const library = document.part_library ?? {};\n const main = document.tracks?.find((track) => track.parts_kind === 'video_clip');\n const out: TimelineClipDescriptor[] = [];\n for (const item of main?.items ?? []) {\n const id = item.part_id;\n if (id == null) continue;\n const part = library[id];\n const clip = part?.video_clip;\n if (clip == null) continue;\n const start = solved.absByPartId.get(id) ?? 0;\n const duration = effectiveVideoClipDurationMs(clip);\n const end = start + duration;\n // Include clips whose midpoint falls in [startMs, endMs). Standard\n // interval overlap would also pull in a clip that only barely crosses\n // the window edge (e.g. clip_b@[4000,8000) vs query [0,5000)); the\n // midpoint rule matches the T2 host-spec pin for that fixture.\n const mid = start + duration / 2;\n if (!(mid >= startMs && mid < endMs)) continue;\n out.push({\n id,\n start_ms: start,\n end_ms: end,\n duration_ms: duration,\n speed_shift: clip.speed_shift,\n volume: clip.volume,\n media_id: clip.origin_media_id,\n });\n }\n return out;\n }\n\n private part(id: string): TimelinePartDescriptor | null {\n const document = this.current.adapter.snapshot();\n const library = document.part_library ?? {};\n const part = library[id];\n if (part == null) return null;\n\n let lane = 'main';\n let kind = 'video_clip';\n for (const track of document.tracks ?? []) {\n const hit = (track.items ?? []).some((item) => item.part_id === id);\n if (!hit) continue;\n const partsKind = track.parts_kind ?? 'video_clip';\n kind = partsKind;\n lane = partsKind === 'video_clip' ? 'main' : partsKind;\n break;\n }\n\n const solved = solveVideoDocument(document);\n const start = solved.absByPartId.get(id) ?? 0;\n let duration = 0;\n if (part.video_clip != null) duration = effectiveVideoClipDurationMs(part.video_clip);\n else if (part.speech != null) duration = part.speech.media_duration_ms ?? 0;\n else if (part.caption != null) duration = part.caption.initial_duration_ms ?? 0;\n else if (part.bgm != null) duration = solved.durationMs;\n\n return {\n id,\n kind,\n lane,\n start_ms: start,\n end_ms: start + duration,\n duration_ms: duration,\n part,\n };\n }\n}\n\nexport interface EditFacade {\n addSpeeches: (input: AddSpeechesInput) => Promise<void>;\n addVideoClips: (input: AddVideoClipsInput) => Promise<void>;\n adjustBgmVolume: (input: AdjustBgmVolumeInput) => Promise<void>;\n adjustSpeechVolume: (input: AdjustSpeechVolumeInput) => Promise<void>;\n adjustVideoClipDuration: (input: AdjustVideoClipDurationInput) => Promise<void>;\n adjustVideoClipVolume: (input: AdjustVideoClipVolumeInput) => Promise<void>;\n changeSpeechScript: (input: ChangeSpeechScriptInput) => Promise<void>;\n changeSpeechVoice: (input: ChangeSpeechVoiceInput) => Promise<void>;\n deleteBgm: (input: DeleteBgmInput) => Promise<void>;\n deleteSpeeches: (input: DeleteSpeechesInput) => Promise<void>;\n deleteVideoClips: (input: DeleteVideoClipsInput) => Promise<void>;\n moveSpeeches: (input: MoveSpeechesInput) => Promise<void>;\n moveVideoClipsByAnchor: (input: MoveVideoClipsByAnchorInput) => Promise<void>;\n moveVideoClips: (input: MoveVideoClipsInput) => Promise<void>;\n replaceVideoClipContent: (input: ReplaceVideoClipContentInput) => Promise<void>;\n replaceVideoClipSequence: (input: ReplaceVideoClipSequenceInput) => Promise<void>;\n setBgm: (input: SetBgmInput) => Promise<void>;\n setCaptionStyle: (input: SetCaptionStyleInput) => Promise<void>;\n setCaptionVisibility: (input: SetCaptionVisibilityInput) => Promise<void>;\n setVideoClipSpeedShift: (input: SetVideoClipSpeedShiftInput) => Promise<void>;\n}\n\nexport interface TimelineFacade {\n snapshot: () => ReturnType<typeof fromVideoDocument>;\n clipsInRange: (startMs: number, endMs: number) => TimelineClipDescriptor[];\n part: (id: string) => TimelinePartDescriptor | null;\n}\n\nexport interface ConsoleShim {\n log: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n}\n\nfunction formatLogArg(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || value === null || value === undefined) {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unstringifiable]';\n }\n}\n\n/**\n * Synchronous journal replay for rollback. Editor methods are `async` only for\n * interface uniformity — their bodies complete before the Promise is returned,\n * so voiding the call applies mutations in-order without yielding.\n */\nfunction replayJournalSync(adapter: PlainMemoryAdapter, journal: readonly JournalEntry[]): void {\n const queue: string[] = [];\n const idFactory: PartIdFactory = (_prefix) => {\n const id = queue.shift();\n if (id == null) throw new Error('unrecorded id');\n return id;\n };\n const editor = new SemanticEditor(adapter, new SchemaValidator(), idFactory);\n\n for (const entry of journal) {\n queue.push(...(entry.generated_ids ?? []));\n const payload = entry.payload;\n switch (entry.kind) {\n case 'MoveVideoClips':\n void editor.moveVideoClips(payload as MoveVideoClipsInput);\n break;\n case 'MoveVideoClipsByAnchor':\n void editor.moveVideoClipsByAnchor(payload as MoveVideoClipsByAnchorInput);\n break;\n case 'DeleteVideoClips':\n void editor.deleteVideoClips(payload as DeleteVideoClipsInput);\n break;\n case 'AddVideoClips':\n void editor.addVideoClips(payload as AddVideoClipsInput);\n break;\n case 'AdjustVideoClipVolume':\n void editor.adjustVideoClipVolume(payload as AdjustVideoClipVolumeInput);\n break;\n case 'SetVideoClipSpeedShift':\n void editor.setVideoClipSpeedShift(payload as SetVideoClipSpeedShiftInput);\n break;\n case 'ReplaceVideoClipContent':\n void editor.replaceVideoClipContent(payload as ReplaceVideoClipContentInput);\n break;\n case 'ReplaceVideoClipSequence':\n void editor.replaceVideoClipSequence(payload as ReplaceVideoClipSequenceInput);\n break;\n case 'AdjustVideoClipDuration':\n void editor.adjustVideoClipDuration(payload as AdjustVideoClipDurationInput);\n break;\n case 'AddSpeeches':\n void editor.addSpeeches(payload as AddSpeechesInput);\n break;\n case 'DeleteSpeeches':\n void editor.deleteSpeeches(payload as DeleteSpeechesInput);\n break;\n case 'MoveSpeeches':\n void editor.moveSpeeches(payload as MoveSpeechesInput);\n break;\n case 'ChangeSpeechScript':\n void editor.changeSpeechScript(payload as ChangeSpeechScriptInput);\n break;\n case 'ChangeSpeechVoice':\n void editor.changeSpeechVoice(payload as ChangeSpeechVoiceInput);\n break;\n case 'AdjustSpeechVolume':\n void editor.adjustSpeechVolume(payload as AdjustSpeechVolumeInput);\n break;\n case 'SetCaptionVisibility':\n void editor.setCaptionVisibility(payload as SetCaptionVisibilityInput);\n break;\n case 'SetCaptionStyle':\n void editor.setCaptionStyle(payload as SetCaptionStyleInput);\n break;\n case 'SetBgm':\n void editor.setBgm(payload as SetBgmInput);\n break;\n case 'DeleteBgm':\n void editor.deleteBgm(payload as DeleteBgmInput);\n break;\n case 'AdjustBgmVolume':\n void editor.adjustBgmVolume(payload as AdjustBgmVolumeInput);\n break;\n default: {\n const _exhaustive: never = entry.kind;\n throw new Error(`replayJournalSync: unsupported kind ${String(_exhaustive)}`);\n }\n }\n if (queue.length > 0) throw new Error('unconsumed ids');\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { t as EditSandboxSession } from "./script-session-B8fc9Ccb.mjs";
1
+ import { t as EditSandboxSession } from "./script-session-DdPA4tTf.mjs";
2
2
  import { parentPort, workerData } from "node:worker_threads";
3
3
  import vm from "node:vm";
4
4
  //#region src/sandbox/worker-entry.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mengine/medeo-tool",
3
- "version": "1.0.1-alpha.0",
3
+ "version": "1.2.1-alpha.0",
4
4
  "license": "UNLICENSED",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,7 +24,7 @@
24
24
  "registry": "https://registry.npmjs.org/"
25
25
  },
26
26
  "dependencies": {
27
- "@mengine/medeo-client": "1.0.1-alpha.0"
27
+ "@mengine/medeo-client": "1.2.1-alpha.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^25.9.1",