@agent-play/sdk 3.0.1 → 3.1.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 +1 -0
- package/dist/.root +1 -0
- package/dist/browser-C8UKcztD.d.ts +341 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +23 -0
- package/dist/browser.js.map +1 -0
- package/dist/chunk-G2WV7OYM.js +260 -0
- package/dist/chunk-G2WV7OYM.js.map +1 -0
- package/dist/index.d.ts +122 -307
- package/dist/index.js +729 -251
- package/dist/index.js.map +1 -1
- package/examples/01-remote-web-ui-langchain.ts +77 -16
- package/examples/02-remote-two-players-langchain.ts +57 -20
- package/examples/README.md +6 -4
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Node.js SDK for **Agent Play**: register agents, stream world state, and call th
|
|
|
8
8
|
|
|
9
9
|
- **[Repository](https://github.com/wilforlan/agent-play)** — source and monorepo layout
|
|
10
10
|
- **[SDK guide](https://github.com/wilforlan/agent-play/blob/main/docs/sdk.md)** — setup, `RemotePlayWorld`, API keys, journeys
|
|
11
|
+
- **[Occupant Model v1](https://github.com/wilforlan/agent-play/blob/main/docs/occupant-model-v1.md)** — `human` / `agent` / `mcp` world model, interaction policy, and fanout + player-chain convergence story
|
|
11
12
|
- **[API reference](https://wilforlan.github.io/agent-play/)** — TypeDoc (SDK + CLI)
|
|
12
13
|
- **Examples** — this package ships `examples/` (see `examples/README.md`)
|
|
13
14
|
|
package/dist/.root
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
87b6637b010478e48a83a8d445041ae4df5d607df7932153cdfee5c601e8e39e
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain types for sessions, journeys, agents, and the shared world map exposed by the SDK and server.
|
|
3
|
+
*/
|
|
4
|
+
/** Role for a single line in the interaction log / chat stream. */
|
|
5
|
+
type WorldInteractionRole = "user" | "assistant" | "tool";
|
|
6
|
+
/**
|
|
7
|
+
* Payload for {@link RemotePlayWorld.recordInteraction}.
|
|
8
|
+
*
|
|
9
|
+
* @property playerId - Player id returned from `addAgent` / `addPlayer`.
|
|
10
|
+
* @property role - Who "spoke" the line.
|
|
11
|
+
* @property text - Plain text; may be truncated for display server-side.
|
|
12
|
+
*/
|
|
13
|
+
type RecordInteractionInput = {
|
|
14
|
+
playerId: string;
|
|
15
|
+
role: WorldInteractionRole;
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
/** How the watch UI should render and coerce a single assist tool argument. */
|
|
19
|
+
type AssistToolFieldType = "string" | "number" | "boolean";
|
|
20
|
+
/**
|
|
21
|
+
* Per-parameter metadata for assist tools. Legacy snapshots may omit `fieldType`; the UI treats that as `"string"`.
|
|
22
|
+
*/
|
|
23
|
+
type AssistToolParameterSpec = {
|
|
24
|
+
fieldType: AssistToolFieldType;
|
|
25
|
+
field?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Metadata for a tool whose name starts with `assist_`, shown as assist actions on the watch UI.
|
|
29
|
+
*
|
|
30
|
+
* @property parameters - Derived from Zod object `schema` when present: each key maps to
|
|
31
|
+
* {@link AssistToolParameterSpec} with `fieldType` from schema introspection. May include `_note` when no schema shape exists.
|
|
32
|
+
*/
|
|
33
|
+
type AssistToolSpec = {
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
parameters: Record<string, unknown>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Serializable shape returned by {@link langchainRegistration} for agent registration.
|
|
40
|
+
*
|
|
41
|
+
* @property type - Always `"langchain"` for this adapter.
|
|
42
|
+
* @property toolNames - All tool names from the agent (must include `chat_tool`).
|
|
43
|
+
* @property assistTools - Subset of tools with `assist_` prefix, for UI buttons.
|
|
44
|
+
*/
|
|
45
|
+
type LangChainAgentRegistration = {
|
|
46
|
+
type: "langchain";
|
|
47
|
+
toolNames: string[];
|
|
48
|
+
assistTools?: AssistToolSpec[];
|
|
49
|
+
};
|
|
50
|
+
/** Minimal player identity in the SDK (without preview URL). */
|
|
51
|
+
type PlayAgentInformation = {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
sid: string;
|
|
55
|
+
createdAt: Date;
|
|
56
|
+
updatedAt: Date;
|
|
57
|
+
};
|
|
58
|
+
/** Input fields for {@link AddAgentInput} / {@link AddPlayerInput} before `agent` is attached. */
|
|
59
|
+
type PlatformAgentInformation = {
|
|
60
|
+
name: string;
|
|
61
|
+
type: string;
|
|
62
|
+
version?: string;
|
|
63
|
+
createdAt?: Date;
|
|
64
|
+
updatedAt?: Date;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Register an automation agent in the world, tied to **agent node identity**.
|
|
68
|
+
*
|
|
69
|
+
* Use **`langchainRegistration(agent)`** for `agent` (requires a **`chat_tool`** tool; `assist_*`
|
|
70
|
+
* tools are indexed for the watch UI).
|
|
71
|
+
*
|
|
72
|
+
* **`nodeId`** is the **agent node id** (from **`agent-play create`** when the server uses a repository).
|
|
73
|
+
* It is sent on the wire as `agentId` for server compatibility; treating it as a node id makes the
|
|
74
|
+
* contract explicit for billing, validation, and event attribution.
|
|
75
|
+
*/
|
|
76
|
+
type AddAgentInput = PlatformAgentInformation & {
|
|
77
|
+
/** Registration from {@link langchainRegistration}. */
|
|
78
|
+
agent: LangChainAgentRegistration;
|
|
79
|
+
/** Main node id that owns the agent (required on repository-backed servers). */
|
|
80
|
+
mainNodeId?: string;
|
|
81
|
+
/** Agent node id — same value the server stores as registered `agentId`. */
|
|
82
|
+
nodeId: string;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Register a player (agent) in the world.
|
|
86
|
+
*
|
|
87
|
+
* @deprecated Prefer {@link AddAgentInput} and `RemotePlayWorld.prototype.addAgent` for SDK and automation; use `nodeId` there instead of `agentId`.
|
|
88
|
+
*
|
|
89
|
+
* Use **`langchainRegistration(agent)`** for `agent` (requires a **`chat_tool`** tool; `assist_*`
|
|
90
|
+
* tools are indexed for the watch UI).
|
|
91
|
+
*
|
|
92
|
+
* **`agentId`** is required: use an id from **`agent-play create`** when the server uses a repository
|
|
93
|
+
* (with account **`password`** from **`RemotePlayWorld`**), or any stable string for local dev without Redis.
|
|
94
|
+
*/
|
|
95
|
+
type AddPlayerInput = PlatformAgentInformation & {
|
|
96
|
+
/** Registration from {@link langchainRegistration}. */
|
|
97
|
+
agent: LangChainAgentRegistration;
|
|
98
|
+
/** Main node id that owns the agent (required on repository-backed servers). */
|
|
99
|
+
mainNodeId?: string;
|
|
100
|
+
/** Registered agent id (or session-local id without Redis). */
|
|
101
|
+
agentId: string;
|
|
102
|
+
};
|
|
103
|
+
/** Zone counter event surfaced on snapshots and signals. */
|
|
104
|
+
type ZoneEventInfo = {
|
|
105
|
+
zoneCount: number;
|
|
106
|
+
flagged?: boolean;
|
|
107
|
+
at: string;
|
|
108
|
+
};
|
|
109
|
+
/** Yield counter event surfaced on snapshots and signals. */
|
|
110
|
+
type YieldEventInfo = {
|
|
111
|
+
yieldCount: number;
|
|
112
|
+
at: string;
|
|
113
|
+
};
|
|
114
|
+
/** Repository-backed summary returned with `addAgent` when the agent is (or maps to) a stored registration. */
|
|
115
|
+
type RegisteredAgentSummary = {
|
|
116
|
+
agentId: string;
|
|
117
|
+
name: string;
|
|
118
|
+
toolNames: string[];
|
|
119
|
+
zoneCount: number;
|
|
120
|
+
yieldCount: number;
|
|
121
|
+
flagged: boolean;
|
|
122
|
+
};
|
|
123
|
+
/** Result of `addAgent` / `addPlayer` including watch URL and registered-agent metadata from the server. */
|
|
124
|
+
type RegisteredPlayer = PlayAgentInformation & {
|
|
125
|
+
previewUrl: string;
|
|
126
|
+
registeredAgent: RegisteredAgentSummary;
|
|
127
|
+
connectionId?: string;
|
|
128
|
+
leaseTtlSeconds?: number;
|
|
129
|
+
};
|
|
130
|
+
/** First step of a journey: user message origin. */
|
|
131
|
+
type OriginJourneyStep = {
|
|
132
|
+
type: "origin";
|
|
133
|
+
content: string;
|
|
134
|
+
messageId: string;
|
|
135
|
+
};
|
|
136
|
+
/** Middle step: tool invocation on the map. */
|
|
137
|
+
type StructureJourneyStep = {
|
|
138
|
+
type: "structure";
|
|
139
|
+
toolName: string;
|
|
140
|
+
toolCallId: string;
|
|
141
|
+
args: Record<string, unknown>;
|
|
142
|
+
result?: string;
|
|
143
|
+
};
|
|
144
|
+
/** Final step: assistant reply. */
|
|
145
|
+
type DestinationJourneyStep = {
|
|
146
|
+
type: "destination";
|
|
147
|
+
content: string;
|
|
148
|
+
messageId: string;
|
|
149
|
+
};
|
|
150
|
+
/** Union of journey step shapes. */
|
|
151
|
+
type JourneyStep = OriginJourneyStep | StructureJourneyStep | DestinationJourneyStep;
|
|
152
|
+
/**
|
|
153
|
+
* Ordered journey with timestamps; sent to the server via `recordJourney`.
|
|
154
|
+
*
|
|
155
|
+
* @property steps - Ordered path from origin through tool steps to destination.
|
|
156
|
+
* @property startedAt, completedAt - Wall times for the run (client or server).
|
|
157
|
+
*/
|
|
158
|
+
type Journey = {
|
|
159
|
+
steps: JourneyStep[];
|
|
160
|
+
startedAt: Date;
|
|
161
|
+
completedAt: Date;
|
|
162
|
+
};
|
|
163
|
+
/** Journey step after server assigns coordinates (and optional structure id). */
|
|
164
|
+
type PositionedStep = JourneyStep & {
|
|
165
|
+
x?: number;
|
|
166
|
+
y?: number;
|
|
167
|
+
structureId?: string;
|
|
168
|
+
};
|
|
169
|
+
type AgentPlayWorldMapBounds = {
|
|
170
|
+
minX: number;
|
|
171
|
+
minY: number;
|
|
172
|
+
maxX: number;
|
|
173
|
+
maxY: number;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* One agent on the world map. Coordinates are grid positions; the server enforces unique `(x,y)` per occupant.
|
|
177
|
+
*/
|
|
178
|
+
type AgentPlayWorldMapAgentOccupant = {
|
|
179
|
+
kind: "agent";
|
|
180
|
+
nodeId?: string;
|
|
181
|
+
agentId: string;
|
|
182
|
+
name: string;
|
|
183
|
+
x: number;
|
|
184
|
+
y: number;
|
|
185
|
+
/**
|
|
186
|
+
* Integration label from addPlayer `type` (e.g. `langchain`). Populated from the snapshot field `platform`. The legacy wire field `agentType` is deprecated and accepted only for backward compatibility when parsing JSON.
|
|
187
|
+
*/
|
|
188
|
+
platform?: string;
|
|
189
|
+
toolNames?: string[];
|
|
190
|
+
assistToolNames?: string[];
|
|
191
|
+
assistTools?: AssistToolSpec[];
|
|
192
|
+
hasChatTool?: boolean;
|
|
193
|
+
stationary?: boolean;
|
|
194
|
+
lastUpdate?: unknown;
|
|
195
|
+
recentInteractions?: Array<{
|
|
196
|
+
role: WorldInteractionRole;
|
|
197
|
+
text: string;
|
|
198
|
+
at: string;
|
|
199
|
+
seq: number;
|
|
200
|
+
}>;
|
|
201
|
+
zoneCount?: number;
|
|
202
|
+
yieldCount?: number;
|
|
203
|
+
flagged?: boolean;
|
|
204
|
+
onZone?: ZoneEventInfo;
|
|
205
|
+
onYield?: YieldEventInfo;
|
|
206
|
+
};
|
|
207
|
+
/** MCP server shown as a separate map occupant (distinct from LangChain agents). */
|
|
208
|
+
type AgentPlayWorldMapHumanOccupant = {
|
|
209
|
+
kind: "human";
|
|
210
|
+
id: string;
|
|
211
|
+
name: string;
|
|
212
|
+
x: number;
|
|
213
|
+
y: number;
|
|
214
|
+
interactive?: boolean;
|
|
215
|
+
};
|
|
216
|
+
/** MCP server shown as a separate map occupant (distinct from LangChain agents). */
|
|
217
|
+
type AgentPlayWorldMapMcpOccupant = {
|
|
218
|
+
kind: "mcp";
|
|
219
|
+
id: string;
|
|
220
|
+
name: string;
|
|
221
|
+
x: number;
|
|
222
|
+
y: number;
|
|
223
|
+
url?: string;
|
|
224
|
+
};
|
|
225
|
+
/** Spatial index: axis-aligned bounds plus every agent and MCP registration placed on the grid. */
|
|
226
|
+
type AgentPlayWorldMap = {
|
|
227
|
+
bounds: AgentPlayWorldMapBounds;
|
|
228
|
+
occupants: (AgentPlayWorldMapHumanOccupant | AgentPlayWorldMapAgentOccupant | AgentPlayWorldMapMcpOccupant)[];
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Session snapshot from {@link RemotePlayWorld.getWorldSnapshot}.
|
|
232
|
+
* Agents and MCP servers appear only under **`worldMap.occupants`** (no separate `players` list).
|
|
233
|
+
*/
|
|
234
|
+
type AgentPlaySnapshot = {
|
|
235
|
+
sid: string;
|
|
236
|
+
worldMap: AgentPlayWorldMap;
|
|
237
|
+
mcpServers?: Array<{
|
|
238
|
+
id: string;
|
|
239
|
+
name: string;
|
|
240
|
+
url?: string;
|
|
241
|
+
}>;
|
|
242
|
+
};
|
|
243
|
+
type PlayerChainNotifyNodeRef = {
|
|
244
|
+
stableKey: string;
|
|
245
|
+
leafIndex: number;
|
|
246
|
+
removed?: boolean;
|
|
247
|
+
updatedAt?: string;
|
|
248
|
+
};
|
|
249
|
+
type PlayerChainFanoutNotify = {
|
|
250
|
+
updatedAt: string;
|
|
251
|
+
nodes: PlayerChainNotifyNodeRef[];
|
|
252
|
+
};
|
|
253
|
+
type PlayerChainGenesisStableKey = "__genesis__";
|
|
254
|
+
type PlayerChainHeaderStableKey = "__header__";
|
|
255
|
+
type PlayerChainGenesisNode = {
|
|
256
|
+
kind: "genesis";
|
|
257
|
+
stableKey: PlayerChainGenesisStableKey;
|
|
258
|
+
text: string;
|
|
259
|
+
};
|
|
260
|
+
type PlayerChainHeaderNode = {
|
|
261
|
+
kind: "header";
|
|
262
|
+
stableKey: PlayerChainHeaderStableKey;
|
|
263
|
+
sid: string;
|
|
264
|
+
bounds: AgentPlayWorldMapBounds;
|
|
265
|
+
};
|
|
266
|
+
type PlayerChainOccupantRemovedNode = {
|
|
267
|
+
kind: "occupant";
|
|
268
|
+
stableKey: string;
|
|
269
|
+
removed: true;
|
|
270
|
+
};
|
|
271
|
+
type PlayerChainOccupantPresentNode = {
|
|
272
|
+
kind: "occupant";
|
|
273
|
+
stableKey: string;
|
|
274
|
+
removed: false;
|
|
275
|
+
occupant: AgentPlayWorldMapHumanOccupant | AgentPlayWorldMapAgentOccupant | AgentPlayWorldMapMcpOccupant;
|
|
276
|
+
};
|
|
277
|
+
type PlayerChainNodeResponse = PlayerChainGenesisNode | PlayerChainHeaderNode | PlayerChainOccupantRemovedNode | PlayerChainOccupantPresentNode;
|
|
278
|
+
/** Full journey + path update (SSE `world:journey`); coordinates are embedded in `path` steps. */
|
|
279
|
+
type WorldJourneyUpdate = {
|
|
280
|
+
playerId: string;
|
|
281
|
+
journey: Journey;
|
|
282
|
+
path: PositionedStep[];
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Axis-aligned rectangle in world coordinates (grid units). Used by the server to clamp paths
|
|
287
|
+
* and by the watch UI to clamp joystick-driven movement.
|
|
288
|
+
*
|
|
289
|
+
* @remarks **Consumers:** {@link clampWorldPosition}, {@link boundsContain}; server `PlayWorld` and
|
|
290
|
+
* play-ui canvas both import these helpers from `@agent-play/sdk`.
|
|
291
|
+
*/
|
|
292
|
+
type WorldBounds = {
|
|
293
|
+
/** Inclusive minimum X. */
|
|
294
|
+
minX: number;
|
|
295
|
+
/** Inclusive minimum Y. */
|
|
296
|
+
minY: number;
|
|
297
|
+
/** Inclusive maximum X. */
|
|
298
|
+
maxX: number;
|
|
299
|
+
/** Inclusive maximum Y. */
|
|
300
|
+
maxY: number;
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* Clamps a point to lie inside `bounds` along both axes.
|
|
304
|
+
*
|
|
305
|
+
* @param p - Position with `x` and `y` in world units.
|
|
306
|
+
* @param bounds - Valid rectangle (`min` ≤ `max` per axis).
|
|
307
|
+
* @returns Same point if inside, otherwise clamped to the nearest edge.
|
|
308
|
+
*
|
|
309
|
+
* @remarks **Callers:** server `PlayWorld` path enrichment; play-ui joystick and preview. **Callees:** `Math.min/Math.max`.
|
|
310
|
+
*/
|
|
311
|
+
declare function clampWorldPosition(p: {
|
|
312
|
+
x: number;
|
|
313
|
+
y: number;
|
|
314
|
+
}, bounds: WorldBounds): {
|
|
315
|
+
x: number;
|
|
316
|
+
y: number;
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* @returns Whether `p` lies inside or on the border of `bounds`.
|
|
320
|
+
*
|
|
321
|
+
* @remarks **Callers:** optional UI checks. **Callees:** none.
|
|
322
|
+
*/
|
|
323
|
+
declare function boundsContain(bounds: WorldBounds, p: {
|
|
324
|
+
x: number;
|
|
325
|
+
y: number;
|
|
326
|
+
}): boolean;
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Parses **`playerChainNotify`** envelopes and merges {@link PlayerChainNodeResponse} slices into {@link AgentPlaySnapshot} (pure functions + fetch ordering for serialized RPC).
|
|
330
|
+
*/
|
|
331
|
+
|
|
332
|
+
declare function sortNodeRefsForSerializedFetch(nodes: ReadonlyArray<PlayerChainNotifyNodeRef>): PlayerChainNotifyNodeRef[];
|
|
333
|
+
declare function parsePlayerChainFanoutNotify(raw: unknown): PlayerChainFanoutNotify | undefined;
|
|
334
|
+
declare function parsePlayerChainFanoutNotifyFromSsePayload(sseData: unknown): PlayerChainFanoutNotify | undefined;
|
|
335
|
+
declare function parsePlayerChainNodeRpcBody(json: unknown): PlayerChainNodeResponse;
|
|
336
|
+
declare function mergeSnapshotWithPlayerChainNode(snapshot: AgentPlaySnapshot, node: PlayerChainNodeResponse): AgentPlaySnapshot;
|
|
337
|
+
|
|
338
|
+
declare const PLAYER_CHAIN_GENESIS_STABLE_KEY: "__genesis__";
|
|
339
|
+
declare const PLAYER_CHAIN_HEADER_STABLE_KEY: "__header__";
|
|
340
|
+
|
|
341
|
+
export { type AgentPlaySnapshot as A, clampWorldPosition as B, mergeSnapshotWithPlayerChainNode as C, type DestinationJourneyStep as D, parsePlayerChainFanoutNotify as E, parsePlayerChainFanoutNotifyFromSsePayload as F, parsePlayerChainNodeRpcBody as G, sortNodeRefsForSerializedFetch as H, type Journey as J, type LangChainAgentRegistration as L, type OriginJourneyStep as O, type PlayerChainNodeResponse as P, type RegisteredPlayer as R, type StructureJourneyStep as S, type WorldInteractionRole as W, type YieldEventInfo as Y, type ZoneEventInfo as Z, type AddAgentInput as a, type AddPlayerInput as b, type RecordInteractionInput as c, type AgentPlayWorldMap as d, type AgentPlayWorldMapAgentOccupant as e, type AgentPlayWorldMapBounds as f, type AgentPlayWorldMapMcpOccupant as g, type AssistToolFieldType as h, type AssistToolParameterSpec as i, type AssistToolSpec as j, type JourneyStep as k, PLAYER_CHAIN_GENESIS_STABLE_KEY as l, PLAYER_CHAIN_HEADER_STABLE_KEY as m, type PlatformAgentInformation as n, type PlayAgentInformation as o, type PlayerChainFanoutNotify as p, type PlayerChainGenesisNode as q, type PlayerChainHeaderNode as r, type PlayerChainNotifyNodeRef as s, type PlayerChainOccupantPresentNode as t, type PlayerChainOccupantRemovedNode as u, type PositionedStep as v, type RegisteredAgentSummary as w, type WorldBounds as x, type WorldJourneyUpdate as y, boundsContain as z };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { A as AgentPlaySnapshot, l as PLAYER_CHAIN_GENESIS_STABLE_KEY, m as PLAYER_CHAIN_HEADER_STABLE_KEY, x as WorldBounds, z as boundsContain, B as clampWorldPosition, C as mergeSnapshotWithPlayerChainNode, E as parsePlayerChainFanoutNotify, F as parsePlayerChainFanoutNotifyFromSsePayload, G as parsePlayerChainNodeRpcBody, H as sortNodeRefsForSerializedFetch } from './browser-C8UKcztD.js';
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PLAYER_CHAIN_GENESIS_STABLE_KEY,
|
|
3
|
+
PLAYER_CHAIN_HEADER_STABLE_KEY,
|
|
4
|
+
boundsContain,
|
|
5
|
+
clampWorldPosition,
|
|
6
|
+
mergeSnapshotWithPlayerChainNode,
|
|
7
|
+
parsePlayerChainFanoutNotify,
|
|
8
|
+
parsePlayerChainFanoutNotifyFromSsePayload,
|
|
9
|
+
parsePlayerChainNodeRpcBody,
|
|
10
|
+
sortNodeRefsForSerializedFetch
|
|
11
|
+
} from "./chunk-G2WV7OYM.js";
|
|
12
|
+
export {
|
|
13
|
+
PLAYER_CHAIN_GENESIS_STABLE_KEY,
|
|
14
|
+
PLAYER_CHAIN_HEADER_STABLE_KEY,
|
|
15
|
+
boundsContain,
|
|
16
|
+
clampWorldPosition,
|
|
17
|
+
mergeSnapshotWithPlayerChainNode,
|
|
18
|
+
parsePlayerChainFanoutNotify,
|
|
19
|
+
parsePlayerChainFanoutNotifyFromSsePayload,
|
|
20
|
+
parsePlayerChainNodeRpcBody,
|
|
21
|
+
sortNodeRefsForSerializedFetch
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// src/lib/world-bounds.ts
|
|
2
|
+
function clampWorldPosition(p, bounds) {
|
|
3
|
+
return {
|
|
4
|
+
x: Math.min(Math.max(p.x, bounds.minX), bounds.maxX),
|
|
5
|
+
y: Math.min(Math.max(p.y, bounds.minY), bounds.maxY)
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function boundsContain(bounds, p) {
|
|
9
|
+
return p.x >= bounds.minX && p.x <= bounds.maxX && p.y >= bounds.minY && p.y <= bounds.maxY;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/lib/world-chain-keys.ts
|
|
13
|
+
var PLAYER_CHAIN_GENESIS_STABLE_KEY = "__genesis__";
|
|
14
|
+
var PLAYER_CHAIN_HEADER_STABLE_KEY = "__header__";
|
|
15
|
+
|
|
16
|
+
// src/lib/parse-occupant-row.ts
|
|
17
|
+
function parseHumanOccupantRow(raw) {
|
|
18
|
+
if (typeof raw.id !== "string" || typeof raw.name !== "string") {
|
|
19
|
+
throw new Error("occupant: human needs id and name");
|
|
20
|
+
}
|
|
21
|
+
if (typeof raw.x !== "number" || typeof raw.y !== "number") {
|
|
22
|
+
throw new Error("occupant: human needs numeric x and y");
|
|
23
|
+
}
|
|
24
|
+
const base = {
|
|
25
|
+
kind: "human",
|
|
26
|
+
id: raw.id,
|
|
27
|
+
name: raw.name,
|
|
28
|
+
x: raw.x,
|
|
29
|
+
y: raw.y
|
|
30
|
+
};
|
|
31
|
+
if (typeof raw.interactive === "boolean") {
|
|
32
|
+
return { ...base, interactive: raw.interactive };
|
|
33
|
+
}
|
|
34
|
+
return base;
|
|
35
|
+
}
|
|
36
|
+
function parseAgentOccupantRow(raw) {
|
|
37
|
+
if (typeof raw.agentId !== "string" || typeof raw.name !== "string") {
|
|
38
|
+
throw new Error("occupant: agent needs agentId and name");
|
|
39
|
+
}
|
|
40
|
+
if (typeof raw.x !== "number" || typeof raw.y !== "number") {
|
|
41
|
+
throw new Error("occupant: agent needs numeric x and y");
|
|
42
|
+
}
|
|
43
|
+
const base = {
|
|
44
|
+
kind: "agent",
|
|
45
|
+
agentId: raw.agentId,
|
|
46
|
+
name: raw.name,
|
|
47
|
+
x: raw.x,
|
|
48
|
+
y: raw.y
|
|
49
|
+
};
|
|
50
|
+
if (typeof raw.nodeId === "string" && raw.nodeId.length > 0) {
|
|
51
|
+
base.nodeId = raw.nodeId;
|
|
52
|
+
}
|
|
53
|
+
let platform;
|
|
54
|
+
if (typeof raw.platform === "string") {
|
|
55
|
+
platform = raw.platform;
|
|
56
|
+
} else if (typeof raw.agentType === "string") {
|
|
57
|
+
platform = raw.agentType;
|
|
58
|
+
}
|
|
59
|
+
if (platform !== void 0) {
|
|
60
|
+
return { ...base, platform };
|
|
61
|
+
}
|
|
62
|
+
return base;
|
|
63
|
+
}
|
|
64
|
+
function parseMcpOccupantRow(raw) {
|
|
65
|
+
if (typeof raw.id !== "string" || typeof raw.name !== "string") {
|
|
66
|
+
throw new Error("occupant: mcp needs id and name");
|
|
67
|
+
}
|
|
68
|
+
if (typeof raw.x !== "number" || typeof raw.y !== "number") {
|
|
69
|
+
throw new Error("occupant: mcp needs numeric x and y");
|
|
70
|
+
}
|
|
71
|
+
const base = {
|
|
72
|
+
kind: "mcp",
|
|
73
|
+
id: raw.id,
|
|
74
|
+
name: raw.name,
|
|
75
|
+
x: raw.x,
|
|
76
|
+
y: raw.y
|
|
77
|
+
};
|
|
78
|
+
if (typeof raw.url === "string") {
|
|
79
|
+
return { ...base, url: raw.url };
|
|
80
|
+
}
|
|
81
|
+
return base;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/lib/player-chain-merge.ts
|
|
85
|
+
function isRecord(v) {
|
|
86
|
+
return typeof v === "object" && v !== null;
|
|
87
|
+
}
|
|
88
|
+
function stableOccupantSortKey(occ) {
|
|
89
|
+
if (occ.kind === "human") {
|
|
90
|
+
return `human:${occ.id}`;
|
|
91
|
+
}
|
|
92
|
+
if (occ.kind === "agent") {
|
|
93
|
+
const nodeId = occ.nodeId;
|
|
94
|
+
if (typeof nodeId !== "string" || nodeId.length === 0) {
|
|
95
|
+
throw new Error("stableOccupantSortKey: invalid agent nodeId");
|
|
96
|
+
}
|
|
97
|
+
return `agent:${nodeId}:${occ.agentId}`;
|
|
98
|
+
}
|
|
99
|
+
return `mcp:${occ.id}`;
|
|
100
|
+
}
|
|
101
|
+
function sortNodeRefsForSerializedFetch(nodes) {
|
|
102
|
+
const removed = nodes.filter((n) => n.removed === true);
|
|
103
|
+
const rest = nodes.filter((n) => n.removed !== true);
|
|
104
|
+
removed.sort((a, b) => b.leafIndex - a.leafIndex);
|
|
105
|
+
rest.sort((a, b) => a.leafIndex - b.leafIndex);
|
|
106
|
+
return [...removed, ...rest];
|
|
107
|
+
}
|
|
108
|
+
function parsePlayerChainFanoutNotify(raw) {
|
|
109
|
+
if (!isRecord(raw)) {
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
if (typeof raw.updatedAt !== "string" || raw.updatedAt.length === 0) {
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
115
|
+
if (!Array.isArray(raw.nodes)) {
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
const nodes = [];
|
|
119
|
+
for (const row of raw.nodes) {
|
|
120
|
+
if (!isRecord(row)) {
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
if (typeof row.stableKey !== "string" || row.stableKey.length === 0) {
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
if (typeof row.leafIndex !== "number" || !Number.isFinite(row.leafIndex)) {
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
const ref = {
|
|
130
|
+
stableKey: row.stableKey,
|
|
131
|
+
leafIndex: row.leafIndex
|
|
132
|
+
};
|
|
133
|
+
if (row.removed === true) {
|
|
134
|
+
ref.removed = true;
|
|
135
|
+
}
|
|
136
|
+
if (typeof row.updatedAt === "string" && row.updatedAt.length > 0) {
|
|
137
|
+
ref.updatedAt = row.updatedAt;
|
|
138
|
+
}
|
|
139
|
+
nodes.push(ref);
|
|
140
|
+
}
|
|
141
|
+
return { updatedAt: raw.updatedAt, nodes };
|
|
142
|
+
}
|
|
143
|
+
function parsePlayerChainFanoutNotifyFromSsePayload(sseData) {
|
|
144
|
+
if (!isRecord(sseData)) {
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
return parsePlayerChainFanoutNotify(sseData.playerChainNotify);
|
|
148
|
+
}
|
|
149
|
+
function parsePlayerChainNodeRpcBody(json) {
|
|
150
|
+
if (!isRecord(json) || !isRecord(json.node)) {
|
|
151
|
+
throw new Error("getPlayerChainNode: invalid response shape");
|
|
152
|
+
}
|
|
153
|
+
const n = json.node;
|
|
154
|
+
if (n.kind === "genesis") {
|
|
155
|
+
if (n.stableKey !== PLAYER_CHAIN_GENESIS_STABLE_KEY || typeof n.text !== "string") {
|
|
156
|
+
throw new Error("getPlayerChainNode: invalid genesis node");
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
kind: "genesis",
|
|
160
|
+
stableKey: PLAYER_CHAIN_GENESIS_STABLE_KEY,
|
|
161
|
+
text: n.text
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (n.kind === "header") {
|
|
165
|
+
if (n.stableKey !== PLAYER_CHAIN_HEADER_STABLE_KEY || typeof n.sid !== "string") {
|
|
166
|
+
throw new Error("getPlayerChainNode: invalid header node");
|
|
167
|
+
}
|
|
168
|
+
const b = n.bounds;
|
|
169
|
+
if (!isRecord(b)) {
|
|
170
|
+
throw new Error("getPlayerChainNode: invalid header bounds");
|
|
171
|
+
}
|
|
172
|
+
const { minX, minY, maxX, maxY } = b;
|
|
173
|
+
if (typeof minX !== "number" || typeof minY !== "number" || typeof maxX !== "number" || typeof maxY !== "number") {
|
|
174
|
+
throw new Error("getPlayerChainNode: invalid header bounds");
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
kind: "header",
|
|
178
|
+
stableKey: PLAYER_CHAIN_HEADER_STABLE_KEY,
|
|
179
|
+
sid: n.sid,
|
|
180
|
+
bounds: { minX, minY, maxX, maxY }
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (n.kind !== "occupant") {
|
|
184
|
+
throw new Error("getPlayerChainNode: unknown node kind");
|
|
185
|
+
}
|
|
186
|
+
if (typeof n.stableKey !== "string" || n.stableKey.length === 0) {
|
|
187
|
+
throw new Error("getPlayerChainNode: invalid occupant stableKey");
|
|
188
|
+
}
|
|
189
|
+
if (n.removed === true) {
|
|
190
|
+
return { kind: "occupant", stableKey: n.stableKey, removed: true };
|
|
191
|
+
}
|
|
192
|
+
const occ = n.occupant;
|
|
193
|
+
if (!isRecord(occ) || occ.kind !== "human" && occ.kind !== "agent" && occ.kind !== "mcp") {
|
|
194
|
+
throw new Error("getPlayerChainNode: invalid occupant payload");
|
|
195
|
+
}
|
|
196
|
+
const occupant = occ.kind === "human" ? parseHumanOccupantRow(occ) : occ.kind === "agent" ? parseAgentOccupantRow(occ) : parseMcpOccupantRow(occ);
|
|
197
|
+
return {
|
|
198
|
+
kind: "occupant",
|
|
199
|
+
stableKey: n.stableKey,
|
|
200
|
+
removed: false,
|
|
201
|
+
occupant
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function mergeSnapshotWithPlayerChainNode(snapshot, node) {
|
|
205
|
+
if (node.kind === "genesis") {
|
|
206
|
+
return snapshot;
|
|
207
|
+
}
|
|
208
|
+
if (node.kind === "header") {
|
|
209
|
+
return {
|
|
210
|
+
...snapshot,
|
|
211
|
+
sid: node.sid,
|
|
212
|
+
worldMap: {
|
|
213
|
+
...snapshot.worldMap,
|
|
214
|
+
bounds: node.bounds
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (node.removed === true) {
|
|
219
|
+
return {
|
|
220
|
+
...snapshot,
|
|
221
|
+
worldMap: {
|
|
222
|
+
...snapshot.worldMap,
|
|
223
|
+
occupants: snapshot.worldMap.occupants.filter(
|
|
224
|
+
(o) => stableOccupantSortKey(o) !== node.stableKey
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
if (node.removed !== false) {
|
|
230
|
+
throw new Error("mergeSnapshotWithPlayerChainNode: invalid occupant node");
|
|
231
|
+
}
|
|
232
|
+
const occ = node.occupant;
|
|
233
|
+
const key = stableOccupantSortKey(occ);
|
|
234
|
+
const occupants = snapshot.worldMap.occupants.filter(
|
|
235
|
+
(o) => stableOccupantSortKey(o) !== key
|
|
236
|
+
);
|
|
237
|
+
return {
|
|
238
|
+
...snapshot,
|
|
239
|
+
worldMap: {
|
|
240
|
+
...snapshot.worldMap,
|
|
241
|
+
occupants: [...occupants, occ]
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export {
|
|
247
|
+
clampWorldPosition,
|
|
248
|
+
boundsContain,
|
|
249
|
+
parseHumanOccupantRow,
|
|
250
|
+
parseAgentOccupantRow,
|
|
251
|
+
parseMcpOccupantRow,
|
|
252
|
+
PLAYER_CHAIN_GENESIS_STABLE_KEY,
|
|
253
|
+
PLAYER_CHAIN_HEADER_STABLE_KEY,
|
|
254
|
+
sortNodeRefsForSerializedFetch,
|
|
255
|
+
parsePlayerChainFanoutNotify,
|
|
256
|
+
parsePlayerChainFanoutNotifyFromSsePayload,
|
|
257
|
+
parsePlayerChainNodeRpcBody,
|
|
258
|
+
mergeSnapshotWithPlayerChainNode
|
|
259
|
+
};
|
|
260
|
+
//# sourceMappingURL=chunk-G2WV7OYM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/world-bounds.ts","../src/lib/world-chain-keys.ts","../src/lib/parse-occupant-row.ts","../src/lib/player-chain-merge.ts"],"sourcesContent":["/**\n * Axis-aligned rectangle in world coordinates (grid units). Used by the server to clamp paths\n * and by the watch UI to clamp joystick-driven movement.\n *\n * @remarks **Consumers:** {@link clampWorldPosition}, {@link boundsContain}; server `PlayWorld` and\n * play-ui canvas both import these helpers from `@agent-play/sdk`.\n */\nexport type WorldBounds = {\n /** Inclusive minimum X. */\n minX: number;\n /** Inclusive minimum Y. */\n minY: number;\n /** Inclusive maximum X. */\n maxX: number;\n /** Inclusive maximum Y. */\n maxY: number;\n};\n\n/**\n * Clamps a point to lie inside `bounds` along both axes.\n *\n * @param p - Position with `x` and `y` in world units.\n * @param bounds - Valid rectangle (`min` ≤ `max` per axis).\n * @returns Same point if inside, otherwise clamped to the nearest edge.\n *\n * @remarks **Callers:** server `PlayWorld` path enrichment; play-ui joystick and preview. **Callees:** `Math.min/Math.max`.\n */\nexport function clampWorldPosition(\n p: { x: number; y: number },\n bounds: WorldBounds\n): { x: number; y: number } {\n return {\n x: Math.min(Math.max(p.x, bounds.minX), bounds.maxX),\n y: Math.min(Math.max(p.y, bounds.minY), bounds.maxY),\n };\n}\n\n/**\n * @returns Whether `p` lies inside or on the border of `bounds`.\n *\n * @remarks **Callers:** optional UI checks. **Callees:** none.\n */\nexport function boundsContain(\n bounds: WorldBounds,\n p: { x: number; y: number }\n): boolean {\n return (\n p.x >= bounds.minX &&\n p.x <= bounds.maxX &&\n p.y >= bounds.minY &&\n p.y <= bounds.maxY\n );\n}\n","export const PLAYER_CHAIN_GENESIS_STABLE_KEY = \"__genesis__\" as const;\nexport const PLAYER_CHAIN_HEADER_STABLE_KEY = \"__header__\" as const;\n","import type {\n AgentPlayWorldMapHumanOccupant,\n AgentPlayWorldMapAgentOccupant,\n AgentPlayWorldMapMcpOccupant,\n} from \"../public-types.js\";\n\nexport function parseHumanOccupantRow(\n raw: Record<string, unknown>\n): AgentPlayWorldMapHumanOccupant {\n if (typeof raw.id !== \"string\" || typeof raw.name !== \"string\") {\n throw new Error(\"occupant: human needs id and name\");\n }\n if (typeof raw.x !== \"number\" || typeof raw.y !== \"number\") {\n throw new Error(\"occupant: human needs numeric x and y\");\n }\n const base: AgentPlayWorldMapHumanOccupant = {\n kind: \"human\",\n id: raw.id,\n name: raw.name,\n x: raw.x,\n y: raw.y,\n };\n if (typeof raw.interactive === \"boolean\") {\n return { ...base, interactive: raw.interactive };\n }\n return base;\n}\n\nexport function parseAgentOccupantRow(\n raw: Record<string, unknown>\n): AgentPlayWorldMapAgentOccupant {\n if (typeof raw.agentId !== \"string\" || typeof raw.name !== \"string\") {\n throw new Error(\"occupant: agent needs agentId and name\");\n }\n if (typeof raw.x !== \"number\" || typeof raw.y !== \"number\") {\n throw new Error(\"occupant: agent needs numeric x and y\");\n }\n const base: AgentPlayWorldMapAgentOccupant = {\n kind: \"agent\",\n agentId: raw.agentId,\n name: raw.name,\n x: raw.x,\n y: raw.y,\n };\n if (typeof raw.nodeId === \"string\" && raw.nodeId.length > 0) {\n base.nodeId = raw.nodeId;\n }\n let platform: string | undefined;\n if (typeof raw.platform === \"string\") {\n platform = raw.platform;\n } else if (typeof raw.agentType === \"string\") {\n platform = raw.agentType;\n }\n if (platform !== undefined) {\n return { ...base, platform };\n }\n return base;\n}\n\nexport function parseMcpOccupantRow(\n raw: Record<string, unknown>\n): AgentPlayWorldMapMcpOccupant {\n if (typeof raw.id !== \"string\" || typeof raw.name !== \"string\") {\n throw new Error(\"occupant: mcp needs id and name\");\n }\n if (typeof raw.x !== \"number\" || typeof raw.y !== \"number\") {\n throw new Error(\"occupant: mcp needs numeric x and y\");\n }\n const base: AgentPlayWorldMapMcpOccupant = {\n kind: \"mcp\",\n id: raw.id,\n name: raw.name,\n x: raw.x,\n y: raw.y,\n };\n if (typeof raw.url === \"string\") {\n return { ...base, url: raw.url };\n }\n return base;\n}\n","/**\n * Parses **`playerChainNotify`** envelopes and merges {@link PlayerChainNodeResponse} slices into {@link AgentPlaySnapshot} (pure functions + fetch ordering for serialized RPC).\n */\n\nimport type {\n AgentPlaySnapshot,\n AgentPlayWorldMapHumanOccupant,\n AgentPlayWorldMapAgentOccupant,\n AgentPlayWorldMapMcpOccupant,\n PlayerChainFanoutNotify,\n PlayerChainNotifyNodeRef,\n PlayerChainNodeResponse,\n} from \"../public-types.js\";\nimport {\n parseHumanOccupantRow,\n parseAgentOccupantRow,\n parseMcpOccupantRow,\n} from \"./parse-occupant-row.js\";\nimport {\n PLAYER_CHAIN_GENESIS_STABLE_KEY,\n PLAYER_CHAIN_HEADER_STABLE_KEY,\n} from \"./world-chain-keys.js\";\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\nfunction stableOccupantSortKey(\n occ:\n | AgentPlayWorldMapHumanOccupant\n | AgentPlayWorldMapAgentOccupant\n | AgentPlayWorldMapMcpOccupant\n): string {\n if (occ.kind === \"human\") {\n return `human:${occ.id}`;\n }\n if (occ.kind === \"agent\") {\n const nodeId = occ.nodeId;\n if (typeof nodeId !== \"string\" || nodeId.length === 0) {\n throw new Error(\"stableOccupantSortKey: invalid agent nodeId\");\n }\n return `agent:${nodeId}:${occ.agentId}`;\n }\n return `mcp:${occ.id}`;\n}\n\nexport function sortNodeRefsForSerializedFetch(\n nodes: ReadonlyArray<PlayerChainNotifyNodeRef>\n): PlayerChainNotifyNodeRef[] {\n const removed = nodes.filter((n) => n.removed === true);\n const rest = nodes.filter((n) => n.removed !== true);\n removed.sort((a, b) => b.leafIndex - a.leafIndex);\n rest.sort((a, b) => a.leafIndex - b.leafIndex);\n return [...removed, ...rest];\n}\n\nexport function parsePlayerChainFanoutNotify(\n raw: unknown\n): PlayerChainFanoutNotify | undefined {\n if (!isRecord(raw)) {\n return undefined;\n }\n if (typeof raw.updatedAt !== \"string\" || raw.updatedAt.length === 0) {\n return undefined;\n }\n if (!Array.isArray(raw.nodes)) {\n return undefined;\n }\n const nodes: PlayerChainNotifyNodeRef[] = [];\n for (const row of raw.nodes) {\n if (!isRecord(row)) {\n return undefined;\n }\n if (typeof row.stableKey !== \"string\" || row.stableKey.length === 0) {\n return undefined;\n }\n if (typeof row.leafIndex !== \"number\" || !Number.isFinite(row.leafIndex)) {\n return undefined;\n }\n const ref: PlayerChainNotifyNodeRef = {\n stableKey: row.stableKey,\n leafIndex: row.leafIndex,\n };\n if (row.removed === true) {\n ref.removed = true;\n }\n if (typeof row.updatedAt === \"string\" && row.updatedAt.length > 0) {\n ref.updatedAt = row.updatedAt;\n }\n nodes.push(ref);\n }\n return { updatedAt: raw.updatedAt, nodes };\n}\n\nexport function parsePlayerChainFanoutNotifyFromSsePayload(\n sseData: unknown\n): PlayerChainFanoutNotify | undefined {\n if (!isRecord(sseData)) {\n return undefined;\n }\n return parsePlayerChainFanoutNotify(sseData.playerChainNotify);\n}\n\nexport function parsePlayerChainNodeRpcBody(json: unknown): PlayerChainNodeResponse {\n if (!isRecord(json) || !isRecord(json.node)) {\n throw new Error(\"getPlayerChainNode: invalid response shape\");\n }\n const n = json.node;\n if (n.kind === \"genesis\") {\n if (\n n.stableKey !== PLAYER_CHAIN_GENESIS_STABLE_KEY ||\n typeof n.text !== \"string\"\n ) {\n throw new Error(\"getPlayerChainNode: invalid genesis node\");\n }\n return {\n kind: \"genesis\",\n stableKey: PLAYER_CHAIN_GENESIS_STABLE_KEY,\n text: n.text,\n };\n }\n if (n.kind === \"header\") {\n if (\n n.stableKey !== PLAYER_CHAIN_HEADER_STABLE_KEY ||\n typeof n.sid !== \"string\"\n ) {\n throw new Error(\"getPlayerChainNode: invalid header node\");\n }\n const b = n.bounds;\n if (!isRecord(b)) {\n throw new Error(\"getPlayerChainNode: invalid header bounds\");\n }\n const { minX, minY, maxX, maxY } = b;\n if (\n typeof minX !== \"number\" ||\n typeof minY !== \"number\" ||\n typeof maxX !== \"number\" ||\n typeof maxY !== \"number\"\n ) {\n throw new Error(\"getPlayerChainNode: invalid header bounds\");\n }\n return {\n kind: \"header\",\n stableKey: PLAYER_CHAIN_HEADER_STABLE_KEY,\n sid: n.sid,\n bounds: { minX, minY, maxX, maxY },\n };\n }\n if (n.kind !== \"occupant\") {\n throw new Error(\"getPlayerChainNode: unknown node kind\");\n }\n if (typeof n.stableKey !== \"string\" || n.stableKey.length === 0) {\n throw new Error(\"getPlayerChainNode: invalid occupant stableKey\");\n }\n if (n.removed === true) {\n return { kind: \"occupant\", stableKey: n.stableKey, removed: true };\n }\n const occ = n.occupant;\n if (\n !isRecord(occ) ||\n (occ.kind !== \"human\" && occ.kind !== \"agent\" && occ.kind !== \"mcp\")\n ) {\n throw new Error(\"getPlayerChainNode: invalid occupant payload\");\n }\n const occupant =\n occ.kind === \"human\"\n ? parseHumanOccupantRow(occ)\n : occ.kind === \"agent\"\n ? parseAgentOccupantRow(occ)\n : parseMcpOccupantRow(occ);\n return {\n kind: \"occupant\",\n stableKey: n.stableKey,\n removed: false,\n occupant,\n };\n}\n\nexport function mergeSnapshotWithPlayerChainNode(\n snapshot: AgentPlaySnapshot,\n node: PlayerChainNodeResponse\n): AgentPlaySnapshot {\n if (node.kind === \"genesis\") {\n return snapshot;\n }\n if (node.kind === \"header\") {\n return {\n ...snapshot,\n sid: node.sid,\n worldMap: {\n ...snapshot.worldMap,\n bounds: node.bounds,\n },\n };\n }\n if (node.removed === true) {\n return {\n ...snapshot,\n worldMap: {\n ...snapshot.worldMap,\n occupants: snapshot.worldMap.occupants.filter(\n (o) => stableOccupantSortKey(o) !== node.stableKey\n ),\n },\n };\n }\n if (node.removed !== false) {\n throw new Error(\"mergeSnapshotWithPlayerChainNode: invalid occupant node\");\n }\n const occ = node.occupant;\n const key = stableOccupantSortKey(occ);\n const occupants = snapshot.worldMap.occupants.filter(\n (o) => stableOccupantSortKey(o) !== key\n );\n return {\n ...snapshot,\n worldMap: {\n ...snapshot.worldMap,\n occupants: [...occupants, occ],\n },\n };\n}\n"],"mappings":"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","names":[]}
|