import type { WorldJsonShape } from '@bitmagic/world-forger/pipeline/index.js'; import type { ScenePayload } from './save.js'; /** Journal location, relative to the project root. */ export declare const JOURNAL_DIR: string; export declare const JOURNAL_FILE = "events.jsonl"; interface Vec3 { x: number; y: number; z: number; } /** The transform fields that actually changed, so a line reads as the edit the human made. */ interface TransformDelta { position?: { from: Vec3 | null; to: Vec3; }; rotation?: { from: Vec3 | null; to: Vec3; }; scale?: { from: Vec3 | null; to: Vec3; }; } export type EditorEvent = { event: 'session.started'; gameId: string; gameUrl: string; editorUrl: string; } | ({ event: 'object.moved'; objectId: string; type?: string; assetId?: string; } & TransformDelta) | { event: 'object.added'; objectId: string; type?: string; assetId?: string; position?: Vec3; } | { event: 'object.deleted'; objectId: string; type?: string; assetId?: string; } | { event: 'spawn.changed'; count: number; playerSpawn?: Vec3; } | { event: 'markers.changed'; count: number; } | { event: 'worldconfig.changed'; path: string[]; } /** * The human asked for a high-quality version of a forger placeholder, and the editor could not * run it — almost always because nobody is logged in. Recorded WITH the confirmed prompt and the * command that would do it, so the agent can pick it up. */ | { event: 'hq.requested'; assetId: string; assetName: string; prompt: string; command: string; } /** * The editor is running the generation itself. `hq.started` and its terminal partner exist so an * agent reading the journal is never surprised by an asset that changed on its own: it can see * the job begin, and see what it cost and how many instances it upgraded when it lands. */ | { event: 'hq.started'; assetId: string; assetName: string; prompt: string; } | { event: 'hq.completed'; assetId: string; assetName: string; instanceCount: number; /** Present when the asset was made from a mesh. Exactly one of these two is. */ glbUrl?: string; /** Present when it was made from a voxel master — the dev-lane path, which builds no mesh. */ masterUrl?: string; assetUrl?: string; } /** `glbUrl` is present when the mesh was already paid for — a retry can skip that step. */ | { event: 'hq.failed'; assetId: string; assetName: string; error: string; glbUrl?: string; } /** * Terrain and voxel-object sculpting, which `deriveSceneEvents` cannot see. * * Every other event here is reconstructed by diffing world.json before and against after. These * four are reported by the editor itself because the diff does not carry them: a terrain save * changes one URL that says nothing about what was sculpted, and a save that FAILED changes * nothing at all — yet an agent whose creator just lost work is exactly who needs to know. */ | { event: 'terrain.saved'; voxelUrl: string; } | { event: 'terrain.saveFailed'; error: string; } | { event: 'voxel.saved'; assetId: string; assetName: string; } | { event: 'voxel.saveFailed'; assetName: string; error: string; } /** * The Record-motion panel: a creator's video became (or failed to become) an animation clip. * `motion.requested` is the logged-out hand-off, like `hq.requested`; the other two bracket the * in-process job so an agent sees the asset row appear with a cause attached. */ | { event: 'motion.requested'; name: string; videoPath: string; command: string; } | { event: 'motion.started'; name: string; locomotionState?: string; } | { event: 'motion.completed'; name: string; motionId: string; animationUrl: string; locomotionState?: string; } | { event: 'motion.failed'; name: string; error: string; } /** * A level was added, renamed, removed, or made the one the game boots into. * * Its own line because a level change is close to invisible in a diff: `set-start` moves one id * and a url, and an agent holding world.json in memory would otherwise see the game boot * somewhere else with nothing to explain why. */ | { event: 'levels.changed'; action: string; levelId: string; name: string; }; /** One journal line: the event, stamped. */ export type JournalLine = EditorEvent & { at: string; }; /** * What changed between the world on disk and what the engine is reporting. * * Pure, and given the BEFORE document rather than reading it — so the caller reads world.json once * for both this and the modification builder, and the whole thing is testable without a filesystem * or a browser. * * Silence is meaningful: an upserted object whose transform is unchanged produces no line. The * engine re-serializes every object it was told is modified, including ones whose only "change" * was being marked, and a journal that logged those would drown the real edits. */ export declare function deriveSceneEvents(before: WorldJsonShape, payload: ScenePayload): EditorEvent[]; /** The one-line human form mirrored to stdout. The JSONL line stays the machine contract. */ export declare function formatEvent(event: EditorEvent): string; /** * The editor-reported events, validated off an untrusted request body. * * These arrive from the game iframe by way of the shell, so nothing about their shape is * guaranteed. An unrecognised event is dropped rather than written: the journal is a contract an * agent parses, and a malformed line there is worse than a missing one. */ export declare function asHostJournalEvent(payload: unknown): EditorEvent | null; export interface EditorJournalOptions { root: string; /** Where the human-readable mirror goes. Defaults to nowhere. */ log?: (message: string) => void; /** Injected in tests so a journal can be written without a clock. */ now?: () => string; } /** * Append-only writer. Failures to write are reported through `log` and never thrown: the journal * is a record OF the edit, and losing the record must not cost the creator the edit itself. */ export declare class EditorJournal { private readonly file; private readonly log; private readonly now; constructor(options: EditorJournalOptions); /** Absolute path of the journal, for the startup banner that tells an agent where to look. */ get path(): string; append(...events: EditorEvent[]): void; } export {};