@adea-ai/spatial 0.111.0 → 0.113.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.
package/README.md CHANGED
@@ -12,8 +12,6 @@ package.
12
12
  `SceneStartPosition`, assigned-props manifests, static-field asset URLs.
13
13
  - `manifests.ts`: `hqHomeManifest` / `hqWorkManifest` mount constants (mirrors
14
14
  the engine's room layout; the engine is source of truth).
15
- - `engine.ts`: agent-sim engine manifest discovery and validation
16
- (`parseAgentSimEngineManifest`, official web hosts).
17
15
  - `scene-spawn.ts`: cross-app spawn encode/parse and portal URL helpers.
18
16
  - `telemetry.ts`: `ScenePerformanceReport` schema, validation, envelope, and
19
17
  navigation instrumentation.
@@ -23,9 +21,7 @@ package.
23
21
  engine's `scenes/sim/assets/`; staged into the web public dir by
24
22
  `scripts/sync-assets.mjs`.
25
23
 
26
- The engine manifest validator requires a nonempty version and a same-origin
27
- JavaScript entry URL. Its version is diagnostic; it does not enforce API or
28
- version-range compatibility or verify entry bytes. Tracked scene data is a
24
+ Tracked scene data is a
29
25
  public mirror, not proof that every file matches a particular private source
30
26
  revision or distributed pack. Manifest and payload acceptance need separate
31
27
  checks against the intended pack.
@@ -81,6 +77,11 @@ own: callers pass `MessageEvent`-shaped values in and post through `postToFrame`
81
77
  frame says so, and
82
78
  `describeSimAvailability` gives truthful copy that states Chat and Dev are not affected.
83
79
 
80
+ - **Scene channel** (`scene.ts`): feeds the running engine the workspace scene Virtual view draws,
81
+ as a full snapshot and then diffs against the last revision the engine acknowledged, one revision in
82
+ flight, with `frame.scene.ack` / `nack` / `perf` back. `createSimSceneFeed` is the host side and
83
+ `parseHostSceneMessage` the engine's validator. Spec: `docs/specs/sim-bridge-scene.md`.
84
+
84
85
  Not part of this package: the hosted entry, the backend endpoints, the private backing
85
86
  origin that authorizes assets before serving cached responses, and any credentials or
86
87
  deployment. Those are separate work.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './asset-manifests';
2
- export { agentSimEngineManifestUrl, isLocalDevHost, isOfficialAgentSimWebOrigin, parseAgentSimEngineManifest, OFFICIAL_AGENT_SIM_WEB_HOSTS, type AgentSimEngineManifest, type AgentSimEngineSurface, } from './engine';
3
2
  export { hqHomeManifest, hqWorkManifest } from './manifests';
4
3
  export { appRouteHref, encodeSceneStartPosition, portalNavigationHref, readSceneStartPosition, type PortalNavigation, type SceneApp, } from './scene-spawn';
5
4
  export { createSceneTelemetryEnvelope, MAX_SCENE_TELEMETRY_BYTES, onRouterTransitionStart, parseScenePerformanceReport, recordNavigation, SCENE_TELEMETRY_LOG_PREFIX, type SceneNavigationType, type ScenePerformanceReport, type SceneRuntimeStats, type SceneTelemetryEnvelope, } from './telemetry';
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { isSerializedSimCharacter } from './sim-bridge/character';
2
2
  export * from './asset-manifests';
3
- export { agentSimEngineManifestUrl, isLocalDevHost, isOfficialAgentSimWebOrigin, parseAgentSimEngineManifest, OFFICIAL_AGENT_SIM_WEB_HOSTS, } from './engine';
4
3
  export { hqHomeManifest, hqWorkManifest } from './manifests';
5
4
  export { appRouteHref, encodeSceneStartPosition, portalNavigationHref, readSceneStartPosition, } from './scene-spawn';
6
5
  export { createSceneTelemetryEnvelope, MAX_SCENE_TELEMETRY_BYTES, onRouterTransitionStart, parseScenePerformanceReport, recordNavigation, SCENE_TELEMETRY_LOG_PREFIX, } from './telemetry';
@@ -6,3 +6,4 @@ export * from './messages';
6
6
  export * from './origin';
7
7
  export * from './save-client';
8
8
  export * from './service';
9
+ export * from './scene';
@@ -6,3 +6,4 @@ export * from './messages';
6
6
  export * from './origin';
7
7
  export * from './save-client';
8
8
  export * from './service';
9
+ export * from './scene';
@@ -1,6 +1,7 @@
1
1
  import type { OriginAllowlist } from './origin';
2
2
  import { type SimSurface } from './launch';
3
3
  import { type SimUnavailableReason } from './availability';
4
+ import { type SimFrameSceneMessage } from './scene';
4
5
  /**
5
6
  * Versioned message schema between the Adea host window and the hosted Agent Sim frame.
6
7
  *
@@ -55,7 +56,8 @@ export type SimFrameRequestMessage = Readonly<{
55
56
  character: string;
56
57
  }>;
57
58
  }>;
58
- export type SimFrameMessage = SimFrameStatusMessage | SimFrameRequestMessage;
59
+ /** The scene channel's acknowledgements, refusals and measurements (see `scene.ts`). */
60
+ export type SimFrameMessage = SimFrameStatusMessage | SimFrameRequestMessage | SimFrameSceneMessage;
59
61
  export type SimHostMessage = Readonly<{
60
62
  type: 'host.launch';
61
63
  payload: Readonly<{
@@ -2,6 +2,7 @@ import { boundedString, isPlainRecord, strictRecord } from './record';
2
2
  import { SIM_SURFACES } from './launch';
3
3
  import { parseSimCharacterValue } from './character';
4
4
  import { SIM_UNAVAILABLE_REASONS } from './availability';
5
+ import { parseFrameScenePayload } from './scene';
5
6
  /**
6
7
  * Versioned message schema between the Adea host window and the hosted Agent Sim frame.
7
8
  *
@@ -142,6 +143,12 @@ export function parseFrameMessage(event, context) {
142
143
  ? { ok: true, message: { type, payload: { requestId, character } } }
143
144
  : fail('invalid_payload');
144
145
  }
146
+ case 'frame.scene.ack':
147
+ case 'frame.scene.nack':
148
+ case 'frame.scene.perf': {
149
+ const message = parseFrameScenePayload(type, payload);
150
+ return message ? { ok: true, message } : fail('invalid_payload');
151
+ }
145
152
  default:
146
153
  return fail('unknown_type');
147
154
  }
@@ -0,0 +1,191 @@
1
+ import type { OriginAllowlist } from './origin';
2
+ import { type SimMessageEventLike } from './messages';
3
+ /**
4
+ * The workspace scene channel of the Agent Sim bridge (M26, #24, #25, #489, #27). The host feeds
5
+ * the engine the workspace scene that Virtual view already draws: areas, agents, lead turn and
6
+ * child-job markers, and lead routes, as a full snapshot and then as diffs against the last
7
+ * revision the engine acknowledged. The engine renders it and never writes it back: there is no
8
+ * scene message from the frame other than an acknowledgement, a refusal and a measurement.
9
+ *
10
+ * Spec: docs/specs/sim-bridge-scene.md. Rules the code holds:
11
+ * - Scene messages share the bridge envelope (channel, version, per-mount bridgeId) and its
12
+ * origin/source checks, with their own size budget (`MAX_SCENE_MESSAGE_BYTES`).
13
+ * - One revision is in flight at a time. While it is unacknowledged, newer scene states coalesce:
14
+ * the next diff is computed from the acknowledged state to the newest one.
15
+ * - A diff names the revision it applies to. The engine refuses a diff whose base is not its
16
+ * current revision (`revision_gap`), and the host answers with a full snapshot.
17
+ * - An ack that names any revision other than the one in flight is ignored.
18
+ * - No ack within `ackTimeoutMs`, an `invalid` or `too_large` refusal, or a gap, resends a full
19
+ * snapshot, backing off; `busy` waits `retryAfterMs` (bounded) and resends; `version_unsupported`
20
+ * stops the feed.
21
+ */
22
+ export declare const SIM_SCENE_VERSION = 1;
23
+ /** A scene message, JSON-encoded, may not exceed this. Snapshots are split into parts. */
24
+ export declare const MAX_SCENE_MESSAGE_BYTES: number;
25
+ export declare const MAX_SCENE_ENTITIES_PER_MESSAGE = 200;
26
+ /** The largest scene the host transmits or records as acknowledged. */
27
+ export declare const MAX_SCENE_ENTITIES = 5000;
28
+ export declare const SIM_SCENE_KINDS: readonly ['workspace', 'area', 'agent', 'turn', 'child_job', 'route'];
29
+ export type SimSceneKind = (typeof SIM_SCENE_KINDS)[number];
30
+ export declare const SIM_SCENE_STATUSES: readonly ['error', 'awaiting_input', 'working', 'queued', 'offline', 'unavailable', 'idle', 'completed', 'cancelled', 'archived'];
31
+ export type SimSceneStatus = (typeof SIM_SCENE_STATUSES)[number];
32
+ export declare const SIM_SCENE_ANIMATIONS: readonly ['rest', 'work', 'wait', 'alert', 'celebrate', 'sleep'];
33
+ export type SimSceneAnimation = (typeof SIM_SCENE_ANIMATIONS)[number];
34
+ export declare const SIM_SCENE_TONES: readonly ['neutral', 'active', 'attention', 'danger', 'success', 'muted'];
35
+ export type SimSceneTone = (typeof SIM_SCENE_TONES)[number];
36
+ /**
37
+ * One scene entity on the wire. Ids are the scene graph's stable ids (`agent:<id>`,
38
+ * `conversation:<id>`, `turn:<intentId>`, ...; a route is `route:<turn id>`), so the same backend
39
+ * object is the same engine object across reloads. Coordinates are map units: the engine places
40
+ * its world from them and may scale, but keeps relative placement.
41
+ */
42
+ export type SimSceneEntity = Readonly<{
43
+ id: string;
44
+ kind: SimSceneKind;
45
+ /** The containing entity; absent only for the workspace. */
46
+ parentId?: string;
47
+ label: string;
48
+ status: SimSceneStatus;
49
+ animation: SimSceneAnimation;
50
+ tone: SimSceneTone;
51
+ /** Areas and the workspace commons. */
52
+ rect?: Readonly<{
53
+ x: number;
54
+ y: number;
55
+ width: number;
56
+ height: number;
57
+ }>;
58
+ /** Agents, turns and child jobs. */
59
+ at?: Readonly<{
60
+ x: number;
61
+ y: number;
62
+ }>;
63
+ /** Agents. */
64
+ role?: 'lead' | 'agent';
65
+ characterRef?: string;
66
+ /** No character reference: the engine shows its explicit default character. */
67
+ fallback?: boolean;
68
+ /** Routes: from an agent to a turn marker. */
69
+ from?: string;
70
+ to?: string;
71
+ lead?: boolean;
72
+ }>;
73
+ export type SimHostSceneMessage = Readonly<{
74
+ type: 'host.scene.snapshot';
75
+ payload: Readonly<{
76
+ sceneVersion: typeof SIM_SCENE_VERSION;
77
+ workspaceId: string;
78
+ revision: number;
79
+ /** 1-based part of `parts`. The engine applies the snapshot once every part arrived. */
80
+ part: number;
81
+ parts: number;
82
+ entities: readonly SimSceneEntity[];
83
+ }>;
84
+ }> | Readonly<{
85
+ type: 'host.scene.diff';
86
+ payload: Readonly<{
87
+ sceneVersion: typeof SIM_SCENE_VERSION;
88
+ workspaceId: string;
89
+ baseRevision: number;
90
+ revision: number;
91
+ upserts: readonly SimSceneEntity[];
92
+ removes: readonly string[];
93
+ }>;
94
+ }>;
95
+ export declare const SIM_SCENE_REFUSALS: readonly ['revision_gap', 'invalid', 'too_large', 'version_unsupported', 'busy'];
96
+ export type SimSceneRefusal = (typeof SIM_SCENE_REFUSALS)[number];
97
+ /** What the engine measured for the scene, for #27's budgets. Durations in ms, memory in bytes. */
98
+ export type SimScenePerf = Readonly<{
99
+ revision: number;
100
+ sampleWindowMs: number;
101
+ frames: number;
102
+ frameP50Ms: number;
103
+ frameP95Ms: number;
104
+ frameMaxMs: number;
105
+ /** Frames over 50 ms in the window. */
106
+ longFrames: number;
107
+ /** From the first snapshot part to the first frame drawn with it. */
108
+ loadMs: number;
109
+ /** How long the last applied revision took to apply. */
110
+ applyMs: number;
111
+ entities: number;
112
+ /** `performance.memory.usedJSHeapSize` where the browser exposes it. */
113
+ heapBytes?: number;
114
+ /** The renderer's own estimate of GPU memory (geometries + textures), when it keeps one. */
115
+ gpuBytes?: number;
116
+ }>;
117
+ export type SimFrameSceneMessage = Readonly<{
118
+ type: 'frame.scene.ack';
119
+ payload: Readonly<{
120
+ revision: number;
121
+ }>;
122
+ }> | Readonly<{
123
+ type: 'frame.scene.nack';
124
+ payload: Readonly<{
125
+ revision: number;
126
+ code: SimSceneRefusal;
127
+ retryAfterMs?: number;
128
+ }>;
129
+ }> | Readonly<{
130
+ type: 'frame.scene.perf';
131
+ payload: SimScenePerf;
132
+ }>;
133
+ /** Reads the payload of a `frame.scene.*` message; null when it is not exactly that shape. */
134
+ export declare function parseFrameScenePayload(type: string, payload: unknown): SimFrameSceneMessage | null;
135
+ type PostTarget = Readonly<{
136
+ postMessage(message: unknown, targetOrigin: string): void;
137
+ }>;
138
+ /** Host to frame, for scene messages. Same origin rule as `postToFrame`, and a size bound. */
139
+ export declare function postSceneToFrame(frameWindow: PostTarget, frameOrigin: string, origins: OriginAllowlist, bridgeId: string, message: SimHostSceneMessage): void;
140
+ export type SimSceneParseFailure = 'origin_refused' | 'source_mismatch' | 'not_a_scene_message' | 'bridge_mismatch' | 'version_unsupported' | 'too_large' | 'invalid';
141
+ /**
142
+ * Engine side: accepts a host scene message only from the embedding window at its exact origin,
143
+ * on this mount's bridge id, in a supported scene version, within budget and with every entity in
144
+ * shape. The engine answers `invalid`, `too_large` or `version_unsupported` with a `nack`.
145
+ */
146
+ export declare function parseHostSceneMessage(event: SimMessageEventLike, context: Readonly<{
147
+ parentOrigin: string;
148
+ parentWindow: unknown;
149
+ bridgeId: string;
150
+ }>): Readonly<{
151
+ ok: true;
152
+ message: SimHostSceneMessage;
153
+ }> | Readonly<{
154
+ ok: false;
155
+ reason: SimSceneParseFailure;
156
+ }>;
157
+ export type SimSceneFeedState = 'idle' | 'sending' | 'synced' | 'backing_off' | 'stopped' | 'disposed';
158
+ export type SimSceneFeedStats = Readonly<{
159
+ state: SimSceneFeedState;
160
+ /** The revision the engine last acknowledged; 0 before the first. */
161
+ ackedRevision: number;
162
+ snapshots: number;
163
+ diffs: number;
164
+ timeouts: number;
165
+ refusals: number;
166
+ /** Acks naming a revision that was not in flight. */
167
+ ignoredAcks: number;
168
+ lastPerf: SimScenePerf | null;
169
+ }>;
170
+ export type SimSceneFeedOptions = Readonly<{
171
+ workspaceId: string;
172
+ send(message: SimHostSceneMessage): void;
173
+ onPerf?(perf: SimScenePerf): void;
174
+ ackTimeoutMs?: number;
175
+ /** Schedules `run` after `ms` and returns a canceller. */
176
+ schedule?: (run: () => void, ms: number) => () => void;
177
+ }>;
178
+ export type SimSceneFeed = Readonly<{
179
+ /** The newest scene. It is sent now, or coalesced into the next send after the pending ack. */
180
+ update(entities: readonly SimSceneEntity[]): void;
181
+ /** A validated `frame.scene.*` message from the current frame. */
182
+ receive(message: SimFrameSceneMessage): void;
183
+ stats(): SimSceneFeedStats;
184
+ dispose(): void;
185
+ }>;
186
+ /**
187
+ * The host side of the scene channel: keeps what the engine acknowledged, sends one revision at a
188
+ * time, and recovers from refusals, gaps and silence by resending a full snapshot with backoff.
189
+ */
190
+ export declare function createSimSceneFeed(options: SimSceneFeedOptions): SimSceneFeed;
191
+ export {};