@miraiclip/assistant
An AI editing assistant for Miraiclip: a vendor-neutral ChatModel contract (OpenAI first; any OpenAI-compatible server works), an agent loop that edits a forked project and lands each request as one undo step, high-level editing tools, and a server handler that keeps API keys off the browser.
npm install @miraiclip/assistant| Version | 0.1.1 |
| Runs in | Browser or Node |
| Exports | 52 |
| Guides | Editing assistant |
Functions
applyChecked
Apply a batch as one all-or-nothing step, with two checks core doesn't do: fields a command would silently ignore are errors, and commands that changed nothing are reported, so the model never claims an edit that didn't happen.
function applyChecked(project: Project, commands: Command[]): ToolOutputGuides: Editing assistant
applyCommandsSchema
apply_commands' input schema: type is an enum of the catalog's commands.
function applyCommandsSchema(catalog: Record<string, unknown>): Record<string, unknown>Guides: Editing assistant
commitFork
Replay a fork's commands onto the real project as one undoable step.
function commitFork(project: Project, fork: RecordingProject, label?: string): CommitResultGuides: Editing assistant
createAssistant
function createAssistant(options: AssistantOptions): AssistantGuides: Editing assistant
createChatHandler
function createChatHandler(model: ChatModel, options?: ChatHandlerOptions): (request: Request) => Promise<Response | null>Guides: Editing assistant
defineChatModel
Check an adapter's shape once, at registration.
function defineChatModel<M extends ChatModel>(model: M): MGuides: Editing assistant
defineTool
function defineTool(tool: AssistantTool): AssistantToolGuides: Editing assistant
describeCommands
One line per command, for a "what changed" list (keyframe edits fold into one line per clip).
function describeCommands(commands: readonly Command[]): string[]Guides: Editing assistant
describeForAssistant
The project as the model sees it: core's describeProject, plus what a
model needs to make RELATIVE edits ("bigger", "louder", "keep the
entrance") — track names, every clip's position, size and opacity, text
styling, volume, effects and its animation recipe.
function describeForAssistant(doc: ProjectDocument): stringGuides: Editing assistant
editorTools
The tools that edit a Miraiclip project: read state, look up a command's schema, apply command batches, and two high-level tools (transitions, animation presets) that use the same logic as the editor's panels.
function editorTools(options?: EditorToolsOptions): AssistantTool[]Guides: Editing assistant
forkProject
function forkProject(project: Project, options?: ForkOptions): RecordingProjectGuides: Editing assistant
openAIChatModel
function openAIChatModel(options: OpenAIChatModelOptions): ChatModelGuides: Editing assistant
parseToolArguments
Parse a tool call's JSON arguments; unparseable JSON is reported to the model as a tool error.
function parseToolArguments(raw: string): Record<string, unknown>Guides: Editing assistant
rateLimitedChatModel
Wrap a model so calls wait for room under your provider's per-minute limits instead of failing with 429s — for batch jobs (evals, bulk edits) and for a server sharing one key. Each call reserves an estimate, which is replaced by the real usage when the response reports it.
function rateLimitedChatModel(model: ChatModel, options: RateLimitOptions): ChatModelGuides: Editing assistant
remoteChatModel
The model a backend serves, as a browser-side ChatModel.
function remoteChatModel(endpoint: string, options?: RemoteChatModelOptions): Promise<ChatModel>Guides: Editing assistant
retryDelayMs
How long the server asked us to wait (Retry-After header, or "try again in 1.2s / 350ms"), else exponential backoff.
function retryDelayMs(res: Pick<Response, "headers">, message: string, attempt: number): numberGuides: Editing assistant
scriptedChatModel
A model that answers from a script: for tests, demos and offline
development. Each complete call takes the next step; a string is a final
text answer, an object can call tools. Requests are kept in requests.
function scriptedChatModel(steps: ScriptStep[], options?: {
id?: string;
}): ChatModel & {
requests: ChatRequest[];
}Guides: Editing assistant
toolsFromDefinitions
Adopt tools that come as definitions plus one runner — e.g.
@miraiclip/audio-sources' audioToolDefinitions + runAudioTool.
Accepts { name, description, input_schema } or OpenAI function tools.
function toolsFromDefinitions(definitions: readonly unknown[], run: (name: string, input: Record<string, unknown>, ctx: ToolContext) => Promise<{
ok: boolean;
result?: unknown;
error?: unknown;
}>, options?: {
changes?: (name: string, input: Record<string, unknown>, result: unknown) => string[] | undefined;
}): AssistantTool[]Guides: Editing assistant
toOpenAIMessages
Neutral messages → Chat Completions messages.
function toOpenAIMessages(messages: readonly ChatMessage[]): OpenAIMessage[]Guides: Editing assistant
unknownFields
Unknown payload fields for a command, judged against the best-matching schema branch.
function unknownFields(schema: unknown, payload: unknown): UnknownFields | nullGuides: Editing assistant
withExplicitIds
Built-in commands that would otherwise get a random id inside the engine.
function withExplicitIds(command: Command): CommandGuides: Editing assistant
Classes
ChatModelError
An error from a model vendor, carrying its HTTP status when there was one.
Extends Error.
new ChatModelError(provider: string, message: string, status?: number | undefined)| Member | Type | Description |
|---|---|---|
provider | string | |
status? | number | undefined |
Guides: Editing assistant
Constants
BACKGROUND_TEMPLATE
The html template set_background uses: one {{color}} fill.
const BACKGROUND_TEMPLATE: "<div style=\"width:100%;height:100%;background:{{color}}\"></div>"DEFAULT_INSTRUCTIONS
const DEFAULT_INSTRUCTIONS: "You are the editing assistant inside a video editor. You change the user's project by calling tools; every change you make lands as ONE undo step when you finish, so act confidently.\n\nHow to work:\n- The current project is summarized below. Clip, track and asset ids there are what tools expect. Call get_state to ...Guides: Editing assistant
Interfaces
Assistant
| Member | Type | Description |
|---|---|---|
tools | readonly AssistantTool[] | |
run | (project: Project, request: string, options?: TurnOptions) => Promise<AssistantTurn> |
AssistantOptions
| Member | Type | Description |
|---|---|---|
model | ChatModel | |
tools? | AssistantTool[] | Default editorTools(). Add your own (audio, captions, stock media…). |
instructions? | string | Extra system instructions (brand voice, house rules). |
maxSteps? | number | Model round-trips per request (default 12). |
modelId? | string | Model id when the adapter serves several. |
maxOutputTokens? | number | |
params? | Record<string, unknown> | Vendor request fields, passed through. |
fork? | ForkOptions |
AssistantTool
Extends ToolSpec.
| Member | Type | Description |
|---|---|---|
run | (input: Record<string, unknown>, ctx: ToolContext) => Promise<ToolOutput> | ToolOutput |
AssistantTurn
| Member | Type | Description |
|---|---|---|
reply | string | The model's final answer to the user. |
changes | string[] | Human-readable list of what the turn changed (empty when nothing). |
commands | readonly Command[] | The commands the turn makes, in order (replayed onto the project on apply). |
status | "applied" | "review" | "no-changes" | "failed" | "canceled" | applied: committed as one undo step. review: waiting for apply()/discard(). no-changes: nothing to apply. failed / canceled: the project wasn't touched. |
error? | string | |
preview | ProjectDocument | The project as the turn left it (for previews in review mode). |
messages | ChatMessage[] | The conversation so far, without the system prompt: pass back as history. |
usage | Required<ChatUsage> | |
apply | () => CommitResult | Commit a reviewed turn (no-op once applied). |
discard | () => void |
ChatHandlerOptions
Keys stay on the server: run the real model adapter in your backend behind
createChatHandler, and give the browser remoteChatModel(endpoint) — a
proxy with the same ChatModel shape. The agent loop and the tools run in
the browser, next to the project; only model calls cross the network.
GET {base}/ → { id, label, models } POST {base}/chat { request: ChatRequest } → application/x-ndjson, one JSON object per line: { "type": "text", "delta": "…" } (as it streams) { "type": "done", "response": ChatResponse } { "type": "error", "error": "…", "status": 401 } errors before streaming → { error } with 400 / 404 / 413, or the vendor's status
Fetch-API handler (Request → Response): mounts in Node 18+, edge
runtimes, Next, Hono, Express (via an adapter).
| Member | Type | Description |
|---|---|---|
basePath? | string | Path prefix the handler is mounted at, e.g. "/api/assistant". |
maxBodyBytes? | number | Reject request bodies larger than this (default 2 MB). |
authorize? | (request: Request) => Promise<Response | undefined> | Response | undefined | Return a Response to refuse a request (auth, rate limits); undefined to allow. |
prepare? | (request: ChatRequest) => ChatRequest | Adjust each request server-side (pin a model, cap tokens, add instructions). |
ChatModel
Extends ChatModelInfo.
| Member | Type | Description |
|---|---|---|
complete | (request: ChatRequest, options?: ChatOptions) => Promise<ChatResponse> |
Guides: Editing assistant
ChatModelInfo
| Member | Type | Description |
|---|---|---|
id | string | Adapter id, e.g. "openai". Alphanumeric with - or _. |
label | string | |
models? | { id: string; label?: string; }[] | Models this adapter serves; the first is the default. |
ChatOptions
| Member | Type | Description |
|---|---|---|
signal? | AbortSignal | |
onText? | (delta: string) => void | Streaming text, as it arrives (adapters that can't stream call it once). |
Guides: Editing assistant
ChatRequest
| Member | Type | Description |
|---|---|---|
messages | ChatMessage[] | |
tools? | ToolSpec[] | |
model? | string | Model id, when the adapter serves several. |
maxOutputTokens? | number | |
params? | Record<string, unknown> | Vendor-specific request fields, passed through untouched (e.g. { reasoning_effort: "low" }). |
Guides: Editing assistant
ChatResponse
| Member | Type | Description |
|---|---|---|
content | string | |
toolCalls | ToolCall[] | |
finish | "stop" | "tool_calls" | "length" | "content_filter" | "other" | Why the model stopped. |
model? | string | |
usage? | ChatUsage |
Guides: Editing assistant
ChatUsage
| Member | Type | Description |
|---|---|---|
inputTokens? | number | |
outputTokens? | number |
EditorToolsOptions
| Member | Type | Description |
|---|---|---|
transitionKinds? | string[] | Transition kinds to offer (default: the built-ins). Add your registered custom kinds. |
commandCatalog? | Record<string, unknown> | The command catalog to offer in apply_commands (default: the built-ins). Pass project.commandCatalog() when you register custom commands. |
ForkOptions
| Member | Type | Description |
|---|---|---|
createFork? | (doc: ReturnType<Project["toJSON"]>) => Project | Build the copy (default: createProject(doc)). Register custom commands here. |
ensureIds? | (command: Command) => Command | Give commands explicit ids (default handles the built-ins with optional ids). |
OpenAIChatModelOptions
OpenAI adapter, over the Chat Completions API with function calling.
Chat Completions is also what most other servers speak (Azure OpenAI,
OpenRouter, Groq, Together, vLLM, Ollama, LM Studio…), so baseUrl points
this one adapter at any of them.
| Member | Type | Description |
|---|---|---|
model | string | Model id, e.g. "gpt-5.4-mini". Required: model names change faster than this package. |
models? | { id: string; label?: string; }[] | Other models the app may pick per request (ChatRequest.model). |
apiKey? | string | API key. Omit for local servers that don't need one. Keep it server-side (see createChatHandler). |
baseUrl? | string | Default https://api.openai.com/v1. Any OpenAI-compatible base URL works. |
organization? | string | |
project? | string | |
headers? | Record<string, string> | |
params? | Record<string, unknown> | Default request fields merged into every call (e.g. { reasoning_effort: "low" }). |
stream? | boolean | Stream responses (default true). |
maxRetries? | number | Retries on rate limits (429) and server errors (500/502/503/504), waiting as long as the server asks (Retry-After, or "try again in 1.2s"), else backing off 1 s, 2 s, 4 s… (capped at 30 s). Default 4; 0 disables. |
id? | string | Adapter id / label (default "openai" / "OpenAI"), e.g. "ollama" when pointing elsewhere. |
label? | string | |
fetch? | FetchLike |
RateLimitOptions
| Member | Type | Description |
|---|---|---|
tokensPerMinute? | number | Tokens per rolling minute across every caller of this model (input + output). |
requestsPerMinute? | number | Requests per rolling minute. |
estimate? | (request: ChatRequest) => number | Token estimate for a request before it's sent (default: JSON length ÷ 4, plus room for the reply). |
RecordingProject
A working copy of a project that records every command that sticks.
The assistant edits the copy while the model thinks (async, many steps); the real project only changes when the turn commits, by replaying the recorded commands in one transaction — one undo step, and nothing half-done if the request fails. Commands inside a transaction that rolls back are dropped, exactly as the copy drops their effects.
Replay must produce the same ids the model saw, so commands whose ids are optional get explicit ones before they run.
Extends Project.
| Member | Type | Description |
|---|---|---|
recorded | readonly Command[] | Commands that changed the copy, in order. |
RemoteChatModelOptions
| Member | Type | Description |
|---|---|---|
fetch? | FetchLike | |
headers? | Record<string, string> | |
info? | { id: string; label: string; models?: { id: string; label?: string; }[]; } | Skip the GET for model info (id/label/models default to "remote"). |
ToolCall
A tool call the model made. arguments is already parsed JSON.
| Member | Type | Description |
|---|---|---|
id | string | |
name | string | |
arguments | Record<string, unknown> |
ToolContext
What a tool's run gets. project is the turn's working copy.
| Member | Type | Description |
|---|---|---|
project | Project | |
signal? | AbortSignal |
ToolSpec
A tool as the model sees it: name, description and a JSON Schema for its input.
| Member | Type | Description |
|---|---|---|
name | string | |
description | string | |
inputSchema | Record<string, unknown> |
TurnOptions
| Member | Type | Description |
|---|---|---|
history? | readonly ChatMessage[] | Earlier messages of this conversation (turn.messages from the last turn). |
context? | string | What the user is looking at, e.g. "Selected: clip t-1. Playhead: 4.2s." |
apply? | "auto" | "review" | "auto" (default) commits when the request finishes; "review" waits for turn.apply(). |
signal? | AbortSignal | |
onEvent? | (event: AssistantEvent) => void | |
label? | string | History label for the undo step (default "Assistant: <request>"). |
UnknownFields
| Member | Type | Description |
|---|---|---|
unknown | string[] | |
allowed | string[] | The fields the command does take (for the error message). |
Types
AssistantEvent
type AssistantEvent = {
type: "text";
delta: string;
} | {
type: "tool-start";
call: ToolCall;
} | {
type: "tool-end";
call: ToolCall;
ok: boolean;
changes?: string[];
error?: string;
} | {
type: "step";
index: number;
};ChatMessage
type ChatMessage = {
role: "system";
content: string;
} | {
role: "user";
content: string;
} | {
role: "assistant";
content: string;
toolCalls?: ToolCall[];
} | {
role: "tool";
toolCallId: string;
name: string;
content: string;
};Guides: Editing assistant
CommitResult
type CommitResult = {
ok: true;
applied: number;
} | {
ok: false;
error: string;
};FetchLike
type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;ScriptStep
type ScriptStep = string | {
text?: string;
toolCalls?: (Omit<ToolCall, "id"> & {
id?: string;
})[];
} | ((request: ChatRequest, index: number) => ScriptStep);ToolOutput
A tool's answer. changes are human-readable lines for the app's "what changed" list.
type ToolOutput = {
ok: true;
result?: unknown;
changes?: string[];
} | {
ok: false;
error: unknown;
};@miraiclip/server-export
Server-side export for Miraiclip: run exportProject in headless Chrome from Node — same pipeline as the browser, preview/export parity by construction.
@miraiclip/mcp
MCP server for Miraiclip: let Claude, Codex, or any MCP client edit a video project through the command catalog — with frame previews so the agent sees its edits, and export to file.