@agent-play/sdk 3.2.1 → 3.3.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/world-bounds.ts","../src/lib/occupancy-grid-model.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/** Minimum playable span aligned with the watch canvas scrolling world (~20×20 cells). */\nexport const MINIMUM_PLAY_WORLD_BOUNDS: WorldBounds = {\n minX: 0,\n minY: 0,\n maxX: 19,\n maxY: 19,\n};\n\nexport function expandBoundsToMinimumPlayArea(bounds: WorldBounds): WorldBounds {\n return {\n minX: Math.min(bounds.minX, MINIMUM_PLAY_WORLD_BOUNDS.minX),\n minY: Math.min(bounds.minY, MINIMUM_PLAY_WORLD_BOUNDS.minY),\n maxX: Math.max(bounds.maxX, MINIMUM_PLAY_WORLD_BOUNDS.maxX),\n maxY: Math.max(bounds.maxY, MINIMUM_PLAY_WORLD_BOUNDS.maxY),\n };\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","import type { WorldBounds } from \"./world-bounds.js\";\r\nimport { MINIMUM_PLAY_WORLD_BOUNDS } from \"./world-bounds.js\";\r\n\r\nexport type OccupancyGridPoint = {\r\n x: number;\r\n y: number;\r\n};\r\n\r\nexport const OCCUPANCY_POINT_MULTIPLIER = 5;\r\nexport const CONTINUOUS_RENDER_OFFSET = 0.2;\r\nexport const DEFAULT_AGENT_SPAWN_MIN_DISTANCE = 0.9;\r\n\r\n/** Bottom-left zone Q1 — agents (spatial rectangle). */\r\nexport const SPATIAL_ZONE_INDEX_AGENTS = 0;\r\n/** Top-left zone Q3 — spaces / amenities (spatial rectangle). */\r\nexport const SPATIAL_ZONE_INDEX_SPACES = 2;\r\n\r\nexport function spatialZoneBounds(quartileIndex: number): WorldBounds {\r\n const { minX, maxX, minY, maxY } = MINIMUM_PLAY_WORLD_BOUNDS;\r\n const spanX = maxX - minX + 1;\r\n const spanY = maxY - minY + 1;\r\n const halfX = spanX / 2;\r\n const halfY = spanY / 2;\r\n const midLeftMax = minX + halfX - 1;\r\n const midRightMin = minX + halfX;\r\n const midBottomMax = minY + halfY - 1;\r\n const midTopMin = minY + halfY;\r\n\r\n switch (quartileIndex) {\r\n case 0:\r\n return { minX, maxX: midLeftMax, minY, maxY: midBottomMax };\r\n case 1:\r\n return { minX: midRightMin, maxX, minY, maxY: midBottomMax };\r\n case 2:\r\n return { minX, maxX: midLeftMax, minY: midTopMin, maxY };\r\n case 3:\r\n return { minX: midRightMin, maxX, minY: midTopMin, maxY };\r\n default:\r\n throw new Error(\r\n `spatialZoneBounds: invalid zone index ${String(quartileIndex)}`\r\n );\r\n }\r\n}\r\n\r\nexport function spatialZoneCenter(quartileIndex: number): OccupancyGridPoint {\r\n const b = spatialZoneBounds(quartileIndex);\r\n return {\r\n x: (b.minX + b.maxX + 1) / 2,\r\n y: (b.minY + b.maxY + 1) / 2,\r\n };\r\n}\r\n\r\nfunction enumerateIntegerCellsInBounds(bounds: WorldBounds): OccupancyGridPoint[] {\r\n const cells: OccupancyGridPoint[] = [];\r\n for (let x = bounds.minX; x <= bounds.maxX; x += 1) {\r\n for (let y = bounds.minY; y <= bounds.maxY; y += 1) {\r\n cells.push({ x, y });\r\n }\r\n }\r\n return cells;\r\n}\r\n\r\nexport function pointCellInSpatialZone(\r\n wx: number,\r\n wy: number,\r\n zoneIndex: number\r\n): boolean {\r\n const bounds = spatialZoneBounds(zoneIndex);\r\n const cx = Math.floor(wx);\r\n const cy = Math.floor(wy);\r\n return (\r\n cx >= bounds.minX &&\r\n cx <= bounds.maxX &&\r\n cy >= bounds.minY &&\r\n cy <= bounds.maxY\r\n );\r\n}\r\n\r\nexport function listOccupancyPointsForSpatialZone(\r\n zoneIndex: number\r\n): readonly OccupancyGridPoint[] {\r\n const bounds = spatialZoneBounds(zoneIndex);\r\n return enumerateIntegerCellsInBounds(bounds).flatMap((cell) => {\r\n const points: OccupancyGridPoint[] = [];\r\n for (let dx = 0; dx < OCCUPANCY_POINT_MULTIPLIER; dx += 1) {\r\n for (let dy = 0; dy < OCCUPANCY_POINT_MULTIPLIER; dy += 1) {\r\n points.push({\r\n x:\r\n cell.x +\r\n CONTINUOUS_RENDER_OFFSET +\r\n (dx + 0.5) / OCCUPANCY_POINT_MULTIPLIER,\r\n y:\r\n cell.y +\r\n CONTINUOUS_RENDER_OFFSET +\r\n (dy + 0.5) / OCCUPANCY_POINT_MULTIPLIER,\r\n });\r\n }\r\n }\r\n return points;\r\n });\r\n}\r\n\r\nexport function occupancyPointsGroupedBySpatialZone(): readonly (\r\n readonly OccupancyGridPoint[]\r\n)[] {\r\n return [\r\n listOccupancyPointsForSpatialZone(0),\r\n listOccupancyPointsForSpatialZone(1),\r\n listOccupancyPointsForSpatialZone(2),\r\n listOccupancyPointsForSpatialZone(3),\r\n ];\r\n}\r\n\r\n/** Agent spawns: Q1 only. */\r\nexport function listAllowedOccupancyPoints(): readonly OccupancyGridPoint[] {\r\n return listOccupancyPointsForSpatialZone(SPATIAL_ZONE_INDEX_AGENTS);\r\n}\r\n\r\nfunction quantizePosition(v: number): number {\r\n return Math.round(v * OCCUPANCY_POINT_MULTIPLIER) / OCCUPANCY_POINT_MULTIPLIER;\r\n}\r\n\r\nexport function occupancyKeyForPosition(x: number, y: number): string {\r\n return `${quantizePosition(x).toFixed(3)},${quantizePosition(y).toFixed(3)}`;\r\n}\r\n\r\nexport function buildRankedOccupancyPointsForSpatialZone(\r\n zoneIndex: number\r\n): OccupancyGridPoint[] {\r\n const center = spatialZoneCenter(zoneIndex);\r\n return [...listOccupancyPointsForSpatialZone(zoneIndex)].sort((left, right) => {\r\n const dl = Math.hypot(left.x - center.x, left.y - center.y);\r\n const dr = Math.hypot(right.x - center.x, right.y - center.y);\r\n if (dl !== dr) {\r\n return dl - dr;\r\n }\r\n if (left.x !== right.x) {\r\n return left.x - right.x;\r\n }\r\n return left.y - right.y;\r\n });\r\n}\r\n\r\n/** Back-compat: agent-zone ranking only. */\r\nexport function buildRankedOccupancyPoints(): OccupancyGridPoint[] {\r\n return buildRankedOccupancyPointsForSpatialZone(SPATIAL_ZONE_INDEX_AGENTS);\r\n}\r\n\r\nexport function boundingWorldRectForOccupancyPoints(\r\n points: readonly OccupancyGridPoint[]\r\n): { minX: number; maxX: number; minY: number; maxY: number } | null {\r\n const head = points[0];\r\n if (head === undefined) {\r\n return null;\r\n }\r\n let minX = head.x;\r\n let maxX = head.x;\r\n let minY = head.y;\r\n let maxY = head.y;\r\n for (const p of points) {\r\n if (p.x < minX) minX = p.x;\r\n if (p.x > maxX) maxX = p.x;\r\n if (p.y < minY) minY = p.y;\r\n if (p.y > maxY) maxY = p.y;\r\n }\r\n return { minX, maxX, minY, maxY };\r\n}\r\n\r\nexport function isAgentSpawnOccupancyPointAvailable(input: {\r\n point: OccupancyGridPoint;\r\n occupiedKeys: ReadonlySet<string>;\r\n existingOccupants: ReadonlyArray<{ x: number; y: number }>;\r\n minDistance?: number;\r\n}): boolean {\r\n const minDistance = input.minDistance ?? DEFAULT_AGENT_SPAWN_MIN_DISTANCE;\r\n const key = occupancyKeyForPosition(input.point.x, input.point.y);\r\n if (input.occupiedKeys.has(key)) {\r\n return false;\r\n }\r\n if (\r\n !pointCellInSpatialZone(\r\n input.point.x,\r\n input.point.y,\r\n SPATIAL_ZONE_INDEX_AGENTS\r\n )\r\n ) {\r\n return false;\r\n }\r\n for (const existing of input.existingOccupants) {\r\n const dist = Math.hypot(\r\n input.point.x - existing.x,\r\n input.point.y - existing.y\r\n );\r\n if (dist < minDistance) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n\r\nexport function isSpaceAnchorOccupancyPointAvailable(input: {\r\n point: OccupancyGridPoint;\r\n occupiedKeys: ReadonlySet<string>;\r\n existingOccupants: ReadonlyArray<{ x: number; y: number }>;\r\n structureAnchors: ReadonlyArray<{ x: number; y: number }>;\r\n minDistance: number;\r\n structureMinDistance: number;\r\n}): boolean {\r\n const key = occupancyKeyForPosition(input.point.x, input.point.y);\r\n if (input.occupiedKeys.has(key)) {\r\n return false;\r\n }\r\n if (\r\n !pointCellInSpatialZone(\r\n input.point.x,\r\n input.point.y,\r\n SPATIAL_ZONE_INDEX_SPACES\r\n )\r\n ) {\r\n return false;\r\n }\r\n for (const existing of input.existingOccupants) {\r\n const dist = Math.hypot(\r\n input.point.x - existing.x,\r\n input.point.y - existing.y\r\n );\r\n if (dist < input.minDistance) {\r\n return false;\r\n }\r\n }\r\n for (const anchor of input.structureAnchors) {\r\n const dist = Math.hypot(\r\n input.point.x - anchor.x,\r\n input.point.y - anchor.y\r\n );\r\n if (dist < input.structureMinDistance) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n\r\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 AgentPlaySpaceAmenityKind,\n AgentPlaySpaceCatalogEntry,\n AgentPlayWorldMapHumanOccupant,\n AgentPlayWorldMapAgentOccupant,\n AgentPlayWorldMapMcpOccupant,\n AgentPlayWorldMapStructureOccupant,\n} from \"../public-types.js\";\n\nconst SPACE_AMENITY_KINDS: readonly AgentPlaySpaceAmenityKind[] = [\n \"supermarket\",\n \"shop\",\n \"car_wash\",\n];\n\nfunction isSpaceAmenityKind(v: string): v is AgentPlaySpaceAmenityKind {\n return (SPACE_AMENITY_KINDS as readonly string[]).includes(v);\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\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 const enableP2a =\n raw.enableP2a === \"on\" || raw.enableP2a === \"off\" ? raw.enableP2a : undefined;\n const realtimeInstructions =\n typeof raw.realtimeInstructions === \"string\" &&\n raw.realtimeInstructions.trim().length > 0\n ? raw.realtimeInstructions\n : undefined;\n const realtimeRaw = raw.realtimeWebrtc;\n const realtimeWebrtc =\n typeof realtimeRaw === \"object\" &&\n realtimeRaw !== null &&\n typeof (realtimeRaw as Record<string, unknown>).clientSecret === \"string\" &&\n ((realtimeRaw as Record<string, unknown>).clientSecret as string).length > 0 &&\n typeof (realtimeRaw as Record<string, unknown>).model === \"string\" &&\n ((realtimeRaw as Record<string, unknown>).model as string).length > 0\n ? (() => {\n const record = realtimeRaw as Record<string, unknown>;\n const parsed: {\n clientSecret: string;\n model: string;\n expiresAt?: string;\n voice?: string;\n } = {\n clientSecret: record.clientSecret as string,\n model: record.model as string,\n };\n if (typeof record.expiresAt === \"string\" && record.expiresAt.length > 0) {\n parsed.expiresAt = record.expiresAt;\n }\n if (typeof record.voice === \"string\" && record.voice.length > 0) {\n parsed.voice = record.voice;\n }\n return parsed;\n })()\n : undefined;\n\n const out: AgentPlayWorldMapAgentOccupant = { ...base };\n if (platform !== undefined) {\n out.platform = platform;\n }\n if (enableP2a !== undefined) {\n out.enableP2a = enableP2a;\n }\n if (realtimeInstructions !== undefined) {\n out.realtimeInstructions = realtimeInstructions;\n }\n if (realtimeWebrtc !== undefined) {\n out.realtimeWebrtc = realtimeWebrtc;\n }\n return out;\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\nexport function parseSpaceCatalogEntry(\n raw: Record<string, unknown>\n): AgentPlaySpaceCatalogEntry {\n if (typeof raw.id !== \"string\" || typeof raw.name !== \"string\") {\n throw new Error(\"space catalog: id and name required\");\n }\n if (typeof raw.description !== \"string\") {\n throw new Error(\"space catalog: description required\");\n }\n if (typeof raw.designKey !== \"string\") {\n throw new Error(\"space catalog: designKey required\");\n }\n const ownerRaw = raw.owner;\n if (!isRecord(ownerRaw) || typeof ownerRaw.displayName !== \"string\") {\n throw new Error(\"space catalog: owner.displayName required\");\n }\n const amenitiesRaw = raw.amenities;\n if (!Array.isArray(amenitiesRaw) || amenitiesRaw.length === 0) {\n throw new Error(\"space catalog: amenities must be a non-empty array\");\n }\n const amenities: AgentPlaySpaceAmenityKind[] = [];\n for (const a of amenitiesRaw) {\n if (typeof a !== \"string\" || !isSpaceAmenityKind(a)) {\n throw new Error(\"space catalog: invalid amenity\");\n }\n amenities.push(a);\n }\n const owner: AgentPlaySpaceCatalogEntry[\"owner\"] = {\n displayName: ownerRaw.displayName,\n };\n if (typeof ownerRaw.playerId === \"string\" && ownerRaw.playerId.length > 0) {\n owner.playerId = ownerRaw.playerId;\n }\n if (typeof ownerRaw.nodeId === \"string\" && ownerRaw.nodeId.length > 0) {\n owner.nodeId = ownerRaw.nodeId;\n }\n const entry: AgentPlaySpaceCatalogEntry = {\n id: raw.id,\n name: raw.name,\n description: raw.description,\n designKey: raw.designKey,\n owner,\n amenities,\n };\n if (Array.isArray(raw.activityObjectIds)) {\n const ids: string[] = [];\n for (const x of raw.activityObjectIds) {\n if (typeof x === \"string\") {\n ids.push(x);\n }\n }\n if (ids.length > 0) {\n entry.activityObjectIds = ids;\n }\n }\n return entry;\n}\n\nexport function parseStructureOccupantRow(\n raw: Record<string, unknown>\n): AgentPlayWorldMapStructureOccupant {\n if (typeof raw.id !== \"string\" || typeof raw.name !== \"string\") {\n throw new Error(\"occupant: structure needs id and name\");\n }\n if (typeof raw.x !== \"number\" || typeof raw.y !== \"number\") {\n throw new Error(\"occupant: structure needs numeric x and y\");\n }\n if (typeof raw.worldId !== \"string\") {\n throw new Error(\"occupant: structure needs worldId\");\n }\n const spaceIdsRaw = raw.spaceIds;\n if (!Array.isArray(spaceIdsRaw) || spaceIdsRaw.length === 0) {\n throw new Error(\"occupant: structure needs non-empty spaceIds\");\n }\n const spaceIds: string[] = [];\n for (const s of spaceIdsRaw) {\n if (typeof s !== \"string\") {\n throw new Error(\"occupant: structure spaceIds must be strings\");\n }\n spaceIds.push(s);\n }\n const base: AgentPlayWorldMapStructureOccupant = {\n kind: \"structure\",\n id: raw.id,\n name: raw.name,\n x: raw.x,\n y: raw.y,\n worldId: raw.worldId,\n spaceIds,\n };\n if (raw.stationary === true) {\n base.stationary = true;\n }\n const pa = raw.primaryAmenity;\n if (typeof pa === \"string\" && isSpaceAmenityKind(pa)) {\n base.primaryAmenity = pa;\n }\n const am = raw.amenities;\n if (Array.isArray(am) && am.length > 0) {\n const list: AgentPlaySpaceAmenityKind[] = [];\n for (const x of am) {\n if (typeof x === \"string\" && isSpaceAmenityKind(x)) {\n list.push(x);\n }\n }\n if (list.length > 0) {\n base.amenities = list;\n }\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 AgentPlayWorldMapStructureOccupant,\n PlayerChainFanoutNotify,\n PlayerChainNotifyNodeRef,\n PlayerChainNodeResponse,\n} from \"../public-types.js\";\nimport {\n parseHumanOccupantRow,\n parseAgentOccupantRow,\n parseMcpOccupantRow,\n parseSpaceCatalogEntry,\n parseStructureOccupantRow,\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 | AgentPlayWorldMapStructureOccupant\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 if (occ.kind === \"structure\") {\n return `structure:${occ.id}`;\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 === \"space\") {\n if (typeof n.stableKey !== \"string\" || n.stableKey.length === 0) {\n throw new Error(\"getPlayerChainNode: invalid space stableKey\");\n }\n if (n.removed === true) {\n return { kind: \"space\", stableKey: n.stableKey, removed: true };\n }\n const sp = n.space;\n if (!isRecord(sp)) {\n throw new Error(\"getPlayerChainNode: invalid space payload\");\n }\n return {\n kind: \"space\",\n stableKey: n.stableKey,\n removed: false,\n space: parseSpaceCatalogEntry(sp),\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\" &&\n occ.kind !== \"agent\" &&\n occ.kind !== \"mcp\" &&\n occ.kind !== \"structure\")\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 : occ.kind === \"mcp\"\n ? parseMcpOccupantRow(occ)\n : parseStructureOccupantRow(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 === \"space\") {\n const spaceIdFromKey = node.stableKey.startsWith(\"space:\")\n ? node.stableKey.slice(\"space:\".length)\n : \"\";\n if (node.removed === true) {\n return {\n ...snapshot,\n spaces: (snapshot.spaces ?? []).filter((s) => s.id !== spaceIdFromKey),\n };\n }\n const merged = (snapshot.spaces ?? []).filter((s) => s.id !== node.space.id);\n merged.push(node.space);\n merged.sort((a, b) => a.id.localeCompare(b.id));\n return { ...snapshot, spaces: merged };\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":["isRecord"]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { W as WorldInteractionRole, L as LangChainAgentRegistration, R as RemotePlayWorldInitAudioOptions, A as AgentPlaySnapshot, P as PlayerChainNodeResponse, a as AddAgentInput, b as RegisteredPlayer, c as AddPlayerInput, d as RecordInteractionInput, J as Journey } from './browser-CvtF5qSC.js';
2
- export { e as AgentPlayWorldMap, f as AgentPlayWorldMapAgentOccupant, g as AgentPlayWorldMapBounds, h as AgentPlayWorldMapMcpOccupant, i as AssistToolFieldType, j as AssistToolParameterSpec, k as AssistToolSpec, D as DestinationJourneyStep, l as JourneyStep, M as MINIMUM_PLAY_WORLD_BOUNDS, O as OriginJourneyStep, m as P2aEnableFlag, n as PLAYER_CHAIN_GENESIS_STABLE_KEY, o as PLAYER_CHAIN_HEADER_STABLE_KEY, p as PlatformAgentInformation, q as PlayAgentInformation, r as PlayerChainFanoutNotify, s as PlayerChainGenesisNode, t as PlayerChainHeaderNode, u as PlayerChainNotifyNodeRef, v as PlayerChainOccupantPresentNode, w as PlayerChainOccupantRemovedNode, x as PositionedStep, y as RealtimeWebrtcClientSecret, z as RegisteredAgentSummary, B as RemotePlayWorldOpenAiAudioOptions, S as StructureJourneyStep, C as WorldBounds, E as WorldJourneyUpdate, Y as YieldEventInfo, Z as ZoneEventInfo, F as boundsContain, G as clampWorldPosition, H as expandBoundsToMinimumPlayArea, I as mergeSnapshotWithPlayerChainNode, K as parsePlayerChainFanoutNotify, N as parsePlayerChainFanoutNotifyFromSsePayload, Q as parsePlayerChainNodeRpcBody, T as sortNodeRefsForSerializedFetch } from './browser-CvtF5qSC.js';
1
+ import { W as WorldInteractionRole, L as LangChainAgentRegistration, R as RemotePlayWorldInitAudioOptions, A as AgentPlaySnapshot, P as PlayerChainNodeResponse, a as AddAgentInput, b as RegisteredPlayer, c as AddPlayerInput, d as RecordInteractionInput, J as Journey } from './browser-BQR40I8R.js';
2
+ export { e as AgentPlayWorldMap, f as AgentPlayWorldMapAgentOccupant, g as AgentPlayWorldMapBounds, h as AgentPlayWorldMapMcpOccupant, i as AssistToolFieldType, j as AssistToolParameterSpec, k as AssistToolSpec, C as CONTINUOUS_RENDER_OFFSET, D as DEFAULT_AGENT_SPAWN_MIN_DISTANCE, l as DestinationJourneyStep, m as JourneyStep, M as MINIMUM_PLAY_WORLD_BOUNDS, O as OCCUPANCY_POINT_MULTIPLIER, n as OccupancyGridPoint, o as OriginJourneyStep, p as P2aEnableFlag, q as PLAYER_CHAIN_GENESIS_STABLE_KEY, r as PLAYER_CHAIN_HEADER_STABLE_KEY, s as PlatformAgentInformation, t as PlayAgentInformation, u as PlayerChainFanoutNotify, v as PlayerChainGenesisNode, w as PlayerChainHeaderNode, x as PlayerChainNotifyNodeRef, y as PlayerChainOccupantPresentNode, z as PlayerChainOccupantRemovedNode, B as PositionedStep, E as RealtimeWebrtcClientSecret, F as RegisteredAgentSummary, G as RemotePlayWorldOpenAiAudioOptions, S as SPATIAL_ZONE_INDEX_AGENTS, H as SPATIAL_ZONE_INDEX_SPACES, I as StructureJourneyStep, K as WorldBounds, N as WorldJourneyUpdate, Y as YieldEventInfo, Z as ZoneEventInfo, Q as boundingWorldRectForOccupancyPoints, T as boundsContain, U as buildRankedOccupancyPoints, V as buildRankedOccupancyPointsForSpatialZone, X as clampWorldPosition, _ as expandBoundsToMinimumPlayArea, $ as isAgentSpawnOccupancyPointAvailable, a0 as isSpaceAnchorOccupancyPointAvailable, a1 as listAllowedOccupancyPoints, a2 as listOccupancyPointsForSpatialZone, a3 as mergeSnapshotWithPlayerChainNode, a4 as occupancyKeyForPosition, a5 as occupancyPointsGroupedBySpatialZone, a6 as parsePlayerChainFanoutNotify, a7 as parsePlayerChainFanoutNotifyFromSsePayload, a8 as parsePlayerChainNodeRpcBody, a9 as pointCellInSpatialZone, aa as sortNodeRefsForSerializedFetch, ab as spatialZoneBounds, ac as spatialZoneCenter } from './browser-BQR40I8R.js';
3
3
  import { WorldIntercomEventPayload, IntercomResponsePayload } from '@agent-play/intercom';
4
- export { AgentPlayAgentNodeEntry, AgentPlayCredentialsFile, loadAgentPlayCredentialsFileFromPath, loadAgentPlayCredentialsFileFromPathSync, loadRootKey, nodeCredentialsMaterialFromHumanPassphrase, parseAgentPlayCredentialsJson, resolveAgentPlayCredentialsPath } from '@agent-play/node-tools';
4
+ export { AgentPlayAgentNodeEntry, AgentPlayCredentialsFile, NodeCredentialMaterial, createNodeCredentialMaterial, loadAgentPlayCredentialsFileFromPath, loadAgentPlayCredentialsFileFromPathSync, loadRootKey, nodeCredentialFromHumanPhrase, nodeCredentialFromPasswHash, nodeCredentialsMaterialFromHumanPassphrase, parseAgentPlayCredentialsJson, resolveAgentPlayCredentialsPath, verifyStoredNodeCredential } from '@agent-play/node-tools';
5
5
 
6
6
  /**
7
7
  * String constants and payload shapes for SSE and in-process world events.
@@ -111,8 +111,9 @@ declare function langchainRegistration(agent: unknown): LangChainAgentRegistrati
111
111
 
112
112
  /**
113
113
  * Root key (from `.root`) plus **human** passphrase as stored in **`~/.agent-play/credentials.json`**
114
- * after **`agent-play create-main-node`**. Material for node id and wire auth is
115
- * **`nodeCredentialsMaterialFromHumanPassphrase(passw)`** (SHA-256 hex; same as CLI **`hashNodePassword`**).
114
+ * after **`agent-play create-main-node`**. The SDK hashes the phrase once locally via
115
+ * **`nodeCredentialFromHumanPhrase`** and sends the resulting **`passwHash`** as the
116
+ * `x-node-passw` header and as the `passwHash` field in repository-backed requests.
116
117
  */
117
118
  type RemotePlayWorldNodeCredentials = {
118
119
  rootKey: string;
@@ -135,8 +136,8 @@ type RemotePlayWorldOptions = {
135
136
  /** Options for {@link RemotePlayWorld.connect}. */
136
137
  type RemotePlayWorldConnectOptions = {
137
138
  /**
138
- * Parent **main** node id. When set, `connect` runs `POST /api/nodes/validate` first, then `GET /api/agent-play/session`.
139
- * When omitted, only `GET /api/agent-play/session` runs.
139
+ * Main node id used for `POST /api/nodes/validate` as **`nodeId`** (not the derived credential id) and remembered for {@link RemotePlayWorld.addAgent} (`mainNodeId` on validate and register).
140
+ * When omitted, connect skips validate and addAgent uses the derived node id for main-parent fields.
140
141
  */
141
142
  mainNodeId?: string;
142
143
  };
@@ -180,11 +181,12 @@ declare class RemotePlayWorld {
180
181
  private readonly rootKey;
181
182
  /** Node id derived from hashed passphrase material + root (main or agent node id). */
182
183
  private readonly derivedNodeId;
183
- /** Hex password material (`hashNodePassword` on normalized human phrase); sent as `password` for repository addAgent. */
184
- private readonly password;
184
+ /** Hex SHA-256 of the normalized human phrase; sent as `x-node-passw` and as `passwHash`. */
185
+ private readonly passwHash;
185
186
  private readonly onSessionEvent;
186
187
  private readonly transportLog;
187
188
  private sid;
189
+ private configuredMainNodeId;
188
190
  private closed;
189
191
  private readonly closeListeners;
190
192
  private readonly playerConnectionInfo;
@@ -201,7 +203,7 @@ declare class RemotePlayWorld {
201
203
  private validateNodeIdentity;
202
204
  /**
203
205
  * Establishes the HTTP session via `GET /api/agent-play/session`. With {@link RemotePlayWorldConnectOptions.mainNodeId},
204
- * validates node identity with `POST /api/nodes/validate` first.
206
+ * validates that node with `POST /api/nodes/validate` using **`nodeId: mainNodeId`** first.
205
207
  */
206
208
  connect(options?: RemotePlayWorldConnectOptions): Promise<void>;
207
209
  close(): Promise<void>;
@@ -232,6 +234,8 @@ declare class RemotePlayWorld {
232
234
  /**
233
235
  * Registers an automation agent using **agent node id** (`nodeId`), sent to the server as `agentId`.
234
236
  *
237
+ * Runs `POST /api/nodes/validate` with **`nodeId`** / **`mainNodeId`** from {@link AddAgentInput} before registering.
238
+ *
235
239
  * If {@link initAudio} was called and **`enableP2a`** is **`"on"`**, this call mints a per-agent
236
240
  * OpenAI Realtime client secret and forwards it as `realtimeWebrtc`.
237
241
  */
package/dist/index.js CHANGED
@@ -1,19 +1,38 @@
1
1
  import {
2
+ CONTINUOUS_RENDER_OFFSET,
3
+ DEFAULT_AGENT_SPAWN_MIN_DISTANCE,
2
4
  MINIMUM_PLAY_WORLD_BOUNDS,
5
+ OCCUPANCY_POINT_MULTIPLIER,
3
6
  PLAYER_CHAIN_GENESIS_STABLE_KEY,
4
7
  PLAYER_CHAIN_HEADER_STABLE_KEY,
8
+ SPATIAL_ZONE_INDEX_AGENTS,
9
+ SPATIAL_ZONE_INDEX_SPACES,
10
+ boundingWorldRectForOccupancyPoints,
5
11
  boundsContain,
12
+ buildRankedOccupancyPoints,
13
+ buildRankedOccupancyPointsForSpatialZone,
6
14
  clampWorldPosition,
7
15
  expandBoundsToMinimumPlayArea,
16
+ isAgentSpawnOccupancyPointAvailable,
17
+ isSpaceAnchorOccupancyPointAvailable,
18
+ listAllowedOccupancyPoints,
19
+ listOccupancyPointsForSpatialZone,
8
20
  mergeSnapshotWithPlayerChainNode,
21
+ occupancyKeyForPosition,
22
+ occupancyPointsGroupedBySpatialZone,
9
23
  parseAgentOccupantRow,
10
24
  parseHumanOccupantRow,
11
25
  parseMcpOccupantRow,
12
26
  parsePlayerChainFanoutNotify,
13
27
  parsePlayerChainFanoutNotifyFromSsePayload,
14
28
  parsePlayerChainNodeRpcBody,
15
- sortNodeRefsForSerializedFetch
16
- } from "./chunk-U2M7HNYN.js";
29
+ parseSpaceCatalogEntry,
30
+ parseStructureOccupantRow,
31
+ pointCellInSpatialZone,
32
+ sortNodeRefsForSerializedFetch,
33
+ spatialZoneBounds,
34
+ spatialZoneCenter
35
+ } from "./chunk-WWIEHWZZ.js";
17
36
 
18
37
  // src/world-events.ts
19
38
  var SESSION_CONNECTED_EVENT = "session:connected";
@@ -90,17 +109,20 @@ function unwrapZodCell(cell) {
90
109
  if (current === null || typeof current !== "object") {
91
110
  return current;
92
111
  }
93
- const def = current._def;
94
- if (!def || typeof def.typeName !== "string") {
112
+ const def = current._def ?? current.def;
113
+ if (!def) {
95
114
  return current;
96
115
  }
97
- const { typeName } = def;
98
- if (typeName === "ZodOptional" || typeName === "ZodNullable" || typeName === "ZodDefault") {
116
+ const typeName = typeof def.typeName === "string" ? def.typeName : typeof def.type === "string" ? def.type : void 0;
117
+ if (typeName === void 0) {
118
+ return current;
119
+ }
120
+ if (typeName === "ZodOptional" || typeName === "ZodNullable" || typeName === "ZodDefault" || typeName === "optional" || typeName === "nullable" || typeName === "default") {
99
121
  const inner = def.innerType;
100
122
  current = inner !== null && typeof inner === "object" ? inner : void 0;
101
123
  continue;
102
124
  }
103
- if (typeName === "ZodEffects") {
125
+ if (typeName === "ZodEffects" || typeName === "effects") {
104
126
  current = def.schema;
105
127
  continue;
106
128
  }
@@ -113,14 +135,18 @@ function fieldTypeFromZodCell(cell) {
113
135
  if (base === null || typeof base !== "object") {
114
136
  return "string";
115
137
  }
116
- const typeName = base._def?.typeName;
117
- if (typeName === "ZodNumber") {
138
+ const def = base._def ?? base.def;
139
+ const typeName = def?.typeName;
140
+ const fallbackType = def?.type;
141
+ const ctorName = base.constructor?.name ?? "";
142
+ const kind = typeName ?? fallbackType ?? ctorName;
143
+ if (kind === "ZodNumber" || kind === "number") {
118
144
  return "number";
119
145
  }
120
- if (typeName === "ZodBoolean") {
146
+ if (kind === "ZodBoolean" || kind === "boolean") {
121
147
  return "boolean";
122
148
  }
123
- if (typeName === "ZodString") {
149
+ if (kind === "ZodString" || kind === "string") {
124
150
  return "string";
125
151
  }
126
152
  return "string";
@@ -130,10 +156,11 @@ function parametersFromSchema(schema) {
130
156
  return {};
131
157
  }
132
158
  const z = schema;
133
- if (typeof z._def?.shape !== "function") {
159
+ const shapeRaw = z._def?.shape;
160
+ const shape = typeof shapeRaw === "function" ? shapeRaw() : shapeRaw !== void 0 && typeof shapeRaw === "object" ? shapeRaw : null;
161
+ if (shape === null) {
134
162
  return { _note: "Pass a Zod object schema on each tool for parameter hints in the watch UI." };
135
163
  }
136
- const shape = z._def.shape();
137
164
  const out = {};
138
165
  for (const key of Object.keys(shape)) {
139
166
  out[key] = {
@@ -188,10 +215,9 @@ function langchainRegistration(agent) {
188
215
  // src/lib/remote-play-world.ts
189
216
  import { randomUUID } from "crypto";
190
217
  import {
191
- deriveNodeIdFromPassword,
192
218
  loadAgentPlayCredentialsFileFromPathSync,
193
219
  loadRootKey,
194
- nodeCredentialsMaterialFromHumanPassphrase,
220
+ nodeCredentialFromHumanPhrase,
195
221
  resolveAgentPlayCredentialsPath
196
222
  } from "@agent-play/node-tools";
197
223
  import { HumanMessage } from "@langchain/core/messages";
@@ -357,9 +383,9 @@ function parseWorldMap(raw) {
357
383
  const occupants = [];
358
384
  const coordKeys = /* @__PURE__ */ new Set();
359
385
  for (const row of occ) {
360
- if (!isRecord(row) || row.kind !== "human" && row.kind !== "agent" && row.kind !== "mcp") {
386
+ if (!isRecord(row) || row.kind !== "human" && row.kind !== "agent" && row.kind !== "mcp" && row.kind !== "structure") {
361
387
  throw new Error(
362
- "getWorldSnapshot: each occupant must have kind human, agent, or mcp"
388
+ "getWorldSnapshot: each occupant must have kind human, agent, mcp, or structure"
363
389
  );
364
390
  }
365
391
  const xy = typeof row.x === "number" && typeof row.y === "number" ? `${row.x},${row.y}` : "";
@@ -374,8 +400,10 @@ function parseWorldMap(raw) {
374
400
  occupants.push(parseHumanOccupantRow(row));
375
401
  } else if (row.kind === "agent") {
376
402
  occupants.push(parseAgentOccupantRow(row));
377
- } else {
403
+ } else if (row.kind === "mcp") {
378
404
  occupants.push(parseMcpOccupantRow(row));
405
+ } else {
406
+ occupants.push(parseStructureOccupantRow(row));
379
407
  }
380
408
  }
381
409
  return { bounds, occupants };
@@ -401,6 +429,18 @@ function parseAgentPlaySnapshot(snapshot) {
401
429
  }
402
430
  if (servers.length > 0) out.mcpServers = servers;
403
431
  }
432
+ if ("spaces" in snapshot && Array.isArray(snapshot.spaces)) {
433
+ const catalog = [];
434
+ for (const row of snapshot.spaces) {
435
+ if (!isRecord(row)) {
436
+ continue;
437
+ }
438
+ catalog.push(parseSpaceCatalogEntry(row));
439
+ }
440
+ if (catalog.length > 0) {
441
+ out.spaces = catalog.sort((a, b) => a.id.localeCompare(b.id));
442
+ }
443
+ }
404
444
  return out;
405
445
  }
406
446
  function parseRegisteredAgentSummary(raw) {
@@ -510,11 +550,12 @@ var RemotePlayWorld = class {
510
550
  rootKey;
511
551
  /** Node id derived from hashed passphrase material + root (main or agent node id). */
512
552
  derivedNodeId;
513
- /** Hex password material (`hashNodePassword` on normalized human phrase); sent as `password` for repository addAgent. */
514
- password;
553
+ /** Hex SHA-256 of the normalized human phrase; sent as `x-node-passw` and as `passwHash`. */
554
+ passwHash;
515
555
  onSessionEvent;
516
556
  transportLog;
517
557
  sid = null;
558
+ configuredMainNodeId = null;
518
559
  closed = false;
519
560
  closeListeners = /* @__PURE__ */ new Set();
520
561
  playerConnectionInfo = /* @__PURE__ */ new Map();
@@ -533,12 +574,12 @@ var RemotePlayWorld = class {
533
574
  const nc = options.nodeCredentials ?? (creds === null ? void 0 : { rootKey: loadRootKey(), passw: creds.passw });
534
575
  if (nc !== void 0 && typeof nc.rootKey === "string" && nc.rootKey.trim().length > 0 && typeof nc.passw === "string" && nc.passw.length > 0) {
535
576
  this.rootKey = nc.rootKey.trim().toLowerCase();
536
- const material = nodeCredentialsMaterialFromHumanPassphrase(nc.passw);
537
- this.password = material;
538
- this.derivedNodeId = deriveNodeIdFromPassword({
539
- password: material,
577
+ const credential = nodeCredentialFromHumanPhrase({
578
+ phrase: nc.passw,
540
579
  rootKey: this.rootKey
541
580
  });
581
+ this.passwHash = credential.passwHash;
582
+ this.derivedNodeId = credential.nodeId;
542
583
  return;
543
584
  }
544
585
  throw new Error(formatMissingCredentialsError());
@@ -581,7 +622,7 @@ var RemotePlayWorld = class {
581
622
  authHeaders() {
582
623
  return {
583
624
  "x-node-id": this.derivedNodeId,
584
- "x-node-passw": this.password
625
+ "x-node-passw": this.passwHash
585
626
  };
586
627
  }
587
628
  jsonHeaders() {
@@ -630,18 +671,21 @@ var RemotePlayWorld = class {
630
671
  }
631
672
  /**
632
673
  * Establishes the HTTP session via `GET /api/agent-play/session`. With {@link RemotePlayWorldConnectOptions.mainNodeId},
633
- * validates node identity with `POST /api/nodes/validate` first.
674
+ * validates that node with `POST /api/nodes/validate` using **`nodeId: mainNodeId`** first.
634
675
  */
635
676
  async connect(options) {
636
677
  const mainNodeIdOpt = options?.mainNodeId?.trim();
637
678
  if (mainNodeIdOpt !== void 0 && mainNodeIdOpt.length > 0) {
679
+ this.configuredMainNodeId = mainNodeIdOpt;
638
680
  const validation = await this.validateNodeIdentity({
639
- nodeId: this.derivedNodeId,
640
- mainNodeId: mainNodeIdOpt
681
+ nodeId: mainNodeIdOpt,
682
+ mainNodeId: void 0
641
683
  });
642
684
  console.info(
643
685
  `[agent-play] Node identity validated (${validation.nodeKind ?? "unknown"}).`
644
686
  );
687
+ } else {
688
+ this.configuredMainNodeId = null;
645
689
  }
646
690
  const res = await fetch(`${this.apiBase}/api/agent-play/session`, {
647
691
  headers: this.authHeaders()
@@ -854,15 +898,16 @@ var RemotePlayWorld = class {
854
898
  /**
855
899
  * Registers an automation agent using **agent node id** (`nodeId`), sent to the server as `agentId`.
856
900
  *
901
+ * Runs `POST /api/nodes/validate` with **`nodeId`** / **`mainNodeId`** from {@link AddAgentInput} before registering.
902
+ *
857
903
  * If {@link initAudio} was called and **`enableP2a`** is **`"on"`**, this call mints a per-agent
858
904
  * OpenAI Realtime client secret and forwards it as `realtimeWebrtc`.
859
905
  */
860
906
  async addAgent(input) {
861
907
  const sid = this.getSessionId();
862
- const effectiveMainNodeId = this.derivedNodeId;
863
908
  const validation = await this.validateNodeIdentity({
864
909
  nodeId: input.nodeId,
865
- mainNodeId: effectiveMainNodeId
910
+ mainNodeId: input.mainNodeId
866
911
  });
867
912
  console.info(
868
913
  [
@@ -870,7 +915,7 @@ var RemotePlayWorld = class {
870
915
  ` status : validated`,
871
916
  ` nodeId : ${input.nodeId}`,
872
917
  ` nodeKind : ${validation.nodeKind ?? "unknown"}`,
873
- ` mainNode : ${effectiveMainNodeId}`
918
+ ` mainNode : ${input.mainNodeId ?? "n/a"}`
874
919
  ].join("\n")
875
920
  );
876
921
  const url = `${this.apiBase}/api/agent-play/players?sid=${encodeURIComponent(sid)}`;
@@ -900,8 +945,8 @@ var RemotePlayWorld = class {
900
945
  name: input.name,
901
946
  type: input.type,
902
947
  agent: input.agent,
903
- mainNodeId: effectiveMainNodeId,
904
- password: this.password,
948
+ mainNodeId: input.mainNodeId,
949
+ passwHash: this.passwHash,
905
950
  agentId: input.nodeId,
906
951
  connectionId,
907
952
  leaseTtlSeconds,
@@ -1363,15 +1408,22 @@ var RemotePlayWorld = class {
1363
1408
 
1364
1409
  // src/index.ts
1365
1410
  import {
1411
+ createNodeCredentialMaterial,
1366
1412
  loadAgentPlayCredentialsFileFromPath,
1367
1413
  loadAgentPlayCredentialsFileFromPathSync as loadAgentPlayCredentialsFileFromPathSync2,
1368
1414
  loadRootKey as loadRootKey2,
1369
- nodeCredentialsMaterialFromHumanPassphrase as nodeCredentialsMaterialFromHumanPassphrase2,
1415
+ nodeCredentialFromHumanPhrase as nodeCredentialFromHumanPhrase2,
1416
+ nodeCredentialFromPasswHash,
1417
+ nodeCredentialsMaterialFromHumanPassphrase,
1370
1418
  parseAgentPlayCredentialsJson,
1371
- resolveAgentPlayCredentialsPath as resolveAgentPlayCredentialsPath2
1419
+ resolveAgentPlayCredentialsPath as resolveAgentPlayCredentialsPath2,
1420
+ verifyStoredNodeCredential
1372
1421
  } from "@agent-play/node-tools";
1373
1422
  export {
1423
+ CONTINUOUS_RENDER_OFFSET,
1424
+ DEFAULT_AGENT_SPAWN_MIN_DISTANCE,
1374
1425
  MINIMUM_PLAY_WORLD_BOUNDS,
1426
+ OCCUPANCY_POINT_MULTIPLIER,
1375
1427
  PLAYER_ADDED_EVENT,
1376
1428
  PLAYER_CHAIN_GENESIS_STABLE_KEY,
1377
1429
  PLAYER_CHAIN_HEADER_STABLE_KEY,
@@ -1381,28 +1433,46 @@ export {
1381
1433
  SESSION_INVALID_EVENT,
1382
1434
  SESSION_SSE_ERROR_EVENT,
1383
1435
  SESSION_SSE_OPEN_EVENT,
1436
+ SPATIAL_ZONE_INDEX_AGENTS,
1437
+ SPATIAL_ZONE_INDEX_SPACES,
1384
1438
  WORLD_AGENT_SIGNAL_EVENT,
1385
1439
  WORLD_INTERACTION_EVENT,
1386
1440
  WORLD_JOURNEY_EVENT,
1387
1441
  agentPlayDebug,
1442
+ boundingWorldRectForOccupancyPoints,
1388
1443
  boundsContain,
1444
+ buildRankedOccupancyPoints,
1445
+ buildRankedOccupancyPointsForSpatialZone,
1389
1446
  clampWorldPosition,
1390
1447
  configureAgentPlayDebug,
1448
+ createNodeCredentialMaterial,
1391
1449
  expandBoundsToMinimumPlayArea,
1392
1450
  intercomResultRecordFromLangChainInvokeOutput,
1393
1451
  isAgentPlayDebugEnabled,
1452
+ isAgentSpawnOccupancyPointAvailable,
1453
+ isSpaceAnchorOccupancyPointAvailable,
1394
1454
  langchainRegistration,
1455
+ listAllowedOccupancyPoints,
1456
+ listOccupancyPointsForSpatialZone,
1395
1457
  loadAgentPlayCredentialsFileFromPath,
1396
1458
  loadAgentPlayCredentialsFileFromPathSync2 as loadAgentPlayCredentialsFileFromPathSync,
1397
1459
  loadRootKey2 as loadRootKey,
1398
1460
  mergeSnapshotWithPlayerChainNode,
1399
- nodeCredentialsMaterialFromHumanPassphrase2 as nodeCredentialsMaterialFromHumanPassphrase,
1461
+ nodeCredentialFromHumanPhrase2 as nodeCredentialFromHumanPhrase,
1462
+ nodeCredentialFromPasswHash,
1463
+ nodeCredentialsMaterialFromHumanPassphrase,
1464
+ occupancyKeyForPosition,
1465
+ occupancyPointsGroupedBySpatialZone,
1400
1466
  parseAgentPlayCredentialsJson,
1401
1467
  parsePlayerChainFanoutNotify,
1402
1468
  parsePlayerChainFanoutNotifyFromSsePayload,
1403
1469
  parsePlayerChainNodeRpcBody,
1470
+ pointCellInSpatialZone,
1404
1471
  resetAgentPlayDebug,
1405
1472
  resolveAgentPlayCredentialsPath2 as resolveAgentPlayCredentialsPath,
1406
- sortNodeRefsForSerializedFetch
1473
+ sortNodeRefsForSerializedFetch,
1474
+ spatialZoneBounds,
1475
+ spatialZoneCenter,
1476
+ verifyStoredNodeCredential
1407
1477
  };
1408
1478
  //# sourceMappingURL=index.js.map