@agent-play/sdk 1.1.0 → 3.0.1
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 +2 -0
- package/dist/index.d.ts +145 -166
- package/dist/index.js +419 -146
- package/dist/index.js.map +1 -1
- package/examples/01-remote-web-ui-langchain.ts +5 -5
- package/examples/02-remote-two-players-langchain.ts +10 -11
- package/examples/README.md +2 -2
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/world-events.ts","../src/lib/world-bounds.ts","../src/lib/agent-play-debug.ts","../src/platforms/langchain.ts","../src/lib/remote-play-world.ts"],"sourcesContent":["/**\n * String constants and payload shapes for SSE and in-process world events.\n *\n * @remarks **Emitters:** server `PlayWorld` and Redis fanout. **Consumers:** watch UI `EventSource`,\n * integration tests, and any host that forwards `POST` events.\n */\nimport type { WorldInteractionRole, WorldStructure } from \"./public-types.js\";\n\n/** Fired when `addPlayer` completes; payload includes snapshot row for the new player. */\nexport const PLAYER_ADDED_EVENT = \"world:player_added\";\n\n/** Fired when structures change (sync tools, layout refresh). */\nexport const WORLD_STRUCTURES_EVENT = \"world:structures\";\n\n/** Fired for each new chat/interaction line. */\nexport const WORLD_INTERACTION_EVENT = \"world:interaction\";\n\n/** Lightweight signals (zone, yield, assist, journey metadata, etc.). */\nexport const WORLD_AGENT_SIGNAL_EVENT = \"world:agent_signal\";\n\n/** Full journey + path update for a player. */\nexport const WORLD_JOURNEY_EVENT = \"world:journey\";\n\n/**\n * Payload for {@link WORLD_AGENT_SIGNAL_EVENT}.\n *\n * @property playerId - Target player.\n * @property kind - Signal category; `journey` often carries `{ stepCount }` in `data`.\n * @property data - Optional free-form metadata.\n */\nexport type WorldAgentSignalPayload = {\n playerId: string;\n kind: \"zone\" | \"yield\" | \"assist\" | \"chat\" | \"metadata\" | \"journey\";\n data?: Record<string, unknown>;\n};\n\n/**\n * Payload for {@link WORLD_INTERACTION_EVENT}.\n *\n * @property seq - Monotonic sequence for ordering in the UI.\n */\nexport type WorldInteractionPayload = {\n playerId: string;\n role: WorldInteractionRole;\n text: string;\n at: string;\n seq: number;\n};\n\n/**\n * Payload for {@link WORLD_STRUCTURES_EVENT}.\n *\n * @property type - Optional agent platform type string.\n */\nexport type WorldStructuresPayload = {\n playerId: string;\n name: string;\n structures: WorldStructure[];\n type?: string;\n};\n","/**\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","/**\n * Optional structured `console.debug` for SDK internals; gated by {@link configureAgentPlayDebug} or `AGENT_PLAY_DEBUG=1`.\n */\ntype DebugConfigure = {\n /** When set, overrides environment: `true` forces debug on, `false` forces off. */\n debug?: boolean;\n};\n\n/**\n * In-memory override for debug enablement (undefined = follow env only).\n *\n * @remarks **Writers:** {@link configureAgentPlayDebug}, {@link resetAgentPlayDebug}.\n * **Readers:** {@link isAgentPlayDebugEnabled}.\n */\nlet configuredDebug: boolean | undefined;\n\n/**\n * Sets whether SDK debug logging is enabled regardless of `AGENT_PLAY_DEBUG`.\n *\n * @param opts.debug - `true` / `false` to force; omit to clear override.\n *\n * @remarks **Callers:** tests and user code. **Callees:** none.\n */\nexport function configureAgentPlayDebug(opts: DebugConfigure): void {\n configuredDebug = opts.debug ?? undefined;\n}\n\n/**\n * Clears the in-memory override so only `AGENT_PLAY_DEBUG` applies.\n *\n * @remarks **Callers:** tests. **Callees:** none.\n */\nexport function resetAgentPlayDebug(): void {\n configuredDebug = undefined;\n}\n\n/**\n * @returns Whether debug logging should run: override wins, else `AGENT_PLAY_DEBUG === \"1\"`.\n *\n * @remarks **Callers:** {@link agentPlayDebug}. **Callees:** `process.env` read.\n */\nexport function isAgentPlayDebugEnabled(): boolean {\n if (configuredDebug === false) return false;\n if (configuredDebug === true) return true;\n return process.env.AGENT_PLAY_DEBUG === \"1\";\n}\n\n/** Max length of JSON detail string before truncation in {@link safeSerialize}. */\nconst MAX_JSON_LENGTH = 2000;\n\n/**\n * Serializes `detail` for log lines, truncating long JSON and handling circular refs.\n *\n * @internal\n * @remarks **Callers:** {@link agentPlayDebug} only. **Callees:** `JSON.stringify` with replacer.\n */\nfunction safeSerialize(detail: unknown): string {\n if (detail === undefined) return \"\";\n try {\n const seen = new WeakSet<object>();\n const json = JSON.stringify(detail, (_k, v: unknown) => {\n if (typeof v === \"object\" && v !== null) {\n if (seen.has(v)) return \"[Circular]\";\n seen.add(v);\n }\n if (typeof v === \"bigint\") return String(v);\n return v;\n });\n if (typeof json !== \"string\") return String(detail);\n return json.length > MAX_JSON_LENGTH\n ? `${json.slice(0, MAX_JSON_LENGTH)}…`\n : json;\n } catch {\n return String(detail);\n }\n}\n\n/**\n * Emits `console.debug` when {@link isAgentPlayDebugEnabled} is true.\n *\n * @param scope - Short label (e.g. `\"langchain\"`).\n * @param message - Human-readable message.\n * @param detail - Optional object serialized by {@link safeSerialize}.\n *\n * @remarks **Callers:** {@link langchainRegistration} and other SDK modules. **Callees:** {@link isAgentPlayDebugEnabled}, {@link safeSerialize}.\n */\nexport function agentPlayDebug(\n scope: string,\n message: string,\n detail?: unknown\n): void {\n if (!isAgentPlayDebugEnabled()) return;\n const tail =\n detail === undefined ? \"\" : ` ${safeSerialize(detail)}`;\n console.debug(`[agent-play:${scope}] ${message}${tail}`);\n}\n","/**\n * LangChain adapter: derives tool names and assist metadata from a LangChain agent for\n * {@link import(\"../public-types.js\").LangChainAgentRegistration}.\n *\n * @remarks **Primary export:** {@link langchainRegistration}. Private helpers build error strings and\n * `AssistToolSpec` rows from Zod schemas when available.\n */\nimport { agentPlayDebug } from \"../lib/agent-play-debug.js\";\nimport type { AssistToolSpec, LangChainAgentRegistration } from \"../public-types.js\";\n\n/** Required tool name enforced by the watch UI contract. */\nconst CHAT_TOOL = \"chat_tool\";\n\n/**\n * Error text when the agent has no `tools` array.\n *\n * @remarks **Callers:** {@link langchainRegistration}. **Callees:** none.\n */\nfunction formatMissingAgentToolsError(): string {\n return [\n \"langchainRegistration: expected a LangChain agent with a tools array.\",\n \"\",\n \" Pass the object returned from createAgent({ tools: [...] }) (or equivalent) so tool names are available for the play world.\",\n \" The tools array must include named tools; see the separate message if \\\"chat_tool\\\" or assist_* tools are missing.\",\n ].join(\"\\n\");\n}\n\n/**\n * Error text when `chat_tool` is missing from tool names.\n *\n * @remarks **Callers:** {@link langchainRegistration}. **Callees:** none.\n */\nfunction formatMissingChatToolError(): string {\n return [\n \"langchainRegistration: missing required tool \\\"chat_tool\\\".\",\n \"\",\n \" Add a tool named \\\"chat_tool\\\" to your LangChain agent so the play world can show chat and proximity interactions.\",\n \" Example: tool(() => \\\"…\\\", { name: \\\"chat_tool\\\", description: \\\"…\\\", schema: z.object({ … }) })\",\n \"\",\n \" Tools whose names start with \\\"assist_\\\" are listed as assist actions on the watch UI; give each a Zod object schema so parameters can be shown in the UI.\",\n ].join(\"\\n\");\n}\n\n/**\n * Best-effort parameter shape from a Zod object schema’s `shape()` for UI hints.\n *\n * @remarks **Callers:** {@link describeTool}. **Callees:** none.\n */\nfunction parametersFromSchema(schema: unknown): Record<string, unknown> {\n if (schema === null || typeof schema !== \"object\") {\n return {};\n }\n const z = schema as {\n _def?: { typeName?: string; shape?: () => Record<string, unknown> };\n };\n if (typeof z._def?.shape !== \"function\") {\n return { _note: \"Pass a Zod object schema on each tool for parameter hints in the watch UI.\" };\n }\n const shape = z._def.shape();\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(shape)) {\n out[key] = { field: key };\n }\n return out;\n}\n\n/**\n * Builds an {@link AssistToolSpec} from a LangChain tool descriptor.\n *\n * @remarks **Callers:** {@link langchainRegistration} for `assist_*` tools only. **Callees:** {@link parametersFromSchema}.\n */\nfunction describeTool(t: {\n name: string;\n description?: string;\n schema?: unknown;\n}): AssistToolSpec {\n return {\n name: t.name,\n description:\n typeof t.description === \"string\" && t.description.length > 0\n ? t.description\n : t.name,\n parameters: parametersFromSchema(t.schema),\n };\n}\n\n/**\n * Reads `agent.tools` or `agent.options.tools` from common LangChain agent shapes.\n *\n * @returns The tools array, or `null` if not found.\n *\n * @remarks **Callers:** {@link langchainRegistration} only. **Callees:** none.\n */\nfunction extractToolsArray(agent: unknown): unknown[] | null {\n if (typeof agent !== \"object\" || agent === null) {\n return null;\n }\n const a = agent as {\n tools?: unknown;\n options?: { tools?: unknown };\n };\n if (Array.isArray(a.tools)) {\n return a.tools;\n }\n if (\n a.options !== undefined &&\n typeof a.options === \"object\" &&\n a.options !== null &&\n \"tools\" in a.options &&\n Array.isArray((a.options as { tools: unknown }).tools)\n ) {\n return (a.options as { tools: unknown[] }).tools;\n }\n return null;\n}\n\n/**\n * Validates a LangChain-style agent exposes tools (including required `chat_tool`) and returns\n * a {@link LangChainAgentRegistration} for `addPlayer`.\n *\n * @param agent - Return value from `createAgent` (or equivalent) with a `tools` array.\n * @throws Error if tools are missing or `chat_tool` is not present.\n *\n * @remarks **Callers:** user code before `RemotePlayWorld.addPlayer`. **Callees:** {@link extractToolsArray},\n * {@link formatMissingAgentToolsError}, {@link formatMissingChatToolError}, {@link describeTool}, {@link agentPlayDebug}.\n */\nexport function langchainRegistration(\n agent: unknown\n): LangChainAgentRegistration {\n const rawTools = extractToolsArray(agent);\n if (rawTools === null) {\n throw new Error(formatMissingAgentToolsError());\n }\n const tools =\n rawTools as readonly { name: string; description?: string; schema?: unknown }[];\n const names = tools.map((x) => x.name);\n if (!names.includes(CHAT_TOOL)) {\n throw new Error(formatMissingChatToolError());\n }\n const assistTools = tools\n .filter((t) => t.name.startsWith(\"assist_\"))\n .map((t) => describeTool(t));\n agentPlayDebug(\"langchain\", \"langchainRegistration\", {\n toolCount: names.length,\n assistCount: assistTools.length,\n });\n return {\n type: \"langchain\",\n toolNames: names,\n assistTools,\n };\n}\n","import type {\n AddPlayerInput,\n Journey,\n RecordInteractionInput,\n RegisteredPlayer,\n WorldStructure,\n WorldStructureKind,\n} from \"../public-types.js\";\n\n/** Options for {@link RemotePlayWorld}: API origin and credentials for RPC calls. */\nexport type RemotePlayWorldOptions = {\n /** Web UI base URL (no trailing slash), e.g. `https://host` or `http://127.0.0.1:3000`. */\n baseUrl: string;\n /** Account API key when the server uses `AgentRepository`; use a non-empty placeholder if none. */\n apiKey: string;\n /** Optional bearer token for authenticated routes. */\n authToken?: string;\n};\n\n/**\n * Builds the error string thrown when {@link RemotePlayWorld}'s constructor receives an empty `apiKey`.\n *\n * @remarks **Callers:** {@link RemotePlayWorld} constructor only.\n * **Callees:** none.\n */\nfunction formatMissingApiKeyError(): string {\n return [\n 'RemotePlayWorld: options.apiKey is required.',\n \"\",\n \" Register an agent with `agent-play create` (after `agent-play login`) and use the printed API key.\",\n \" Pass it here so addPlayer can authenticate against the server repository when Redis is enabled.\",\n \" If the server has no agent repository (local dev), still pass a non-empty placeholder string.\",\n ].join(\"\\n\");\n}\n\n/**\n * Strips a single trailing slash from a base URL for consistent `fetch` URL construction.\n *\n * @remarks **Callers:** {@link RemotePlayWorld} constructor.\n * **Callees:** none.\n */\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\n/**\n * Narrowing type guard for plain object records.\n *\n * @remarks **Callers:** JSON response parsing in {@link RemotePlayWorld.start}, {@link RemotePlayWorld.addPlayer},\n * {@link RemotePlayWorld.registerMcp}, and structure parsing helpers.\n */\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\nconst STRUCTURE_KINDS: readonly WorldStructureKind[] = [\n \"home\",\n \"tool\",\n \"api\",\n \"database\",\n \"model\",\n];\n\n/**\n * @remarks **Callers:** {@link parseWorldStructure}.\n */\nfunction isWorldStructureKind(s: string): s is WorldStructureKind {\n return (STRUCTURE_KINDS as readonly string[]).includes(s);\n}\n\n/**\n * Parses one server JSON structure into {@link WorldStructure} or `null` if invalid.\n *\n * @remarks **Callers:** {@link parseStructures}.\n * **Callees:** {@link isRecord}, {@link isWorldStructureKind}.\n */\nfunction parseWorldStructure(x: unknown): WorldStructure | null {\n if (!isRecord(x)) return null;\n if (typeof x.id !== \"string\" || typeof x.kind !== \"string\") return null;\n if (!isWorldStructureKind(x.kind)) return null;\n if (typeof x.x !== \"number\" || typeof x.y !== \"number\") return null;\n const out: WorldStructure = {\n id: x.id,\n kind: x.kind,\n x: x.x,\n y: x.y,\n };\n if (typeof x.toolName === \"string\") out.toolName = x.toolName;\n if (typeof x.label === \"string\") out.label = x.label;\n return out;\n}\n\n/**\n * Parses `structures` JSON array from `addPlayer` response.\n *\n * @remarks **Callers:** {@link RemotePlayWorld.addPlayer}.\n * **Callees:** {@link parseWorldStructure}.\n */\nfunction parseStructures(v: unknown): WorldStructure[] {\n if (!Array.isArray(v)) return [];\n const out: WorldStructure[] = [];\n for (const x of v) {\n const row = parseWorldStructure(x);\n if (row !== null) out.push(row);\n }\n return out;\n}\n\n/**\n * Error message for invalid `seconds` in {@link RemotePlayWorld.hold}.\n *\n * @remarks **Callers:** {@link RemotePlayWorld.hold `hold().for()`} only.\n */\nfunction formatInvalidHoldSecondsError(): string {\n return [\n \"RemotePlayWorld.hold().for(seconds): seconds must be a finite number.\",\n \"\",\n \" Example: await world.hold().for(3600)\",\n ].join(\"\\n\");\n}\n\n/**\n * Return value of {@link RemotePlayWorld.hold}: a delayed promise helper for long-running processes.\n */\nexport type RemotePlayWorldHold = {\n /**\n * Sleeps for the given number of seconds (non-negative).\n *\n * @remarks **Callers:** integration scripts that must keep the Node process alive after `start()`.\n * **Callees:** `setTimeout` via `Promise`.\n */\n for: (seconds: number) => Promise<void>;\n};\n\n/**\n * HTTP client for Agent Play: starts a session, registers players, records journeys and\n * interactions, and syncs structures. Designed for long-running Node processes with\n * {@link RemotePlayWorld.hold `hold().for()`} to keep the process alive.\n *\n * @remarks **Callers:** user code and SDK examples. All public methods except `constructor` require\n * {@link RemotePlayWorld.start} to have succeeded first (except `start` itself).\n *\n * **Protocol:** Uses `fetch` to `GET /api/agent-play/session`, `POST /api/agent-play/players`, and\n * `POST /api/agent-play/sdk/rpc` with JSON body `{ op, payload }`, plus MCP registration\n * `POST /api/agent-play/mcp/register`.\n */\nexport class RemotePlayWorld {\n /** Normalized {@link RemotePlayWorldOptions.baseUrl} (no trailing slash). Used for `fetch` base. */\n private readonly apiBase: string;\n /** Trimmed account API key; sent on `addPlayer` and implied for RPC auth expectations. */\n private readonly apiKey: string;\n /** Optional bearer token merged into request headers when set. */\n private readonly authToken: string | undefined;\n /** Session id from `GET /api/agent-play/session`; `null` until {@link RemotePlayWorld.start} succeeds. */\n private sid: string | null = null;\n /** When true, {@link RemotePlayWorld.close} is a no-op and listeners already ran. */\n private closed = false;\n /** Unsubscribe callbacks registered via {@link RemotePlayWorld.onClose}. */\n private readonly closeListeners = new Set<() => void>();\n\n /**\n * @param options - Base URL, API key, and optional auth token.\n * @throws Error if `apiKey` is missing or whitespace-only (see {@link formatMissingApiKeyError}).\n *\n * @remarks **Callees:** {@link formatMissingApiKeyError}, {@link normalizeBaseUrl}.\n */\n constructor(options: RemotePlayWorldOptions) {\n if (typeof options.apiKey !== \"string\" || options.apiKey.trim().length === 0) {\n throw new Error(formatMissingApiKeyError());\n }\n this.apiBase = normalizeBaseUrl(options.baseUrl);\n this.apiKey = options.apiKey.trim();\n this.authToken = options.authToken;\n }\n\n /**\n * Registers a one-shot listener invoked when {@link RemotePlayWorld.close} runs (e.g. process shutdown).\n *\n * @param handler - Synchronous callback; errors are swallowed.\n * @returns Unsubscribe function that removes this `handler` from the set.\n *\n * @remarks **Callers:** user code. **Callees:** `Set.prototype.add` / `delete`.\n */\n onClose(handler: () => void): () => void {\n this.closeListeners.add(handler);\n return () => {\n this.closeListeners.delete(handler);\n };\n }\n\n /**\n * Returns a helper that sleeps for wall-clock seconds (useful for `await world.hold().for(3600)`).\n *\n * @remarks **Callers:** user code. **Callees:** {@link formatInvalidHoldSecondsError}.\n */\n hold(): RemotePlayWorldHold {\n return {\n for: async (seconds: number) => {\n if (typeof seconds !== \"number\" || !Number.isFinite(seconds)) {\n throw new Error(formatInvalidHoldSecondsError());\n }\n const ms = Math.max(0, seconds) * 1000;\n await new Promise<void>((resolve) => {\n setTimeout(resolve, ms);\n });\n },\n };\n }\n\n /**\n * Authorization header map for requests that only need session cookie or bearer auth.\n *\n * @internal\n * @remarks **Callers:** {@link RemotePlayWorld.start}, {@link RemotePlayWorld.addPlayer} (via headers),\n * {@link RemotePlayWorld.registerMcp}, and any future GET-only calls.\n */\n private authHeaders(): Record<string, string> {\n if (this.authToken === undefined) return {};\n return { Authorization: `Bearer ${this.authToken}` };\n }\n\n /**\n * Headers for JSON `POST` bodies (RPC, players, MCP).\n *\n * @internal\n * @remarks **Callers:** {@link RemotePlayWorld.addPlayer}, {@link RemotePlayWorld.rpc}, {@link RemotePlayWorld.registerMcp}.\n * **Callees:** {@link authHeaders}.\n */\n private jsonHeaders(): Record<string, string> {\n return {\n \"content-type\": \"application/json\",\n ...this.authHeaders(),\n };\n }\n\n /**\n * Creates a session and stores `sid` from `GET /api/agent-play/session`.\n *\n * @throws Error if the response is not OK or JSON lacks a non-empty `sid` string.\n *\n * @remarks **Callers:** user code. **Callees:** `fetch`, {@link isRecord}.\n */\n async start(): Promise<void> {\n const res = await fetch(`${this.apiBase}/api/agent-play/session`, {\n headers: this.authHeaders(),\n });\n if (!res.ok) {\n throw new Error(`session failed: ${res.status}`);\n }\n const json: unknown = await res.json();\n if (!isRecord(json) || typeof json.sid !== \"string\" || json.sid.length === 0) {\n throw new Error(\"session: invalid response\");\n }\n this.sid = json.sid;\n }\n\n /**\n * Marks the client closed and invokes all {@link onClose} listeners once.\n *\n * @remarks **Callers:** user code. **Callees:** `Array.from` over {@link closeListeners}.\n */\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n for (const handler of Array.from(this.closeListeners)) {\n try {\n handler();\n } catch {\n // ignore listener errors\n }\n }\n }\n\n /**\n * @returns Current session id.\n * @throws Error if {@link RemotePlayWorld.start} has not been called successfully.\n *\n * @remarks **Callers:** user code. **Callees:** none.\n */\n getSessionId(): string {\n if (this.sid === null) {\n throw new Error(\"RemotePlayWorld.start() must be called first\");\n }\n return this.sid;\n }\n\n /**\n * @returns Absolute watch URL for the session (`/agent-play/watch` on `apiBase`). Query `sid` is not appended;\n * consumers append `?sid=` from {@link getSessionId} when needed.\n *\n * @remarks **Callers:** user code. **Callees:** `URL` constructor.\n */\n getPreviewUrl(): string {\n const u = new URL(\"/agent-play/watch\", this.apiBase);\n u.search = \"\";\n return u.toString();\n }\n\n /**\n * Registers a player agent with the server for the current session.\n *\n * @param input - Name, type, `agent` registration from {@link langchainRegistration}, optional `agentId`.\n * @returns Resolved player row with `previewUrl` and `structures`.\n * @throws Error on HTTP errors or malformed JSON.\n *\n * @remarks **Callers:** user code. **Callees:** {@link getSessionId}, `fetch`, `JSON.parse`, {@link parseStructures}.\n */\n async addPlayer(input: AddPlayerInput): Promise<RegisteredPlayer> {\n const sid = this.getSessionId();\n const url = `${this.apiBase}/api/agent-play/players?sid=${encodeURIComponent(sid)}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({\n name: input.name,\n type: input.type,\n agent: input.agent,\n apiKey: this.apiKey,\n agentId: input.agentId,\n }),\n });\n const bodyText = await res.text();\n if (!res.ok) {\n throw new Error(`addPlayer: ${res.status} ${bodyText}`);\n }\n let json: unknown;\n try {\n json = JSON.parse(bodyText) as unknown;\n } catch {\n throw new Error(\"addPlayer: invalid JSON\");\n }\n if (!isRecord(json)) {\n throw new Error(\"addPlayer: invalid response shape\");\n }\n const playerId = json.playerId;\n const previewUrl = json.previewUrl;\n if (typeof playerId !== \"string\" || typeof previewUrl !== \"string\") {\n throw new Error(\"addPlayer: missing playerId or previewUrl\");\n }\n const structures = parseStructures(json.structures);\n const now = new Date();\n return {\n id: playerId,\n name: input.name,\n sid,\n createdAt: now,\n updatedAt: now,\n previewUrl,\n structures,\n };\n }\n\n /**\n * Appends a chat-style interaction line for a player (RPC `recordInteraction`).\n *\n * @remarks **Callers:** user code. **Callees:** {@link rpc}.\n */\n async recordInteraction(input: RecordInteractionInput): Promise<void> {\n await this.rpc(\"recordInteraction\", {\n playerId: input.playerId,\n role: input.role,\n text: input.text,\n });\n }\n\n /**\n * Records a full journey for a player (RPC `recordJourney`).\n *\n * @remarks **Callers:** user code. **Callees:** {@link rpc}.\n */\n async recordJourney(playerId: string, journey: Journey): Promise<void> {\n await this.rpc(\"recordJourney\", { playerId, journey });\n }\n\n /**\n * Re-syncs layout structures from an ordered tool name list (RPC `syncPlayerStructuresFromTools`).\n *\n * @remarks **Callers:** user code. **Callees:** {@link rpc}.\n */\n async syncPlayerStructuresFromTools(\n playerId: string,\n toolNames: string[]\n ): Promise<void> {\n await this.rpc(\"syncPlayerStructuresFromTools\", { playerId, toolNames });\n }\n\n /**\n * Registers an MCP server metadata row for the session (HTTP POST, not the same as RPC `op`).\n *\n * @returns New registration id string from JSON `{ id }`.\n *\n * @remarks **Callers:** user code. **Callees:** `fetch`, {@link getSessionId}, {@link jsonHeaders}.\n */\n async registerMcp(options: { name: string; url?: string }): Promise<string> {\n const sid = this.getSessionId();\n const url = `${this.apiBase}/api/agent-play/mcp/register?sid=${encodeURIComponent(sid)}`;\n const body: { name: string; url?: string } = { name: options.name };\n if (options.url !== undefined) {\n body.url = options.url;\n }\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify(body),\n });\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`registerMcp: ${res.status} ${text}`);\n }\n let json: unknown;\n try {\n json = JSON.parse(text) as unknown;\n } catch {\n throw new Error(\"registerMcp: invalid JSON\");\n }\n if (!isRecord(json) || typeof json.id !== \"string\") {\n throw new Error(\"registerMcp: invalid response\");\n }\n return json.id;\n }\n\n /**\n * Posts `{ op, payload }` to `/api/agent-play/sdk/rpc?sid=...`.\n *\n * @internal\n * @remarks **Callers:** {@link recordInteraction}, {@link recordJourney}, {@link syncPlayerStructuresFromTools}.\n * **Callees:** {@link getSessionId}, `fetch`, {@link jsonHeaders}.\n */\n private async rpc(op: string, payload: unknown): Promise<void> {\n const sid = this.getSessionId();\n const url = `${this.apiBase}/api/agent-play/sdk/rpc?sid=${encodeURIComponent(sid)}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({ op, payload }),\n });\n if (!res.ok) {\n const t = await res.text();\n throw new Error(`rpc ${op}: ${res.status} ${t}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/world-events.ts","../src/lib/world-bounds.ts","../src/lib/agent-play-debug.ts","../src/platforms/langchain.ts","../src/lib/parse-occupant-row.ts","../src/lib/world-chain-keys.ts","../src/lib/player-chain-merge.ts","../src/lib/remote-play-world.ts"],"sourcesContent":["/**\n * String constants and payload shapes for SSE and in-process world events.\n *\n * @remarks **Emitters:** server `PlayWorld` and Redis fanout. **Consumers:** watch UI `EventSource`,\n * integration tests, and any host that forwards `POST` events.\n */\nimport type { WorldInteractionRole } from \"./public-types.js\";\n\n/** Fired when `addPlayer` completes; payload includes snapshot row for the new player. */\nexport const PLAYER_ADDED_EVENT = \"world:player_added\";\n\n/** Fired for each new chat/interaction line. */\nexport const WORLD_INTERACTION_EVENT = \"world:interaction\";\n\n/** Lightweight signals (zone, yield, assist, journey metadata, etc.). */\nexport const WORLD_AGENT_SIGNAL_EVENT = \"world:agent_signal\";\n\n/** Full journey + path update for a player. */\nexport const WORLD_JOURNEY_EVENT = \"world:journey\";\n\n/**\n * Payload for {@link WORLD_AGENT_SIGNAL_EVENT}.\n *\n * @property playerId - Target player.\n * @property kind - Signal category; `journey` often carries `{ stepCount }` in `data`.\n * @property data - Optional free-form metadata.\n */\nexport type WorldAgentSignalPayload = {\n playerId: string;\n kind: \"zone\" | \"yield\" | \"assist\" | \"chat\" | \"metadata\" | \"journey\";\n data?: Record<string, unknown>;\n};\n\n/**\n * Payload for {@link WORLD_INTERACTION_EVENT}.\n *\n * @property seq - Monotonic sequence for ordering in the UI.\n */\nexport type WorldInteractionPayload = {\n playerId: string;\n role: WorldInteractionRole;\n text: string;\n at: string;\n seq: number;\n};\n","/**\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","/**\n * Optional structured `console.debug` for SDK internals; gated by {@link configureAgentPlayDebug} or `AGENT_PLAY_DEBUG=1`.\n */\ntype DebugConfigure = {\n /** When set, overrides environment: `true` forces debug on, `false` forces off. */\n debug?: boolean;\n};\n\n/**\n * In-memory override for debug enablement (undefined = follow env only).\n *\n * @remarks **Writers:** {@link configureAgentPlayDebug}, {@link resetAgentPlayDebug}.\n * **Readers:** {@link isAgentPlayDebugEnabled}.\n */\nlet configuredDebug: boolean | undefined;\n\n/**\n * Sets whether SDK debug logging is enabled regardless of `AGENT_PLAY_DEBUG`.\n *\n * @param opts.debug - `true` / `false` to force; omit to clear override.\n *\n * @remarks **Callers:** tests and user code. **Callees:** none.\n */\nexport function configureAgentPlayDebug(opts: DebugConfigure): void {\n configuredDebug = opts.debug ?? undefined;\n}\n\n/**\n * Clears the in-memory override so only `AGENT_PLAY_DEBUG` applies.\n *\n * @remarks **Callers:** tests. **Callees:** none.\n */\nexport function resetAgentPlayDebug(): void {\n configuredDebug = undefined;\n}\n\n/**\n * @returns Whether debug logging should run: override wins, else `AGENT_PLAY_DEBUG === \"1\"`.\n *\n * @remarks **Callers:** {@link agentPlayDebug}. **Callees:** `process.env` read.\n */\nexport function isAgentPlayDebugEnabled(): boolean {\n if (configuredDebug === false) return false;\n if (configuredDebug === true) return true;\n return process.env.AGENT_PLAY_DEBUG === \"1\";\n}\n\n/** Max length of JSON detail string before truncation in {@link safeSerialize}. */\nconst MAX_JSON_LENGTH = 2000;\n\n/**\n * Serializes `detail` for log lines, truncating long JSON and handling circular refs.\n *\n * @internal\n * @remarks **Callers:** {@link agentPlayDebug} only. **Callees:** `JSON.stringify` with replacer.\n */\nfunction safeSerialize(detail: unknown): string {\n if (detail === undefined) return \"\";\n try {\n const seen = new WeakSet<object>();\n const json = JSON.stringify(detail, (_k, v: unknown) => {\n if (typeof v === \"object\" && v !== null) {\n if (seen.has(v)) return \"[Circular]\";\n seen.add(v);\n }\n if (typeof v === \"bigint\") return String(v);\n return v;\n });\n if (typeof json !== \"string\") return String(detail);\n return json.length > MAX_JSON_LENGTH\n ? `${json.slice(0, MAX_JSON_LENGTH)}…`\n : json;\n } catch {\n return String(detail);\n }\n}\n\n/**\n * Emits `console.debug` when {@link isAgentPlayDebugEnabled} is true.\n *\n * @param scope - Short label (e.g. `\"langchain\"`).\n * @param message - Human-readable message.\n * @param detail - Optional object serialized by {@link safeSerialize}.\n *\n * @remarks **Callers:** {@link langchainRegistration} and other SDK modules. **Callees:** {@link isAgentPlayDebugEnabled}, {@link safeSerialize}.\n */\nexport function agentPlayDebug(\n scope: string,\n message: string,\n detail?: unknown\n): void {\n if (!isAgentPlayDebugEnabled()) return;\n const tail =\n detail === undefined ? \"\" : ` ${safeSerialize(detail)}`;\n console.debug(`[agent-play:${scope}] ${message}${tail}`);\n}\n","/**\n * LangChain adapter: derives tool names and assist metadata from a LangChain agent for\n * {@link import(\"../public-types.js\").LangChainAgentRegistration}.\n *\n * @remarks **Primary export:** {@link langchainRegistration}. Private helpers build error strings and\n * `AssistToolSpec` rows from Zod schemas when available.\n */\nimport { agentPlayDebug } from \"../lib/agent-play-debug.js\";\nimport type { AssistToolSpec, LangChainAgentRegistration } from \"../public-types.js\";\n\n/** Required tool name enforced by the watch UI contract. */\nconst CHAT_TOOL = \"chat_tool\";\n\n/**\n * Error text when the agent has no `tools` array.\n *\n * @remarks **Callers:** {@link langchainRegistration}. **Callees:** none.\n */\nfunction formatMissingAgentToolsError(): string {\n return [\n \"langchainRegistration: expected a LangChain agent with a tools array.\",\n \"\",\n \" Pass the object returned from createAgent({ tools: [...] }) (or equivalent) so tool names are available for the play world.\",\n \" The tools array must include named tools; see the separate message if \\\"chat_tool\\\" or assist_* tools are missing.\",\n ].join(\"\\n\");\n}\n\n/**\n * Error text when `chat_tool` is missing from tool names.\n *\n * @remarks **Callers:** {@link langchainRegistration}. **Callees:** none.\n */\nfunction formatMissingChatToolError(): string {\n return [\n \"langchainRegistration: missing required tool \\\"chat_tool\\\".\",\n \"\",\n \" Add a tool named \\\"chat_tool\\\" to your LangChain agent so the play world can show chat and proximity interactions.\",\n \" Example: tool(() => \\\"…\\\", { name: \\\"chat_tool\\\", description: \\\"…\\\", schema: z.object({ … }) })\",\n \"\",\n \" Tools whose names start with \\\"assist_\\\" are listed as assist actions on the watch UI; give each a Zod object schema so parameters can be shown in the UI.\",\n ].join(\"\\n\");\n}\n\n/**\n * Best-effort parameter shape from a Zod object schema’s `shape()` for UI hints.\n *\n * @remarks **Callers:** {@link describeTool}. **Callees:** none.\n */\nfunction parametersFromSchema(schema: unknown): Record<string, unknown> {\n if (schema === null || typeof schema !== \"object\") {\n return {};\n }\n const z = schema as {\n _def?: { typeName?: string; shape?: () => Record<string, unknown> };\n };\n if (typeof z._def?.shape !== \"function\") {\n return { _note: \"Pass a Zod object schema on each tool for parameter hints in the watch UI.\" };\n }\n const shape = z._def.shape();\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(shape)) {\n out[key] = { field: key };\n }\n return out;\n}\n\n/**\n * Builds an {@link AssistToolSpec} from a LangChain tool descriptor.\n *\n * @remarks **Callers:** {@link langchainRegistration} for `assist_*` tools only. **Callees:** {@link parametersFromSchema}.\n */\nfunction describeTool(t: {\n name: string;\n description?: string;\n schema?: unknown;\n}): AssistToolSpec {\n return {\n name: t.name,\n description:\n typeof t.description === \"string\" && t.description.length > 0\n ? t.description\n : t.name,\n parameters: parametersFromSchema(t.schema),\n };\n}\n\n/**\n * Reads `agent.tools` or `agent.options.tools` from common LangChain agent shapes.\n *\n * @returns The tools array, or `null` if not found.\n *\n * @remarks **Callers:** {@link langchainRegistration} only. **Callees:** none.\n */\nfunction extractToolsArray(agent: unknown): unknown[] | null {\n if (typeof agent !== \"object\" || agent === null) {\n return null;\n }\n const a = agent as {\n tools?: unknown;\n options?: { tools?: unknown };\n };\n if (Array.isArray(a.tools)) {\n return a.tools;\n }\n if (\n a.options !== undefined &&\n typeof a.options === \"object\" &&\n a.options !== null &&\n \"tools\" in a.options &&\n Array.isArray((a.options as { tools: unknown }).tools)\n ) {\n return (a.options as { tools: unknown[] }).tools;\n }\n return null;\n}\n\n/**\n * Validates a LangChain-style agent exposes tools (including required `chat_tool`) and returns\n * a {@link LangChainAgentRegistration} for `addPlayer`.\n *\n * @param agent - Return value from `createAgent` (or equivalent) with a `tools` array.\n * @throws Error if tools are missing or `chat_tool` is not present.\n *\n * @remarks **Callers:** user code before `RemotePlayWorld.addPlayer`. **Callees:** {@link extractToolsArray},\n * {@link formatMissingAgentToolsError}, {@link formatMissingChatToolError}, {@link describeTool}, {@link agentPlayDebug}.\n */\nexport function langchainRegistration(\n agent: unknown\n): LangChainAgentRegistration {\n const rawTools = extractToolsArray(agent);\n if (rawTools === null) {\n throw new Error(formatMissingAgentToolsError());\n }\n const tools =\n rawTools as readonly { name: string; description?: string; schema?: unknown }[];\n const names = tools.map((x) => x.name);\n if (!names.includes(CHAT_TOOL)) {\n throw new Error(formatMissingChatToolError());\n }\n const assistTools = tools\n .filter((t) => t.name.startsWith(\"assist_\"))\n .map((t) => describeTool(t));\n agentPlayDebug(\"langchain\", \"langchainRegistration\", {\n toolCount: names.length,\n assistCount: assistTools.length,\n });\n return {\n type: \"langchain\",\n toolNames: names,\n assistTools,\n };\n}\n","import type {\n AgentPlayWorldMapAgentOccupant,\n AgentPlayWorldMapMcpOccupant,\n} from \"../public-types.js\";\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 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","export const PLAYER_CHAIN_GENESIS_STABLE_KEY = \"__genesis__\" as const;\nexport const PLAYER_CHAIN_HEADER_STABLE_KEY = \"__header__\" as const;\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 AgentPlayWorldMapAgentOccupant,\n AgentPlayWorldMapMcpOccupant,\n PlayerChainFanoutNotify,\n PlayerChainNotifyNodeRef,\n PlayerChainNodeResponse,\n} from \"../public-types.js\";\nimport {\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: AgentPlayWorldMapAgentOccupant | AgentPlayWorldMapMcpOccupant\n): string {\n if (occ.kind === \"agent\") {\n return `agent:${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 (!isRecord(occ) || (occ.kind !== \"agent\" && occ.kind !== \"mcp\")) {\n throw new Error(\"getPlayerChainNode: invalid occupant payload\");\n }\n const occupant =\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) {\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 const key = stableOccupantSortKey(node.occupant);\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, node.occupant],\n },\n };\n}\n","import type {\n AddPlayerInput,\n AgentPlaySnapshot,\n AgentPlayWorldMap,\n AgentPlayWorldMapBounds,\n Journey,\n RecordInteractionInput,\n RegisteredAgentSummary,\n RegisteredPlayer,\n PlayerChainNodeResponse,\n} from \"../public-types.js\";\nimport {\n parseAgentOccupantRow,\n parseMcpOccupantRow,\n} from \"./parse-occupant-row.js\";\nimport {\n mergeSnapshotWithPlayerChainNode,\n parsePlayerChainFanoutNotifyFromSsePayload,\n parsePlayerChainNodeRpcBody,\n sortNodeRefsForSerializedFetch,\n} from \"./player-chain-merge.js\";\n\nexport type RemotePlayWorldOptions = {\n baseUrl: string;\n apiKey: string;\n authToken?: string;\n};\n\nfunction formatMissingApiKeyError(): string {\n return [\n \"RemotePlayWorld: options.apiKey is required.\",\n \"\",\n \" Register an agent with `agent-play create` (after `agent-play login`) and use the printed API key.\",\n \" Pass it here so addPlayer can authenticate against the server repository when Redis is enabled.\",\n \" If the server has no agent repository (local dev), still pass a non-empty placeholder string.\",\n ].join(\"\\n\");\n}\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\nfunction parseBounds(raw: unknown): AgentPlayWorldMapBounds {\n if (!isRecord(raw)) {\n throw new Error(\"getWorldSnapshot: worldMap.bounds must be an object\");\n }\n const { minX, minY, maxX, maxY } = raw;\n if (\n typeof minX !== \"number\" ||\n typeof minY !== \"number\" ||\n typeof maxX !== \"number\" ||\n typeof maxY !== \"number\"\n ) {\n throw new Error(\n \"getWorldSnapshot: bounds need numeric minX, minY, maxX, maxY\"\n );\n }\n return { minX, minY, maxX, maxY };\n}\n\nfunction parseWorldMap(raw: unknown): AgentPlayWorldMap {\n if (!isRecord(raw)) {\n throw new Error(\"getWorldSnapshot: worldMap must be an object\");\n }\n const bounds = parseBounds(raw.bounds);\n const occ = raw.occupants;\n if (!Array.isArray(occ)) {\n throw new Error(\"getWorldSnapshot: worldMap.occupants must be an array\");\n }\n const occupants: AgentPlayWorldMap[\"occupants\"] = [];\n const coordKeys = new Set<string>();\n for (const row of occ) {\n if (!isRecord(row) || (row.kind !== \"agent\" && row.kind !== \"mcp\")) {\n throw new Error(\"getWorldSnapshot: each occupant must have kind agent or mcp\");\n }\n const xy =\n typeof row.x === \"number\" && typeof row.y === \"number\"\n ? `${row.x},${row.y}`\n : \"\";\n if (xy.length === 0) {\n throw new Error(\"getWorldSnapshot: occupant missing coordinates\");\n }\n if (coordKeys.has(xy)) {\n throw new Error(\"getWorldSnapshot: duplicate world map coordinate\");\n }\n coordKeys.add(xy);\n if (row.kind === \"agent\") {\n occupants.push(parseAgentOccupantRow(row));\n } else {\n occupants.push(parseMcpOccupantRow(row));\n }\n }\n return { bounds, occupants };\n}\n\nfunction parseAgentPlaySnapshot(snapshot: unknown): AgentPlaySnapshot {\n if (!isRecord(snapshot) || typeof snapshot.sid !== \"string\") {\n throw new Error(\"getWorldSnapshot: invalid snapshot\");\n }\n const worldMap = parseWorldMap(snapshot.worldMap);\n const out: AgentPlaySnapshot = { sid: snapshot.sid, worldMap };\n if (\"mcpServers\" in snapshot && Array.isArray(snapshot.mcpServers)) {\n const servers: NonNullable<AgentPlaySnapshot[\"mcpServers\"]> = [];\n for (const m of snapshot.mcpServers) {\n if (!isRecord(m) || typeof m.id !== \"string\" || typeof m.name !== \"string\") {\n continue;\n }\n const row: { id: string; name: string; url?: string } = {\n id: m.id,\n name: m.name,\n };\n if (typeof m.url === \"string\") row.url = m.url;\n servers.push(row);\n }\n if (servers.length > 0) out.mcpServers = servers;\n }\n return out;\n}\n\nfunction parseRegisteredAgentSummary(raw: unknown): RegisteredAgentSummary {\n if (!isRecord(raw)) {\n throw new Error(\"addPlayer: registeredAgent missing\");\n }\n if (typeof raw.agentId !== \"string\" || typeof raw.name !== \"string\") {\n throw new Error(\"addPlayer: registeredAgent.agentId and name required\");\n }\n if (!Array.isArray(raw.toolNames)) {\n throw new Error(\"addPlayer: registeredAgent.toolNames must be an array\");\n }\n const toolNames: string[] = [];\n for (const t of raw.toolNames) {\n if (typeof t !== \"string\") {\n throw new Error(\"addPlayer: registeredAgent.toolNames must be strings\");\n }\n toolNames.push(t);\n }\n if (\n typeof raw.zoneCount !== \"number\" ||\n typeof raw.yieldCount !== \"number\" ||\n typeof raw.flagged !== \"boolean\"\n ) {\n throw new Error(\"addPlayer: registeredAgent counters invalid\");\n }\n return {\n agentId: raw.agentId,\n name: raw.name,\n toolNames,\n zoneCount: raw.zoneCount,\n yieldCount: raw.yieldCount,\n flagged: raw.flagged,\n };\n}\n\nfunction formatInvalidHoldSecondsError(): string {\n return [\n \"RemotePlayWorld.hold().for(seconds): seconds must be a finite number.\",\n \"\",\n \" Example: await world.hold().for(3600)\",\n ].join(\"\\n\");\n}\n\nexport type RemotePlayWorldHold = {\n for: (seconds: number) => Promise<void>;\n};\n\n/**\n * HTTP client for the Agent Play web UI: session, snapshot RPC, mutating RPC with `sid`, and optional SSE subscription.\n *\n * Incremental updates: {@link RemotePlayWorld.subscribeWorldState} listens for **`playerChainNotify`** in SSE `data`, then fetches each changed leaf via {@link RemotePlayWorld.getPlayerChainNode} and merges with {@link mergeSnapshotWithPlayerChainNode}.\n */\nexport class RemotePlayWorld {\n private readonly apiBase: string;\n private readonly apiKey: string;\n private readonly authToken: string | undefined;\n private sid: string | null = null;\n private closed = false;\n private readonly closeListeners = new Set<() => void>();\n\n constructor(options: RemotePlayWorldOptions) {\n if (typeof options.apiKey !== \"string\" || options.apiKey.trim().length === 0) {\n throw new Error(formatMissingApiKeyError());\n }\n this.apiBase = normalizeBaseUrl(options.baseUrl);\n this.apiKey = options.apiKey.trim();\n this.authToken = options.authToken;\n }\n\n onClose(handler: () => void): () => void {\n this.closeListeners.add(handler);\n return () => {\n this.closeListeners.delete(handler);\n };\n }\n\n hold(): RemotePlayWorldHold {\n return {\n for: async (seconds: number) => {\n if (typeof seconds !== \"number\" || !Number.isFinite(seconds)) {\n throw new Error(formatInvalidHoldSecondsError());\n }\n const ms = Math.max(0, seconds) * 1000;\n await new Promise<void>((resolve) => {\n setTimeout(resolve, ms);\n });\n },\n };\n }\n\n private authHeaders(): Record<string, string> {\n if (this.authToken === undefined) return {};\n return { Authorization: `Bearer ${this.authToken}` };\n }\n\n private jsonHeaders(): Record<string, string> {\n return {\n \"content-type\": \"application/json\",\n ...this.authHeaders(),\n };\n }\n\n private mergeAuthFetch(\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> {\n const headers = new Headers(init?.headers);\n const auth = this.authHeaders();\n for (const [k, v] of Object.entries(auth)) {\n headers.set(k, v);\n }\n return fetch(input, { ...init, headers });\n }\n\n async connect(): Promise<void> {\n const res = await fetch(`${this.apiBase}/api/agent-play/session`, {\n headers: this.authHeaders(),\n });\n if (!res.ok) {\n throw new Error(`session failed: ${res.status}`);\n }\n const json: unknown = await res.json();\n if (!isRecord(json) || typeof json.sid !== \"string\" || json.sid.length === 0) {\n throw new Error(\"session: invalid response\");\n }\n this.sid = json.sid;\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n for (const handler of Array.from(this.closeListeners)) {\n try {\n handler();\n } catch {\n // ignore listener errors\n }\n }\n }\n\n getSessionId(): string {\n if (this.sid === null) {\n throw new Error(\"RemotePlayWorld.connect() must be called first\");\n }\n return this.sid;\n }\n\n getPreviewUrl(): string {\n const u = new URL(\"/agent-play/watch\", this.apiBase);\n u.search = \"\";\n return u.toString();\n }\n\n async getWorldSnapshot(): Promise<AgentPlaySnapshot> {\n const res = await fetch(`${this.apiBase}/api/agent-play/sdk/rpc`, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({ op: \"getWorldSnapshot\", payload: {} }),\n });\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`getWorldSnapshot: ${res.status} ${text}`);\n }\n let json: unknown;\n try {\n json = JSON.parse(text) as unknown;\n } catch {\n throw new Error(\"getWorldSnapshot: invalid JSON\");\n }\n if (!isRecord(json) || !(\"snapshot\" in json)) {\n throw new Error(\"getWorldSnapshot: invalid response shape\");\n }\n return parseAgentPlaySnapshot(json.snapshot);\n }\n\n /**\n * Fetches one player-chain node (genesis, header, occupant row, or removal) for `stableKey`, same snapshot scope as {@link RemotePlayWorld.getWorldSnapshot}.\n */\n async getPlayerChainNode(stableKey: string): Promise<PlayerChainNodeResponse> {\n const trimmed = stableKey.trim();\n if (trimmed.length === 0) {\n throw new Error(\"getPlayerChainNode: stableKey is required\");\n }\n const res = await fetch(`${this.apiBase}/api/agent-play/sdk/rpc`, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({\n op: \"getPlayerChainNode\",\n payload: { stableKey: trimmed },\n }),\n });\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`getPlayerChainNode: ${res.status} ${text}`);\n }\n let json: unknown;\n try {\n json = JSON.parse(text) as unknown;\n } catch {\n throw new Error(\"getPlayerChainNode: invalid JSON\");\n }\n return parsePlayerChainNodeRpcBody(json);\n }\n\n /**\n * Opens the session SSE stream, emits an initial snapshot from {@link RemotePlayWorld.getWorldSnapshot}, then on each **`playerChainNotify`** merges nodes in deterministic order via {@link RemotePlayWorld.getPlayerChainNode}.\n */\n subscribeWorldState(callbacks: {\n onSnapshot: (snapshot: AgentPlaySnapshot) => void;\n onError?: (err: Error) => void;\n }): { close: () => void } {\n let closeSource: (() => void) | null = null;\n const task = (async () => {\n try {\n const { createEventSource } = await import(\"eventsource-client\");\n let snapshot = await this.getWorldSnapshot();\n callbacks.onSnapshot(snapshot);\n const source = createEventSource({\n url: `${this.apiBase}/api/agent-play/events?sid=${encodeURIComponent(\n this.getSessionId()\n )}`,\n fetch: (input, init) => this.mergeAuthFetch(input, init),\n });\n closeSource = () => {\n source.close();\n };\n for await (const msg of source) {\n if (typeof msg.data !== \"string\") {\n continue;\n }\n let data: unknown;\n try {\n data = JSON.parse(msg.data) as unknown;\n } catch {\n continue;\n }\n const notify = parsePlayerChainFanoutNotifyFromSsePayload(data);\n if (notify === undefined || notify.nodes.length === 0) {\n continue;\n }\n const ordered = sortNodeRefsForSerializedFetch(notify.nodes);\n for (const ref of ordered) {\n const node = await this.getPlayerChainNode(ref.stableKey);\n snapshot = mergeSnapshotWithPlayerChainNode(snapshot, node);\n }\n callbacks.onSnapshot(snapshot);\n }\n } catch (e) {\n callbacks.onError?.(\n e instanceof Error ? e : new Error(String(e))\n );\n }\n })();\n return {\n close: () => {\n closeSource?.();\n void task;\n },\n };\n }\n\n async addPlayer(input: AddPlayerInput): Promise<RegisteredPlayer> {\n const sid = this.getSessionId();\n const url = `${this.apiBase}/api/agent-play/players?sid=${encodeURIComponent(sid)}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({\n name: input.name,\n type: input.type,\n agent: input.agent,\n apiKey: this.apiKey,\n agentId: input.agentId,\n }),\n });\n const bodyText = await res.text();\n if (!res.ok) {\n throw new Error(`addPlayer: ${res.status} ${bodyText}`);\n }\n let body: unknown;\n try {\n body = JSON.parse(bodyText) as unknown;\n } catch {\n throw new Error(\"addPlayer: invalid JSON\");\n }\n if (!isRecord(body)) {\n throw new Error(\"addPlayer: invalid response shape\");\n }\n const playerId = body.playerId;\n const previewUrl = body.previewUrl;\n if (typeof playerId !== \"string\" || typeof previewUrl !== \"string\") {\n throw new Error(\"addPlayer: missing playerId or previewUrl\");\n }\n const registeredAgent = parseRegisteredAgentSummary(body.registeredAgent);\n const now = new Date();\n return {\n id: playerId,\n name: input.name,\n sid,\n createdAt: now,\n updatedAt: now,\n previewUrl,\n registeredAgent,\n };\n }\n\n async recordInteraction(input: RecordInteractionInput): Promise<void> {\n await this.rpc(\"recordInteraction\", {\n playerId: input.playerId,\n role: input.role,\n text: input.text,\n });\n }\n\n async recordJourney(playerId: string, journey: Journey): Promise<void> {\n await this.rpc(\"recordJourney\", { playerId, journey });\n }\n\n async registerMcp(options: { name: string; url?: string }): Promise<string> {\n const sid = this.getSessionId();\n const url = `${this.apiBase}/api/agent-play/mcp/register?sid=${encodeURIComponent(sid)}`;\n const body: { name: string; url?: string } = { name: options.name };\n if (options.url !== undefined) {\n body.url = options.url;\n }\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify(body),\n });\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`registerMcp: ${res.status} ${text}`);\n }\n let json: unknown;\n try {\n json = JSON.parse(text) as unknown;\n } catch {\n throw new Error(\"registerMcp: invalid JSON\");\n }\n if (!isRecord(json) || typeof json.id !== \"string\") {\n throw new Error(\"registerMcp: invalid response\");\n }\n return json.id;\n }\n\n private async rpc(op: string, payload: unknown): Promise<void> {\n const sid = this.getSessionId();\n const url = `${this.apiBase}/api/agent-play/sdk/rpc?sid=${encodeURIComponent(sid)}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({ op, payload }),\n });\n if (!res.ok) {\n const t = await res.text();\n throw new Error(`rpc ${op}: ${res.status} ${t}`);\n }\n }\n}\n"],"mappings":"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","names":["isRecord"]}
|
|
@@ -66,16 +66,16 @@ async function main() {
|
|
|
66
66
|
world.onClose(() => {
|
|
67
67
|
console.log("RemotePlayWorld closed.");
|
|
68
68
|
});
|
|
69
|
-
await world.
|
|
69
|
+
await world.connect();
|
|
70
|
+
|
|
71
|
+
const agentId =
|
|
72
|
+
process.env.AGENT_PLAY_AGENT_ID?.trim() ?? "example-local-agent-1";
|
|
70
73
|
|
|
71
74
|
const player = await world.addPlayer({
|
|
72
75
|
name: "remote-demo",
|
|
73
76
|
type: "langchain",
|
|
74
77
|
agent: langchainRegistration(agent),
|
|
75
|
-
|
|
76
|
-
process.env.AGENT_PLAY_AGENT_ID.length > 0
|
|
77
|
-
? { agentId: process.env.AGENT_PLAY_AGENT_ID }
|
|
78
|
-
: {}),
|
|
78
|
+
agentId,
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
console.log("Open the watch UI (session is server-side; UI resolves session via API):");
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Same architecture as example 01: **RemotePlayWorld** talks to **@agent-play/web-ui** over HTTP
|
|
5
5
|
* (`/api/agent-play/session`, `/api/agent-play/players`, `/api/agent-play/sdk/rpc`). One session
|
|
6
|
-
* (`sid`) holds multiple players; each `addPlayer`
|
|
7
|
-
*
|
|
6
|
+
* (`sid`) holds multiple players; each `addPlayer` uses a required **`agentId`** (from **`agent-play create`** when
|
|
7
|
+
* using a repository, or the example defaults locally).
|
|
8
8
|
*
|
|
9
9
|
* Open the printed preview URL once: both avatars share the same world and session.
|
|
10
10
|
*
|
|
@@ -84,25 +84,24 @@ async function main() {
|
|
|
84
84
|
const holdSeconds = Number(process.env.AGENT_PLAY_HOLD_SECONDS ?? 3600);
|
|
85
85
|
|
|
86
86
|
const world = new RemotePlayWorld({ baseUrl: base, apiKey });
|
|
87
|
-
await world.
|
|
87
|
+
await world.connect();
|
|
88
|
+
|
|
89
|
+
const agentIdA =
|
|
90
|
+
process.env.AGENT_PLAY_AGENT_ID_ALPHA?.trim() ?? "example-local-agent-alpha";
|
|
91
|
+
const agentIdB =
|
|
92
|
+
process.env.AGENT_PLAY_AGENT_ID_BETA?.trim() ?? "example-local-agent-beta";
|
|
88
93
|
|
|
89
94
|
const playerA = await world.addPlayer({
|
|
90
95
|
name: "alpha",
|
|
91
96
|
type: "langchain",
|
|
92
97
|
agent: langchainRegistration(agentAlpha),
|
|
93
|
-
|
|
94
|
-
process.env.AGENT_PLAY_AGENT_ID_ALPHA.length > 0
|
|
95
|
-
? { agentId: process.env.AGENT_PLAY_AGENT_ID_ALPHA }
|
|
96
|
-
: {}),
|
|
98
|
+
agentId: agentIdA,
|
|
97
99
|
});
|
|
98
100
|
const playerB = await world.addPlayer({
|
|
99
101
|
name: "beta",
|
|
100
102
|
type: "langchain",
|
|
101
103
|
agent: langchainRegistration(agentBeta),
|
|
102
|
-
|
|
103
|
-
process.env.AGENT_PLAY_AGENT_ID_BETA.length > 0
|
|
104
|
-
? { agentId: process.env.AGENT_PLAY_AGENT_ID_BETA }
|
|
105
|
-
: {}),
|
|
104
|
+
agentId: agentIdB,
|
|
106
105
|
});
|
|
107
106
|
|
|
108
107
|
console.log("Session id:", world.getSessionId());
|
package/examples/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Run the **web UI** first so APIs exist:
|
|
|
9
9
|
npm run dev -w @agent-play/web-ui
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
With a **registered-agent** repository (**`REDIS_URL`** on the server): run **`agent-play login`**, **`agent-play create-key`** (once per account), **`agent-play create`** for each agent
|
|
12
|
+
With a **registered-agent** repository (**`REDIS_URL`** on the server): run **`agent-play login`**, **`agent-play create-key`** (once per account), **`agent-play create`** for each agent. Set **`AGENT_PLAY_API_KEY`** and pass **`AGENT_PLAY_AGENT_ID`** (and **`AGENT_PLAY_AGENT_ID_ALPHA`** / **`AGENT_PLAY_AGENT_ID_BETA`** for example 02) so **`addPlayer`** uses real ids from **`agent-play create`**. Without Redis, the examples default to stable local **`agentId`** strings.
|
|
13
13
|
|
|
14
14
|
| Order | File | Purpose |
|
|
15
15
|
|------:|------|---------|
|
|
@@ -45,7 +45,7 @@ npx tsx -r dotenv/config examples/02-remote-two-players-langchain.ts
|
|
|
45
45
|
|
|
46
46
|
- `GET /api/agent-play/session` — Creates or resumes a session (`sid`).
|
|
47
47
|
- `POST /api/agent-play/players` — Registers a player; response includes a **preview URL** for `/agent-play/watch`.
|
|
48
|
-
- `POST /api/agent-play/sdk/rpc` —
|
|
48
|
+
- `POST /api/agent-play/sdk/rpc` — `getWorldSnapshot` and `getPlayerChainNode` (no `sid` query); `recordInteraction`, `recordJourney` (with `sid` for mutating ops). **`RemotePlayWorld.subscribeWorldState`** uses **`getPlayerChainNode`** when SSE **`data`** includes **`playerChainNotify`**.
|
|
49
49
|
- Watch UI loads snapshot via RPC + SSE (`/api/agent-play/...`) for live world state across instances when Redis is enabled.
|
|
50
50
|
|
|
51
51
|
Assist actions on the watch UI call `POST /api/agent-play/assist-tool` when **`assist_*`** tools were registered via **`langchainRegistration`**.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-play/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.1",
|
|
5
5
|
"description": "Node.js SDK to register agents, stream world state, and connect to the Agent Play web UI over HTTP.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|