/** One in-flight or per-beat generation notice, published on the plugin * pubsub `generation` channel and returned by the `pendingGenerations` * snapshot. Value strings mirror @mulmobridge/protocol's GENERATION_KINDS * so MulmoClaude's host bridge maps 1:1 without a lookup table. */ export interface MulmoScriptGenerationEvent { kind: "beatImage" | "beatAudio" | "characterImage" | "movie" | "pdf"; /** Wire `stories/…` path of the script the generation belongs to. */ filePath: string; /** Which stories root `filePath` is relative to (#3014). Absent = the * host's default root, which is every event this package emitted before * roots existed. */ root?: string; /** beatIndex (as string) for beat*, character key for characterImage, "" for movie/pdf. */ key: string; /** false = started, true = finished (reload the asset off disk). */ done: boolean; /** Only set on done=true when the work failed. */ error?: string; } /** Plugin pubsub event name the host publishes generation events on * (full channel: `plugin::generation`). */ export declare const GENERATION_EVENT = "generation"; /** Plugin pubsub event name for "this script changed on disk" * (full channel: `plugin::scriptChanged`). */ export declare const SCRIPT_CHANGED_EVENT = "scriptChanged"; /** * A script was written — by the agent, or by another View. * * `origin` is who wrote it. A View passes its own id on every write and ignores the echo of * its own: without that, a keystroke would round-trip through the server and reload the very * element the caret is in. An agent write carries no origin, so every View reloads. */ export interface MulmoScriptChangedEvent { filePath: string; /** Which stories root `filePath` is relative to (#3014). Absent = default. */ root?: string; origin?: string; } /** * Whether a View watching `watching` should reload because of `event`. * * A pure rule rather than a condition inside the subscriber, because the case that matters is * the one that is invisible when it is wrong: a View acting on the echo of its own write * rebuilds the element the caret is in, on every keystroke. */ export declare const shouldReloadForScriptChange: (event: MulmoScriptChangedEvent, watching: string, ownOrigin: string, watchingRoot?: string) => boolean; /** The default root: what a caller that names no root is asking for. */ export declare const DEFAULT_ROOT = ""; /** * The one spelling of a root that every comparison and every key must use. * * It lives HERE, in the module both the server and the View import, because * the server had its own copy and the browser side compared raw strings — so * `publishGeneration` emitted `"repoA"` while a View watching `" repoA "` * dropped every event of its own generation (CodeRabbit on #3015). A rule * that two sides must agree on cannot live on one of the two sides. * * Trimmed, because the codebase's other opaque "which project root" reader — * `readCommandScope` in `@mulmoclaude/core/remote-host` — trims its value and * shares the "absent = the host's own root" convention. Without this, * `" repoA "` is one root there and a different one here. * * A non-string reads as the default root rather than throwing. The type says * that cannot happen, but this package is published and its callers include * untyped JavaScript: a subscription whose `root()` returns `42` reached * `.trim()` and threw from INSIDE a pubsub callback, where nothing catches it * and the View simply stops updating (Codex P2 on #3015). The guard belongs * here rather than at the three call sites, because a rule enforced by * enumerating its callers is what this PR got wrong repeatedly. */ export declare const normalizeRoot: (root: string | undefined) => string; /** * Two roots are the same when they name the same one, with absent meaning the * host's default (#3014). * * The identity of a script is the PAIR, not the path: `stories/deck.json` in * two repositories is two files, and comparing paths alone would reload the * View watching one because the other was saved. Absent normalises to the * default so a pre-`root` event and a default-root watcher still match — that * equivalence is what keeps every existing card working untouched. */ export declare const sameRoot: (a: string | undefined, b: string | undefined) => boolean; interface BeatRef { filePath: string; beatIndex: number; } interface CharacterRef { filePath: string; key: string; } /** Session tag for hosts that surface per-session generation indicators * (MulmoClaude's sidebar). Optional everywhere; hosts without sessions * ignore it. */ interface SessionTag { chatSessionId?: string | undefined; } /** * Which registered stories root the named script lives in (#3014). * * Intersected into the whole union below, so every dispatch that names a * `filePath` can name its root — a union member that could not would make * root-aware calls untypeable while the handler happily read `args.root` * off an untyped record (Codex P1 on #3015). */ interface RootTag { root?: string | undefined; } export type MulmoScriptDispatchArgs = RootTag & (({ kind: "save"; } & { filePath?: string; script?: unknown; filename?: string; }) | { kind: "updateBeat"; filePath: string; beatIndex: number; beat: unknown; origin?: string; } | { kind: "updateScript"; filePath: string; script: unknown; origin?: string; } | ({ kind: "beatImage"; } & BeatRef) | ({ kind: "beatAudio"; } & BeatRef) | ({ kind: "beatMovie"; } & BeatRef) | ({ kind: "renderBeat"; } & BeatRef & SessionTag & { force?: boolean; }) | ({ kind: "generateBeatAudio"; } & BeatRef & SessionTag & { force?: boolean; }) | ({ kind: "uploadBeatImage"; } & BeatRef & { imageData: string; }) | ({ kind: "characterImage"; } & CharacterRef) | ({ kind: "renderCharacter"; } & CharacterRef & SessionTag & { force?: boolean; }) | ({ kind: "uploadCharacterImage"; } & CharacterRef & { imageData: string; }) | ({ kind: "movieStatus"; } & { filePath: string; }) | ({ kind: "pdfStatus"; } & { filePath: string; }) | ({ kind: "generateMovie"; } & { filePath: string; } & SessionTag) | ({ kind: "generatePdf"; } & { filePath: string; } & SessionTag) | { kind: "pendingGenerations"; filePath: string; }); export type MulmoScriptDispatchKind = MulmoScriptDispatchArgs["kind"]; /** Failure half of every dispatch response. `code` mirrors the phase-1 * outcome codes so a host can log/telemetry on it; the View only reads * `error`. */ export interface DispatchFailure { ok: false; code?: "bad_request" | "not_found" | "server_error"; error: string; } export type DispatchEnvelope = ({ ok: true; } & T) | DispatchFailure; /** The success payload of each dispatch `kind`, BEFORE the root tag below is * applied. Not exported: `MulmoScriptDispatchResult` is the type callers use. */ interface DispatchResultPayloads { save: { script: Record; filePath: string; message: string; }; updateBeat: Record; updateScript: Record; beatImage: { image: string | null; }; beatAudio: { audio: string | null; }; beatMovie: { moviePath: string | null; }; renderBeat: { image: string; }; generateBeatAudio: { audio: string; }; uploadBeatImage: { image: string; }; characterImage: { image: string | null; }; renderCharacter: { image: string; }; uploadCharacterImage: { image: string; }; movieStatus: { moviePath: string | null; }; pdfStatus: { pdfPath: string | null; }; generateMovie: { moviePath: string; }; generatePdf: { pdfPath: string; }; pendingGenerations: { pending: MulmoScriptGenerationEvent[]; }; } /** * Maps a dispatch `kind` to its success payload, every one of them carrying * the root it acted in. * * A host builds its cards from these results, and a card's identity is the * PAIR `(root, filePath)` — `stories/deck.json` exists in every registered * root. Without the root here, two repositories' identically-named decks * collapse onto one card, which is #3014's third collision point, and no * amount of fixing the host's identity function helps because the value never * arrives. * * `root` was threaded through the ARGS and the EVENTS in #3015 and not through * the results — the same shape that PR got wrong repeatedly: the comparison * widened while the path carrying the data to it did not. Applied as a mapped * type rather than field by field so a kind added later cannot be forgotten. * * Absent means the default root, so every pre-`root` card is unchanged. */ export type MulmoScriptDispatchResult = { [K in keyof DispatchResultPayloads]: DispatchResultPayloads[K] & RootTag; }; export {}; //# sourceMappingURL=contract.d.ts.map