@cyberart-io/engine 0.0.8 → 0.0.9
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.
- package/CHANGELOG.md +37 -0
- package/README.md +305 -1
- package/dist/headless.d.ts +2494 -37
- package/dist/headless.js +1 -1
- package/dist/index.d.ts +2589 -267
- package/dist/index.js +1 -1
- package/docs/asset-resolver.md +1 -1
- package/docs/browser-harness.md +1 -1
- package/docs/capability-manifest.md +1 -1
- package/docs/content-revision.md +64 -0
- package/docs/events.md +1 -1
- package/docs/headless-harness.md +1 -1
- package/docs/job-orchestration.md +55 -0
- package/docs/portal-lifecycle.md +50 -0
- package/docs/presentation-bindings.md +93 -0
- package/docs/presentation-sequences.md +150 -0
- package/docs/production-scenario.md +81 -0
- package/docs/remote-cart-manifest.md +66 -0
- package/docs/replay-inspector.md +1 -1
- package/docs/runtime-group.md +1 -1
- package/docs/selection-trace.md +60 -0
- package/docs/semantic-layers.md +72 -0
- package/docs/snapshots.md +1 -1
- package/docs/visual-layers.md +1 -1
- package/docs/world-graph.md +39 -0
- package/docs/world-patch.md +61 -0
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@cyberart-io/engine`. Each entry links the guide in `docs/` and the pull request on [cyberart-io/cyberart.io](https://github.com/cyberart-io/cyberart.io). Versions before 0.0.9 predate this file.
|
|
4
|
+
|
|
5
|
+
## 0.0.9 — 2026-09-05
|
|
6
|
+
|
|
7
|
+
Eleven Animation Framework modules. All are additive: no existing export changed signature, and the `@cyberart-io/engine` / `@cyberart-io/engine/headless` entries export the same new API. Every module ships a `snapshot()` / `restore()` pair that validates JSON before mutating, and typed `*.state.*` / `*.diagnostic.*` event contracts for the router.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Semantic layers** — `createSemanticLayerController`. Cart-published named regions (mask / polygon / rect / depth) in normalized coordinates; host-driven hover / focus / selected, a11y names and roles, hit-test policies `pass-through` / `absorb` / `exclusive` / `depth-ordered`; `inspectPublished()` for segmentation clients. [docs/semantic-layers.md](docs/semantic-layers.md) · [#30](https://github.com/cyberart-io/cyberart.io/pull/30)
|
|
12
|
+
- **Remote cart manifests** — `signRemoteCartManifest`, `loadRemoteCart`, `createRemoteCartRegistry`, `createRemoteCartSandbox`, `createHostGrantSet`, `createDeclaredAssetResolver`. HMAC-signed versioned catalogs, declared asset hashes, deny-by-default host grants, no ambient network. [docs/remote-cart-manifest.md](docs/remote-cart-manifest.md) · [#31](https://github.com/cyberart-io/cyberart.io/pull/31)
|
|
13
|
+
- **Portal lifecycle** — `createPortalLifecycle`, `cabinetPortal`, `paintingPortal`. One enter / exit / abort contract for inhabiting another cart exclusively; parent suspend in the runtime group, exclusive grant transfer, nested stack, versioned outcome. [docs/portal-lifecycle.md](docs/portal-lifecycle.md) · [#33](https://github.com/cyberart-io/cyberart.io/pull/33)
|
|
14
|
+
- **Job orchestration** — `createJobCoordinator`, `createMemoryJobPersistence`; `createHeadlessJobWorker` on the headless entry. Durable queued → claimed → awaiting-evaluation → ready → applied lifecycle with retries, leases, evaluator feedback, and host-owned `accept`. Results are refs, not payloads. [docs/job-orchestration.md](docs/job-orchestration.md) · [#34](https://github.com/cyberart-io/cyberart.io/pull/34)
|
|
15
|
+
- **World graph** — `createWorldGraph`, `worldGraphEventContracts`. Optional spatial graph with stable ids, canonical vs per-observer known projections, locked / disabled edges with opaque host policy, semantic-time transit, atomic `applyPatch`. [docs/world-graph.md](docs/world-graph.md) · [#35](https://github.com/cyberart-io/cyberart.io/pull/35)
|
|
16
|
+
- **Presentation sequences** — `definePresentationSequence`, `createPresentationSequencePlayer`; `createHeadlessAudioAdapter` on the headless entry. One frame-stepped clock across audio, captions, semantic focus, visual layers, and cues; skip / replay / replace / queue; declared capability fallbacks (text-only when audio fails). [docs/presentation-sequences.md](docs/presentation-sequences.md) · [#36](https://github.com/cyberart-io/cyberart.io/pull/36)
|
|
17
|
+
- **Content revision** — `createContentRevisionActivator`, `createStaticContentAdapter`, `createRemoteContentAdapter`, `contentRevisionEventContracts`. Atomic staging and commit of an external content revision; last-known-good rollback, supersede, pin, exact revisions only. [docs/content-revision.md](docs/content-revision.md) · [#37](https://github.com/cyberart-io/cyberart.io/pull/37)
|
|
18
|
+
- **Presentation bindings** — `definePresentationBindings`, `createPresentationBindingRuntime`. Versioned manifest projecting host state onto visual versions, presence, props, semantic regions, hotspots, and sequences; fail-closed defaults, priority conflict resolution, explanation of winners and rejected alternatives. [docs/presentation-bindings.md](docs/presentation-bindings.md) · [#39](https://github.com/cyberart-io/cyberart.io/pull/39)
|
|
19
|
+
- **World patch** — `createWorldPatchApplier`, `createMemoryWorldPersistence`, `worldPatchEventContracts`. Transactional dry-run and atomic commit of a generated world patch as one revision; `stale-base` rejection, idempotent `patchId`, optional world-graph composition, host-injected persistence. [docs/world-patch.md](docs/world-patch.md) · [#40](https://github.com/cyberart-io/cyberart.io/pull/40)
|
|
20
|
+
- **Production scenario** — `defineProductionScenario`, `createProductionScenarioRunner`, `localizeProductionScenarioFailure`, `productionScenarioEventContracts`. One JSON scenario drives the real browser harness / runtime group / compositor / layers / bindings / sequences / audio / snapshot / replay chain; required participants and layers fail closed; `ProductionScenarioEvidenceBundle` with hashes and `firstBrokenBoundary`. [docs/production-scenario.md](docs/production-scenario.md) · [#41](https://github.com/cyberart-io/cyberart.io/pull/41)
|
|
21
|
+
- **Selection trace** — `createSelectionTrace`, `attachSelectionTrace`. Why a binding, revision, asset, sequence, or fallback won; distinct reason codes for binding conflict, asset failure, revision rollback, sequence interruption, and text-only fallback; whitelist-based projection redaction and signed-URL scrubbing; correlates with replay-inspector records and attaches to a production-scenario evidence bundle. [docs/selection-trace.md](docs/selection-trace.md) · [#42](https://github.com/cyberart-io/cyberart.io/pull/42)
|
|
22
|
+
|
|
23
|
+
### Docs
|
|
24
|
+
|
|
25
|
+
- README sections and cross-links for every module above; `docs/` is included in the tarball and verified by `pnpm run pack:engine`. [#43](https://github.com/cyberart-io/cyberart.io/pull/43)
|
|
26
|
+
|
|
27
|
+
## 0.0.8 — 2026-08-23
|
|
28
|
+
|
|
29
|
+
- Frame benchmarking: `benchmarkCartFrames` (headless) and `onFrameTiming` (live). [docs/frame-benchmark.md](docs/frame-benchmark.md)
|
|
30
|
+
|
|
31
|
+
## 0.0.7 — 2026-08-23
|
|
32
|
+
|
|
33
|
+
- Calculation carts and versioned visual layers. [docs/calculation-carts.md](docs/calculation-carts.md), [docs/visual-layers.md](docs/visual-layers.md)
|
|
34
|
+
|
|
35
|
+
## 0.0.6 — 2026-08-23
|
|
36
|
+
|
|
37
|
+
- MIDI, executable modules, snapshots, and the audio broker. [docs/midi.md](docs/midi.md), [docs/executable-modules.md](docs/executable-modules.md), [docs/snapshots.md](docs/snapshots.md), [docs/audio.md](docs/audio.md)
|
package/README.md
CHANGED
|
@@ -96,13 +96,24 @@ Full API for the event router, deterministic replay, and CI harness (so agents c
|
|
|
96
96
|
- [Presentation cue](docs/presentation-cue.md) — deterministic `createPresentationTimeline`, duplicate policy, reduced-motion, lifecycle events
|
|
97
97
|
- [Capability manifest](docs/capability-manifest.md) — versioned JSON for runtime features, phases, managers, assets, events, permissions, integrations
|
|
98
98
|
- [Executable modules](docs/executable-modules.md) — trusted versioned factories, host allowlists, isolation, per-module failures
|
|
99
|
+
- [Remote cart manifests](docs/remote-cart-manifest.md) — signed catalogs, declared asset hashes, inspectable host grants
|
|
99
100
|
- [Normalized geometry](docs/normalized-geometry.md) — coordinate spaces, contain/cover/crop layout, landmarks, hit regions, debug overlay
|
|
100
101
|
- [Runtime group](docs/runtime-group.md) — `createRuntimeGroup`, shared router attach, lockstep clock; `createHeadlessMultiCartHarness` from `@cyberart-io/engine/headless`
|
|
101
102
|
- [Calculation carts](docs/calculation-carts.md) — `kind: 'calculation'`, update + events without paint; sibling render carts still paint
|
|
102
103
|
- [Compositor](docs/compositor.md) — `createCompositor`, transparent stacking, `screen` blend, `writeComposedFrame`
|
|
103
104
|
- [Visual layers](docs/visual-layers.md) — `createVisualLayerController`, versioned overlay `v1`/`v2`, deterministic swaps, `hostState` JSON
|
|
105
|
+
- [Semantic layers](docs/semantic-layers.md) — `createSemanticLayerController`, named mask/polygon/depth regions, host a11y and hover/focus/selected
|
|
106
|
+
- [Presentation sequences](docs/presentation-sequences.md) — `definePresentationSequence`, shared clock for audio / caption / focus / visual, skip / fallback / restore
|
|
107
|
+
- [Presentation bindings](docs/presentation-bindings.md) — `definePresentationBindings`, host state projection to visual / presence / regions / sequences, fail-closed defaults
|
|
108
|
+
- [Portal lifecycle](docs/portal-lifecycle.md) — `createPortalLifecycle`, enter/exit/abort, grant transfer, nested stack restore
|
|
109
|
+
- [Job orchestration](docs/job-orchestration.md) — `createJobCoordinator`, durable jobs, headless fake worker, host-owned apply
|
|
110
|
+
- [World graph](docs/world-graph.md) — `createWorldGraph`, known vs canonical projections, host path policy, semantic-time transit
|
|
111
|
+
- [World patch](docs/world-patch.md) — `createWorldPatchApplier`, transactional validation, atomic world revision, host persistence adapter
|
|
112
|
+
- [Content revision](docs/content-revision.md) — `createContentRevisionActivator`, atomic remote/static bundle swap, last-known-good rollback
|
|
104
113
|
- [Browser harness](docs/browser-harness.md) — `createBrowserHarness`, DOM clicks, viewport/DPR, composed screenshots
|
|
114
|
+
- [Production scenario](docs/production-scenario.md) — `defineProductionScenario` / `createProductionScenarioRunner`, full host/runtime/cart composition, evidence bundles
|
|
105
115
|
- [Replay inspector](docs/replay-inspector.md) — `createReplayInspector`, causation trees, redacted export/import, headless replay
|
|
116
|
+
- [Selection trace](docs/selection-trace.md) — `createSelectionTrace`, binding/revision/asset/sequence fallback records, secret-safe export, CYB-82 evidence attach
|
|
106
117
|
- [MIDI](docs/midi.md) — host-owned `MidiManager`, note/CC/pitch in and out, `inject` / fake port, structured `requestAccess`
|
|
107
118
|
- [Snapshots](docs/snapshots.md) — versioned envelope, `defineSnapshot` / `applySnapshotMigrations`, host-owned `hostState`
|
|
108
119
|
- [Audio](docs/audio.md) — `createAudioBroker`, `createAudioCueTimeline`, headless cue traces (PCM is not deterministic)
|
|
@@ -416,6 +427,38 @@ host.destroy();
|
|
|
416
427
|
|
|
417
428
|
Unknown ids, wrong versions, and refs missing from the allowlist fail closed. A throwing module returns `{ ok: false, error }` and the next allowlisted invoke still runs. Isolation is in-process (trusted factories), not a worker or iframe. Carts may declare required refs as optional `modules.refs` on a [capability manifest](docs/capability-manifest.md). Full API and the reproduce command: [executable modules](docs/executable-modules.md).
|
|
418
429
|
|
|
430
|
+
## Remote cart manifests
|
|
431
|
+
|
|
432
|
+
Signed, versioned catalogs for third-party carts. HMAC over the canonical body, declared asset hashes, and deny-by-default host grants. Isolation is in-process trusted factories, not `eval`. There is no ambient network: tests inject a registry map or `fetch`.
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
import {
|
|
436
|
+
createRemoteCartRegistry,
|
|
437
|
+
createRemoteCartSandbox,
|
|
438
|
+
loadRemoteCart,
|
|
439
|
+
signRemoteCartManifest,
|
|
440
|
+
} from '@cyberart-io/engine';
|
|
441
|
+
|
|
442
|
+
const signed = await signRemoteCartManifest(body, secret, 'key-1');
|
|
443
|
+
if (!signed.ok) throw new Error(signed.errors.map((e) => e.detail).join('; '));
|
|
444
|
+
const loaded = await loadRemoteCart({
|
|
445
|
+
source: { kind: 'registry', id: 'vendor.remote' },
|
|
446
|
+
keys: { 'key-1': secret },
|
|
447
|
+
registry: createRemoteCartRegistry({ 'vendor.remote': signed.manifest }),
|
|
448
|
+
expectedVersion: '1.0.0',
|
|
449
|
+
});
|
|
450
|
+
if (!loaded.ok) throw new Error(loaded.errors.map((e) => e.detail).join('; '));
|
|
451
|
+
const sandbox = createRemoteCartSandbox({
|
|
452
|
+
manifest: loaded.manifest,
|
|
453
|
+
keys: { 'key-1': secret },
|
|
454
|
+
grants: ['audio'],
|
|
455
|
+
bytesByUrl,
|
|
456
|
+
});
|
|
457
|
+
await sandbox.loadAssets(); // re-verifies HMAC; hash mismatch refuses load
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
`createHostGrantSet`, `createDeclaredAssetResolver`, `snapshotProvenance()`, and the diagnostic code table: [remote cart manifests](docs/remote-cart-manifest.md).
|
|
461
|
+
|
|
419
462
|
## Runtime group
|
|
420
463
|
|
|
421
464
|
Several production carts, one router, one lockstep clock. Use this instead of intercepting each `onEvent` and republishing by hand.
|
|
@@ -438,6 +481,25 @@ group.destroy();
|
|
|
438
481
|
|
|
439
482
|
Headless / CI: `createHeadlessMultiCartHarness` from `@cyberart-io/engine/headless` is the same handle after `installHeadlessCanvas()`. Full options: [runtime group](docs/runtime-group.md). Calculation carts (no canvas / paint): [calculation carts](docs/calculation-carts.md). Causation trees, redaction, and tape replay: [replay inspector](docs/replay-inspector.md).
|
|
440
483
|
|
|
484
|
+
## Portal lifecycle
|
|
485
|
+
|
|
486
|
+
One enter / exit / abort contract for leaving a cart, inhabiting another exclusively, and returning a versioned outcome. Cabinet, painting, dream, and nested world are host metaphors over the same primitive. The parent is suspended in the runtime group; exclusive grants (`audio`, `controller`, `fullscreen`) transfer for the child's lifetime.
|
|
487
|
+
|
|
488
|
+
```ts
|
|
489
|
+
import { cabinetPortal, createPortalLifecycle } from '@cyberart-io/engine';
|
|
490
|
+
|
|
491
|
+
const portal = createPortalLifecycle({
|
|
492
|
+
group,
|
|
493
|
+
grants: { lobby: lobbyGrants, game: gameGrants },
|
|
494
|
+
rootId: 'lobby',
|
|
495
|
+
declarations: [{ id: 'lobby', targets: ['game'] }],
|
|
496
|
+
});
|
|
497
|
+
cabinetPortal(portal).enter({ from: 'lobby', to: 'game', seed });
|
|
498
|
+
const outcome = portal.exit({ score: 108 }); // { schemaVersion: 1, kind: 'completed', payload }
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Host chrome reads `portal.stack()` / `portal.activeId()`; persist `portal.snapshot()` in envelope `hostState`. Nested stacks, abort, and mid-portal restore: [portal lifecycle](docs/portal-lifecycle.md).
|
|
502
|
+
|
|
441
503
|
## Visual layers
|
|
442
504
|
|
|
443
505
|
Durable overlay / mask / sprite versions (`v1`, `v2`) with deterministic show, hide, replace, and crossfade. An accepted `host.state.accepted` (`sceneId: 'alpha'`) runs a presentation cue; the compositor reveals a **preloaded** alternate source in one `setLayer`. Failed assets keep the prior valid frame and emit `visual.layer.failed`. Persist `controller.snapshot()` in envelope `hostState` — do not put pixels in the envelope.
|
|
@@ -478,6 +540,248 @@ layers.step(2);
|
|
|
478
540
|
|
|
479
541
|
Headless capture: `captureVisualLayers(layers)` from `@cyberart-io/engine` or `@cyberart-io/engine/headless`. Full API: [visual layers](docs/visual-layers.md).
|
|
480
542
|
|
|
543
|
+
## Semantic layers
|
|
544
|
+
|
|
545
|
+
Named regions the cart publishes as mask, polygon, rect, and/or depth in normalized coordinates. The host drives hover / focus / selected and supplies a11y names and roles; geometry, blend, and hit-test policy (`pass-through`, `absorb`, `exclusive`, `depth-ordered`) stay with the cart.
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
import { createSemanticLayerController } from '@cyberart-io/engine';
|
|
549
|
+
|
|
550
|
+
const regions = createSemanticLayerController({
|
|
551
|
+
compositor,
|
|
552
|
+
visualLayers,
|
|
553
|
+
regions: [
|
|
554
|
+
{
|
|
555
|
+
id: 'cabinet',
|
|
556
|
+
hitTest: 'absorb',
|
|
557
|
+
compositorLayerId: 'hotspot',
|
|
558
|
+
visualLayerId: 'hotspot',
|
|
559
|
+
geometry: { rect: { x: 0.2, y: 0.2, width: 0.3, height: 0.3 }, depth: 0.4 },
|
|
560
|
+
visuals: { idle: { version: 'idle' }, hover: { version: 'hover', blend: 'screen' } },
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
});
|
|
564
|
+
regions.setRegionA11y('cabinet', { name: 'Arcade cabinet', role: 'button' });
|
|
565
|
+
regions.setRegionState('cabinet', { hover: true });
|
|
566
|
+
const hit = regions.hitTest({ x: 0.3, y: 0.3 });
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
`inspectPublished()` returns `{ id, geometryKinds, a11y }` for segmentation clients. Snapshot / restore validate the whole region set before mutating: [semantic layers](docs/semantic-layers.md).
|
|
570
|
+
|
|
571
|
+
## Presentation sequences
|
|
572
|
+
|
|
573
|
+
One frame-stepped clock for a semantic beat across audio, captions, semantic-region focus, visual layers, and typed cues. Hosts pass bindings; the engine does not own dialogue graphs.
|
|
574
|
+
|
|
575
|
+
```ts
|
|
576
|
+
import {
|
|
577
|
+
createPresentationSequencePlayer,
|
|
578
|
+
definePresentationSequence,
|
|
579
|
+
} from '@cyberart-io/engine';
|
|
580
|
+
import { createHeadlessAudioAdapter } from '@cyberart-io/engine/headless';
|
|
581
|
+
|
|
582
|
+
const defined = definePresentationSequence({
|
|
583
|
+
id: 'spoken-line',
|
|
584
|
+
interruptionPolicy: 'replace',
|
|
585
|
+
tracks: [
|
|
586
|
+
{
|
|
587
|
+
id: 'caption',
|
|
588
|
+
kind: 'caption',
|
|
589
|
+
steps: [
|
|
590
|
+
{
|
|
591
|
+
id: 'line-1',
|
|
592
|
+
timing: { kind: 'absolute', atFrame: 0 },
|
|
593
|
+
durationFrames: 6,
|
|
594
|
+
effect: { kind: 'caption', textBinding: 'line', visible: true },
|
|
595
|
+
restoreOnComplete: true,
|
|
596
|
+
},
|
|
597
|
+
],
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
fallbacks: [
|
|
601
|
+
{ id: 'text-only', when: { capability: 'audio', status: 'failed' }, omitTrackIds: ['audio'] },
|
|
602
|
+
],
|
|
603
|
+
});
|
|
604
|
+
const player = createPresentationSequencePlayer({
|
|
605
|
+
originFrame: 0,
|
|
606
|
+
audio: createHeadlessAudioAdapter({ originFrame: 0 }),
|
|
607
|
+
sequences: [defined.sequence],
|
|
608
|
+
});
|
|
609
|
+
player.playSequence('spoken-line', {
|
|
610
|
+
invocationId: 'inv-1',
|
|
611
|
+
bindings: { captions: { line: 'spoken line' } },
|
|
612
|
+
});
|
|
613
|
+
player.step(6);
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Skip, replay, pause/resume, replace/queue, failed-audio fallback, and mid-line restore: [presentation sequences](docs/presentation-sequences.md).
|
|
617
|
+
|
|
618
|
+
## Presentation bindings
|
|
619
|
+
|
|
620
|
+
Host-authoritative state stays on the host. A versioned binding manifest projects a JSON snapshot onto visual versions, presence, props, semantic regions, hotspots, and sequences. Invalid or missing state applies the declared defaults as one complete set.
|
|
621
|
+
|
|
622
|
+
```ts
|
|
623
|
+
import {
|
|
624
|
+
createPresentationBindingRuntime,
|
|
625
|
+
definePresentationBindings,
|
|
626
|
+
} from '@cyberart-io/engine';
|
|
627
|
+
|
|
628
|
+
const defined = definePresentationBindings({
|
|
629
|
+
id: 'lobby-room',
|
|
630
|
+
schemaVersion: 1,
|
|
631
|
+
bindings: [
|
|
632
|
+
{
|
|
633
|
+
id: 'night-room',
|
|
634
|
+
priority: 30,
|
|
635
|
+
when: { path: 'timeOfDay', eq: 'night' },
|
|
636
|
+
targets: [{ kind: 'visual-layer', layerId: 'room', version: 'night' }],
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
defaults: [{ kind: 'visual-layer', layerId: 'room', version: 'day' }],
|
|
640
|
+
});
|
|
641
|
+
if (!defined.ok) throw new Error(defined.errors.map((e) => e.detail).join('; '));
|
|
642
|
+
const runtime = createPresentationBindingRuntime({
|
|
643
|
+
manifest: defined.manifest,
|
|
644
|
+
visual,
|
|
645
|
+
semantic,
|
|
646
|
+
sequences,
|
|
647
|
+
});
|
|
648
|
+
runtime.apply({ timeOfDay: 'night' }, { revision: 83 });
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Fail-closed validation, conflict priority, snapshot restore, and host a11y overrides: [presentation bindings](docs/presentation-bindings.md).
|
|
652
|
+
|
|
653
|
+
## Production scenario
|
|
654
|
+
|
|
655
|
+
One scenario definition drives the real production chain: browser harness, runtime group, compositor, visual/semantic layers, bindings, sequences, headless audio, snapshots, and replay. Required carts and layers are declared so an isolated effect test cannot go green while the shipped composition omits them.
|
|
656
|
+
|
|
657
|
+
```ts
|
|
658
|
+
import {
|
|
659
|
+
createProductionScenarioRunner,
|
|
660
|
+
defineProductionScenario,
|
|
661
|
+
} from '@cyberart-io/engine';
|
|
662
|
+
|
|
663
|
+
const defined = defineProductionScenario({
|
|
664
|
+
id: 'room-whistle',
|
|
665
|
+
schemaVersion: 1,
|
|
666
|
+
seed: `0x${'82'.repeat(32)}`,
|
|
667
|
+
required: { participants: ['room', 'ripple'], layers: ['room', 'ripple'] },
|
|
668
|
+
});
|
|
669
|
+
const runner = createProductionScenarioRunner({
|
|
670
|
+
scenario: defined.scenario,
|
|
671
|
+
participants,
|
|
672
|
+
compositorLayers,
|
|
673
|
+
host,
|
|
674
|
+
});
|
|
675
|
+
const evidence = await runner.run([{ type: 'click', selector: '#whistle' }, { type: 'step', frames: 2 }]);
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
CI and local Vitest execute that same definition. Full contract, matrix axes, and one-command reproduce: [production scenario](docs/production-scenario.md).
|
|
679
|
+
|
|
680
|
+
## Selection trace
|
|
681
|
+
|
|
682
|
+
Why this binding, revision, asset, or caption won — not a second debugger. Same query/export API in the browser package and headless. There is no Player UI.
|
|
683
|
+
|
|
684
|
+
```ts
|
|
685
|
+
import { createSelectionTrace, attachSelectionTrace } from '@cyberart-io/engine';
|
|
686
|
+
|
|
687
|
+
const trace = createSelectionTrace({
|
|
688
|
+
projectionWhitelist: ['inventory', 'roomId'],
|
|
689
|
+
});
|
|
690
|
+
trace.recordSelectionDecision({
|
|
691
|
+
kind: 'asset',
|
|
692
|
+
reason: 'text-only-fallback',
|
|
693
|
+
summary: 'npc audio not ready; caption selected',
|
|
694
|
+
correlationId,
|
|
695
|
+
asset: { logicalRef: 'npc.line.audio', ready: false, fallback: 'npc.line.caption' },
|
|
696
|
+
});
|
|
697
|
+
const exported = trace.exportTrace({ correlationId });
|
|
698
|
+
const evidence = attachSelectionTrace(bundle, exported);
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Reason codes, redaction, and one-command reproduce: [selection trace](docs/selection-trace.md).
|
|
702
|
+
|
|
703
|
+
## Job orchestration
|
|
704
|
+
|
|
705
|
+
Durable lifecycle for long-running generated work: queued → claimed → awaiting-evaluation → ready → applied, plus retry / failed / canceled / superseded. Results are refs (`uri` + `kind`), not embedded media. Wall-clock completion never mutates authoritative host state; the host calls `accept(jobId)` on `ready` under its own policy.
|
|
706
|
+
|
|
707
|
+
```ts
|
|
708
|
+
import { createHeadlessJobWorker, createJobCoordinator } from '@cyberart-io/engine/headless';
|
|
709
|
+
|
|
710
|
+
const worker = createHeadlessJobWorker();
|
|
711
|
+
const jobs = createJobCoordinator({ definitions: [frontierDefinition], worker, now: () => clock });
|
|
712
|
+
const submitted = jobs.submit({
|
|
713
|
+
definitionId: 'frontier-generate',
|
|
714
|
+
idempotencyKey: 'frontier:day-1',
|
|
715
|
+
request: { prompt: '...', seed },
|
|
716
|
+
correlationId: playerIntentId,
|
|
717
|
+
});
|
|
718
|
+
if (!submitted.ok) throw new Error(submitted.errors.map((e) => e.detail).join('; '));
|
|
719
|
+
const jobId = submitted.job.id;
|
|
720
|
+
jobs.claim('worker-1');
|
|
721
|
+
worker.complete(jobId, { schemaVersion: 1, kind: 'world-chunk', uri: 'ref://...' });
|
|
722
|
+
jobs.evaluate(jobId, 'accept');
|
|
723
|
+
jobs.accept(jobId); // host policy decides when
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
Production hosts import `createJobCoordinator` from `@cyberart-io/engine` and persist `snapshot()` in envelope `hostState`. Leases, evaluator revisions, and redacted trace export: [job orchestration](docs/job-orchestration.md).
|
|
727
|
+
|
|
728
|
+
## World graph
|
|
729
|
+
|
|
730
|
+
Optional spatial graph for rooms, regions, frontiers, discovery, and entity transit. Stable host-authored ids, canonical vs per-observer known projections, `open` / `locked` / `disabled` edges with opaque lock JSON, and host-supplied semantic time. It does not render maps or encode quest rules.
|
|
731
|
+
|
|
732
|
+
```ts
|
|
733
|
+
import { createWorldGraph } from '@cyberart-io/engine';
|
|
734
|
+
|
|
735
|
+
const graph = createWorldGraph({ graphId: 'adventure' });
|
|
736
|
+
graph.applyPatch({ nodes, edges }); // atomic; failure rolls back
|
|
737
|
+
graph.discover('walker', { nodeIds: ['lodge', 'room-1'], edgeIds: ['lodge-room-1'] });
|
|
738
|
+
const minimap = graph.projectLocal('walker', 'room-1');
|
|
739
|
+
const path = graph.findPath('lodge', 'station', hostPolicy, { semanticTime: day });
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
Transit, migrations, `world.graph.*` contracts: [world graph](docs/world-graph.md).
|
|
743
|
+
|
|
744
|
+
## World patch
|
|
745
|
+
|
|
746
|
+
Transactional validation and atomic application of a generated world patch. Rooms, exits, characters, assets, dialogue, graph edges, and presentation bindings become one world revision or none. Stale `baseRevision` is rejected (no silent rebase); re-submitting an accepted `patchId` is idempotent.
|
|
747
|
+
|
|
748
|
+
```ts
|
|
749
|
+
import { createMemoryWorldPersistence, createWorldPatchApplier } from '@cyberart-io/engine';
|
|
750
|
+
|
|
751
|
+
const world = createWorldPatchApplier({
|
|
752
|
+
persistence: createMemoryWorldPersistence(),
|
|
753
|
+
content: { available: [{ revision: '8' }] },
|
|
754
|
+
bindings: { listedIds: ['lobby-room'] },
|
|
755
|
+
now: () => hostClock,
|
|
756
|
+
});
|
|
757
|
+
world.dryRun(patch); // diagnostics only, inspect() unchanged
|
|
758
|
+
world.commit(patch); // one revision, or the previous world unchanged
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
Hosts inject their own `begin` / `applyWorldRevision` / `commit` / `rollback` persistence. Exactly one `world.patch.state.accepted` per revision: [world patch](docs/world-patch.md).
|
|
762
|
+
|
|
763
|
+
## Content revision
|
|
764
|
+
|
|
765
|
+
Atomic staging and commit of an external content revision (room art, masks, portraits, line audio). Every declared asset is resolved and validated before `activeBundle()` changes; consumers see the last known good revision or the new one, never a mix. Snapshots store exact revisions, never `"latest"`.
|
|
766
|
+
|
|
767
|
+
```ts
|
|
768
|
+
import {
|
|
769
|
+
createContentRevisionActivator,
|
|
770
|
+
createHostGrantSet,
|
|
771
|
+
createStaticContentAdapter,
|
|
772
|
+
} from '@cyberart-io/engine';
|
|
773
|
+
|
|
774
|
+
const activator = createContentRevisionActivator({
|
|
775
|
+
adapters: [createStaticContentAdapter({ revisions: catalogs })],
|
|
776
|
+
grants: createHostGrantSet(['audio']),
|
|
777
|
+
});
|
|
778
|
+
await activator.activate({ contentId: 'room-overlook', revision: '8' });
|
|
779
|
+
activator.activeBundle(); // complete revision 8, or null
|
|
780
|
+
activator.rollback(); // idempotent; last known good
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
`createRemoteContentAdapter({ fetch })` reads signed remote catalogs. Supersede, pin, health checks, and `content.revision.*` contracts: [content revision](docs/content-revision.md).
|
|
784
|
+
|
|
481
785
|
## Normalized geometry
|
|
482
786
|
|
|
483
787
|
Shared 0–1 content-box coordinates, landmarks, and hit regions. Pixel `PresentationRegion` on the presentation adapter is unchanged.
|
|
@@ -653,7 +957,7 @@ A canvas the host adopted is left in place on destroy; a canvas the engine creat
|
|
|
653
957
|
|
|
654
958
|
## Publishing this package (maintainers)
|
|
655
959
|
|
|
656
|
-
Not part of writing a cart. Engine source lives in `packages/engine/src/` (not mixed into the site). The npm tarball is built from `packages/engine/src/index.ts` and `packages/engine/src/headless.ts` and contains minified `dist/index.js` + `dist/headless.js`, rolled-up `.d.ts` for both, `LICENSE`, `README.md`, `docs/` (
|
|
960
|
+
Not part of writing a cart. Engine source lives in `packages/engine/src/` (not mixed into the site). The npm tarball is built from `packages/engine/src/index.ts` and `packages/engine/src/headless.ts` and contains minified `dist/index.js` + `dist/headless.js`, rolled-up `.d.ts` for both, `LICENSE`, `README.md`, `CHANGELOG.md`, `docs/` (every guide listed under [Feature guides](#feature-guides)), and `package.json`. Add a `CHANGELOG.md` entry and bump `version` in the same commit; tag it `@cyberart-io/engine@<version>` and publish a GitHub release with the same notes. Site code that still imports `src/ui/lib/...` hits thin re-export shims so those paths keep working.
|
|
657
961
|
|
|
658
962
|
```bash
|
|
659
963
|
pnpm run pack:engine
|