@agent-play/sdk 3.3.7 → 3.3.8

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.
@@ -41,7 +41,7 @@ type AssistToolSpec = {
41
41
  * Serializable shape returned by {@link langchainRegistration} for agent registration.
42
42
  *
43
43
  * @property type - Always `"langchain"` for this adapter.
44
- * @property toolNames - All tool names from the agent (must include `chat_tool`).
44
+ * @property toolNames - All tool names from the agent (must include `chat_tool`). Used for assist/chat UI only; **does not** spawn map structures (see world map v3).
45
45
  * @property assistTools - Subset of tools with `assist_` prefix, for UI buttons.
46
46
  */
47
47
  type LangChainAgentRegistration = {
@@ -106,6 +106,12 @@ type AddAgentInput = PlatformAgentInformation & {
106
106
  mainNodeId?: string;
107
107
  /** Agent node id — same value the server stores as registered `agentId`. */
108
108
  nodeId: string;
109
+ /**
110
+ * Human passphrase for this agent node (from **`agent-play create-agent-node`** or
111
+ * **`AGENT_PLAY_AGENT_NODE_ID_*_*_PASSW`**). When omitted, the main node passphrase from
112
+ * {@link RemotePlayWorld} construction is used (local dev only).
113
+ */
114
+ agentPassphrase?: string;
109
115
  /**
110
116
  * When **`"on"`**, registration enables OpenAI Realtime provisioning for this agent.
111
117
  * Omitted or **`"off"`** disables realtime voice for this registration.
package/dist/browser.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { A as AgentPlaySnapshot, e as AgentPlayWorldLayout, C as CONTINUOUS_RENDER_OFFSET, n as CarWashCar, o as CarWashCarSchema, p as ComputeTalkAgentPowerUpsEarnedInput, D as DEFAULT_AGENT_SPAWN_MIN_DISTANCE, q as DEFAULT_PLAYER_WALLET_BALANCE_USD, M as MINIMUM_PLAY_WORLD_BOUNDS, t as MINIMUM_STREET_LAYOUT_BOUNDS, O as OCCUPANCY_POINT_MULTIPLIER, u as OccupancyGridPoint, v as OccupantGroup, y as PLAYER_CHAIN_GENESIS_STABLE_KEY, z as PLAYER_CHAIN_HEADER_STABLE_KEY, Q as PlayerWallet, S as PlayerWalletSchema, U as PurchaseRecord, V as PurchaseRecordSchema, _ as SPACE_STRUCTURE_ANCHOR_MIN_DISTANCE, $ as SPATIAL_ZONE_INDEX_AGENTS, a0 as SPATIAL_ZONE_INDEX_SPACES, a1 as STREET_NAME_POOL, a2 as SaleState, a3 as SaleStateSchema, a4 as ShopItem, a5 as ShopItemSchema, a6 as SpaceContentItem, a7 as Street, a8 as StreetPoolEntry, aa as SupermarketItem, ab as SupermarketItemSchema, ac as TALK_AGENT_PU_BILLED_SECONDS_PER_UNIT, ad as TALK_AGENT_PU_MAX_PER_LEG, ae as TALK_PRICE_PER_60S_USD, af as TALK_PRICE_PER_SECOND_USD, ag as TALK_TICK_SECONDS, ah as WALLET_BUNDLE_OFFERS, ai as WalletBundleId, aj as WalletBundleOffer, ak as WorldBounds, am as WorldLayout, an as WorldLayoutBoundsField, ap as Zone, ar as applyBoundsFieldUpdateToLayout, as as availableCellsForZone, at as boundingWorldRectForOccupancyPoints, au as boundsContain, av as buildRankedOccupancyPoints, aw as buildRankedOccupancyPointsForSpatialZone, ax as buildRankedOccupancyPointsForZone, ay as buildRankedOccupancyPointsInRect, az as cellsForZone, aA as centerOfZone, aB as clampWorldPosition, aC as computeTalkAgentPowerUpsEarned, aD as costForSeconds, aE as createInitialAgentRewardWallet, aF as createInitialPlayerWallet, aG as createVerticalStripSeedLayout, aH as desaturateColor, aI as enumerateIntegerCellsInRect, aJ as expandBoundsToMinimumPlayArea, aK as getStreetPoolEntryById, aL as getWalletBundleById, aM as isAgentSpawnOccupancyPointAvailable, aN as isAgentSpawnOccupancyPointAvailableInRect, aO as isAgentSpawnOccupancyPointAvailableInZone, aP as isItemAvailableForPurchase, aQ as isSpaceAnchorOccupancyPointAvailable, aR as isSpaceAnchorOccupancyPointAvailableInRect, aS as isSpaceAnchorOccupancyPointAvailableInZone, aT as listAllowedOccupancyPoints, aU as listOccupancyPointsForSpatialZone, aV as listOccupancyPointsForZone, aW as listOccupancyPointsInRect, aX as mergeSnapshotWithPlayerChainNode, aY as migrateWorldLayoutBounds, aZ as nextStreetFromPool, a_ as occupancyKeyForPosition, a$ as occupancyPointsGroupedBySpatialZone, b0 as occupancyPointsGroupedByZones, b1 as parsePlayerChainFanoutNotify, b2 as parsePlayerChainFanoutNotifyFromSsePayload, b3 as parsePlayerChainNodeRpcBody, b4 as pickZoneForGroup, b5 as pointCellInRect, b6 as pointCellInSpatialZone, b7 as pointCellInZone, b8 as primaryZoneForGroup, b9 as sortNodeRefsForSerializedFetch, ba as spatialZoneBounds, bb as spatialZoneCenter, bc as streetFromPoolEntry, bd as zonesForGroup } from './browser-da9E-kSP.js';
1
+ export { A as AgentPlaySnapshot, e as AgentPlayWorldLayout, C as CONTINUOUS_RENDER_OFFSET, n as CarWashCar, o as CarWashCarSchema, p as ComputeTalkAgentPowerUpsEarnedInput, D as DEFAULT_AGENT_SPAWN_MIN_DISTANCE, q as DEFAULT_PLAYER_WALLET_BALANCE_USD, M as MINIMUM_PLAY_WORLD_BOUNDS, t as MINIMUM_STREET_LAYOUT_BOUNDS, O as OCCUPANCY_POINT_MULTIPLIER, u as OccupancyGridPoint, v as OccupantGroup, y as PLAYER_CHAIN_GENESIS_STABLE_KEY, z as PLAYER_CHAIN_HEADER_STABLE_KEY, Q as PlayerWallet, S as PlayerWalletSchema, U as PurchaseRecord, V as PurchaseRecordSchema, _ as SPACE_STRUCTURE_ANCHOR_MIN_DISTANCE, $ as SPATIAL_ZONE_INDEX_AGENTS, a0 as SPATIAL_ZONE_INDEX_SPACES, a1 as STREET_NAME_POOL, a2 as SaleState, a3 as SaleStateSchema, a4 as ShopItem, a5 as ShopItemSchema, a6 as SpaceContentItem, a7 as Street, a8 as StreetPoolEntry, aa as SupermarketItem, ab as SupermarketItemSchema, ac as TALK_AGENT_PU_BILLED_SECONDS_PER_UNIT, ad as TALK_AGENT_PU_MAX_PER_LEG, ae as TALK_PRICE_PER_60S_USD, af as TALK_PRICE_PER_SECOND_USD, ag as TALK_TICK_SECONDS, ah as WALLET_BUNDLE_OFFERS, ai as WalletBundleId, aj as WalletBundleOffer, ak as WorldBounds, am as WorldLayout, an as WorldLayoutBoundsField, ap as Zone, ar as applyBoundsFieldUpdateToLayout, as as availableCellsForZone, at as boundingWorldRectForOccupancyPoints, au as boundsContain, av as buildRankedOccupancyPoints, aw as buildRankedOccupancyPointsForSpatialZone, ax as buildRankedOccupancyPointsForZone, ay as buildRankedOccupancyPointsInRect, az as cellsForZone, aA as centerOfZone, aB as clampWorldPosition, aC as computeTalkAgentPowerUpsEarned, aD as costForSeconds, aE as createInitialAgentRewardWallet, aF as createInitialPlayerWallet, aG as createVerticalStripSeedLayout, aH as desaturateColor, aI as enumerateIntegerCellsInRect, aJ as expandBoundsToMinimumPlayArea, aK as getStreetPoolEntryById, aL as getWalletBundleById, aM as isAgentSpawnOccupancyPointAvailable, aN as isAgentSpawnOccupancyPointAvailableInRect, aO as isAgentSpawnOccupancyPointAvailableInZone, aP as isItemAvailableForPurchase, aQ as isSpaceAnchorOccupancyPointAvailable, aR as isSpaceAnchorOccupancyPointAvailableInRect, aS as isSpaceAnchorOccupancyPointAvailableInZone, aT as listAllowedOccupancyPoints, aU as listOccupancyPointsForSpatialZone, aV as listOccupancyPointsForZone, aW as listOccupancyPointsInRect, aX as mergeSnapshotWithPlayerChainNode, aY as migrateWorldLayoutBounds, aZ as nextStreetFromPool, a_ as occupancyKeyForPosition, a$ as occupancyPointsGroupedBySpatialZone, b0 as occupancyPointsGroupedByZones, b1 as parsePlayerChainFanoutNotify, b2 as parsePlayerChainFanoutNotifyFromSsePayload, b3 as parsePlayerChainNodeRpcBody, b4 as pickZoneForGroup, b5 as pointCellInRect, b6 as pointCellInSpatialZone, b7 as pointCellInZone, b8 as primaryZoneForGroup, b9 as sortNodeRefsForSerializedFetch, ba as spatialZoneBounds, bb as spatialZoneCenter, bc as streetFromPoolEntry, bd as zonesForGroup } from './browser-Djc4apJO.js';
2
2
  import 'zod';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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-da9E-kSP.js';
2
- export { e as AgentPlayWorldLayout, f as AgentPlayWorldLayoutZone, g as AgentPlayWorldMap, h as AgentPlayWorldMapAgentOccupant, i as AgentPlayWorldMapBounds, j as AgentPlayWorldMapMcpOccupant, k as AssistToolFieldType, l as AssistToolParameterSpec, m as AssistToolSpec, C as CONTINUOUS_RENDER_OFFSET, n as CarWashCar, o as CarWashCarSchema, p as ComputeTalkAgentPowerUpsEarnedInput, D as DEFAULT_AGENT_SPAWN_MIN_DISTANCE, q as DEFAULT_PLAYER_WALLET_BALANCE_USD, r as DestinationJourneyStep, s as JourneyStep, M as MINIMUM_PLAY_WORLD_BOUNDS, t as MINIMUM_STREET_LAYOUT_BOUNDS, O as OCCUPANCY_POINT_MULTIPLIER, u as OccupancyGridPoint, v as OccupantGroup, w as OriginJourneyStep, x as P2aEnableFlag, y as PLAYER_CHAIN_GENESIS_STABLE_KEY, z as PLAYER_CHAIN_HEADER_STABLE_KEY, B as PlatformAgentInformation, E as PlayAgentInformation, F as PlayerChainFanoutNotify, G as PlayerChainGenesisNode, H as PlayerChainHeaderNode, I as PlayerChainNotifyNodeRef, K as PlayerChainOccupantPresentNode, N as PlayerChainOccupantRemovedNode, Q as PlayerWallet, S as PlayerWalletSchema, T as PositionedStep, U as PurchaseRecord, V as PurchaseRecordSchema, X as RealtimeWebrtcClientSecret, Y as RegisteredAgentSummary, Z as RemotePlayWorldOpenAiAudioOptions, _ as SPACE_STRUCTURE_ANCHOR_MIN_DISTANCE, $ as SPATIAL_ZONE_INDEX_AGENTS, a0 as SPATIAL_ZONE_INDEX_SPACES, a1 as STREET_NAME_POOL, a2 as SaleState, a3 as SaleStateSchema, a4 as ShopItem, a5 as ShopItemSchema, a6 as SpaceContentItem, a7 as Street, a8 as StreetPoolEntry, a9 as StructureJourneyStep, aa as SupermarketItem, ab as SupermarketItemSchema, ac as TALK_AGENT_PU_BILLED_SECONDS_PER_UNIT, ad as TALK_AGENT_PU_MAX_PER_LEG, ae as TALK_PRICE_PER_60S_USD, af as TALK_PRICE_PER_SECOND_USD, ag as TALK_TICK_SECONDS, ah as WALLET_BUNDLE_OFFERS, ai as WalletBundleId, aj as WalletBundleOffer, ak as WorldBounds, al as WorldJourneyUpdate, am as WorldLayout, an as WorldLayoutBoundsField, ao as YieldEventInfo, ap as Zone, aq as ZoneEventInfo, ar as applyBoundsFieldUpdateToLayout, as as availableCellsForZone, at as boundingWorldRectForOccupancyPoints, au as boundsContain, av as buildRankedOccupancyPoints, aw as buildRankedOccupancyPointsForSpatialZone, ax as buildRankedOccupancyPointsForZone, ay as buildRankedOccupancyPointsInRect, az as cellsForZone, aA as centerOfZone, aB as clampWorldPosition, aC as computeTalkAgentPowerUpsEarned, aD as costForSeconds, aE as createInitialAgentRewardWallet, aF as createInitialPlayerWallet, aG as createVerticalStripSeedLayout, aH as desaturateColor, aI as enumerateIntegerCellsInRect, aJ as expandBoundsToMinimumPlayArea, aK as getStreetPoolEntryById, aL as getWalletBundleById, aM as isAgentSpawnOccupancyPointAvailable, aN as isAgentSpawnOccupancyPointAvailableInRect, aO as isAgentSpawnOccupancyPointAvailableInZone, aP as isItemAvailableForPurchase, aQ as isSpaceAnchorOccupancyPointAvailable, aR as isSpaceAnchorOccupancyPointAvailableInRect, aS as isSpaceAnchorOccupancyPointAvailableInZone, aT as listAllowedOccupancyPoints, aU as listOccupancyPointsForSpatialZone, aV as listOccupancyPointsForZone, aW as listOccupancyPointsInRect, aX as mergeSnapshotWithPlayerChainNode, aY as migrateWorldLayoutBounds, aZ as nextStreetFromPool, a_ as occupancyKeyForPosition, a$ as occupancyPointsGroupedBySpatialZone, b0 as occupancyPointsGroupedByZones, b1 as parsePlayerChainFanoutNotify, b2 as parsePlayerChainFanoutNotifyFromSsePayload, b3 as parsePlayerChainNodeRpcBody, b4 as pickZoneForGroup, b5 as pointCellInRect, b6 as pointCellInSpatialZone, b7 as pointCellInZone, b8 as primaryZoneForGroup, b9 as sortNodeRefsForSerializedFetch, ba as spatialZoneBounds, bb as spatialZoneCenter, bc as streetFromPoolEntry, bd as zonesForGroup } from './browser-da9E-kSP.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-Djc4apJO.js';
2
+ export { e as AgentPlayWorldLayout, f as AgentPlayWorldLayoutZone, g as AgentPlayWorldMap, h as AgentPlayWorldMapAgentOccupant, i as AgentPlayWorldMapBounds, j as AgentPlayWorldMapMcpOccupant, k as AssistToolFieldType, l as AssistToolParameterSpec, m as AssistToolSpec, C as CONTINUOUS_RENDER_OFFSET, n as CarWashCar, o as CarWashCarSchema, p as ComputeTalkAgentPowerUpsEarnedInput, D as DEFAULT_AGENT_SPAWN_MIN_DISTANCE, q as DEFAULT_PLAYER_WALLET_BALANCE_USD, r as DestinationJourneyStep, s as JourneyStep, M as MINIMUM_PLAY_WORLD_BOUNDS, t as MINIMUM_STREET_LAYOUT_BOUNDS, O as OCCUPANCY_POINT_MULTIPLIER, u as OccupancyGridPoint, v as OccupantGroup, w as OriginJourneyStep, x as P2aEnableFlag, y as PLAYER_CHAIN_GENESIS_STABLE_KEY, z as PLAYER_CHAIN_HEADER_STABLE_KEY, B as PlatformAgentInformation, E as PlayAgentInformation, F as PlayerChainFanoutNotify, G as PlayerChainGenesisNode, H as PlayerChainHeaderNode, I as PlayerChainNotifyNodeRef, K as PlayerChainOccupantPresentNode, N as PlayerChainOccupantRemovedNode, Q as PlayerWallet, S as PlayerWalletSchema, T as PositionedStep, U as PurchaseRecord, V as PurchaseRecordSchema, X as RealtimeWebrtcClientSecret, Y as RegisteredAgentSummary, Z as RemotePlayWorldOpenAiAudioOptions, _ as SPACE_STRUCTURE_ANCHOR_MIN_DISTANCE, $ as SPATIAL_ZONE_INDEX_AGENTS, a0 as SPATIAL_ZONE_INDEX_SPACES, a1 as STREET_NAME_POOL, a2 as SaleState, a3 as SaleStateSchema, a4 as ShopItem, a5 as ShopItemSchema, a6 as SpaceContentItem, a7 as Street, a8 as StreetPoolEntry, a9 as StructureJourneyStep, aa as SupermarketItem, ab as SupermarketItemSchema, ac as TALK_AGENT_PU_BILLED_SECONDS_PER_UNIT, ad as TALK_AGENT_PU_MAX_PER_LEG, ae as TALK_PRICE_PER_60S_USD, af as TALK_PRICE_PER_SECOND_USD, ag as TALK_TICK_SECONDS, ah as WALLET_BUNDLE_OFFERS, ai as WalletBundleId, aj as WalletBundleOffer, ak as WorldBounds, al as WorldJourneyUpdate, am as WorldLayout, an as WorldLayoutBoundsField, ao as YieldEventInfo, ap as Zone, aq as ZoneEventInfo, ar as applyBoundsFieldUpdateToLayout, as as availableCellsForZone, at as boundingWorldRectForOccupancyPoints, au as boundsContain, av as buildRankedOccupancyPoints, aw as buildRankedOccupancyPointsForSpatialZone, ax as buildRankedOccupancyPointsForZone, ay as buildRankedOccupancyPointsInRect, az as cellsForZone, aA as centerOfZone, aB as clampWorldPosition, aC as computeTalkAgentPowerUpsEarned, aD as costForSeconds, aE as createInitialAgentRewardWallet, aF as createInitialPlayerWallet, aG as createVerticalStripSeedLayout, aH as desaturateColor, aI as enumerateIntegerCellsInRect, aJ as expandBoundsToMinimumPlayArea, aK as getStreetPoolEntryById, aL as getWalletBundleById, aM as isAgentSpawnOccupancyPointAvailable, aN as isAgentSpawnOccupancyPointAvailableInRect, aO as isAgentSpawnOccupancyPointAvailableInZone, aP as isItemAvailableForPurchase, aQ as isSpaceAnchorOccupancyPointAvailable, aR as isSpaceAnchorOccupancyPointAvailableInRect, aS as isSpaceAnchorOccupancyPointAvailableInZone, aT as listAllowedOccupancyPoints, aU as listOccupancyPointsForSpatialZone, aV as listOccupancyPointsForZone, aW as listOccupancyPointsInRect, aX as mergeSnapshotWithPlayerChainNode, aY as migrateWorldLayoutBounds, aZ as nextStreetFromPool, a_ as occupancyKeyForPosition, a$ as occupancyPointsGroupedBySpatialZone, b0 as occupancyPointsGroupedByZones, b1 as parsePlayerChainFanoutNotify, b2 as parsePlayerChainFanoutNotifyFromSsePayload, b3 as parsePlayerChainNodeRpcBody, b4 as pickZoneForGroup, b5 as pointCellInRect, b6 as pointCellInSpatialZone, b7 as pointCellInZone, b8 as primaryZoneForGroup, b9 as sortNodeRefsForSerializedFetch, ba as spatialZoneBounds, bb as spatialZoneCenter, bc as streetFromPoolEntry, bd as zonesForGroup } from './browser-Djc4apJO.js';
3
3
  import { WorldIntercomEventPayload, IntercomResponsePayload } from '@agent-play/intercom';
4
4
  export { AgentPlayAgentNodeEntry, AgentPlayCredentialsFile, NodeCredentialMaterial, createNodeCredentialMaterial, loadAgentPlayCredentialsFileFromPath, loadAgentPlayCredentialsFileFromPathSync, loadRootKey, nodeCredentialFromHumanPhrase, nodeCredentialFromPasswHash, nodeCredentialsMaterialFromHumanPassphrase, parseAgentPlayCredentialsJson, resolveAgentPlayCredentialsPath, verifyStoredNodeCredential } from '@agent-play/node-tools';
5
5
  import 'zod';
@@ -126,7 +126,12 @@ type RemotePlayWorldLogging = "off" | "on";
126
126
  type RemotePlayWorldOptions = {
127
127
  baseUrl?: string;
128
128
  /**
129
- * `rootKey` from `.root` and **`passw`** human phrase from **`credentials.json`** (see **`loadAgentPlayCredentialsFileFromPathSync`** in **@agent-play/node-tools**).
129
+ * Main node bootstrap credentials: `rootKey` from `.root` and **`passw`** human phrase for the
130
+ * main node (session connect, snapshot, SSE). Alias: **`nodeCredentials`**.
131
+ */
132
+ mainNodeCredentials?: RemotePlayWorldNodeCredentials;
133
+ /**
134
+ * @deprecated Alias for {@link RemotePlayWorldOptions.mainNodeCredentials}.
130
135
  */
131
136
  nodeCredentials?: RemotePlayWorldNodeCredentials;
132
137
  /** Called for session lifecycle events (`session:connected`, `session:closed`; see `world-events`). */
@@ -182,16 +187,17 @@ type SubscribeIntercomCommandsOptions = ({
182
187
  declare class RemotePlayWorld {
183
188
  private readonly apiBase;
184
189
  private readonly rootKey;
185
- /** Node id derived from hashed passphrase material + root (main or agent node id). */
186
- private readonly derivedNodeId;
187
- /** Hex SHA-256 of the normalized human phrase; sent as `x-node-passw` and as `passwHash`. */
188
- private readonly passwHash;
190
+ /** Main node id derived from main passphrase material (session bootstrap). */
191
+ private readonly mainDerivedNodeId;
192
+ /** Main node passwHash (session bootstrap, snapshot, SSE). */
193
+ private readonly mainPasswHash;
189
194
  private readonly onSessionEvent;
190
195
  private readonly transportLog;
191
196
  private sid;
192
197
  private configuredMainNodeId;
193
198
  private closed;
194
199
  private readonly closeListeners;
200
+ private readonly playerAgentCredentials;
195
201
  private readonly playerConnectionInfo;
196
202
  private audioInitOptions;
197
203
  constructor(options?: RemotePlayWorldOptions);
@@ -200,8 +206,15 @@ declare class RemotePlayWorld {
200
206
  private emitSessionEvent;
201
207
  onClose(handler: () => void): () => void;
202
208
  hold(): RemotePlayWorldHold;
203
- private authHeaders;
209
+ private credentialFromHumanPhrase;
210
+ private resolveAgentCredential;
211
+ private mainAuthHeaders;
212
+ private agentAuthHeaders;
213
+ private agentAuthHeadersForPlayer;
204
214
  private jsonHeaders;
215
+ private mainJsonHeaders;
216
+ private agentJsonHeaders;
217
+ private agentJsonHeadersForPlayer;
205
218
  private mergeAuthFetch;
206
219
  private validateNodeIdentity;
207
220
  /**
package/dist/index.js CHANGED
@@ -137,7 +137,8 @@ function formatMissingAgentToolsError() {
137
137
  return [
138
138
  "langchainRegistration: expected a LangChain agent with a tools array.",
139
139
  "",
140
- " Pass the object returned from createAgent({ tools: [...] }) (or equivalent) so tool names are available for the play world.",
140
+ " Pass the object returned from createAgent({ tools: [...] }) (or equivalent) so tool names are available for assist/chat UI.",
141
+ " Tool names no longer spawn map structures; author spaces separately with an owner (see world map v3 docs).",
141
142
  ' The tools array must include named tools; see the separate message if "chat_tool" or assist_* tools are missing.'
142
143
  ].join("\n");
143
144
  }
@@ -395,7 +396,7 @@ var PLAYER_CONNECTION_HEARTBEAT_MAX_ATTEMPTS = 10;
395
396
  var PLAYER_CONNECTION_HEARTBEAT_RETRY_DELAY_MS = 1e4;
396
397
  function formatMissingCredentialsError() {
397
398
  return [
398
- "RemotePlayWorld: provide nodeCredentials: { rootKey, passw },",
399
+ "RemotePlayWorld: provide mainNodeCredentials: { rootKey, passw },",
399
400
  "or run agent-play create-main-node so ~/.agent-play/credentials.json exists."
400
401
  ].join(" ");
401
402
  }
@@ -606,16 +607,17 @@ function normalizeIntercomSubscribePlayerIds(options) {
606
607
  var RemotePlayWorld = class {
607
608
  apiBase;
608
609
  rootKey;
609
- /** Node id derived from hashed passphrase material + root (main or agent node id). */
610
- derivedNodeId;
611
- /** Hex SHA-256 of the normalized human phrase; sent as `x-node-passw` and as `passwHash`. */
612
- passwHash;
610
+ /** Main node id derived from main passphrase material (session bootstrap). */
611
+ mainDerivedNodeId;
612
+ /** Main node passwHash (session bootstrap, snapshot, SSE). */
613
+ mainPasswHash;
613
614
  onSessionEvent;
614
615
  transportLog;
615
616
  sid = null;
616
617
  configuredMainNodeId = null;
617
618
  closed = false;
618
619
  closeListeners = /* @__PURE__ */ new Set();
620
+ playerAgentCredentials = /* @__PURE__ */ new Map();
619
621
  playerConnectionInfo = /* @__PURE__ */ new Map();
620
622
  audioInitOptions = null;
621
623
  constructor(options = {}) {
@@ -629,15 +631,15 @@ var RemotePlayWorld = class {
629
631
  this.apiBase = normalizeBaseUrl(resolvedBaseUrl);
630
632
  this.onSessionEvent = options.onSessionEvent;
631
633
  this.transportLog = options.logging === "on";
632
- const nc = options.nodeCredentials ?? (creds === null ? void 0 : { rootKey: loadRootKey(), passw: creds.passw });
634
+ const nc = options.mainNodeCredentials ?? options.nodeCredentials ?? (creds === null ? void 0 : { rootKey: loadRootKey(), passw: creds.passw });
633
635
  if (nc !== void 0 && typeof nc.rootKey === "string" && nc.rootKey.trim().length > 0 && typeof nc.passw === "string" && nc.passw.length > 0) {
634
636
  this.rootKey = nc.rootKey.trim().toLowerCase();
635
637
  const credential = nodeCredentialFromHumanPhrase({
636
638
  phrase: nc.passw,
637
639
  rootKey: this.rootKey
638
640
  });
639
- this.passwHash = credential.passwHash;
640
- this.derivedNodeId = credential.nodeId;
641
+ this.mainPasswHash = credential.passwHash;
642
+ this.mainDerivedNodeId = credential.nodeId;
641
643
  return;
642
644
  }
643
645
  throw new Error(formatMissingCredentialsError());
@@ -677,21 +679,58 @@ var RemotePlayWorld = class {
677
679
  }
678
680
  };
679
681
  }
680
- authHeaders() {
682
+ credentialFromHumanPhrase(phrase) {
683
+ const credential = nodeCredentialFromHumanPhrase({
684
+ phrase,
685
+ rootKey: this.rootKey
686
+ });
687
+ return { nodeId: credential.nodeId, passwHash: credential.passwHash };
688
+ }
689
+ resolveAgentCredential(input) {
690
+ const phrase = input.agentPassphrase !== void 0 && input.agentPassphrase.length > 0 ? input.agentPassphrase : void 0;
691
+ if (phrase === void 0) {
692
+ return { nodeId: input.nodeId.trim(), passwHash: this.mainPasswHash };
693
+ }
694
+ const derived = this.credentialFromHumanPhrase(phrase);
695
+ return { nodeId: input.nodeId.trim(), passwHash: derived.passwHash };
696
+ }
697
+ mainAuthHeaders() {
681
698
  return {
682
- "x-node-id": this.derivedNodeId,
683
- "x-node-passw": this.passwHash
699
+ "x-node-id": this.mainDerivedNodeId,
700
+ "x-node-passw": this.mainPasswHash
684
701
  };
685
702
  }
686
- jsonHeaders() {
703
+ agentAuthHeaders(credential) {
704
+ return {
705
+ "x-node-id": credential.nodeId,
706
+ "x-node-passw": credential.passwHash
707
+ };
708
+ }
709
+ agentAuthHeadersForPlayer(playerId) {
710
+ const stored = this.playerAgentCredentials.get(playerId);
711
+ if (stored === void 0) {
712
+ return this.mainAuthHeaders();
713
+ }
714
+ return this.agentAuthHeaders(stored);
715
+ }
716
+ jsonHeaders(auth) {
687
717
  return {
688
718
  "content-type": "application/json",
689
- ...this.authHeaders()
719
+ ...auth
690
720
  };
691
721
  }
722
+ mainJsonHeaders() {
723
+ return this.jsonHeaders(this.mainAuthHeaders());
724
+ }
725
+ agentJsonHeaders(credential) {
726
+ return this.jsonHeaders(this.agentAuthHeaders(credential));
727
+ }
728
+ agentJsonHeadersForPlayer(playerId) {
729
+ return this.jsonHeaders(this.agentAuthHeadersForPlayer(playerId));
730
+ }
692
731
  mergeAuthFetch(input, init) {
693
732
  const headers = new Headers(init?.headers);
694
- const auth = this.authHeaders();
733
+ const auth = this.mainAuthHeaders();
695
734
  for (const [k, v] of Object.entries(auth)) {
696
735
  headers.set(k, v);
697
736
  }
@@ -707,7 +746,7 @@ var RemotePlayWorld = class {
707
746
  }
708
747
  const res = await fetch(`${this.apiBase}/api/nodes/validate`, {
709
748
  method: "POST",
710
- headers: this.jsonHeaders(),
749
+ headers: this.agentJsonHeaders(options.auth),
711
750
  body: JSON.stringify(body)
712
751
  });
713
752
  let json;
@@ -717,13 +756,13 @@ var RemotePlayWorld = class {
717
756
  throw new Error(`node validation failed: ${String(res.status)} invalid JSON`);
718
757
  }
719
758
  if (!isRecord(json) || json.ok !== true) {
720
- const reason = isRecord(json) && typeof json.reason === "string" ? json.reason : `HTTP ${String(res.status)}`;
759
+ const reason = isRecord(json) && typeof json.reason === "string" ? json.reason : isRecord(json) && typeof json.error === "string" ? json.error : `HTTP ${String(res.status)}`;
721
760
  throw new Error(`node validation failed: ${reason}`);
722
761
  }
723
762
  const nodeKind = isRecord(json) && typeof json.nodeKind === "string" ? json.nodeKind : void 0;
724
763
  agentPlayDebug("remote-play-world", "node identity validated", {
725
764
  nodeKind,
726
- derivedNodeIdPrefix: `${this.derivedNodeId.slice(0, 8)}\u2026`
765
+ nodeIdPrefix: `${options.auth.nodeId.slice(0, 8)}\u2026`
727
766
  });
728
767
  return nodeKind !== void 0 ? { nodeKind } : {};
729
768
  }
@@ -734,10 +773,24 @@ var RemotePlayWorld = class {
734
773
  async connect(options) {
735
774
  const mainNodeIdOpt = options?.mainNodeId?.trim();
736
775
  if (mainNodeIdOpt !== void 0 && mainNodeIdOpt.length > 0) {
776
+ if (mainNodeIdOpt !== this.mainDerivedNodeId) {
777
+ throw new Error(
778
+ [
779
+ "connect: mainNodeId does not match the node id derived from mainNodeCredentials.passw.",
780
+ ` mainNodeId (option): ${mainNodeIdOpt}`,
781
+ ` derived node id: ${this.mainDerivedNodeId}`,
782
+ "Ensure AGENT_PLAY_MAIN_NODE_ID_* and AGENT_PLAY_MAIN_NODE_ID_*_PASSW are paired from the same create-main-node output."
783
+ ].join("\n")
784
+ );
785
+ }
737
786
  this.configuredMainNodeId = mainNodeIdOpt;
738
787
  const validation = await this.validateNodeIdentity({
739
- nodeId: mainNodeIdOpt,
740
- mainNodeId: void 0
788
+ nodeId: this.mainDerivedNodeId,
789
+ mainNodeId: void 0,
790
+ auth: {
791
+ nodeId: this.mainDerivedNodeId,
792
+ passwHash: this.mainPasswHash
793
+ }
741
794
  });
742
795
  console.info(
743
796
  `[agent-play] Node identity validated (${validation.nodeKind ?? "unknown"}).`
@@ -746,7 +799,7 @@ var RemotePlayWorld = class {
746
799
  this.configuredMainNodeId = null;
747
800
  }
748
801
  const res = await fetch(`${this.apiBase}/api/agent-play/session`, {
749
- headers: this.authHeaders()
802
+ headers: this.mainAuthHeaders()
750
803
  });
751
804
  if (!res.ok) {
752
805
  throw new Error(`session failed: ${res.status}`);
@@ -815,7 +868,7 @@ var RemotePlayWorld = class {
815
868
  async getWorldSnapshot() {
816
869
  const res = await fetch(`${this.apiBase}/api/agent-play/sdk/rpc`, {
817
870
  method: "POST",
818
- headers: this.jsonHeaders(),
871
+ headers: this.mainJsonHeaders(),
819
872
  body: JSON.stringify({ op: "getWorldSnapshot", payload: {} })
820
873
  });
821
874
  const text = await res.text();
@@ -843,7 +896,7 @@ var RemotePlayWorld = class {
843
896
  }
844
897
  const res = await fetch(`${this.apiBase}/api/agent-play/sdk/rpc`, {
845
898
  method: "POST",
846
- headers: this.jsonHeaders(),
899
+ headers: this.mainJsonHeaders(),
847
900
  body: JSON.stringify({
848
901
  op: "getPlayerChainNode",
849
902
  payload: { stableKey: trimmed }
@@ -963,9 +1016,15 @@ var RemotePlayWorld = class {
963
1016
  */
964
1017
  async addAgent(input) {
965
1018
  const sid = this.getSessionId();
1019
+ const agentCredentialInput = { nodeId: input.nodeId };
1020
+ if (input.agentPassphrase !== void 0 && input.agentPassphrase.length > 0) {
1021
+ agentCredentialInput.agentPassphrase = input.agentPassphrase;
1022
+ }
1023
+ const agentCredential = this.resolveAgentCredential(agentCredentialInput);
966
1024
  const validation = await this.validateNodeIdentity({
967
1025
  nodeId: input.nodeId,
968
- mainNodeId: input.mainNodeId
1026
+ mainNodeId: input.mainNodeId,
1027
+ auth: agentCredential
969
1028
  });
970
1029
  console.info(
971
1030
  [
@@ -1002,7 +1061,7 @@ var RemotePlayWorld = class {
1002
1061
  type: input.type,
1003
1062
  agent: input.agent,
1004
1063
  mainNodeId: input.mainNodeId,
1005
- passwHash: this.passwHash,
1064
+ passwHash: agentCredential.passwHash,
1006
1065
  agentId: input.nodeId,
1007
1066
  connectionId,
1008
1067
  leaseTtlSeconds,
@@ -1017,7 +1076,7 @@ var RemotePlayWorld = class {
1017
1076
  const requestBody = JSON.stringify(requestPayload);
1018
1077
  const res = await fetch(url, {
1019
1078
  method: "POST",
1020
- headers: this.jsonHeaders(),
1079
+ headers: this.agentJsonHeaders(agentCredential),
1021
1080
  body: requestBody
1022
1081
  });
1023
1082
  const bodyText = await res.text();
@@ -1041,6 +1100,7 @@ var RemotePlayWorld = class {
1041
1100
  const registeredAgent = parseRegisteredAgentSummary(body.registeredAgent);
1042
1101
  const bodyConnectionId = typeof body.connectionId === "string" && body.connectionId.length > 0 ? body.connectionId : connectionId;
1043
1102
  const bodyLeaseTtlSeconds = typeof body.leaseTtlSeconds === "number" && Number.isFinite(body.leaseTtlSeconds) ? body.leaseTtlSeconds : leaseTtlSeconds;
1103
+ this.playerAgentCredentials.set(playerId, agentCredential);
1044
1104
  const existingConnection = this.playerConnectionInfo.get(playerId);
1045
1105
  if (existingConnection !== void 0) {
1046
1106
  clearInterval(existingConnection.timer);
@@ -1116,14 +1176,18 @@ var RemotePlayWorld = class {
1116
1176
  return this.addAgent(payload);
1117
1177
  }
1118
1178
  async recordInteraction(input) {
1119
- await this.rpc("recordInteraction", {
1120
- playerId: input.playerId,
1121
- role: input.role,
1122
- text: input.text
1123
- });
1179
+ await this.rpc(
1180
+ "recordInteraction",
1181
+ {
1182
+ playerId: input.playerId,
1183
+ role: input.role,
1184
+ text: input.text
1185
+ },
1186
+ input.playerId
1187
+ );
1124
1188
  }
1125
1189
  async recordJourney(playerId, journey) {
1126
- await this.rpc("recordJourney", { playerId, journey });
1190
+ await this.rpc("recordJourney", { playerId, journey }, playerId);
1127
1191
  }
1128
1192
  async sendIntercomResponse(payload) {
1129
1193
  const sid = this.getSessionId();
@@ -1141,7 +1205,7 @@ var RemotePlayWorld = class {
1141
1205
  });
1142
1206
  const res = await fetch(url, {
1143
1207
  method: "POST",
1144
- headers: this.jsonHeaders(),
1208
+ headers: this.agentJsonHeadersForPlayer(payload.fromPlayerId),
1145
1209
  body: JSON.stringify({ op: INTERCOM_RESPONSE_OP, payload })
1146
1210
  });
1147
1211
  const okText = await res.text();
@@ -1357,7 +1421,7 @@ var RemotePlayWorld = class {
1357
1421
  }
1358
1422
  const res = await fetch(url, {
1359
1423
  method: "POST",
1360
- headers: this.jsonHeaders(),
1424
+ headers: this.mainJsonHeaders(),
1361
1425
  body: JSON.stringify(body)
1362
1426
  });
1363
1427
  const text = await res.text();
@@ -1375,12 +1439,12 @@ var RemotePlayWorld = class {
1375
1439
  }
1376
1440
  return json.id;
1377
1441
  }
1378
- async rpc(op, payload) {
1442
+ async rpc(op, payload, playerId) {
1379
1443
  const sid = this.getSessionId();
1380
1444
  const url = `${this.apiBase}/api/agent-play/sdk/rpc?sid=${encodeURIComponent(sid)}`;
1381
1445
  const res = await fetch(url, {
1382
1446
  method: "POST",
1383
- headers: this.jsonHeaders(),
1447
+ headers: this.agentJsonHeadersForPlayer(playerId),
1384
1448
  body: JSON.stringify({ op, payload })
1385
1449
  });
1386
1450
  if (!res.ok) {
@@ -1402,7 +1466,7 @@ var RemotePlayWorld = class {
1402
1466
  try {
1403
1467
  const res = await fetch(url, {
1404
1468
  method: "POST",
1405
- headers: this.jsonHeaders(),
1469
+ headers: this.agentJsonHeadersForPlayer(input.playerId),
1406
1470
  body: bodyJson
1407
1471
  });
1408
1472
  const text = await res.text();
@@ -1456,7 +1520,7 @@ var RemotePlayWorld = class {
1456
1520
  const url = `${this.apiBase}/api/agent-play/players/disconnect?sid=${encodeURIComponent(sid)}`;
1457
1521
  await fetch(url, {
1458
1522
  method: "POST",
1459
- headers: this.jsonHeaders(),
1523
+ headers: this.agentJsonHeadersForPlayer(input.playerId),
1460
1524
  body: JSON.stringify({
1461
1525
  playerId: input.playerId,
1462
1526
  connectionId: input.connectionId