agents 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/dist/{agent-routing-B2XLNMxq.d.ts → agent-routing-0DR0Tk2v.d.ts} +205 -132
  2. package/dist/agent-routing.d.ts +1 -1
  3. package/dist/agent-routing.js.map +1 -1
  4. package/dist/agent-tool-types.d.ts +28 -22
  5. package/dist/agent-tool-types.js +23 -1
  6. package/dist/agent-tool-types.js.map +1 -1
  7. package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
  8. package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
  9. package/dist/{agent-tools-Dj5Mzlw7.d.ts → agent-tools-YrATZJu2.d.ts} +34 -20
  10. package/dist/agent-tools.d.ts +20 -18
  11. package/dist/agent-tools.js.map +1 -1
  12. package/dist/ai-fm7xx1BK.js +312 -0
  13. package/dist/ai-fm7xx1BK.js.map +1 -0
  14. package/dist/base64-redaction-C1Uu29t5.js +59 -0
  15. package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
  16. package/dist/browser/ai-sdk.d.ts +90 -0
  17. package/dist/browser/ai-sdk.js +51 -0
  18. package/dist/browser/ai-sdk.js.map +1 -0
  19. package/dist/browser/ai.d.ts +4 -4
  20. package/dist/browser/ai.js +1 -347
  21. package/dist/browser/index.d.ts +81 -79
  22. package/dist/browser/index.js +361 -2
  23. package/dist/browser/index.js.map +1 -0
  24. package/dist/browser/tanstack-ai.d.ts +96 -1
  25. package/dist/browser/tanstack-ai.js +62 -2
  26. package/dist/browser/tanstack-ai.js.map +1 -1
  27. package/dist/browser-DE3d-RLk.d.ts +275 -0
  28. package/dist/browser-tool-CQ10e6Kj.js +480 -0
  29. package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
  30. package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
  31. package/dist/capability-B4WbF81e.js.map +1 -1
  32. package/dist/{capability-runner-BUBa6Ake.d.ts → capability-runner-DBAnGr6L.d.ts} +7 -2
  33. package/dist/chat/index.d.ts +340 -234
  34. package/dist/chat/index.js +592 -66
  35. package/dist/chat/index.js.map +1 -1
  36. package/dist/chat/react.d.ts +42 -2
  37. package/dist/chat/react.js +191 -35
  38. package/dist/chat/react.js.map +1 -1
  39. package/dist/chat/transport.d.ts +1 -1
  40. package/dist/chat/transport.js +1 -1
  41. package/dist/chat-sdk/index.d.ts +17 -3
  42. package/dist/chat-sdk/index.js +28 -2
  43. package/dist/chat-sdk/index.js.map +1 -1
  44. package/dist/{client-C9DAjZIY.js → client-4OlMIJx-.js} +102 -29
  45. package/dist/client-4OlMIJx-.js.map +1 -0
  46. package/dist/{client-BxdkCuVy.d.ts → client-je3koD9Z.d.ts} +45 -10
  47. package/dist/client.d.ts +7 -5
  48. package/dist/client.js +2 -2
  49. package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
  50. package/dist/connector-58XDGvc3.js.map +1 -0
  51. package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
  52. package/dist/context/index.d.ts +29 -0
  53. package/dist/context/index.js +64 -17
  54. package/dist/context/index.js.map +1 -1
  55. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  56. package/dist/{current-agent-C4w86kcN.d.ts → current-agent-vAk_5JMt.d.ts} +4 -4
  57. package/dist/{durable-object-lifecycle-ei7M7lqB.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +15 -7
  58. package/dist/harness/pi/index.d.ts +344 -0
  59. package/dist/harness/pi/index.js +873 -0
  60. package/dist/harness/pi/index.js.map +1 -0
  61. package/dist/images-iiTDSyRu.js +1631 -0
  62. package/dist/images-iiTDSyRu.js.map +1 -0
  63. package/dist/{index-Dlgtd3Mj.d.ts → index-BreL0YrQ.d.ts} +2 -2
  64. package/dist/index-kbSUIer3.d.ts +343 -0
  65. package/dist/index.d.ts +46 -44
  66. package/dist/index.js +1 -1
  67. package/dist/internal_context.d.ts +1 -1
  68. package/dist/lifecycle/index.d.ts +3 -3
  69. package/dist/lifecycle/index.js +1 -1
  70. package/dist/{lifecycle-Mm_jQh7r.js → lifecycle-CFu6OV03.js} +39 -10
  71. package/dist/{lifecycle-Mm_jQh7r.js.map → lifecycle-CFu6OV03.js.map} +1 -1
  72. package/dist/mcp/client/index.d.ts +20 -20
  73. package/dist/mcp/client/x402.js +21 -19
  74. package/dist/mcp/client/x402.js.map +1 -1
  75. package/dist/mcp/index.d.ts +35 -35
  76. package/dist/mcp/index.js +1 -1
  77. package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
  78. package/dist/message-builder-DAl9AhHH.js.map +1 -0
  79. package/dist/models/ai-sdk/index.d.ts +353 -0
  80. package/dist/models/ai-sdk/index.js +2443 -0
  81. package/dist/models/ai-sdk/index.js.map +1 -0
  82. package/dist/models/pi-ai/index.d.ts +313 -0
  83. package/dist/models/pi-ai/index.js +1795 -0
  84. package/dist/models/pi-ai/index.js.map +1 -0
  85. package/dist/observability/ai/index.js +20 -4
  86. package/dist/observability/ai/index.js.map +1 -1
  87. package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
  88. package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
  89. package/dist/queue/index.d.ts +3 -3
  90. package/dist/queue/index.js +1 -1
  91. package/dist/{queue-BC9F5cUo.js → queue-BB-zfIYF.js} +2 -2
  92. package/dist/{queue-BC9F5cUo.js.map → queue-BB-zfIYF.js.map} +1 -1
  93. package/dist/react.d.ts +6 -6
  94. package/dist/react.js +160 -64
  95. package/dist/react.js.map +1 -1
  96. package/dist/{retries-D9B2UCq3.d.ts → retries-CV6AkXmQ.d.ts} +18 -9
  97. package/dist/retries.d.ts +11 -9
  98. package/dist/retries.js +11 -1
  99. package/dist/retries.js.map +1 -1
  100. package/dist/routing/index.d.ts +2 -2
  101. package/dist/routing/index.js +1 -1
  102. package/dist/{scheduler-D_KHqxBa.js → scheduler-DZ3xQfeK.js} +2 -2
  103. package/dist/{scheduler-D_KHqxBa.js.map → scheduler-DZ3xQfeK.js.map} +1 -1
  104. package/dist/{scheduler-BGq6M5Kd.d.ts → scheduler-RdACNXDP.d.ts} +3 -3
  105. package/dist/schedules/index.d.ts +1 -1
  106. package/dist/schedules/index.js +1 -1
  107. package/dist/serializable.d.ts +1 -1
  108. package/dist/sessions/index.d.ts +70 -3
  109. package/dist/sessions/index.js +276 -67
  110. package/dist/sessions/index.js.map +1 -1
  111. package/dist/skills/index.d.ts +29 -314
  112. package/dist/socket-address.d.ts +16 -0
  113. package/dist/socket-address.js +19 -0
  114. package/dist/socket-address.js.map +1 -0
  115. package/dist/{src-BNU3ZiJM.js → src-BZeu-SCD.js} +423 -115
  116. package/dist/src-BZeu-SCD.js.map +1 -0
  117. package/dist/state/index.d.ts +1 -1
  118. package/dist/state/index.js +1 -1
  119. package/dist/streams/index.d.ts +1 -1
  120. package/dist/streams/index.js +1 -1
  121. package/dist/{streams-BcE8JP2X.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
  122. package/dist/{streams-CCPRV6dt.js → streams-DW-UCOUc.js} +9 -2
  123. package/dist/streams-DW-UCOUc.js.map +1 -0
  124. package/dist/sub-routing.d.ts +12 -12
  125. package/dist/tasks/index.d.ts +1 -1
  126. package/dist/tasks/index.js +1 -1
  127. package/dist/{tasks-D4nLqVSI.js → tasks-68DESxf0.js} +2 -2
  128. package/dist/{tasks-D4nLqVSI.js.map → tasks-68DESxf0.js.map} +1 -1
  129. package/dist/{tasks-V2lCT8ZV.d.ts → tasks-9YxYtByh.d.ts} +2 -2
  130. package/dist/transport-PfFnt-Ld.d.ts +525 -0
  131. package/dist/{types-DCD6BNZH.d.ts → types-BA4anrjI.d.ts} +2 -2
  132. package/dist/voice/index.d.ts +2 -2
  133. package/dist/websockets/index.d.ts +23 -6
  134. package/dist/websockets/index.js +1 -1
  135. package/dist/{websockets-D7IzWbZb.js → websockets-Ci-LZFHH.js} +45 -20
  136. package/dist/{websockets-D7IzWbZb.js.map → websockets-Ci-LZFHH.js.map} +1 -1
  137. package/dist/wire-types-CnMt6_HR.js.map +1 -1
  138. package/dist/wire-types-CrjKvDyi.d.ts +300 -0
  139. package/dist/workflow-types.d.ts +25 -25
  140. package/dist/workflows.d.ts +21 -21
  141. package/dist/workflows.js +1 -1
  142. package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
  143. package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
  144. package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
  145. package/docs/agent-tools.md +29 -4
  146. package/docs/browse-the-web.md +87 -5
  147. package/docs/chat-agents.md +124 -16
  148. package/docs/context.md +27 -0
  149. package/docs/durable-execution.md +2 -0
  150. package/docs/harnesses/pi.md +173 -0
  151. package/docs/human-in-the-loop.md +4 -0
  152. package/docs/index.md +3 -0
  153. package/docs/lifecycle.md +20 -4
  154. package/docs/models-pi-ai.md +325 -0
  155. package/docs/models.md +504 -0
  156. package/docs/resumable-streaming.md +2 -0
  157. package/docs/routing.md +13 -13
  158. package/docs/sessions.md +36 -1
  159. package/docs/state.md +1 -0
  160. package/docs/sub-agents.md +9 -0
  161. package/package.json +40 -2
  162. package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
  163. package/dist/browser/ai.js.map +0 -1
  164. package/dist/client-C9DAjZIY.js.map +0 -1
  165. package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
  166. package/dist/connector-CptFKzRh.js.map +0 -1
  167. package/dist/message-builder-BymO4N_D.js.map +0 -1
  168. package/dist/src-BNU3ZiJM.js.map +0 -1
  169. package/dist/streams-CCPRV6dt.js.map +0 -1
  170. package/dist/ws-chat-transport-rWwta645.js.map +0 -1
@@ -2,7 +2,7 @@ import {
2
2
  n as WebSocketChatTransport,
3
3
  r as WebSocketChatTransportOptions,
4
4
  t as AgentConnection
5
- } from "../ws-chat-transport-UNRIS2xl.js";
5
+ } from "../ws-chat-transport-BbT1A5Lc.js";
6
6
  export {
7
7
  type AgentConnection,
8
8
  WebSocketChatTransport,
@@ -1,2 +1,2 @@
1
- import { t as WebSocketChatTransport } from "../ws-chat-transport-rWwta645.js";
1
+ import { t as WebSocketChatTransport } from "../ws-chat-transport-cqMKq3IY.js";
2
2
  export { WebSocketChatTransport };
@@ -1,8 +1,8 @@
1
1
  import {
2
- _n as DynamicAgentClass,
2
+ bn as DynamicAgentClass,
3
3
  c as Agent,
4
- vn as DynamicAgentStub
5
- } from "../agent-routing-B2XLNMxq.js";
4
+ xn as DynamicAgentStub
5
+ } from "../agent-routing-0DR0Tk2v.js";
6
6
  import { Lock, QueueEntry, StateAdapter } from "chat";
7
7
 
8
8
  //#region src/chat-sdk/agent.d.ts
@@ -64,6 +64,16 @@ interface ChatSdkStateAdapterOptions {
64
64
  name?: string;
65
65
  keyShard?: (key: string) => string | undefined;
66
66
  shardKey?: (threadId: string) => string;
67
+ /**
68
+ * Re-extend every lock this adapter acquired, by its original TTL, every
69
+ * third of that TTL until it is released. The Chat SDK takes a fixed 30
70
+ * second thread lock and never extends it while a handler runs, so a handler
71
+ * that outlives it lets the next message take a second lock and run
72
+ * concurrently. A lock held by an isolate that dies stops being extended and
73
+ * expires on its own TTL.
74
+ * @default false
75
+ */
76
+ lockHeartbeat?: boolean;
67
77
  }
68
78
  //#endregion
69
79
  //#region src/chat-sdk/adapter.d.ts
@@ -78,6 +88,8 @@ declare class ChatSdkStateAdapter implements StateAdapter {
78
88
  private readonly defaultName;
79
89
  private readonly keyShard?;
80
90
  private readonly shardKey;
91
+ private readonly lockHeartbeat;
92
+ private readonly lockHeartbeats;
81
93
  private connected;
82
94
  constructor(options?: ChatSdkStateAdapterOptions);
83
95
  connect(): Promise<void>;
@@ -89,6 +101,8 @@ declare class ChatSdkStateAdapter implements StateAdapter {
89
101
  releaseLock(lock: Lock): Promise<void>;
90
102
  extendLock(lock: Lock, ttlMs: number): Promise<boolean>;
91
103
  forceReleaseLock(threadId: string): Promise<void>;
104
+ private startLockHeartbeat;
105
+ private stopLockHeartbeat;
92
106
  enqueue(
93
107
  threadId: string,
94
108
  entry: QueueEntry,
@@ -1,4 +1,4 @@
1
- import { i as getCurrentAgent, t as Agent } from "../src-BNU3ZiJM.js";
1
+ import { i as getCurrentAgent, t as Agent } from "../src-BZeu-SCD.js";
2
2
  //#region src/chat-sdk/agent.ts
3
3
  const NEXT_CLEANUP_AT_KEY = "next_cleanup_at";
4
4
  const CLEANUP_SCHEDULE_ID_KEY = "cleanup_schedule_id";
@@ -363,6 +363,7 @@ function defaultKeyShard(key, shardThread = defaultThreadShard) {
363
363
  }
364
364
  var ChatSdkStateAdapter = class {
365
365
  constructor(options = {}) {
366
+ this.lockHeartbeats = /* @__PURE__ */ new Map();
366
367
  this.connected = false;
367
368
  const parent = options.parent ?? getCurrentAgent().agent;
368
369
  if (!parent) throw new Error("ChatSdkStateAdapter requires a parent Agent. Pass `parent` or create it inside an Agent context.");
@@ -371,12 +372,15 @@ var ChatSdkStateAdapter = class {
371
372
  this.defaultName = options.name ?? "default";
372
373
  this.keyShard = options.keyShard;
373
374
  this.shardKey = options.shardKey ?? defaultThreadShard;
375
+ this.lockHeartbeat = options.lockHeartbeat ?? false;
374
376
  }
375
377
  async connect() {
376
378
  this.connected = true;
377
379
  }
378
380
  async disconnect() {
379
381
  this.connected = false;
382
+ for (const timer of this.lockHeartbeats.values()) clearInterval(timer);
383
+ this.lockHeartbeats.clear();
380
384
  }
381
385
  async subscribe(threadId) {
382
386
  await (await this.stateAgent(threadId)).subscribe(threadId);
@@ -388,9 +392,12 @@ var ChatSdkStateAdapter = class {
388
392
  return (await this.stateAgent(threadId)).isSubscribed(threadId);
389
393
  }
390
394
  async acquireLock(threadId, ttlMs) {
391
- return (await this.stateAgent(threadId)).acquireLock(threadId, ttlMs);
395
+ const lock = await (await this.stateAgent(threadId)).acquireLock(threadId, ttlMs);
396
+ if (lock) this.startLockHeartbeat(lock, ttlMs);
397
+ return lock;
392
398
  }
393
399
  async releaseLock(lock) {
400
+ this.stopLockHeartbeat(lock.token);
394
401
  await (await this.stateAgent(lock.threadId)).releaseLock(lock.threadId, lock.token);
395
402
  }
396
403
  async extendLock(lock, ttlMs) {
@@ -399,6 +406,25 @@ var ChatSdkStateAdapter = class {
399
406
  async forceReleaseLock(threadId) {
400
407
  await (await this.stateAgent(threadId)).forceReleaseLock(threadId);
401
408
  }
409
+ startLockHeartbeat(lock, ttlMs) {
410
+ if (!this.lockHeartbeat || ttlMs <= 0 || !this.connected) return;
411
+ const timer = setInterval(() => {
412
+ if (!this.connected) {
413
+ this.stopLockHeartbeat(lock.token);
414
+ return;
415
+ }
416
+ this.extendLock(lock, ttlMs).then((extended) => {
417
+ if (!extended) this.stopLockHeartbeat(lock.token);
418
+ }, () => {});
419
+ }, ttlMs / 3);
420
+ this.lockHeartbeats.set(lock.token, timer);
421
+ }
422
+ stopLockHeartbeat(token) {
423
+ const timer = this.lockHeartbeats.get(token);
424
+ if (timer === void 0) return;
425
+ clearInterval(timer);
426
+ this.lockHeartbeats.delete(token);
427
+ }
402
428
  async enqueue(threadId, entry, maxSize) {
403
429
  return (await this.stateAgent(threadId)).enqueue(threadId, JSON.stringify(entry), maxSize);
404
430
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/chat-sdk/agent.ts","../../src/chat-sdk/adapter.ts","../../src/chat-sdk/index.ts"],"sourcesContent":["import { Agent } from \"../index\";\n\ninterface StoredLock {\n threadId: string;\n token: string;\n expiresAt: number;\n}\n\ninterface StoredQueueEntry {\n enqueuedAt: number;\n expiresAt: number;\n}\n\nconst NEXT_CLEANUP_AT_KEY = \"next_cleanup_at\";\nconst CLEANUP_SCHEDULE_ID_KEY = \"cleanup_schedule_id\";\n\nexport class ChatSdkStateAgent extends Agent {\n async onStart(): Promise<void> {\n this.migrate();\n await this.scheduleNextCleanup();\n }\n\n subscribe(threadId: string): void {\n this.sql`\n INSERT OR IGNORE INTO chat_sdk_state_subscriptions (thread_id)\n VALUES (${threadId})\n `;\n }\n\n unsubscribe(threadId: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_subscriptions\n WHERE thread_id = ${threadId}\n `;\n }\n\n isSubscribed(threadId: string): boolean {\n const rows = this.sql<{ found: number }>`\n SELECT 1 as found\n FROM chat_sdk_state_subscriptions\n WHERE thread_id = ${threadId}\n LIMIT 1\n `;\n return rows.length > 0;\n }\n\n async acquireLock(\n threadId: string,\n ttlMs: number\n ): Promise<StoredLock | null> {\n const result = this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_locks WHERE thread_id = ? AND expires_at <= ?\",\n threadId,\n now\n );\n\n const existing = this.ctx.storage.sql\n .exec(\n \"SELECT 1 FROM chat_sdk_state_locks WHERE thread_id = ? LIMIT 1\",\n threadId\n )\n .toArray();\n if (existing.length > 0) {\n return null;\n }\n\n const token = crypto.randomUUID();\n const expiresAt = now + ttlMs;\n\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_locks (thread_id, token, expires_at) VALUES (?, ?, ?)\",\n threadId,\n token,\n expiresAt\n );\n\n return { threadId, token, expiresAt };\n });\n\n await this.scheduleCleanupForExpiry(result?.expiresAt ?? null);\n return result;\n }\n\n releaseLock(threadId: string, token: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE thread_id = ${threadId} AND token = ${token}\n `;\n }\n\n async extendLock(\n threadId: string,\n token: string,\n ttlMs: number\n ): Promise<boolean> {\n const result = this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n const rows = this.ctx.storage.sql\n .exec<{ thread_id: string }>(\n `UPDATE chat_sdk_state_locks SET expires_at = ?\n WHERE thread_id = ? AND token = ? AND expires_at > ?\n RETURNING thread_id`,\n now + ttlMs,\n threadId,\n token,\n now\n )\n .toArray();\n return rows.length > 0;\n });\n if (result) {\n await this.scheduleCleanupForExpiry(Date.now() + ttlMs);\n }\n return result;\n }\n\n forceReleaseLock(threadId: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE thread_id = ${threadId}\n `;\n }\n\n async enqueue(\n threadId: string,\n value: string,\n maxSize: number\n ): Promise<number> {\n const parsed = parseQueueEntry(value);\n\n const count = this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_queue (thread_id, value, enqueued_at, expires_at) VALUES (?, ?, ?, ?)\",\n threadId,\n value,\n parsed.enqueuedAt,\n parsed.expiresAt\n );\n\n this.ctx.storage.sql.exec(\n `DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND id NOT IN (\n SELECT id FROM chat_sdk_state_queue\n WHERE thread_id = ?\n ORDER BY id DESC\n LIMIT ?\n )`,\n threadId,\n threadId,\n maxSize\n );\n\n const row = this.ctx.storage.sql\n .exec<{ count: number }>(\n \"SELECT COUNT(*) as count FROM chat_sdk_state_queue WHERE thread_id = ?\",\n threadId\n )\n .one();\n return row.count;\n });\n await this.scheduleCleanupForExpiry(parsed.expiresAt);\n return count;\n }\n\n popQueue(threadId: string): string | null {\n return this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND expires_at <= ?\",\n threadId,\n now\n );\n\n const rows = this.ctx.storage.sql\n .exec<{ id: number; value: string }>(\n \"SELECT id, value FROM chat_sdk_state_queue WHERE thread_id = ? ORDER BY id ASC LIMIT 1\",\n threadId\n )\n .toArray();\n const row = rows[0];\n if (!row) {\n return null;\n }\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_queue WHERE id = ?\",\n row.id\n );\n return row.value;\n });\n }\n\n queueDepth(threadId: string): number {\n const rows = this.sql<{ count: number }>`\n SELECT COUNT(*) as count\n FROM chat_sdk_state_queue\n WHERE thread_id = ${threadId} AND expires_at > ${Date.now()}\n `;\n return rows[0]?.count ?? 0;\n }\n\n async listAppend(\n key: string,\n value: string,\n maxLength?: number,\n ttlMs?: number\n ): Promise<void> {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n\n this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_lists (key, value, expires_at) VALUES (?, ?, ?)\",\n key,\n value,\n expiresAt\n );\n\n if (expiresAt !== null) {\n // Chat SDK history lists use a list-level TTL: any append refreshes the\n // expiry for the whole logical list, not only the new row.\n this.ctx.storage.sql.exec(\n \"UPDATE chat_sdk_state_lists SET expires_at = ? WHERE key = ?\",\n expiresAt,\n key\n );\n }\n\n if (maxLength != null && maxLength > 0) {\n this.ctx.storage.sql.exec(\n `DELETE FROM chat_sdk_state_lists WHERE key = ? AND id NOT IN (\n SELECT id FROM chat_sdk_state_lists\n WHERE key = ?\n ORDER BY id DESC\n LIMIT ?\n )`,\n key,\n key,\n maxLength\n );\n }\n });\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n\n listGet(key: string): string[] {\n const now = Date.now();\n\n this.sql`\n DELETE FROM chat_sdk_state_lists\n WHERE key = ${key}\n AND expires_at IS NOT NULL\n AND expires_at <= ${now}\n `;\n\n return this.sql<{ value: string }>`\n SELECT value\n FROM chat_sdk_state_lists\n WHERE key = ${key}\n ORDER BY id ASC\n `.map((row) => row.value);\n }\n\n cacheGet(key: string): string | null {\n return this.readCacheValue(key, Date.now());\n }\n\n async cacheSet(key: string, value: string, ttlMs?: number): Promise<void> {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n this.upsertCacheValue(key, value, expiresAt);\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n\n async cacheSetIfNotExists(\n key: string,\n value: string,\n ttlMs?: number\n ): Promise<boolean> {\n const now = Date.now();\n\n const inserted = this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_cache WHERE key = ? AND expires_at IS NOT NULL AND expires_at <= ?\",\n key,\n now\n );\n\n if (this.readCacheValue(key, now) !== null) {\n return false;\n }\n\n const expiresAt = ttlMs && ttlMs > 0 ? now + ttlMs : null;\n this.upsertCacheValue(key, value, expiresAt);\n return true;\n });\n if (inserted) {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n return inserted;\n }\n\n cacheDelete(key: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_cache\n WHERE key = ${key}\n `;\n }\n\n async cleanupExpired(payload?: { expiresAt?: number }): Promise<void> {\n const current = this.readCleanupMetadata();\n if (\n payload?.expiresAt !== undefined &&\n current.nextCleanupAt !== null &&\n payload.expiresAt !== current.nextCleanupAt\n ) {\n return;\n }\n\n const now = Date.now();\n this.clearCleanupMetadata();\n\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_cache\n WHERE expires_at IS NOT NULL AND expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_queue\n WHERE expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_lists\n WHERE expires_at IS NOT NULL AND expires_at <= ${now}\n `;\n await this.scheduleNextCleanup();\n }\n\n private migrate(): void {\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_subscriptions (\n thread_id TEXT PRIMARY KEY\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_locks (\n thread_id TEXT PRIMARY KEY,\n token TEXT NOT NULL,\n expires_at INTEGER NOT NULL\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_cache (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at INTEGER\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_queue (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n thread_id TEXT NOT NULL,\n value TEXT NOT NULL,\n enqueued_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_lists (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n expires_at INTEGER\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )\n `;\n\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_locks_expires\n ON chat_sdk_state_locks(expires_at)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_cache_expires\n ON chat_sdk_state_cache(expires_at)\n WHERE expires_at IS NOT NULL\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_thread\n ON chat_sdk_state_queue(thread_id, id)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_expires\n ON chat_sdk_state_queue(expires_at)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_key\n ON chat_sdk_state_lists(key, id)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_expires\n ON chat_sdk_state_lists(expires_at)\n WHERE expires_at IS NOT NULL\n `;\n }\n\n private readCacheValue(key: string, now: number): string | null {\n const rows = this.sql<{ value: string }>`\n SELECT value\n FROM chat_sdk_state_cache\n WHERE key = ${key}\n AND (expires_at IS NULL OR expires_at > ${now})\n `;\n return rows[0]?.value ?? null;\n }\n\n private upsertCacheValue(\n key: string,\n value: string,\n expiresAt: number | null\n ): void {\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_cache (key, value, expires_at)\n VALUES (${key}, ${value}, ${expiresAt})\n `;\n }\n\n private async scheduleCleanupForExpiry(\n expiresAt: number | null\n ): Promise<void> {\n if (expiresAt === null) {\n return;\n }\n\n await this.ensureCleanupScheduled(expiresAt);\n }\n\n private async scheduleNextCleanup(): Promise<void> {\n const next = this.nextExpiry();\n if (next === null) {\n const current = this.readCleanupMetadata();\n if (current.scheduleId) {\n await this.cancelSchedule(current.scheduleId).catch(() => false);\n }\n this.clearCleanupMetadata();\n return;\n }\n\n await this.ensureCleanupScheduled(next);\n }\n\n private async ensureCleanupScheduled(expiresAt: number): Promise<void> {\n const current = this.readCleanupMetadata();\n if (current.nextCleanupAt !== null && current.nextCleanupAt <= expiresAt) {\n return;\n }\n\n if (current.scheduleId) {\n await this.cancelSchedule(current.scheduleId).catch(() => false);\n }\n\n const delaySeconds = Math.max(\n 0,\n Math.ceil((expiresAt - Date.now()) / 1000)\n );\n const schedule = await this.schedule(delaySeconds, \"cleanupExpired\", {\n expiresAt\n });\n this.writeCleanupMetadata(expiresAt, schedule.id);\n }\n\n private nextExpiry(): number | null {\n const rows = this.sql<{ expires_at: number | null }>`\n SELECT MIN(expires_at) as expires_at\n FROM (\n SELECT expires_at FROM chat_sdk_state_locks\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_queue\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_cache WHERE expires_at IS NOT NULL\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_lists WHERE expires_at IS NOT NULL\n )\n `;\n return rows[0]?.expires_at ?? null;\n }\n\n private readCleanupMetadata(): {\n nextCleanupAt: number | null;\n scheduleId: string | null;\n } {\n const rows = this.sql<{ key: string; value: string }>`\n SELECT key, value\n FROM chat_sdk_state_metadata\n WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})\n `;\n const values = new Map(rows.map((row) => [row.key, row.value]));\n const nextCleanupAt = Number(values.get(NEXT_CLEANUP_AT_KEY));\n return {\n nextCleanupAt: Number.isFinite(nextCleanupAt) ? nextCleanupAt : null,\n scheduleId: values.get(CLEANUP_SCHEDULE_ID_KEY) ?? null\n };\n }\n\n private writeCleanupMetadata(expiresAt: number, scheduleId: string): void {\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)\n VALUES (${NEXT_CLEANUP_AT_KEY}, ${String(expiresAt)})\n `;\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)\n VALUES (${CLEANUP_SCHEDULE_ID_KEY}, ${scheduleId})\n `;\n }\n\n private clearCleanupMetadata(): void {\n this.sql`\n DELETE FROM chat_sdk_state_metadata\n WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})\n `;\n }\n}\n\nfunction parseQueueEntry(value: string): StoredQueueEntry {\n const raw = JSON.parse(value) as Record<string, unknown>;\n if (typeof raw.enqueuedAt !== \"number\" || typeof raw.expiresAt !== \"number\") {\n throw new Error(\n \"ChatSdkStateAgent expected QueueEntry JSON with numeric TTLs\"\n );\n }\n\n return {\n enqueuedAt: raw.enqueuedAt,\n expiresAt: raw.expiresAt\n };\n}\n","import type {\n Lock as ChatSdkLock,\n QueueEntry as ChatSdkQueueEntry,\n StateAdapter as ChatSdkStateAdapterInterface\n} from \"chat\";\nimport {\n getCurrentAgent,\n type SubAgentClass,\n type SubAgentStub\n} from \"../index\";\nimport { ChatSdkStateAgent } from \"./agent\";\nimport type { ChatSdkStateAdapterOptions } from \"./types\";\n\nconst THREAD_STATE_PREFIX = \"thread-state:\";\nconst CHANNEL_STATE_PREFIX = \"channel-state:\";\nconst MESSAGE_HISTORY_PREFIX = \"msg-history:\";\nconst TRANSCRIPTS_USER_PREFIX = \"transcripts:user:\";\n\nfunction parseStoredJson<T>(raw: string, label: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch (error) {\n throw new Error(`ChatSdkStateAdapter expected JSON-encoded ${label}`, {\n cause: error\n });\n }\n}\n\nexport function defaultThreadShard(threadId: string): string {\n return threadId.split(\":\").slice(0, 2).join(\":\") || \"default\";\n}\n\nexport function defaultKeyShard(\n key: string,\n shardThread: (threadId: string) => string = defaultThreadShard\n): string | undefined {\n for (const prefix of [\n THREAD_STATE_PREFIX,\n CHANNEL_STATE_PREFIX,\n MESSAGE_HISTORY_PREFIX,\n TRANSCRIPTS_USER_PREFIX\n ]) {\n if (key.startsWith(prefix)) {\n return shardThread(key.slice(prefix.length));\n }\n }\n\n return undefined;\n}\n\nexport class ChatSdkStateAdapter implements ChatSdkStateAdapterInterface {\n private readonly parent: NonNullable<ChatSdkStateAdapterOptions[\"parent\"]>;\n private readonly agentClass: SubAgentClass<ChatSdkStateAgent>;\n private readonly defaultName: string;\n private readonly keyShard?: (key: string) => string | undefined;\n private readonly shardKey: (threadId: string) => string;\n private connected = false;\n\n constructor(options: ChatSdkStateAdapterOptions = {}) {\n const parent = options.parent ?? getCurrentAgent().agent;\n if (!parent) {\n throw new Error(\n \"ChatSdkStateAdapter requires a parent Agent. Pass `parent` or create it inside an Agent context.\"\n );\n }\n\n this.parent = parent;\n this.agentClass = options.agent ?? ChatSdkStateAgent;\n this.defaultName = options.name ?? \"default\";\n this.keyShard = options.keyShard;\n this.shardKey = options.shardKey ?? defaultThreadShard;\n }\n\n async connect(): Promise<void> {\n this.connected = true;\n }\n\n async disconnect(): Promise<void> {\n this.connected = false;\n }\n\n async subscribe(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).subscribe(threadId);\n }\n\n async unsubscribe(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).unsubscribe(threadId);\n }\n\n async isSubscribed(threadId: string): Promise<boolean> {\n return (await this.stateAgent(threadId)).isSubscribed(threadId);\n }\n\n async acquireLock(\n threadId: string,\n ttlMs: number\n ): Promise<ChatSdkLock | null> {\n return (await this.stateAgent(threadId)).acquireLock(threadId, ttlMs);\n }\n\n async releaseLock(lock: ChatSdkLock): Promise<void> {\n await (\n await this.stateAgent(lock.threadId)\n ).releaseLock(lock.threadId, lock.token);\n }\n\n async extendLock(lock: ChatSdkLock, ttlMs: number): Promise<boolean> {\n return (await this.stateAgent(lock.threadId)).extendLock(\n lock.threadId,\n lock.token,\n ttlMs\n );\n }\n\n async forceReleaseLock(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).forceReleaseLock(threadId);\n }\n\n async enqueue(\n threadId: string,\n entry: ChatSdkQueueEntry,\n maxSize: number\n ): Promise<number> {\n return (await this.stateAgent(threadId)).enqueue(\n threadId,\n JSON.stringify(entry),\n maxSize\n );\n }\n\n async dequeue(threadId: string): Promise<ChatSdkQueueEntry | null> {\n const raw = await (await this.stateAgent(threadId)).popQueue(threadId);\n return raw === null\n ? null\n : parseStoredJson<ChatSdkQueueEntry>(raw, `queue entry for ${threadId}`);\n }\n\n async queueDepth(threadId: string): Promise<number> {\n return (await this.stateAgent(threadId)).queueDepth(threadId);\n }\n\n async appendToList(\n key: string,\n value: unknown,\n options?: { maxLength?: number; ttlMs?: number }\n ): Promise<void> {\n await (\n await this.stateAgentForKey(key)\n ).listAppend(\n key,\n JSON.stringify(value),\n options?.maxLength,\n options?.ttlMs\n );\n }\n\n async getList<T = unknown>(key: string): Promise<T[]> {\n const raw = await (await this.stateAgentForKey(key)).listGet(key);\n return raw.map((value) =>\n parseStoredJson<T>(value, `list entry for ${key}`)\n );\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const raw = await (await this.stateAgentForKey(key)).cacheGet(key);\n return raw === null ? null : parseStoredJson<T>(raw, `cache key ${key}`);\n }\n\n async set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> {\n await (\n await this.stateAgentForKey(key)\n ).cacheSet(key, JSON.stringify(value), ttlMs);\n }\n\n async setIfNotExists<T = unknown>(\n key: string,\n value: T,\n ttlMs?: number\n ): Promise<boolean> {\n return (await this.stateAgentForKey(key)).cacheSetIfNotExists(\n key,\n JSON.stringify(value),\n ttlMs\n );\n }\n\n async delete(key: string): Promise<void> {\n await (await this.stateAgentForKey(key)).cacheDelete(key);\n }\n\n private async stateAgent(\n threadId?: string\n ): Promise<SubAgentStub<ChatSdkStateAgent>> {\n this.ensureConnected();\n const name = threadId ? this.shardKey(threadId) : this.defaultName;\n return this.parent.subAgent(this.agentClass, name);\n }\n\n private async stateAgentForKey(\n key: string\n ): Promise<SubAgentStub<ChatSdkStateAgent>> {\n this.ensureConnected();\n const name =\n this.keyShard?.(key) ??\n defaultKeyShard(key, this.shardKey) ??\n this.defaultName;\n return this.parent.subAgent(this.agentClass, name);\n }\n\n private ensureConnected(): void {\n if (!this.connected) {\n throw new Error(\"ChatSdkStateAdapter is not connected\");\n }\n }\n}\n","export {\n ChatSdkStateAdapter,\n defaultKeyShard,\n defaultThreadShard\n} from \"./adapter\";\nexport { ChatSdkStateAgent } from \"./agent\";\nexport type { ChatSdkStateParent, ChatSdkStateAdapterOptions } from \"./types\";\n\nimport { ChatSdkStateAdapter } from \"./adapter\";\nimport type { ChatSdkStateAdapterOptions } from \"./types\";\n\nexport function createChatSdkState(\n options: ChatSdkStateAdapterOptions = {}\n): ChatSdkStateAdapter {\n return new ChatSdkStateAdapter(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/chat-sdk/agent.ts","../../src/chat-sdk/adapter.ts","../../src/chat-sdk/index.ts"],"sourcesContent":["import { Agent } from \"../index\";\n\ninterface StoredLock {\n threadId: string;\n token: string;\n expiresAt: number;\n}\n\ninterface StoredQueueEntry {\n enqueuedAt: number;\n expiresAt: number;\n}\n\nconst NEXT_CLEANUP_AT_KEY = \"next_cleanup_at\";\nconst CLEANUP_SCHEDULE_ID_KEY = \"cleanup_schedule_id\";\n\nexport class ChatSdkStateAgent extends Agent {\n async onStart(): Promise<void> {\n this.migrate();\n await this.scheduleNextCleanup();\n }\n\n subscribe(threadId: string): void {\n this.sql`\n INSERT OR IGNORE INTO chat_sdk_state_subscriptions (thread_id)\n VALUES (${threadId})\n `;\n }\n\n unsubscribe(threadId: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_subscriptions\n WHERE thread_id = ${threadId}\n `;\n }\n\n isSubscribed(threadId: string): boolean {\n const rows = this.sql<{ found: number }>`\n SELECT 1 as found\n FROM chat_sdk_state_subscriptions\n WHERE thread_id = ${threadId}\n LIMIT 1\n `;\n return rows.length > 0;\n }\n\n async acquireLock(\n threadId: string,\n ttlMs: number\n ): Promise<StoredLock | null> {\n const result = this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_locks WHERE thread_id = ? AND expires_at <= ?\",\n threadId,\n now\n );\n\n const existing = this.ctx.storage.sql\n .exec(\n \"SELECT 1 FROM chat_sdk_state_locks WHERE thread_id = ? LIMIT 1\",\n threadId\n )\n .toArray();\n if (existing.length > 0) {\n return null;\n }\n\n const token = crypto.randomUUID();\n const expiresAt = now + ttlMs;\n\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_locks (thread_id, token, expires_at) VALUES (?, ?, ?)\",\n threadId,\n token,\n expiresAt\n );\n\n return { threadId, token, expiresAt };\n });\n\n await this.scheduleCleanupForExpiry(result?.expiresAt ?? null);\n return result;\n }\n\n releaseLock(threadId: string, token: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE thread_id = ${threadId} AND token = ${token}\n `;\n }\n\n async extendLock(\n threadId: string,\n token: string,\n ttlMs: number\n ): Promise<boolean> {\n const result = this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n const rows = this.ctx.storage.sql\n .exec<{ thread_id: string }>(\n `UPDATE chat_sdk_state_locks SET expires_at = ?\n WHERE thread_id = ? AND token = ? AND expires_at > ?\n RETURNING thread_id`,\n now + ttlMs,\n threadId,\n token,\n now\n )\n .toArray();\n return rows.length > 0;\n });\n if (result) {\n await this.scheduleCleanupForExpiry(Date.now() + ttlMs);\n }\n return result;\n }\n\n forceReleaseLock(threadId: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE thread_id = ${threadId}\n `;\n }\n\n async enqueue(\n threadId: string,\n value: string,\n maxSize: number\n ): Promise<number> {\n const parsed = parseQueueEntry(value);\n\n const count = this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_queue (thread_id, value, enqueued_at, expires_at) VALUES (?, ?, ?, ?)\",\n threadId,\n value,\n parsed.enqueuedAt,\n parsed.expiresAt\n );\n\n this.ctx.storage.sql.exec(\n `DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND id NOT IN (\n SELECT id FROM chat_sdk_state_queue\n WHERE thread_id = ?\n ORDER BY id DESC\n LIMIT ?\n )`,\n threadId,\n threadId,\n maxSize\n );\n\n const row = this.ctx.storage.sql\n .exec<{ count: number }>(\n \"SELECT COUNT(*) as count FROM chat_sdk_state_queue WHERE thread_id = ?\",\n threadId\n )\n .one();\n return row.count;\n });\n await this.scheduleCleanupForExpiry(parsed.expiresAt);\n return count;\n }\n\n popQueue(threadId: string): string | null {\n return this.ctx.storage.transactionSync(() => {\n const now = Date.now();\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_queue WHERE thread_id = ? AND expires_at <= ?\",\n threadId,\n now\n );\n\n const rows = this.ctx.storage.sql\n .exec<{ id: number; value: string }>(\n \"SELECT id, value FROM chat_sdk_state_queue WHERE thread_id = ? ORDER BY id ASC LIMIT 1\",\n threadId\n )\n .toArray();\n const row = rows[0];\n if (!row) {\n return null;\n }\n\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_queue WHERE id = ?\",\n row.id\n );\n return row.value;\n });\n }\n\n queueDepth(threadId: string): number {\n const rows = this.sql<{ count: number }>`\n SELECT COUNT(*) as count\n FROM chat_sdk_state_queue\n WHERE thread_id = ${threadId} AND expires_at > ${Date.now()}\n `;\n return rows[0]?.count ?? 0;\n }\n\n async listAppend(\n key: string,\n value: string,\n maxLength?: number,\n ttlMs?: number\n ): Promise<void> {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n\n this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"INSERT INTO chat_sdk_state_lists (key, value, expires_at) VALUES (?, ?, ?)\",\n key,\n value,\n expiresAt\n );\n\n if (expiresAt !== null) {\n // Chat SDK history lists use a list-level TTL: any append refreshes the\n // expiry for the whole logical list, not only the new row.\n this.ctx.storage.sql.exec(\n \"UPDATE chat_sdk_state_lists SET expires_at = ? WHERE key = ?\",\n expiresAt,\n key\n );\n }\n\n if (maxLength != null && maxLength > 0) {\n this.ctx.storage.sql.exec(\n `DELETE FROM chat_sdk_state_lists WHERE key = ? AND id NOT IN (\n SELECT id FROM chat_sdk_state_lists\n WHERE key = ?\n ORDER BY id DESC\n LIMIT ?\n )`,\n key,\n key,\n maxLength\n );\n }\n });\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n\n listGet(key: string): string[] {\n const now = Date.now();\n\n this.sql`\n DELETE FROM chat_sdk_state_lists\n WHERE key = ${key}\n AND expires_at IS NOT NULL\n AND expires_at <= ${now}\n `;\n\n return this.sql<{ value: string }>`\n SELECT value\n FROM chat_sdk_state_lists\n WHERE key = ${key}\n ORDER BY id ASC\n `.map((row) => row.value);\n }\n\n cacheGet(key: string): string | null {\n return this.readCacheValue(key, Date.now());\n }\n\n async cacheSet(key: string, value: string, ttlMs?: number): Promise<void> {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n this.upsertCacheValue(key, value, expiresAt);\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n\n async cacheSetIfNotExists(\n key: string,\n value: string,\n ttlMs?: number\n ): Promise<boolean> {\n const now = Date.now();\n\n const inserted = this.ctx.storage.transactionSync(() => {\n this.ctx.storage.sql.exec(\n \"DELETE FROM chat_sdk_state_cache WHERE key = ? AND expires_at IS NOT NULL AND expires_at <= ?\",\n key,\n now\n );\n\n if (this.readCacheValue(key, now) !== null) {\n return false;\n }\n\n const expiresAt = ttlMs && ttlMs > 0 ? now + ttlMs : null;\n this.upsertCacheValue(key, value, expiresAt);\n return true;\n });\n if (inserted) {\n const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : null;\n await this.scheduleCleanupForExpiry(expiresAt);\n }\n return inserted;\n }\n\n cacheDelete(key: string): void {\n this.sql`\n DELETE FROM chat_sdk_state_cache\n WHERE key = ${key}\n `;\n }\n\n async cleanupExpired(payload?: { expiresAt?: number }): Promise<void> {\n const current = this.readCleanupMetadata();\n if (\n payload?.expiresAt !== undefined &&\n current.nextCleanupAt !== null &&\n payload.expiresAt !== current.nextCleanupAt\n ) {\n return;\n }\n\n const now = Date.now();\n this.clearCleanupMetadata();\n\n this.sql`\n DELETE FROM chat_sdk_state_locks\n WHERE expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_cache\n WHERE expires_at IS NOT NULL AND expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_queue\n WHERE expires_at <= ${now}\n `;\n this.sql`\n DELETE FROM chat_sdk_state_lists\n WHERE expires_at IS NOT NULL AND expires_at <= ${now}\n `;\n await this.scheduleNextCleanup();\n }\n\n private migrate(): void {\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_subscriptions (\n thread_id TEXT PRIMARY KEY\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_locks (\n thread_id TEXT PRIMARY KEY,\n token TEXT NOT NULL,\n expires_at INTEGER NOT NULL\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_cache (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at INTEGER\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_queue (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n thread_id TEXT NOT NULL,\n value TEXT NOT NULL,\n enqueued_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_lists (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n expires_at INTEGER\n )\n `;\n\n this.sql`\n CREATE TABLE IF NOT EXISTS chat_sdk_state_metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )\n `;\n\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_locks_expires\n ON chat_sdk_state_locks(expires_at)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_cache_expires\n ON chat_sdk_state_cache(expires_at)\n WHERE expires_at IS NOT NULL\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_thread\n ON chat_sdk_state_queue(thread_id, id)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_queue_expires\n ON chat_sdk_state_queue(expires_at)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_key\n ON chat_sdk_state_lists(key, id)\n `;\n this.sql`\n CREATE INDEX IF NOT EXISTS idx_chat_sdk_state_lists_expires\n ON chat_sdk_state_lists(expires_at)\n WHERE expires_at IS NOT NULL\n `;\n }\n\n private readCacheValue(key: string, now: number): string | null {\n const rows = this.sql<{ value: string }>`\n SELECT value\n FROM chat_sdk_state_cache\n WHERE key = ${key}\n AND (expires_at IS NULL OR expires_at > ${now})\n `;\n return rows[0]?.value ?? null;\n }\n\n private upsertCacheValue(\n key: string,\n value: string,\n expiresAt: number | null\n ): void {\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_cache (key, value, expires_at)\n VALUES (${key}, ${value}, ${expiresAt})\n `;\n }\n\n private async scheduleCleanupForExpiry(\n expiresAt: number | null\n ): Promise<void> {\n if (expiresAt === null) {\n return;\n }\n\n await this.ensureCleanupScheduled(expiresAt);\n }\n\n private async scheduleNextCleanup(): Promise<void> {\n const next = this.nextExpiry();\n if (next === null) {\n const current = this.readCleanupMetadata();\n if (current.scheduleId) {\n await this.cancelSchedule(current.scheduleId).catch(() => false);\n }\n this.clearCleanupMetadata();\n return;\n }\n\n await this.ensureCleanupScheduled(next);\n }\n\n private async ensureCleanupScheduled(expiresAt: number): Promise<void> {\n const current = this.readCleanupMetadata();\n if (current.nextCleanupAt !== null && current.nextCleanupAt <= expiresAt) {\n return;\n }\n\n if (current.scheduleId) {\n await this.cancelSchedule(current.scheduleId).catch(() => false);\n }\n\n const delaySeconds = Math.max(\n 0,\n Math.ceil((expiresAt - Date.now()) / 1000)\n );\n const schedule = await this.schedule(delaySeconds, \"cleanupExpired\", {\n expiresAt\n });\n this.writeCleanupMetadata(expiresAt, schedule.id);\n }\n\n private nextExpiry(): number | null {\n const rows = this.sql<{ expires_at: number | null }>`\n SELECT MIN(expires_at) as expires_at\n FROM (\n SELECT expires_at FROM chat_sdk_state_locks\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_queue\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_cache WHERE expires_at IS NOT NULL\n UNION ALL\n SELECT expires_at FROM chat_sdk_state_lists WHERE expires_at IS NOT NULL\n )\n `;\n return rows[0]?.expires_at ?? null;\n }\n\n private readCleanupMetadata(): {\n nextCleanupAt: number | null;\n scheduleId: string | null;\n } {\n const rows = this.sql<{ key: string; value: string }>`\n SELECT key, value\n FROM chat_sdk_state_metadata\n WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})\n `;\n const values = new Map(rows.map((row) => [row.key, row.value]));\n const nextCleanupAt = Number(values.get(NEXT_CLEANUP_AT_KEY));\n return {\n nextCleanupAt: Number.isFinite(nextCleanupAt) ? nextCleanupAt : null,\n scheduleId: values.get(CLEANUP_SCHEDULE_ID_KEY) ?? null\n };\n }\n\n private writeCleanupMetadata(expiresAt: number, scheduleId: string): void {\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)\n VALUES (${NEXT_CLEANUP_AT_KEY}, ${String(expiresAt)})\n `;\n this.sql`\n INSERT OR REPLACE INTO chat_sdk_state_metadata (key, value)\n VALUES (${CLEANUP_SCHEDULE_ID_KEY}, ${scheduleId})\n `;\n }\n\n private clearCleanupMetadata(): void {\n this.sql`\n DELETE FROM chat_sdk_state_metadata\n WHERE key IN (${NEXT_CLEANUP_AT_KEY}, ${CLEANUP_SCHEDULE_ID_KEY})\n `;\n }\n}\n\nfunction parseQueueEntry(value: string): StoredQueueEntry {\n const raw = JSON.parse(value) as Record<string, unknown>;\n if (typeof raw.enqueuedAt !== \"number\" || typeof raw.expiresAt !== \"number\") {\n throw new Error(\n \"ChatSdkStateAgent expected QueueEntry JSON with numeric TTLs\"\n );\n }\n\n return {\n enqueuedAt: raw.enqueuedAt,\n expiresAt: raw.expiresAt\n };\n}\n","import type {\n Lock as ChatSdkLock,\n QueueEntry as ChatSdkQueueEntry,\n StateAdapter as ChatSdkStateAdapterInterface\n} from \"chat\";\nimport {\n getCurrentAgent,\n type SubAgentClass,\n type SubAgentStub\n} from \"../index\";\nimport { ChatSdkStateAgent } from \"./agent\";\nimport type { ChatSdkStateAdapterOptions } from \"./types\";\n\nconst THREAD_STATE_PREFIX = \"thread-state:\";\nconst CHANNEL_STATE_PREFIX = \"channel-state:\";\nconst MESSAGE_HISTORY_PREFIX = \"msg-history:\";\nconst TRANSCRIPTS_USER_PREFIX = \"transcripts:user:\";\n\nfunction parseStoredJson<T>(raw: string, label: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch (error) {\n throw new Error(`ChatSdkStateAdapter expected JSON-encoded ${label}`, {\n cause: error\n });\n }\n}\n\nexport function defaultThreadShard(threadId: string): string {\n return threadId.split(\":\").slice(0, 2).join(\":\") || \"default\";\n}\n\nexport function defaultKeyShard(\n key: string,\n shardThread: (threadId: string) => string = defaultThreadShard\n): string | undefined {\n for (const prefix of [\n THREAD_STATE_PREFIX,\n CHANNEL_STATE_PREFIX,\n MESSAGE_HISTORY_PREFIX,\n TRANSCRIPTS_USER_PREFIX\n ]) {\n if (key.startsWith(prefix)) {\n return shardThread(key.slice(prefix.length));\n }\n }\n\n return undefined;\n}\n\nexport class ChatSdkStateAdapter implements ChatSdkStateAdapterInterface {\n private readonly parent: NonNullable<ChatSdkStateAdapterOptions[\"parent\"]>;\n private readonly agentClass: SubAgentClass<ChatSdkStateAgent>;\n private readonly defaultName: string;\n private readonly keyShard?: (key: string) => string | undefined;\n private readonly shardKey: (threadId: string) => string;\n private readonly lockHeartbeat: boolean;\n private readonly lockHeartbeats = new Map<\n string,\n ReturnType<typeof setInterval>\n >();\n private connected = false;\n\n constructor(options: ChatSdkStateAdapterOptions = {}) {\n const parent = options.parent ?? getCurrentAgent().agent;\n if (!parent) {\n throw new Error(\n \"ChatSdkStateAdapter requires a parent Agent. Pass `parent` or create it inside an Agent context.\"\n );\n }\n\n this.parent = parent;\n this.agentClass = options.agent ?? ChatSdkStateAgent;\n this.defaultName = options.name ?? \"default\";\n this.keyShard = options.keyShard;\n this.shardKey = options.shardKey ?? defaultThreadShard;\n this.lockHeartbeat = options.lockHeartbeat ?? false;\n }\n\n async connect(): Promise<void> {\n this.connected = true;\n }\n\n async disconnect(): Promise<void> {\n this.connected = false;\n for (const timer of this.lockHeartbeats.values()) clearInterval(timer);\n this.lockHeartbeats.clear();\n }\n\n async subscribe(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).subscribe(threadId);\n }\n\n async unsubscribe(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).unsubscribe(threadId);\n }\n\n async isSubscribed(threadId: string): Promise<boolean> {\n return (await this.stateAgent(threadId)).isSubscribed(threadId);\n }\n\n async acquireLock(\n threadId: string,\n ttlMs: number\n ): Promise<ChatSdkLock | null> {\n const lock = await (\n await this.stateAgent(threadId)\n ).acquireLock(threadId, ttlMs);\n if (lock) this.startLockHeartbeat(lock, ttlMs);\n return lock;\n }\n\n async releaseLock(lock: ChatSdkLock): Promise<void> {\n this.stopLockHeartbeat(lock.token);\n await (\n await this.stateAgent(lock.threadId)\n ).releaseLock(lock.threadId, lock.token);\n }\n\n async extendLock(lock: ChatSdkLock, ttlMs: number): Promise<boolean> {\n return (await this.stateAgent(lock.threadId)).extendLock(\n lock.threadId,\n lock.token,\n ttlMs\n );\n }\n\n async forceReleaseLock(threadId: string): Promise<void> {\n await (await this.stateAgent(threadId)).forceReleaseLock(threadId);\n }\n\n private startLockHeartbeat(lock: ChatSdkLock, ttlMs: number): void {\n // An acquire that was in flight when `disconnect()` ran still resolves.\n if (!this.lockHeartbeat || ttlMs <= 0 || !this.connected) return;\n const timer = setInterval(() => {\n if (!this.connected) {\n this.stopLockHeartbeat(lock.token);\n return;\n }\n this.extendLock(lock, ttlMs).then(\n (extended) => {\n if (!extended) this.stopLockHeartbeat(lock.token);\n },\n () => {\n // A failed extension is retried on the next beat; the lock's own\n // TTL still bounds how long it outlives this isolate.\n }\n );\n }, ttlMs / 3);\n this.lockHeartbeats.set(lock.token, timer);\n }\n\n private stopLockHeartbeat(token: string): void {\n const timer = this.lockHeartbeats.get(token);\n if (timer === undefined) return;\n clearInterval(timer);\n this.lockHeartbeats.delete(token);\n }\n\n async enqueue(\n threadId: string,\n entry: ChatSdkQueueEntry,\n maxSize: number\n ): Promise<number> {\n return (await this.stateAgent(threadId)).enqueue(\n threadId,\n JSON.stringify(entry),\n maxSize\n );\n }\n\n async dequeue(threadId: string): Promise<ChatSdkQueueEntry | null> {\n const raw = await (await this.stateAgent(threadId)).popQueue(threadId);\n return raw === null\n ? null\n : parseStoredJson<ChatSdkQueueEntry>(raw, `queue entry for ${threadId}`);\n }\n\n async queueDepth(threadId: string): Promise<number> {\n return (await this.stateAgent(threadId)).queueDepth(threadId);\n }\n\n async appendToList(\n key: string,\n value: unknown,\n options?: { maxLength?: number; ttlMs?: number }\n ): Promise<void> {\n await (\n await this.stateAgentForKey(key)\n ).listAppend(\n key,\n JSON.stringify(value),\n options?.maxLength,\n options?.ttlMs\n );\n }\n\n async getList<T = unknown>(key: string): Promise<T[]> {\n const raw = await (await this.stateAgentForKey(key)).listGet(key);\n return raw.map((value) =>\n parseStoredJson<T>(value, `list entry for ${key}`)\n );\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const raw = await (await this.stateAgentForKey(key)).cacheGet(key);\n return raw === null ? null : parseStoredJson<T>(raw, `cache key ${key}`);\n }\n\n async set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> {\n await (\n await this.stateAgentForKey(key)\n ).cacheSet(key, JSON.stringify(value), ttlMs);\n }\n\n async setIfNotExists<T = unknown>(\n key: string,\n value: T,\n ttlMs?: number\n ): Promise<boolean> {\n return (await this.stateAgentForKey(key)).cacheSetIfNotExists(\n key,\n JSON.stringify(value),\n ttlMs\n );\n }\n\n async delete(key: string): Promise<void> {\n await (await this.stateAgentForKey(key)).cacheDelete(key);\n }\n\n private async stateAgent(\n threadId?: string\n ): Promise<SubAgentStub<ChatSdkStateAgent>> {\n this.ensureConnected();\n const name = threadId ? this.shardKey(threadId) : this.defaultName;\n return this.parent.subAgent(this.agentClass, name);\n }\n\n private async stateAgentForKey(\n key: string\n ): Promise<SubAgentStub<ChatSdkStateAgent>> {\n this.ensureConnected();\n const name =\n this.keyShard?.(key) ??\n defaultKeyShard(key, this.shardKey) ??\n this.defaultName;\n return this.parent.subAgent(this.agentClass, name);\n }\n\n private ensureConnected(): void {\n if (!this.connected) {\n throw new Error(\"ChatSdkStateAdapter is not connected\");\n }\n }\n}\n","export {\n ChatSdkStateAdapter,\n defaultKeyShard,\n defaultThreadShard\n} from \"./adapter\";\nexport { ChatSdkStateAgent } from \"./agent\";\nexport type { ChatSdkStateParent, ChatSdkStateAdapterOptions } from \"./types\";\n\nimport { ChatSdkStateAdapter } from \"./adapter\";\nimport type { ChatSdkStateAdapterOptions } from \"./types\";\n\nexport function createChatSdkState(\n options: ChatSdkStateAdapterOptions = {}\n): ChatSdkStateAdapter {\n return new ChatSdkStateAdapter(options);\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { camelCaseToKebabCase, isInternalJsStubProp } from "./utils.js";
2
2
  import "./types.js";
3
3
  import { i as capnWebTransportUrl, n as CAPNWEB_TRANSPORT_SEND } from "./transport-protocol-BxyngQ_R.js";
4
- import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
4
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
5
+ import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
5
6
  import { PartySocket } from "partysocket";
6
7
  import { RpcTarget, newWebSocketRpcSession } from "capnweb";
7
8
  //#region src/websockets/capnweb-socket.ts
@@ -131,6 +132,40 @@ function isTerminalCloseEvent(event) {
131
132
  return event.code === 1008 || event.code >= 4e3 && event.code <= 4999;
132
133
  }
133
134
  /**
135
+ * PartySocket's `shouldReconnectOnClose`, plus a record of whether that
136
+ * decision ended reconnection so the close can surface a connection error.
137
+ *
138
+ * Reconnection ends on a terminal close code, when the caller's
139
+ * `shouldReconnectOnClose` declines, or when `maxRetries` is spent (a
140
+ * failed handshake never reaches the server's close codes, so it only
141
+ * stops this way). An explicit `close()` has already cleared
142
+ * `shouldReconnect` by the time PartySocket asks, and is not an error.
143
+ * PartySocket asks before it schedules the next attempt, so `retryCount`
144
+ * still counts the attempt that just closed.
145
+ * @internal
146
+ */
147
+ function createCloseClassifier(options) {
148
+ let finalClose = false;
149
+ return {
150
+ shouldReconnectOnClose(event) {
151
+ const reconnect = (options.shouldReconnectOnClose()?.(event) ?? true) && !isTerminalCloseEvent(event);
152
+ const socket = options.socket();
153
+ if (socket?.shouldReconnect === false) finalClose = false;
154
+ else if (!reconnect) finalClose = true;
155
+ else {
156
+ const maxRetries = options.maxRetries() ?? Number.POSITIVE_INFINITY;
157
+ finalClose = (socket?.retryCount ?? 0) >= maxRetries;
158
+ }
159
+ return reconnect;
160
+ },
161
+ takeFinalClose() {
162
+ const final = finalClose;
163
+ finalClose = false;
164
+ return final;
165
+ }
166
+ };
167
+ }
168
+ /**
134
169
  * Default timeout (in milliseconds) applied to non-streaming RPC calls
135
170
  * that don't pass an explicit `timeout`. Acts as a backstop so calls
136
171
  * whose response is lost (e.g. the connection drops mid-flight) reject
@@ -259,6 +294,8 @@ function createStubProxy(call) {
259
294
  return (...args) => call(method, args);
260
295
  } });
261
296
  }
297
+ var _connect = /* @__PURE__ */ new WeakMap();
298
+ var _AgentClient_brand = /* @__PURE__ */ new WeakSet();
262
299
  var _capnWeb = /* @__PURE__ */ new WeakMap();
263
300
  var _nativeQueue = /* @__PURE__ */ new WeakMap();
264
301
  /**
@@ -273,6 +310,9 @@ var AgentClient = class extends PartySocket {
273
310
  }
274
311
  /**
275
312
  * Promise that resolves when identity has been received from the server.
313
+ * When the server also sends the stored state on connect, it resolves only
314
+ * after that state has been applied, so `state` is current once it settles.
315
+ * Servers without that signal resolve on identity alone.
276
316
  * Useful for waiting before making calls that depend on knowing the instance.
277
317
  * Resets on connection close so it can be awaited again after reconnect.
278
318
  */
@@ -290,8 +330,13 @@ var AgentClient = class extends PartySocket {
290
330
  capnWeb.current = socket;
291
331
  }) } : {};
292
332
  const agentNamespace = camelCaseToKebabCase(options.agent);
293
- const shouldReconnectOnClose = options.shouldReconnectOnClose;
294
- const classifyReconnect = (event) => (shouldReconnectOnClose?.(event) ?? true) && !isTerminalCloseEvent(event);
333
+ const self = { current: null };
334
+ const closeClassifier = createCloseClassifier({
335
+ socket: () => self.current,
336
+ shouldReconnectOnClose: () => options.shouldReconnectOnClose,
337
+ maxRetries: () => options.maxRetries
338
+ });
339
+ const classifyReconnect = closeClassifier.shouldReconnectOnClose;
295
340
  const socketOptions = options.basePath ? {
296
341
  basePath: options.basePath,
297
342
  path: options.path,
@@ -308,14 +353,20 @@ var AgentClient = class extends PartySocket {
308
353
  shouldReconnectOnClose: classifyReconnect
309
354
  };
310
355
  super(socketOptions);
356
+ _classPrivateMethodInitSpec(this, _AgentClient_brand);
311
357
  this.state = void 0;
312
358
  this.identified = false;
313
359
  this.connectionError = null;
314
360
  this._pendingCalls = /* @__PURE__ */ new Map();
315
361
  this._previousName = null;
316
362
  this._previousAgent = null;
363
+ _classPrivateFieldInitSpec(this, _connect, {
364
+ stateSeen: false,
365
+ pendingIdentity: null
366
+ });
317
367
  _classPrivateFieldInitSpec(this, _capnWeb, void 0);
318
368
  _classPrivateFieldInitSpec(this, _nativeQueue, new NativeCallQueue());
369
+ self.current = this;
319
370
  _classPrivateFieldSet2(_capnWeb, this, capnWeb);
320
371
  this.agent = agentNamespace;
321
372
  this.name = options.name || "default";
@@ -330,32 +381,29 @@ var AgentClient = class extends PartySocket {
330
381
  return;
331
382
  }
332
383
  if (parsedMessage.type === "cf_agent_identity") {
333
- const oldName = this._previousName;
334
- const oldAgent = this._previousAgent;
335
- const newName = parsedMessage.name;
336
- const newAgent = parsedMessage.agent;
337
- this.identified = true;
338
- this._resolveReady();
339
- if (oldName !== null && oldAgent !== null && (oldName !== newName || oldAgent !== newAgent)) if (this.options.onIdentityChange) this.options.onIdentityChange(oldName, newName, oldAgent, newAgent);
340
- else {
341
- const agentChanged = oldAgent !== newAgent;
342
- const nameChanged = oldName !== newName;
343
- let changeDescription = "";
344
- if (agentChanged && nameChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}", instance "${oldName}" → "${newName}"`;
345
- else if (agentChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}"`;
346
- else changeDescription = `instance "${oldName}" → "${newName}"`;
347
- console.warn(`[agents] Identity changed on reconnect: ${changeDescription}. This can happen with server-side routing (e.g., basePath with getAgentByName) where the instance is determined by auth/session. Provide onIdentityChange callback to handle this explicitly, or ignore if this is expected for your routing pattern.`);
384
+ const identity = {
385
+ name: parsedMessage.name,
386
+ agent: parsedMessage.agent
387
+ };
388
+ if (parsedMessage.stateFollows === true && !_classPrivateFieldGet2(_connect, this).stateSeen) {
389
+ _classPrivateFieldGet2(_connect, this).pendingIdentity = identity;
390
+ return;
348
391
  }
349
- this._previousName = newName;
350
- this._previousAgent = newAgent;
351
- this.name = newName;
352
- this.agent = newAgent;
353
- this.options.onIdentity?.(newName, newAgent);
392
+ _assertClassBrand(_AgentClient_brand, this, _applyIdentity).call(this, identity.name, identity.agent);
354
393
  return;
355
394
  }
356
395
  if (parsedMessage.type === "cf_agent_state") {
357
396
  this.state = parsedMessage.state;
358
- this.options.onStateUpdate?.(parsedMessage.state, "server");
397
+ try {
398
+ this.options.onStateUpdate?.(parsedMessage.state, "server");
399
+ } finally {
400
+ _classPrivateFieldGet2(_connect, this).stateSeen = true;
401
+ const pending = _classPrivateFieldGet2(_connect, this).pendingIdentity;
402
+ if (pending) {
403
+ _classPrivateFieldGet2(_connect, this).pendingIdentity = null;
404
+ _assertClassBrand(_AgentClient_brand, this, _applyIdentity).call(this, pending.name, pending.agent);
405
+ }
406
+ }
359
407
  return;
360
408
  }
361
409
  if (parsedMessage.type === "cf_agent_state_error") {
@@ -394,14 +442,18 @@ var AgentClient = class extends PartySocket {
394
442
  if (native) _classPrivateFieldGet2(_nativeQueue, this).flush(native);
395
443
  });
396
444
  this.addEventListener("close", (event) => {
397
- const terminalClose = isTerminalCloseEvent(event);
445
+ const finalClose = closeClassifier.takeFinalClose();
398
446
  this.identified = false;
399
447
  this._resetReady();
400
- if (this.shouldReconnect) this._rejectPendingCalls("Connection closed", { onlyTransmitted: true });
448
+ _classPrivateFieldSet2(_connect, this, {
449
+ stateSeen: false,
450
+ pendingIdentity: null
451
+ });
452
+ if (this.shouldReconnect && !finalClose) this._rejectPendingCalls("Connection closed", { onlyTransmitted: true });
401
453
  else {
402
454
  this._rejectPendingCalls("Connection closed");
403
455
  _classPrivateFieldGet2(_nativeQueue, this).rejectAll("Connection closed");
404
- if (terminalClose) {
456
+ if (finalClose) {
405
457
  const error = new AgentConnectionError(event);
406
458
  this.connectionError = error;
407
459
  this.options.onConnectionError?.(error);
@@ -493,6 +545,27 @@ var AgentClient = class extends PartySocket {
493
545
  });
494
546
  }
495
547
  };
548
+ function _applyIdentity(newName, newAgent) {
549
+ const oldName = this._previousName;
550
+ const oldAgent = this._previousAgent;
551
+ this.identified = true;
552
+ this._resolveReady();
553
+ if (oldName !== null && oldAgent !== null && (oldName !== newName || oldAgent !== newAgent)) if (this.options.onIdentityChange) this.options.onIdentityChange(oldName, newName, oldAgent, newAgent);
554
+ else {
555
+ const agentChanged = oldAgent !== newAgent;
556
+ const nameChanged = oldName !== newName;
557
+ let changeDescription = "";
558
+ if (agentChanged && nameChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}", instance "${oldName}" → "${newName}"`;
559
+ else if (agentChanged) changeDescription = `agent "${oldAgent}" → "${newAgent}"`;
560
+ else changeDescription = `instance "${oldName}" → "${newName}"`;
561
+ console.warn(`[agents] Identity changed on reconnect: ${changeDescription}. This can happen with server-side routing (e.g., basePath with getAgentByName) where the instance is determined by auth/session. Provide onIdentityChange callback to handle this explicitly, or ignore if this is expected for your routing pattern.`);
562
+ }
563
+ this._previousName = newName;
564
+ this._previousAgent = newAgent;
565
+ this.name = newName;
566
+ this.agent = newAgent;
567
+ this.options.onIdentity?.(newName, newAgent);
568
+ }
496
569
  /**
497
570
  * Make an HTTP request to an Agent
498
571
  * @param opts Connection options
@@ -513,6 +586,6 @@ function agentFetch(opts, init) {
513
586
  }, init);
514
587
  }
515
588
  //#endregion
516
- export { agentFetch as a, nativeCall as c, NativeCallQueue as i, splitCallOptions as l, AgentConnectionError as n, createStubProxy as o, DEFAULT_CALL_TIMEOUT_MS as r, isTerminalCloseEvent as s, AgentClient as t, boundCapnWebSocket as u };
589
+ export { agentFetch as a, isTerminalCloseEvent as c, boundCapnWebSocket as d, NativeCallQueue as i, nativeCall as l, AgentConnectionError as n, createCloseClassifier as o, DEFAULT_CALL_TIMEOUT_MS as r, createStubProxy as s, AgentClient as t, splitCallOptions as u };
517
590
 
518
- //# sourceMappingURL=client-C9DAjZIY.js.map
591
+ //# sourceMappingURL=client-4OlMIJx-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-4OlMIJx-.js","names":[],"sources":["../src/websockets/capnweb-socket.ts","../src/client.ts"],"sourcesContent":["import { newWebSocketRpcSession, RpcTarget, type RpcStub } from \"capnweb\";\nimport {\n CAPNWEB_TRANSPORT_SEND,\n capnWebTransportUrl,\n type TransportClientEvents,\n type TransportHostPipe,\n type TransportMessage\n} from \"./transport-protocol\";\n\n/** Local root the host calls to deliver frames. */\nclass Inbox extends RpcTarget implements TransportClientEvents {\n constructor(private readonly socket: CapnWebSocket) {\n super();\n }\n\n message(value: TransportMessage): void {\n this.socket.dispatchEvent(new MessageEvent(\"message\", { data: value }));\n }\n}\n\n/**\n * A WebSocket-shaped client for the Cap'n Web connection transport.\n *\n * It has the constructor, `readyState`, `send()`, `close()`, and events of\n * a `WebSocket`, so PartySocket (and therefore `useAgent` and\n * `AgentClient`) can drive it as a drop-in socket implementation and keep\n * owning reconnection, buffering, and backoff. Frames sent here travel\n * through the host's pipe method; frames from the host arrive as\n * `message` events.\n *\n * @experimental The transport is experimental.\n */\nexport class CapnWebSocket extends EventTarget {\n static readonly CONNECTING = 0;\n static readonly OPEN = 1;\n static readonly CLOSING = 2;\n static readonly CLOSED = 3;\n readonly CONNECTING = 0;\n readonly OPEN = 1;\n readonly CLOSING = 2;\n readonly CLOSED = 3;\n\n readonly url: string;\n readyState = CapnWebSocket.CONNECTING;\n binaryType: BinaryType = \"arraybuffer\";\n bufferedAmount = 0;\n extensions = \"\";\n protocol = \"\";\n onopen: ((event: Event) => void) | null = null;\n onmessage: ((event: MessageEvent) => void) | null = null;\n onclose: ((event: CloseEvent) => void) | null = null;\n onerror: ((event: Event) => void) | null = null;\n\n readonly #socket: WebSocket;\n readonly #root: RpcStub<TransportHostPipe>;\n\n constructor(url: string | URL, protocols?: string | string[]) {\n super();\n this.url = capnWebTransportUrl(url);\n this.#socket = protocols\n ? new WebSocket(this.url, protocols)\n : new WebSocket(this.url);\n this.#root = newWebSocketRpcSession<TransportHostPipe>(\n this.#socket,\n new Inbox(this)\n );\n this.#socket.addEventListener(\"open\", () => {\n this.readyState = CapnWebSocket.OPEN;\n this.protocol = this.#socket.protocol;\n this.dispatchEvent(new Event(\"open\"));\n });\n this.#socket.addEventListener(\"close\", (event) => {\n this.readyState = CapnWebSocket.CLOSED;\n this.dispatchEvent(\n new CloseEvent(\"close\", {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean\n })\n );\n });\n this.#socket.addEventListener(\"error\", () => {\n this.dispatchEvent(new Event(\"error\"));\n });\n }\n\n override dispatchEvent(event: Event): boolean {\n const handler = Reflect.get(this, `on${event.type}`) as unknown;\n if (typeof handler === \"function\") Reflect.apply(handler, this, [event]);\n return super.dispatchEvent(event);\n }\n\n /**\n * Invoke one of the host's native callables on the session root. The\n * result keeps Cap'n Web semantics: an `RpcTarget` arrives as a live\n * stub, a `ReadableStream` streams, and chained calls pipeline.\n */\n invoke(method: string, args: unknown[]): Promise<unknown> {\n if (this.readyState !== CapnWebSocket.OPEN) {\n return Promise.reject(new Error(\"Connection closed\"));\n }\n // The root stub answers every property with a callable, so an unknown\n // method is only detected by the host, which rejects the call. The\n // result is an RpcPromise; resolve through a plain Promise so callers\n // hold nothing exotic.\n const root = this.#root as unknown as Record<\n string,\n (...a: unknown[]) => unknown\n >;\n return Promise.resolve(root[method](...args));\n }\n\n send(data: TransportMessage): void {\n if (this.readyState !== CapnWebSocket.OPEN) {\n throw new DOMException(\"WebSocket is not open\", \"InvalidStateError\");\n }\n void this.#root[CAPNWEB_TRANSPORT_SEND](data).catch(() => {\n this.dispatchEvent(new Event(\"error\"));\n });\n }\n\n close(code?: number, reason?: string): void {\n if (this.readyState >= CapnWebSocket.CLOSING) return;\n this.readyState = CapnWebSocket.CLOSING;\n try {\n this.#root[Symbol.dispose]();\n } catch {\n // The session is already gone; closing the socket is enough.\n }\n this.#socket.close(code, reason);\n }\n}\n\n/**\n * A `CapnWebSocket` subclass that reports each instance it constructs.\n * PartySocket instantiates the class it is given; this is how a client\n * gets hold of the live socket to call `invoke()` without reaching into\n * PartySocket internals.\n */\nexport function boundCapnWebSocket(\n onCreate: (socket: CapnWebSocket) => void\n): typeof CapnWebSocket {\n return class BoundCapnWebSocket extends CapnWebSocket {\n constructor(url: string | URL, protocols?: string | string[]) {\n super(url, protocols);\n onCreate(this);\n }\n };\n}\n","import {\n type PartyFetchOptions,\n PartySocket,\n type PartySocketOptions\n} from \"partysocket\";\nimport type { Agent, RPCRequest, RPCResponse } from \"./\";\nimport type {\n ClientParameters,\n Method,\n RPCMethod,\n SerializableReturnValue,\n SerializableValue\n} from \"./serializable\";\nimport { MessageType } from \"./types\";\nimport { camelCaseToKebabCase, isInternalJsStubProp } from \"./utils\";\nimport {\n boundCapnWebSocket,\n type CapnWebSocket\n} from \"./websockets/capnweb-socket\";\nimport type { AgentTransport } from \"./websockets/transport-protocol\";\n\nexport type { AgentTransport } from \"./websockets/transport-protocol\";\n\nexport class AgentConnectionError extends Error {\n code: number;\n reason: string;\n wasClean: boolean;\n\n constructor(event: CloseEvent) {\n const reason = event.reason || `WebSocket closed with code ${event.code}`;\n super(`Agent connection closed: ${reason}`);\n this.name = \"AgentConnectionError\";\n this.code = event.code;\n this.reason = event.reason;\n this.wasClean = event.wasClean;\n }\n}\n\nexport function isTerminalCloseEvent(event: CloseEvent): boolean {\n return event.code === 1008 || (event.code >= 4000 && event.code <= 4999);\n}\n\ntype ReconnectingSocket = { shouldReconnect: boolean; retryCount: number };\n\n/**\n * PartySocket's `shouldReconnectOnClose`, plus a record of whether that\n * decision ended reconnection so the close can surface a connection error.\n *\n * Reconnection ends on a terminal close code, when the caller's\n * `shouldReconnectOnClose` declines, or when `maxRetries` is spent (a\n * failed handshake never reaches the server's close codes, so it only\n * stops this way). An explicit `close()` has already cleared\n * `shouldReconnect` by the time PartySocket asks, and is not an error.\n * PartySocket asks before it schedules the next attempt, so `retryCount`\n * still counts the attempt that just closed.\n * @internal\n */\nexport function createCloseClassifier(options: {\n socket: () => ReconnectingSocket | null | undefined;\n shouldReconnectOnClose: () => ((event: CloseEvent) => boolean) | undefined;\n maxRetries: () => number | undefined;\n}): {\n shouldReconnectOnClose: (event: CloseEvent) => boolean;\n takeFinalClose: () => boolean;\n} {\n let finalClose = false;\n return {\n shouldReconnectOnClose(event) {\n const reconnect =\n (options.shouldReconnectOnClose()?.(event) ?? true) &&\n !isTerminalCloseEvent(event);\n const socket = options.socket();\n if (socket?.shouldReconnect === false) {\n finalClose = false;\n } else if (!reconnect) {\n finalClose = true;\n } else {\n const maxRetries = options.maxRetries() ?? Number.POSITIVE_INFINITY;\n finalClose = (socket?.retryCount ?? 0) >= maxRetries;\n }\n return reconnect;\n },\n takeFinalClose() {\n const final = finalClose;\n finalClose = false;\n return final;\n }\n };\n}\n\ntype TerminalReconnectOptions = {\n shouldReconnectOnClose?: (event: CloseEvent) => boolean;\n};\n\n/**\n * Options for creating an AgentClient\n */\nexport type AgentClientOptions<State = unknown> = Omit<\n PartySocketOptions,\n \"party\" | \"room\"\n> &\n TerminalReconnectOptions & {\n /**\n * Wire the connection travels on. `\"cf-websocket\"` (default) is a\n * hibernating WebSocket where `call()` sends JSON `rpc` frames.\n * `\"capnweb\"` carries protocol frames over a Cap'n Web session whose\n * root also serves the host's `callables` natively, so `call()` and\n * `stub` invoke them directly and an `RpcTarget` result is a live stub.\n * The Durable Object stays in memory while a capnweb connection is open.\n * @experimental The `\"capnweb\"` transport is experimental.\n */\n transport?: AgentTransport;\n /** Name of the agent to connect to (ignored if basePath is set) */\n agent: string;\n /** Name of the specific Agent instance (ignored if basePath is set) */\n name?: string;\n /**\n * Full URL path - bypasses agent/name URL construction.\n * When set, the client connects to this path directly.\n * Server must handle routing manually (e.g., with getAgentByName + fetch).\n * @example\n * // Client connects to /user, server routes based on session\n * useAgent({ agent: \"UserAgent\", basePath: \"user\" })\n */\n basePath?: string;\n /** Called when the Agent's state is updated */\n onStateUpdate?: (state: State, source: \"server\" | \"client\") => void;\n /** Called when a state update fails (e.g., connection is readonly) */\n onStateUpdateError?: (error: string) => void;\n /**\n * Called when the server sends the agent's identity on connect.\n * Useful when using basePath, as the actual instance name is determined server-side.\n * @param name The actual agent instance name\n * @param agent The agent class name (kebab-case)\n */\n onIdentity?: (name: string, agent: string) => void;\n /**\n * Called when identity changes on reconnect (different instance than before).\n * If not provided and identity changes, a warning will be logged.\n * @param oldName Previous instance name\n * @param newName New instance name\n * @param oldAgent Previous agent class name\n * @param newAgent New agent class name\n */\n onIdentityChange?: (\n oldName: string,\n newName: string,\n oldAgent: string,\n newAgent: string\n ) => void;\n /**\n * Additional path to append to the URL.\n * Works with both standard routing and basePath.\n * @example\n * // With basePath: /user/settings\n * { basePath: \"user\", path: \"settings\" }\n * // Standard: /agents/my-agent/room/settings\n * { agent: \"MyAgent\", name: \"room\", path: \"settings\" }\n */\n path?: string;\n /**\n * Default timeout (in milliseconds) applied to non-streaming `call()`s\n * that don't pass an explicit `timeout`. Defaults to 30 000 ms.\n * Set to `0` to disable. Streaming calls never get a default timeout.\n */\n defaultCallTimeout?: number;\n /**\n * Called when the connection closes and will not reconnect on its own: a\n * terminal close code (1008 or 4000-4999), `shouldReconnectOnClose`\n * returning false, or `maxRetries` spent.\n */\n onConnectionError?: (error: AgentConnectionError) => void;\n };\n\n/**\n * Default timeout (in milliseconds) applied to non-streaming RPC calls\n * that don't pass an explicit `timeout`. Acts as a backstop so calls\n * whose response is lost (e.g. the connection drops mid-flight) reject\n * instead of hanging forever. Override per client via\n * `defaultCallTimeout`, or per call via `timeout` (0 disables).\n */\nexport const DEFAULT_CALL_TIMEOUT_MS = 30_000;\n\n/**\n * Options for streaming RPC calls\n */\nexport type StreamOptions = {\n /** Called when a chunk of data is received */\n onChunk?: (chunk: unknown) => void;\n /** Called when the stream ends */\n onDone?: (finalChunk: unknown) => void;\n /** Called when an error occurs */\n onError?: (error: string) => void;\n};\n\n/**\n * Options for RPC calls\n */\nexport type CallOptions = {\n /** Timeout in milliseconds. If the call doesn't complete within this time, it will be rejected. */\n timeout?: number;\n /** Streaming options for handling streaming responses */\n stream?: StreamOptions;\n};\n\n/**\n * Normalize `call()` options. The legacy shape is the stream callbacks\n * themselves (`{ onChunk, onDone, onError }`); the current shape nests\n * them under `stream` beside `timeout`.\n */\nexport function splitCallOptions(\n options: CallOptions | StreamOptions | undefined\n): {\n stream: StreamOptions | undefined;\n timeout: number | undefined;\n} {\n const legacy =\n options !== undefined &&\n (\"onChunk\" in options || \"onDone\" in options || \"onError\" in options);\n return legacy\n ? { stream: options as StreamOptions, timeout: undefined }\n : {\n stream: (options as CallOptions | undefined)?.stream,\n timeout: (options as CallOptions | undefined)?.timeout\n };\n}\n\n/**\n * One native call on the Cap'n Web transport, with the timeout and\n * stream-callback contract `call()` has on the JSON wire. The timeout\n * covers the whole call, draining included: on expiry the stream reader is\n * cancelled and the promise rejects once. A `ReadableStream` result is\n * drained into the stream callbacks when they are given; any other value,\n * live stubs included, passes straight through.\n */\nexport async function nativeCall<T>(\n socket: CapnWebSocket,\n method: string,\n args: unknown[],\n options: CallOptions | StreamOptions | undefined,\n defaultTimeout: number\n): Promise<T> {\n const { stream, timeout } = splitCallOptions(options);\n const effectiveTimeout =\n timeout !== undefined ? timeout : stream ? undefined : defaultTimeout;\n let reader: ReadableStreamDefaultReader<unknown> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const work = (async () => {\n const result = await socket.invoke(method, args);\n if (stream && result instanceof ReadableStream) {\n reader = (result as ReadableStream<unknown>).getReader();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n stream.onChunk?.(value);\n }\n stream.onDone?.(undefined);\n return undefined as T;\n }\n return result as T;\n })();\n\n const expiry = effectiveTimeout\n ? new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n void reader?.cancel().catch(() => {});\n reject(\n new Error(\n `RPC call to ${method} timed out after ${effectiveTimeout}ms`\n )\n );\n }, effectiveTimeout);\n })\n : undefined;\n\n try {\n return await (expiry ? Promise.race([work, expiry]) : work);\n } catch (error) {\n // Keep the rejection observed on the losing side of the race.\n void work.catch(() => {});\n stream?.onError?.(error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/**\n * Native calls issued while the Cap'n Web socket is still connecting or\n * between reconnects. They must not fall back to JSON frames — that would\n * change which methods are reachable and lose return-by-reference — so\n * they wait here and run on the next open socket, or reject when the\n * connection ends for good. Their timeout starts when they are issued.\n */\nexport class NativeCallQueue {\n #pending: Array<{\n run: (socket: CapnWebSocket) => void;\n reject: (error: Error) => void;\n }> = [];\n\n enqueue<T>(\n method: string,\n args: unknown[],\n options: CallOptions | StreamOptions | undefined,\n defaultTimeout: number\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const { stream, timeout } = splitCallOptions(options);\n const effectiveTimeout =\n timeout !== undefined ? timeout : stream ? undefined : defaultTimeout;\n const startedAt = Date.now();\n const entry = {\n run: (socket: CapnWebSocket) => {\n // Spend the remaining budget, not a fresh one.\n const remaining =\n effectiveTimeout === undefined\n ? undefined\n : Math.max(1, effectiveTimeout - (Date.now() - startedAt));\n const opts =\n remaining === undefined ? options : { stream, timeout: remaining };\n nativeCall<T>(socket, method, args, opts, defaultTimeout).then(\n resolve,\n reject\n );\n },\n reject: (error: Error) => {\n stream?.onError?.(error.message);\n reject(error);\n }\n };\n this.#pending.push(entry);\n if (effectiveTimeout) {\n setTimeout(() => {\n const index = this.#pending.indexOf(entry);\n if (index === -1) return;\n this.#pending.splice(index, 1);\n entry.reject(\n new Error(\n `RPC call to ${method} timed out after ${effectiveTimeout}ms`\n )\n );\n }, effectiveTimeout);\n }\n });\n }\n\n /** Run everything queued on a socket that just opened. */\n flush(socket: CapnWebSocket): void {\n const pending = this.#pending;\n this.#pending = [];\n for (const entry of pending) entry.run(socket);\n }\n\n /** Reject everything queued; the connection will not come back. */\n rejectAll(reason: string): void {\n const pending = this.#pending;\n this.#pending = [];\n const error = new Error(reason);\n for (const entry of pending) entry.reject(error);\n }\n}\n\n/**\n * Options for the agentFetch function\n */\nexport type AgentClientFetchOptions = Omit<\n PartyFetchOptions,\n \"party\" | \"room\"\n> & {\n /** Name of the agent to connect to (ignored if basePath is set) */\n agent: string;\n /** Name of the specific Agent instance (ignored if basePath is set) */\n name?: string;\n /**\n * Full URL path - bypasses agent/name URL construction.\n * When set, the request is made to this path directly.\n */\n basePath?: string;\n};\n\n// ---- Shared RPC Type Utilities ----\n\ntype AllOptional<T> = T extends [infer A, ...infer R]\n ? undefined extends A\n ? AllOptional<R>\n : false\n : true;\n\nexport type RPCMethods<T> = {\n [K in keyof T as T[K] extends RPCMethod<T[K]> ? K : never]: RPCMethod<T[K]>;\n};\n\ntype OptionalParametersMethod<T extends RPCMethod> =\n AllOptional<ClientParameters<T>> extends true ? T : never;\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- generic agent type constraint\nexport type AgentMethods<T> = Omit<RPCMethods<T>, keyof Agent<any, any>>;\n\nexport type OptionalAgentMethods<T> = {\n [K in keyof AgentMethods<T> as AgentMethods<T>[K] extends OptionalParametersMethod<\n AgentMethods<T>[K]\n >\n ? K\n : never]: OptionalParametersMethod<AgentMethods<T>[K]>;\n};\n\nexport type RequiredAgentMethods<T> = Omit<\n AgentMethods<T>,\n keyof OptionalAgentMethods<T>\n>;\n\nexport type AgentPromiseReturnType<T, K extends keyof AgentMethods<T>> =\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- generic promise return type\n ReturnType<AgentMethods<T>[K]> extends Promise<any>\n ? ReturnType<AgentMethods<T>[K]>\n : Promise<ReturnType<AgentMethods<T>[K]>>;\n\nexport type AgentStub<T> = {\n [K in keyof AgentMethods<T>]: (\n ...args: ClientParameters<AgentMethods<T>[K]>\n ) => AgentPromiseReturnType<AgentMethods<T>, K>;\n};\n\nexport type UntypedAgentStub = Record<string, Method>;\n\ntype AgentClientStub<AgentT> = keyof AgentMethods<AgentT> extends never\n ? UntypedAgentStub\n : AgentStub<AgentT>;\n\ntype OptionalArgsAgentClientCall<AgentT> = <\n K extends keyof OptionalAgentMethods<AgentT>\n>(\n method: K,\n args?: ClientParameters<OptionalAgentMethods<AgentT>[K]>,\n options?: CallOptions | StreamOptions\n) => AgentPromiseReturnType<AgentT, K>;\n\ntype RequiredArgsAgentClientCall<AgentT> = <\n K extends keyof RequiredAgentMethods<AgentT>\n>(\n method: K,\n args: ClientParameters<RequiredAgentMethods<AgentT>[K]>,\n options?: CallOptions | StreamOptions\n) => AgentPromiseReturnType<AgentT, K>;\n\ntype TypedAgentClientCall<AgentT> = OptionalArgsAgentClientCall<AgentT> &\n RequiredArgsAgentClientCall<AgentT>;\n\ntype UntypedAgentClientCall = {\n <T extends SerializableReturnValue>(\n method: string,\n args?: SerializableValue[],\n options?: CallOptions | StreamOptions\n ): Promise<T>;\n <T = unknown>(\n method: string,\n args?: unknown[],\n options?: CallOptions | StreamOptions\n ): Promise<T>;\n};\n\ntype AgentClientCall<AgentT> = keyof AgentMethods<AgentT> extends never\n ? UntypedAgentClientCall\n : TypedAgentClientCall<AgentT>;\n\n/**\n * Creates a proxy that wraps RPC method calls.\n * Internal JS methods (toJSON, then, etc.) return undefined to avoid\n * triggering RPC calls during serialization (e.g., console.log)\n */\nexport function createStubProxy<T = Record<string, Method>>(\n call: (method: string, args: unknown[]) => unknown\n): T {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- proxy needs any for dynamic method access\n return new Proxy<any>(\n {},\n {\n get: (_target, method) => {\n if (isInternalJsStubProp(method)) {\n return undefined;\n }\n return (...args: unknown[]) => call(method as string, args);\n }\n }\n );\n}\n\n/**\n * WebSocket client for connecting to an Agent\n */\nexport class AgentClient<\n AgentT = unknown,\n State = AgentT extends { get state(): infer S } ? S : AgentT\n> extends PartySocket {\n /**\n * @deprecated Use agentFetch instead\n */\n static fetch(_opts: PartyFetchOptions): Promise<Response> {\n throw new Error(\n \"AgentClient.fetch is not implemented, use agentFetch instead\"\n );\n }\n agent: string;\n name: string;\n call: AgentClientCall<AgentT>;\n stub: AgentClientStub<AgentT>;\n\n /**\n * The current agent state, updated on server broadcasts and client setState calls.\n * Starts as undefined until the first state message is received from the server.\n */\n state: State | undefined = undefined;\n\n /**\n * Whether the client has received identity from the server.\n * Becomes true with `ready`: after the identity message, or after the\n * initial state message when the server sends one.\n * Resets to false on connection close.\n */\n identified = false;\n\n /**\n * Set when the connection closed and will not reconnect on its own (see\n * `onConnectionError`). Cleared when a connection opens.\n */\n connectionError: AgentConnectionError | null = null;\n\n /**\n * Promise that resolves when identity has been received from the server.\n * When the server also sends the stored state on connect, it resolves only\n * after that state has been applied, so `state` is current once it settles.\n * Servers without that signal resolve on identity alone.\n * Useful for waiting before making calls that depend on knowing the instance.\n * Resets on connection close so it can be awaited again after reconnect.\n */\n get ready(): Promise<void> {\n return this._readyPromise;\n }\n\n private options: AgentClientOptions<State>;\n private _pendingCalls = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n stream?: StreamOptions;\n /**\n * Whether the request was actually transmitted to the server.\n * Requests issued while disconnected sit in PartySocket's internal\n * buffer (transmitted: false) and are flushed on the next open —\n * they survive a transient close instead of being rejected, since\n * the server never saw them and re-delivery can't double-execute.\n */\n transmitted: boolean;\n }\n >();\n private _readyPromise!: Promise<void>;\n private _resolveReady!: () => void;\n private _previousName: string | null = null;\n private _previousAgent: string | null = null;\n\n private _resetReady() {\n this._readyPromise = new Promise((resolve) => {\n this._resolveReady = resolve;\n });\n }\n\n /** Connect-sequence progress for the current socket. */\n #connect: {\n stateSeen: boolean;\n pendingIdentity: { name: string; agent: string } | null;\n } = { stateSeen: false, pendingIdentity: null };\n\n #applyIdentity(newName: string, newAgent: string): void {\n const oldName = this._previousName;\n const oldAgent = this._previousAgent;\n\n // Resolve ready/identified\n this.identified = true;\n this._resolveReady();\n\n // Detect identity change on reconnect\n if (\n oldName !== null &&\n oldAgent !== null &&\n (oldName !== newName || oldAgent !== newAgent)\n ) {\n if (this.options.onIdentityChange) {\n this.options.onIdentityChange(oldName, newName, oldAgent, newAgent);\n } else {\n const agentChanged = oldAgent !== newAgent;\n const nameChanged = oldName !== newName;\n let changeDescription = \"\";\n if (agentChanged && nameChanged) {\n changeDescription = `agent \"${oldAgent}\" → \"${newAgent}\", instance \"${oldName}\" → \"${newName}\"`;\n } else if (agentChanged) {\n changeDescription = `agent \"${oldAgent}\" → \"${newAgent}\"`;\n } else {\n changeDescription = `instance \"${oldName}\" → \"${newName}\"`;\n }\n console.warn(\n `[agents] Identity changed on reconnect: ${changeDescription}. ` +\n \"This can happen with server-side routing (e.g., basePath with getAgentByName) \" +\n \"where the instance is determined by auth/session. \" +\n \"Provide onIdentityChange callback to handle this explicitly, \" +\n \"or ignore if this is expected for your routing pattern.\"\n );\n }\n }\n\n // Always update from server identity (server is authoritative)\n this._previousName = newName;\n this._previousAgent = newAgent;\n this.name = newName;\n this.agent = newAgent;\n\n // Call onIdentity callback\n this.options.onIdentity?.(newName, newAgent);\n }\n\n /**\n * The live Cap'n Web socket on the `\"capnweb\"` transport. PartySocket\n * constructs a new one on every reconnect; the bound class reports each\n * into this holder, which exists before `super()` runs.\n */\n readonly #capnWeb: { current: CapnWebSocket | null };\n readonly #nativeQueue = new NativeCallQueue();\n\n constructor(options: AgentClientOptions<State>) {\n const capnWeb: { current: CapnWebSocket | null } = { current: null };\n const socketClass =\n options.transport === \"capnweb\"\n ? {\n WebSocket: boundCapnWebSocket((socket) => {\n capnWeb.current = socket;\n })\n }\n : {};\n const agentNamespace = camelCaseToKebabCase(options.agent);\n const self: { current: AgentClient<AgentT, State> | null } = {\n current: null\n };\n const closeClassifier = createCloseClassifier({\n socket: () => self.current,\n shouldReconnectOnClose: () => options.shouldReconnectOnClose,\n maxRetries: () => options.maxRetries\n });\n const classifyReconnect = closeClassifier.shouldReconnectOnClose;\n\n // If basePath is provided, use it directly; otherwise construct from agent/name\n const socketOptions = options.basePath\n ? {\n basePath: options.basePath,\n path: options.path,\n ...options,\n ...socketClass,\n shouldReconnectOnClose: classifyReconnect\n }\n : {\n party: agentNamespace,\n prefix: \"agents\",\n room: options.name || \"default\",\n path: options.path,\n ...options,\n ...socketClass,\n shouldReconnectOnClose: classifyReconnect\n };\n\n super(socketOptions);\n self.current = this;\n this.#capnWeb = capnWeb;\n this.agent = agentNamespace;\n this.name = options.name || \"default\";\n this.options = options;\n\n // Initialize ready promise\n this._resetReady();\n\n this.addEventListener(\"message\", (event) => {\n if (typeof event.data === \"string\") {\n let parsedMessage: Record<string, unknown>;\n try {\n parsedMessage = JSON.parse(event.data);\n } catch (_error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_IDENTITY) {\n const identity = {\n name: parsedMessage.name as string,\n agent: parsedMessage.agent as string\n };\n // The server flags an identity whose state frame is next, so\n // `ready` never resolves with the stored state still missing.\n if (parsedMessage.stateFollows === true && !this.#connect.stateSeen) {\n this.#connect.pendingIdentity = identity;\n return;\n }\n this.#applyIdentity(identity.name, identity.agent);\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_STATE) {\n this.state = parsedMessage.state as State;\n try {\n this.options.onStateUpdate?.(\n parsedMessage.state as State,\n \"server\"\n );\n } finally {\n this.#connect.stateSeen = true;\n const pending = this.#connect.pendingIdentity;\n if (pending) {\n this.#connect.pendingIdentity = null;\n this.#applyIdentity(pending.name, pending.agent);\n }\n }\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_STATE_ERROR) {\n this.options.onStateUpdateError?.(parsedMessage.error as string);\n return;\n }\n if (parsedMessage.type === MessageType.RPC) {\n const response = parsedMessage as RPCResponse;\n const pending = this._pendingCalls.get(response.id);\n if (!pending) {\n console.warn(\n `[AgentClient] Discarded an RPC response with no matching pending call (id \"${response.id}\"). ` +\n \"The call likely timed out or was rejected when its connection closed before the response arrived.\"\n );\n return;\n }\n\n if (!response.success) {\n pending.reject(new Error(response.error));\n this._pendingCalls.delete(response.id);\n pending.stream?.onError?.(response.error);\n return;\n }\n\n // Handle streaming responses\n if (\"done\" in response) {\n if (response.done) {\n pending.resolve(response.result);\n this._pendingCalls.delete(response.id);\n pending.stream?.onDone?.(response.result);\n } else {\n pending.stream?.onChunk?.(response.result);\n }\n } else {\n // Non-streaming response\n pending.resolve(response.result);\n this._pendingCalls.delete(response.id);\n }\n }\n }\n });\n\n // PartySocket flushes its internal message buffer right before\n // dispatching \"open\" — anything queued has now been transmitted.\n this.addEventListener(\"open\", () => {\n this.connectionError = null;\n for (const pending of this._pendingCalls.values()) {\n pending.transmitted = true;\n }\n const native = this.#capnWeb.current;\n if (native) this.#nativeQueue.flush(native);\n });\n\n // Clean up pending calls and reset ready state when connection closes\n this.addEventListener(\"close\", (event) => {\n const finalClose = closeClassifier.takeFinalClose();\n // Reset ready state for next connection\n this.identified = false;\n this._resetReady();\n this.#connect = { stateSeen: false, pendingIdentity: null };\n\n if (this.shouldReconnect && !finalClose) {\n // Transient disconnect: reject calls whose request was already\n // transmitted — their response can never arrive. Buffered calls\n // stay pending; PartySocket re-sends them on reconnect.\n this._rejectPendingCalls(\"Connection closed\", {\n onlyTransmitted: true\n });\n } else {\n // Permanent close (close() called or reconnection ended): nothing\n // will ever flush the buffer, so reject everything.\n this._rejectPendingCalls(\"Connection closed\");\n this.#nativeQueue.rejectAll(\"Connection closed\");\n if (finalClose) {\n const error = new AgentConnectionError(event);\n this.connectionError = error;\n this.options.onConnectionError?.(error);\n }\n }\n });\n\n this.call = this._callImpl.bind(this) as AgentClientCall<AgentT>;\n this.stub = createStubProxy((method, args) =>\n this._callImpl(method, args)\n ) as AgentClientStub<AgentT>;\n }\n\n /**\n * Reject pending RPC calls with the given reason.\n * With `onlyTransmitted`, calls still sitting in the send buffer are\n * kept pending (they'll be flushed on reconnect).\n */\n private _rejectPendingCalls(\n reason: string,\n { onlyTransmitted = false }: { onlyTransmitted?: boolean } = {}\n ) {\n const error = new Error(reason);\n for (const [id, pending] of this._pendingCalls) {\n if (onlyTransmitted && !pending.transmitted) continue;\n this._pendingCalls.delete(id);\n pending.reject(error);\n pending.stream?.onError?.(reason);\n }\n }\n\n setState(state: State) {\n this.send(JSON.stringify({ state, type: MessageType.CF_AGENT_STATE }));\n this.state = state;\n this.options.onStateUpdate?.(state, \"client\");\n }\n\n /**\n * Close the connection and immediately reject all pending RPC calls.\n * This provides immediate feedback on intentional close rather than\n * waiting for the WebSocket close handshake to complete.\n *\n * Note: Any calls made after `close()` will be rejected when the\n * underlying WebSocket close event fires.\n */\n close(code?: number, reason?: string) {\n // Immediately reject all pending calls on intentional close\n this._rejectPendingCalls(\"Connection closed\");\n\n // Then close the underlying socket\n super.close(code, reason);\n }\n\n /**\n * Call a method on the Agent.\n * When AgentT is provided, method names are inferred from the agent's methods.\n * Falls back to untyped string-based calls when AgentT is not provided.\n */\n private async _callImpl(\n method: string,\n args: unknown[] = [],\n options?: CallOptions | StreamOptions\n ): Promise<unknown> {\n // On the Cap'n Web transport, callables are native methods on the\n // session root: invoke them directly so an RpcTarget result arrives as\n // a live stub. The JSON `rpc` frame below is the WebSocket wire's protocol.\n if (this.options.transport === \"capnweb\") {\n const timeout =\n this.options.defaultCallTimeout ?? DEFAULT_CALL_TIMEOUT_MS;\n const native = this.#capnWeb.current;\n if (native && this.readyState === this.OPEN) {\n return nativeCall(native, method, args, options, timeout);\n }\n if (this.connectionError && this.readyState === this.CLOSED) {\n throw new Error(\"Connection closed\");\n }\n // Connecting or between reconnects: hold the call as a native call\n // rather than queueing a JSON frame that would change its meaning.\n return this.#nativeQueue.enqueue(method, args, options, timeout);\n }\n if (this.connectionError && this.readyState === this.CLOSED) {\n throw new Error(\"Connection closed\");\n }\n\n return new Promise((resolve, reject) => {\n const id = crypto.randomUUID();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const { stream: streamOptions, timeout } = splitCallOptions(options);\n\n // Apply the default timeout as a backstop for non-streaming calls\n // so a lost response rejects instead of hanging forever. An\n // explicit `timeout` (including 0 = disabled) always wins.\n const effectiveTimeout =\n timeout !== undefined\n ? timeout\n : streamOptions\n ? undefined\n : (this.options.defaultCallTimeout ?? DEFAULT_CALL_TIMEOUT_MS);\n\n if (effectiveTimeout) {\n timeoutId = setTimeout(() => {\n const pending = this._pendingCalls.get(id);\n this._pendingCalls.delete(id);\n const errorMessage = `RPC call to ${method} timed out after ${effectiveTimeout}ms`;\n pending?.stream?.onError?.(errorMessage);\n reject(new Error(errorMessage));\n }, effectiveTimeout);\n }\n\n this._pendingCalls.set(id, {\n reject: (e: Error) => {\n if (timeoutId) clearTimeout(timeoutId);\n reject(e);\n },\n resolve: (value: unknown) => {\n if (timeoutId) clearTimeout(timeoutId);\n resolve(value);\n },\n stream: streamOptions,\n // If the socket is open, send() below transmits synchronously;\n // otherwise the request is buffered until the next open event\n // (the \"open\" listener then marks it transmitted).\n transmitted: this.readyState === this.OPEN\n });\n\n const request: RPCRequest = {\n args,\n id,\n method,\n type: MessageType.RPC\n };\n\n this.send(JSON.stringify(request));\n });\n }\n}\n\n/**\n * Make an HTTP request to an Agent\n * @param opts Connection options\n * @param init Request initialization options\n * @returns Promise resolving to a Response\n */\nexport function agentFetch(opts: AgentClientFetchOptions, init?: RequestInit) {\n const agentNamespace = camelCaseToKebabCase(opts.agent);\n\n // If basePath is provided, use it directly; otherwise construct from agent/name\n // When basePath is set, room/party aren't used by PartySocket (basePath replaces the URL)\n if (opts.basePath) {\n return PartySocket.fetch(\n { basePath: opts.basePath, ...opts } as unknown as PartyFetchOptions,\n init\n );\n }\n\n return PartySocket.fetch(\n {\n party: agentNamespace,\n prefix: \"agents\",\n room: opts.name || \"default\",\n ...opts\n },\n init\n );\n}\n"],"mappings":"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"}