experimental-a2 0.17.0 → 0.18.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 (211) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -1
  3. package/dist/{actor-DBbe5xeE.d.ts → actor-Bjce9_5U.d.ts} +5 -5
  4. package/dist/{actor-DBbe5xeE.d.ts.map → actor-Bjce9_5U.d.ts.map} +1 -1
  5. package/dist/actor-client.d.ts +1 -1
  6. package/dist/actor-react.d.ts +3 -3
  7. package/dist/actor-react.js +1 -1
  8. package/dist/actor.d.ts +1 -1
  9. package/dist/actor.js +2 -2
  10. package/dist/{ai-UrWfKeuv.js → ai-BpOGm-zB.js} +2 -2
  11. package/dist/{ai-UrWfKeuv.js.map → ai-BpOGm-zB.js.map} +1 -1
  12. package/dist/{ai-vpGtcaCB.d.ts → ai-DrUaMyRt.d.ts} +3 -3
  13. package/dist/{ai-vpGtcaCB.d.ts.map → ai-DrUaMyRt.d.ts.map} +1 -1
  14. package/dist/ai-client.d.ts +2 -2
  15. package/dist/ai-client.js +2 -2
  16. package/dist/ai-fx.browser.d.ts +1 -0
  17. package/dist/ai-fx.browser.js +6 -0
  18. package/dist/ai-fx.browser.js.map +1 -0
  19. package/dist/ai-fx.d.ts +10 -0
  20. package/dist/ai-fx.d.ts.map +1 -0
  21. package/dist/ai-fx.js +634 -0
  22. package/dist/ai-fx.js.map +1 -0
  23. package/dist/ai-harness-BuLh_EYi.js +273 -0
  24. package/dist/ai-harness-BuLh_EYi.js.map +1 -0
  25. package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
  26. package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
  27. package/dist/ai-pi.browser.d.ts +1 -0
  28. package/dist/ai-pi.browser.js +6 -0
  29. package/dist/ai-pi.browser.js.map +1 -0
  30. package/dist/ai-pi.d.ts +17 -0
  31. package/dist/ai-pi.d.ts.map +1 -0
  32. package/dist/ai-pi.js +904 -0
  33. package/dist/ai-pi.js.map +1 -0
  34. package/dist/ai-react.d.ts +5 -5
  35. package/dist/ai-react.js +2 -2
  36. package/dist/ai-server.d.ts +47 -25
  37. package/dist/ai-server.d.ts.map +1 -1
  38. package/dist/ai-server.js +106 -37
  39. package/dist/ai-server.js.map +1 -1
  40. package/dist/ai.d.ts +1 -1
  41. package/dist/ai.js +1 -1
  42. package/dist/bounded-cache-CPHWF-8h.js +41 -0
  43. package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
  44. package/dist/{client-COaiaDub.js → client-BReKqwZC.js} +3 -3
  45. package/dist/{client-COaiaDub.js.map → client-BReKqwZC.js.map} +1 -1
  46. package/dist/{client-qRnRUzBS.d.ts → client-h_DKFAjj.d.ts} +3 -3
  47. package/dist/{client-qRnRUzBS.d.ts.map → client-h_DKFAjj.d.ts.map} +1 -1
  48. package/dist/client.d.ts +3 -3
  49. package/dist/client.js +1 -1
  50. package/dist/index.d.ts +5 -5
  51. package/dist/{initial-snapshot-BrLso5x_.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +2 -2
  52. package/dist/{initial-snapshot-BrLso5x_.d.ts.map → initial-snapshot-Bx-c0pDP.d.ts.map} +1 -1
  53. package/dist/{internal-CbVvEwad.js → internal-B2T3U4Rq.js} +8 -2
  54. package/dist/internal-B2T3U4Rq.js.map +1 -0
  55. package/dist/otel.d.ts +1 -1
  56. package/dist/{react-BbJ-g6Uu.d.ts → react-BBXgHWFc.d.ts} +4 -4
  57. package/dist/{react-BbJ-g6Uu.d.ts.map → react-BBXgHWFc.d.ts.map} +1 -1
  58. package/dist/react.d.ts +1 -1
  59. package/dist/react.js +1 -1
  60. package/dist/reducer-BAXc8JyF.js.map +1 -1
  61. package/dist/{reducer-BYu6DT7h.d.ts → reducer-CqA59Q6U.d.ts} +2 -2
  62. package/dist/{reducer-BYu6DT7h.d.ts.map → reducer-CqA59Q6U.d.ts.map} +1 -1
  63. package/dist/scheduler-qstash.d.ts +2 -2
  64. package/dist/scheduler-qstash.js +2 -2
  65. package/dist/scheduler-vercel.d.ts +2 -2
  66. package/dist/scheduler-vercel.js +2 -2
  67. package/dist/{server-Rj64fNvX.js → server-Dw_k_7kO.js} +93 -60
  68. package/dist/server-Dw_k_7kO.js.map +1 -0
  69. package/dist/{server-CGM8WzRG.d.ts → server-En7cbi3v.d.ts} +36 -7
  70. package/dist/server-En7cbi3v.d.ts.map +1 -0
  71. package/dist/server.d.ts +4 -4
  72. package/dist/server.js +1 -1
  73. package/dist/{session-clear-Dshpuhff.d.ts → session-clear-Bo-XTFEy.d.ts} +2 -2
  74. package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
  75. package/dist/{store-Gpkq7WBo.d.ts → store-Djdv0bnZ.d.ts} +31 -2
  76. package/dist/store-Djdv0bnZ.d.ts.map +1 -0
  77. package/dist/store-N8PXxDAS.js.map +1 -1
  78. package/dist/store-memory.d.ts +1 -1
  79. package/dist/store-memory.js +32 -6
  80. package/dist/store-memory.js.map +1 -1
  81. package/dist/{store-polling-4_y8lETm.js → store-polling-B5FcwJfo.js} +2 -2
  82. package/dist/{store-polling-4_y8lETm.js.map → store-polling-B5FcwJfo.js.map} +1 -1
  83. package/dist/store-postgres.d.ts +1 -1
  84. package/dist/store-postgres.js +54 -3
  85. package/dist/store-postgres.js.map +1 -1
  86. package/dist/{store-presence-polling-Q1C-k2dd.js → store-presence-polling-BAwS8RWn.js} +3 -3
  87. package/dist/{store-presence-polling-Q1C-k2dd.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
  88. package/dist/store-redis-http.d.ts +1 -1
  89. package/dist/store-redis-http.js +3 -3
  90. package/dist/{store-redis-notify-tqWNnMRu.js → store-redis-notify-BRITti_o.js} +187 -68
  91. package/dist/store-redis-notify-BRITti_o.js.map +1 -0
  92. package/dist/store-redis.d.ts +1 -1
  93. package/dist/store-redis.js +1 -1
  94. package/dist/store-sqlite.d.ts +1 -1
  95. package/dist/store-sqlite.js +32 -2
  96. package/dist/store-sqlite.js.map +1 -1
  97. package/dist/{telemetry-cZH-cibj.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
  98. package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
  99. package/dist/{wire-DjfHOvdW.js → wire-rSUglYo8.js} +2 -2
  100. package/dist/{wire-DjfHOvdW.js.map → wire-rSUglYo8.js.map} +1 -1
  101. package/docs/01-quickstart.mdx +49 -83
  102. package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
  103. package/docs/actors/01-introduction.mdx +48 -86
  104. package/docs/actors/02-concurrency.mdx +43 -70
  105. package/docs/actors/03-timers.mdx +28 -42
  106. package/docs/actors/04-routes.mdx +110 -150
  107. package/docs/actors/meta.ts +1 -1
  108. package/docs/agents/01-introduction.mdx +257 -0
  109. package/docs/agents/02-tools.mdx +247 -0
  110. package/docs/agents/03-conversations.mdx +538 -0
  111. package/docs/agents/04-harnesses.mdx +136 -0
  112. package/docs/agents/meta.ts +1 -0
  113. package/docs/client/01-react.mdx +294 -0
  114. package/docs/client/02-transports.mdx +86 -0
  115. package/docs/client/03-presence.mdx +224 -0
  116. package/docs/client/meta.ts +1 -0
  117. package/docs/contract/01-events.mdx +57 -0
  118. package/docs/contract/02-state.mdx +142 -0
  119. package/docs/contract/03-sessions.mdx +175 -0
  120. package/docs/index.mdx +108 -144
  121. package/docs/reference/01-core.mdx +139 -0
  122. package/docs/reference/02-server.mdx +855 -0
  123. package/docs/reference/03-client.mdx +301 -0
  124. package/docs/reference/04-ai.mdx +722 -0
  125. package/docs/reference/05-adapters.mdx +351 -0
  126. package/docs/reference/06-errors.mdx +138 -0
  127. package/docs/reference/meta.ts +1 -1
  128. package/docs/server/01-handlers.mdx +200 -0
  129. package/docs/server/02-timers.mdx +130 -0
  130. package/docs/server/03-cancellation.mdx +141 -0
  131. package/docs/server/04-routes.mdx +243 -0
  132. package/docs/server/05-production.mdx +322 -0
  133. package/docs/server/06-guarantees.mdx +123 -0
  134. package/examples/playground/DEPLOY.md +1 -1
  135. package/examples/playground/app/a0/[appId]/page.tsx +11 -6
  136. package/examples/playground/app/a0/app-list.tsx +9 -2
  137. package/examples/playground/app/a0/app-view.tsx +9 -1
  138. package/examples/playground/app/a0/apps/[appId]/route.ts +10 -5
  139. package/examples/playground/app/a0/apps.test.ts +12 -0
  140. package/examples/playground/app/a0/apps.ts +22 -7
  141. package/examples/playground/app/a0/chat.tsx +47 -1
  142. package/examples/playground/app/a0/fx/events/route.ts +9 -0
  143. package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
  144. package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
  145. package/examples/playground/app/a0/model.test.ts +18 -1
  146. package/examples/playground/app/a0/model.ts +47 -10
  147. package/examples/playground/app/a0/no-photon.cjs +4 -0
  148. package/examples/playground/app/a0/no-quickjs.cjs +3 -0
  149. package/examples/playground/app/a0/pi/events/route.ts +9 -0
  150. package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
  151. package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
  152. package/examples/playground/app/a0/sandbox-log.ts +10 -4
  153. package/examples/playground/app/a0/sandbox-session.ts +152 -0
  154. package/examples/playground/app/a0/scheduler/route.ts +2 -2
  155. package/examples/playground/app/a0/server.ts +100 -45
  156. package/examples/playground/app/a0/session.ts +17 -5
  157. package/examples/playground/app/a0/shell.tsx +11 -3
  158. package/examples/playground/app/agent/ingress.test.ts +6 -3
  159. package/examples/playground/app/agent/server.ts +9 -6
  160. package/examples/playground/app/agent/telemetry.test.ts +71 -50
  161. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +1 -1
  162. package/examples/playground/app/chat/server.test.ts +30 -26
  163. package/examples/playground/app/chat/server.ts +46 -46
  164. package/examples/playground/next.config.ts +17 -5
  165. package/examples/playground/package.json +4 -1
  166. package/examples/playground/vercel.json +18 -0
  167. package/package.json +27 -3
  168. package/src/ai-agent.ts +261 -87
  169. package/src/ai-fx.browser.ts +5 -0
  170. package/src/ai-fx.ts +833 -0
  171. package/src/ai-harness.ts +518 -0
  172. package/src/ai-model-metadata.ts +11 -6
  173. package/src/ai-pi.browser.ts +5 -0
  174. package/src/ai-pi.ts +1263 -0
  175. package/src/ai-sdk-step.ts +2 -2
  176. package/src/ai-server.ts +3 -1
  177. package/src/internal.ts +7 -0
  178. package/src/libfx.d.ts +34 -0
  179. package/src/reducer.ts +1 -1
  180. package/src/server.ts +177 -19
  181. package/src/store-memory.ts +42 -7
  182. package/src/store-postgres.ts +80 -0
  183. package/src/store-redis-core.ts +210 -79
  184. package/src/store-redis-notify.ts +2 -0
  185. package/src/store-sqlite.ts +60 -0
  186. package/src/store.ts +33 -0
  187. package/src/telemetry.ts +1 -0
  188. package/dist/internal-CbVvEwad.js.map +0 -1
  189. package/dist/server-CGM8WzRG.d.ts.map +0 -1
  190. package/dist/server-Rj64fNvX.js.map +0 -1
  191. package/dist/session-clear-Dshpuhff.d.ts.map +0 -1
  192. package/dist/store-Gpkq7WBo.d.ts.map +0 -1
  193. package/dist/store-redis-notify-tqWNnMRu.js.map +0 -1
  194. package/dist/telemetry-cZH-cibj.d.ts.map +0 -1
  195. package/docs/concepts/01-contracts.mdx +0 -237
  196. package/docs/concepts/02-handlers.mdx +0 -284
  197. package/docs/concepts/03-durability.mdx +0 -314
  198. package/docs/concepts/04-state.mdx +0 -209
  199. package/docs/guides/01-timers.mdx +0 -193
  200. package/docs/guides/02-cancellation.mdx +0 -161
  201. package/docs/guides/03-react.mdx +0 -324
  202. package/docs/guides/04-local-first.mdx +0 -47
  203. package/docs/guides/05-production.mdx +0 -534
  204. package/docs/guides/06-ai-agents.mdx +0 -1408
  205. package/docs/guides/08-application-data.mdx +0 -113
  206. package/docs/guides/09-presence.mdx +0 -262
  207. package/docs/guides/10-transports.mdx +0 -221
  208. package/docs/reference/01-api.mdx +0 -2358
  209. package/docs/reference/02-errors.mdx +0 -150
  210. /package/docs/{concepts → contract}/meta.ts +0 -0
  211. /package/docs/{guides → server}/meta.ts +0 -0
@@ -1,5 +1,5 @@
1
- import { i as POLL_TIMINGS } from "./internal-CbVvEwad.js";
2
- import { t as defaultSleep } from "./store-polling-4_y8lETm.js";
1
+ import { i as POLL_TIMINGS } from "./internal-B2T3U4Rq.js";
2
+ import { t as defaultSleep } from "./store-polling-B5FcwJfo.js";
3
3
  //#region src/store-presence-polling.ts
4
4
  const key = ({ participant, field }) => JSON.stringify([participant, field]);
5
5
  function pollPresence({ read, deliver, sleep = defaultSleep }) {
@@ -91,4 +91,4 @@ function pollPresence({ read, deliver, sleep = defaultSleep }) {
91
91
  //#endregion
92
92
  export { pollPresence as t };
93
93
 
94
- //# sourceMappingURL=store-presence-polling-Q1C-k2dd.js.map
94
+ //# sourceMappingURL=store-presence-polling-BAwS8RWn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-presence-polling-Q1C-k2dd.js","names":[],"sources":["../src/store-presence-polling.ts"],"sourcesContent":["import type { PresencePatch } from './contract.ts'\nimport { POLL_TIMINGS } from './internal.ts'\nimport { defaultSleep, type Sleeper } from './store-polling.ts'\nimport type { PresenceRow } from './store.ts'\n\nconst key = ({\n participant,\n field,\n}: Pick<PresenceRow, 'participant' | 'field'>): string =>\n JSON.stringify([participant, field])\n\nexport function pollPresence({\n read,\n deliver,\n sleep = defaultSleep,\n}: {\n read: () => Promise<PresenceRow[]>\n deliver: (patch: PresencePatch) => void\n sleep?: (ms: number) => Sleeper\n}): {\n ready: Promise<void>\n update(patch: PresencePatch): void\n wake(): void\n stop(): void\n} {\n let stopped = false\n let pending: Sleeper | undefined\n let interval = Math.min(\n POLL_TIMINGS.activeFloorMs,\n POLL_TIMINGS.idleCeilingMs,\n )\n let version = 0\n const seen = new Map<string, Omit<PresenceRow, 'expiresAt'>>()\n const activate = (): void => {\n version += 1\n interval = Math.min(POLL_TIMINGS.activeFloorMs, POLL_TIMINGS.idleCeilingMs)\n pending?.cancel()\n }\n const ready = read()\n .then((rows) => {\n if (stopped) return\n for (const row of rows) seen.set(key(row), row)\n return undefined\n })\n // Seeding is best effort; foreground snapshots still report read errors.\n .catch(() => undefined)\n\n const run = async (): Promise<void> => {\n await ready\n for (;;) {\n if (stopped) return\n const before = version\n pending = sleep(interval)\n // oxlint-disable-next-line no-await-in-loop -- adaptive presence polling\n await pending.promise\n if (stopped) return\n try {\n const reading = version\n // oxlint-disable-next-line no-await-in-loop -- read each new presence snapshot\n const rows = await read()\n if (stopped) return\n if (version !== reading) continue\n const live = new Set<string>()\n let changed = false\n for (const row of rows) {\n const id = key(row)\n live.add(id)\n const prior = seen.get(id)\n seen.set(id, row)\n if (\n prior?.seen === row.seen &&\n prior.at.getTime() === row.at.getTime() &&\n JSON.stringify(prior.value) === JSON.stringify(row.value)\n )\n continue\n changed = true\n deliver({\n participant: row.participant,\n values: { [row.field]: row.value },\n seen: row.seen,\n at: row.at,\n })\n }\n for (const [id, row] of seen) {\n if (live.has(id)) continue\n seen.delete(id)\n changed = true\n deliver({\n participant: row.participant,\n values: { [row.field]: null },\n seen: row.seen,\n at: new Date(),\n })\n }\n if (changed) activate()\n else if (version === before)\n interval = Math.min(interval * 2, POLL_TIMINGS.idleCeilingMs)\n } catch {\n interval = POLL_TIMINGS.idleCeilingMs\n }\n }\n }\n void run()\n return {\n ready,\n wake: activate,\n update(patch) {\n for (const [field, value] of Object.entries(patch.values)) {\n seen.set(key({ participant: patch.participant, field }), {\n participant: patch.participant,\n field,\n value,\n seen: patch.seen,\n at: patch.at,\n })\n }\n activate()\n },\n stop() {\n stopped = true\n pending?.cancel()\n seen.clear()\n },\n }\n}\n"],"mappings":";;;AAKA,MAAM,OAAO,EACX,aACA,YAEA,KAAK,UAAU,CAAC,aAAa,KAAK,CAAC;AAErC,SAAgB,aAAa,EAC3B,MACA,SACA,QAAQ,gBAUR;CACA,IAAI,UAAU;CACd,IAAI;CACJ,IAAI,WAAW,KAAK,IAClB,aAAa,eACb,aAAa,aACf;CACA,IAAI,UAAU;CACd,MAAM,uBAAO,IAAI,IAA4C;CAC7D,MAAM,iBAAuB;EAC3B,WAAW;EACX,WAAW,KAAK,IAAI,aAAa,eAAe,aAAa,aAAa;EAC1E,SAAS,OAAO;CAClB;CACA,MAAM,QAAQ,KAAK,CAAC,CACjB,MAAM,SAAS;EACd,IAAI,SAAS;EACb,KAAK,MAAM,OAAO,MAAM,KAAK,IAAI,IAAI,GAAG,GAAG,GAAG;CAEhD,CAAC,CAAC,CAED,YAAY,KAAA,CAAS;CAExB,MAAM,MAAM,YAA2B;EACrC,MAAM;EACN,SAAS;GACP,IAAI,SAAS;GACb,MAAM,SAAS;GACf,UAAU,MAAM,QAAQ;GAExB,MAAM,QAAQ;GACd,IAAI,SAAS;GACb,IAAI;IACF,MAAM,UAAU;IAEhB,MAAM,OAAO,MAAM,KAAK;IACxB,IAAI,SAAS;IACb,IAAI,YAAY,SAAS;IACzB,MAAM,uBAAO,IAAI,IAAY;IAC7B,IAAI,UAAU;IACd,KAAK,MAAM,OAAO,MAAM;KACtB,MAAM,KAAK,IAAI,GAAG;KAClB,KAAK,IAAI,EAAE;KACX,MAAM,QAAQ,KAAK,IAAI,EAAE;KACzB,KAAK,IAAI,IAAI,GAAG;KAChB,IACE,OAAO,SAAS,IAAI,QACpB,MAAM,GAAG,QAAQ,MAAM,IAAI,GAAG,QAAQ,KACtC,KAAK,UAAU,MAAM,KAAK,MAAM,KAAK,UAAU,IAAI,KAAK,GAExD;KACF,UAAU;KACV,QAAQ;MACN,aAAa,IAAI;MACjB,QAAQ,GAAG,IAAI,QAAQ,IAAI,MAAM;MACjC,MAAM,IAAI;MACV,IAAI,IAAI;KACV,CAAC;IACH;IACA,KAAK,MAAM,CAAC,IAAI,QAAQ,MAAM;KAC5B,IAAI,KAAK,IAAI,EAAE,GAAG;KAClB,KAAK,OAAO,EAAE;KACd,UAAU;KACV,QAAQ;MACN,aAAa,IAAI;MACjB,QAAQ,GAAG,IAAI,QAAQ,KAAK;MAC5B,MAAM,IAAI;MACV,oBAAI,IAAI,KAAK;KACf,CAAC;IACH;IACA,IAAI,SAAS,SAAS;SACjB,IAAI,YAAY,QACnB,WAAW,KAAK,IAAI,WAAW,GAAG,aAAa,aAAa;GAChE,QAAQ;IACN,WAAW,aAAa;GAC1B;EACF;CACF;CACA,IAAS;CACT,OAAO;EACL;EACA,MAAM;EACN,OAAO,OAAO;GACZ,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,MAAM,MAAM,GACtD,KAAK,IAAI,IAAI;IAAE,aAAa,MAAM;IAAa;GAAM,CAAC,GAAG;IACvD,aAAa,MAAM;IACnB;IACA;IACA,MAAM,MAAM;IACZ,IAAI,MAAM;GACZ,CAAC;GAEH,SAAS;EACX;EACA,OAAO;GACL,UAAU;GACV,SAAS,OAAO;GAChB,KAAK,MAAM;EACb;CACF;AACF"}
1
+ {"version":3,"file":"store-presence-polling-BAwS8RWn.js","names":[],"sources":["../src/store-presence-polling.ts"],"sourcesContent":["import type { PresencePatch } from './contract.ts'\nimport { POLL_TIMINGS } from './internal.ts'\nimport { defaultSleep, type Sleeper } from './store-polling.ts'\nimport type { PresenceRow } from './store.ts'\n\nconst key = ({\n participant,\n field,\n}: Pick<PresenceRow, 'participant' | 'field'>): string =>\n JSON.stringify([participant, field])\n\nexport function pollPresence({\n read,\n deliver,\n sleep = defaultSleep,\n}: {\n read: () => Promise<PresenceRow[]>\n deliver: (patch: PresencePatch) => void\n sleep?: (ms: number) => Sleeper\n}): {\n ready: Promise<void>\n update(patch: PresencePatch): void\n wake(): void\n stop(): void\n} {\n let stopped = false\n let pending: Sleeper | undefined\n let interval = Math.min(\n POLL_TIMINGS.activeFloorMs,\n POLL_TIMINGS.idleCeilingMs,\n )\n let version = 0\n const seen = new Map<string, Omit<PresenceRow, 'expiresAt'>>()\n const activate = (): void => {\n version += 1\n interval = Math.min(POLL_TIMINGS.activeFloorMs, POLL_TIMINGS.idleCeilingMs)\n pending?.cancel()\n }\n const ready = read()\n .then((rows) => {\n if (stopped) return\n for (const row of rows) seen.set(key(row), row)\n return undefined\n })\n // Seeding is best effort; foreground snapshots still report read errors.\n .catch(() => undefined)\n\n const run = async (): Promise<void> => {\n await ready\n for (;;) {\n if (stopped) return\n const before = version\n pending = sleep(interval)\n // oxlint-disable-next-line no-await-in-loop -- adaptive presence polling\n await pending.promise\n if (stopped) return\n try {\n const reading = version\n // oxlint-disable-next-line no-await-in-loop -- read each new presence snapshot\n const rows = await read()\n if (stopped) return\n if (version !== reading) continue\n const live = new Set<string>()\n let changed = false\n for (const row of rows) {\n const id = key(row)\n live.add(id)\n const prior = seen.get(id)\n seen.set(id, row)\n if (\n prior?.seen === row.seen &&\n prior.at.getTime() === row.at.getTime() &&\n JSON.stringify(prior.value) === JSON.stringify(row.value)\n )\n continue\n changed = true\n deliver({\n participant: row.participant,\n values: { [row.field]: row.value },\n seen: row.seen,\n at: row.at,\n })\n }\n for (const [id, row] of seen) {\n if (live.has(id)) continue\n seen.delete(id)\n changed = true\n deliver({\n participant: row.participant,\n values: { [row.field]: null },\n seen: row.seen,\n at: new Date(),\n })\n }\n if (changed) activate()\n else if (version === before)\n interval = Math.min(interval * 2, POLL_TIMINGS.idleCeilingMs)\n } catch {\n interval = POLL_TIMINGS.idleCeilingMs\n }\n }\n }\n void run()\n return {\n ready,\n wake: activate,\n update(patch) {\n for (const [field, value] of Object.entries(patch.values)) {\n seen.set(key({ participant: patch.participant, field }), {\n participant: patch.participant,\n field,\n value,\n seen: patch.seen,\n at: patch.at,\n })\n }\n activate()\n },\n stop() {\n stopped = true\n pending?.cancel()\n seen.clear()\n },\n }\n}\n"],"mappings":";;;AAKA,MAAM,OAAO,EACX,aACA,YAEA,KAAK,UAAU,CAAC,aAAa,KAAK,CAAC;AAErC,SAAgB,aAAa,EAC3B,MACA,SACA,QAAQ,gBAUR;CACA,IAAI,UAAU;CACd,IAAI;CACJ,IAAI,WAAW,KAAK,IAClB,aAAa,eACb,aAAa,aACf;CACA,IAAI,UAAU;CACd,MAAM,uBAAO,IAAI,IAA4C;CAC7D,MAAM,iBAAuB;EAC3B,WAAW;EACX,WAAW,KAAK,IAAI,aAAa,eAAe,aAAa,aAAa;EAC1E,SAAS,OAAO;CAClB;CACA,MAAM,QAAQ,KAAK,CAAC,CACjB,MAAM,SAAS;EACd,IAAI,SAAS;EACb,KAAK,MAAM,OAAO,MAAM,KAAK,IAAI,IAAI,GAAG,GAAG,GAAG;CAEhD,CAAC,CAAC,CAED,YAAY,KAAA,CAAS;CAExB,MAAM,MAAM,YAA2B;EACrC,MAAM;EACN,SAAS;GACP,IAAI,SAAS;GACb,MAAM,SAAS;GACf,UAAU,MAAM,QAAQ;GAExB,MAAM,QAAQ;GACd,IAAI,SAAS;GACb,IAAI;IACF,MAAM,UAAU;IAEhB,MAAM,OAAO,MAAM,KAAK;IACxB,IAAI,SAAS;IACb,IAAI,YAAY,SAAS;IACzB,MAAM,uBAAO,IAAI,IAAY;IAC7B,IAAI,UAAU;IACd,KAAK,MAAM,OAAO,MAAM;KACtB,MAAM,KAAK,IAAI,GAAG;KAClB,KAAK,IAAI,EAAE;KACX,MAAM,QAAQ,KAAK,IAAI,EAAE;KACzB,KAAK,IAAI,IAAI,GAAG;KAChB,IACE,OAAO,SAAS,IAAI,QACpB,MAAM,GAAG,QAAQ,MAAM,IAAI,GAAG,QAAQ,KACtC,KAAK,UAAU,MAAM,KAAK,MAAM,KAAK,UAAU,IAAI,KAAK,GAExD;KACF,UAAU;KACV,QAAQ;MACN,aAAa,IAAI;MACjB,QAAQ,GAAG,IAAI,QAAQ,IAAI,MAAM;MACjC,MAAM,IAAI;MACV,IAAI,IAAI;KACV,CAAC;IACH;IACA,KAAK,MAAM,CAAC,IAAI,QAAQ,MAAM;KAC5B,IAAI,KAAK,IAAI,EAAE,GAAG;KAClB,KAAK,OAAO,EAAE;KACd,UAAU;KACV,QAAQ;MACN,aAAa,IAAI;MACjB,QAAQ,GAAG,IAAI,QAAQ,KAAK;MAC5B,MAAM,IAAI;MACV,oBAAI,IAAI,KAAK;KACf,CAAC;IACH;IACA,IAAI,SAAS,SAAS;SACjB,IAAI,YAAY,QACnB,WAAW,KAAK,IAAI,WAAW,GAAG,aAAa,aAAa;GAChE,QAAQ;IACN,WAAW,aAAa;GAC1B;EACF;CACF;CACA,IAAS;CACT,OAAO;EACL;EACA,MAAM;EACN,OAAO,OAAO;GACZ,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,MAAM,MAAM,GACtD,KAAK,IAAI,IAAI;IAAE,aAAa,MAAM;IAAa;GAAM,CAAC,GAAG;IACvD,aAAa,MAAM;IACnB;IACA;IACA,MAAM,MAAM;IACZ,IAAI,MAAM;GACZ,CAAC;GAEH,SAAS;EACX;EACA,OAAO;GACL,UAAU;GACV,SAAS,OAAO;GAChB,KAAK,MAAM;EACb;CACF;AACF"}
@@ -1,4 +1,4 @@
1
- import { r as Clock, s as IdSource, t as A2Store } from "./store-Gpkq7WBo.js";
1
+ import { r as Clock, s as IdSource, t as A2Store } from "./store-Djdv0bnZ.js";
2
2
  //#region src/store-redis-http.d.ts
3
3
  type RedisHttpStoreOptions = {
4
4
  /** REST endpoint, e.g. `process.env.UPSTASH_REDIS_REST_URL`. */
@@ -1,8 +1,8 @@
1
1
  import { i as A2Error } from "./session-clear-DGI4jTQ5.js";
2
- import { r as NOTIFY_TIMINGS } from "./internal-CbVvEwad.js";
3
- import { t as defaultSleep } from "./store-polling-4_y8lETm.js";
2
+ import { r as NOTIFY_TIMINGS } from "./internal-B2T3U4Rq.js";
3
+ import { t as defaultSleep } from "./store-polling-B5FcwJfo.js";
4
4
  import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./store-N8PXxDAS.js";
5
- import { t as createRedisNotifyStore } from "./store-redis-notify-tqWNnMRu.js";
5
+ import { t as createRedisNotifyStore } from "./store-redis-notify-BRITti_o.js";
6
6
  //#region src/redis-http-subscriptions.ts
7
7
  function redisHttpSubscriptions({ url, token, onReconnect, fallback }) {
8
8
  const subscriptions = /* @__PURE__ */ new Map();
@@ -1,10 +1,10 @@
1
1
  import { i as A2Error, r as sessionClearedError, s as supersededAttemptError, t as CLEAR_EVENT_TYPE } from "./session-clear-DGI4jTQ5.js";
2
- import { g as nullProtoRecord, r as NOTIFY_TIMINGS, v as stateTailReaders } from "./internal-CbVvEwad.js";
3
- import { i as streamActivities, n as pollingStream, r as createStreamActivity, t as defaultSleep } from "./store-polling-4_y8lETm.js";
2
+ import { _ as nullProtoRecord, r as NOTIFY_TIMINGS, y as stateTailReaders } from "./internal-B2T3U4Rq.js";
3
+ import { i as streamActivities, n as pollingStream, r as createStreamActivity, t as defaultSleep } from "./store-polling-B5FcwJfo.js";
4
4
  import { n as isSerializedEvent, r as parseSerializedEvent, t as bufferedEvents } from "./stream-buffer-Cx7AAPGq.js";
5
5
  import { t as idempotentReplay } from "./idempotent-replay-JdgMQRRm.js";
6
6
  import { t as decodeReturnedEventIds } from "./store-codec-DTG0Ftek.js";
7
- import { t as pollPresence } from "./store-presence-polling-Q1C-k2dd.js";
7
+ import { t as pollPresence } from "./store-presence-polling-BAwS8RWn.js";
8
8
  //#region src/presence-recovery.ts
9
9
  const key = ({ participant, field }) => JSON.stringify([participant, field]);
10
10
  function presenceRecovery({ read, deliver, isActive }) {
@@ -102,6 +102,22 @@ function presenceRecovery({ read, deliver, isActive }) {
102
102
  const FEED_PAGE_SIZE = 1e3;
103
103
  const READ_CONCURRENCY = 16;
104
104
  const META_CHUNK_SIZE = 1e3;
105
+ /** The metadata columns of a full stored row. */
106
+ const STORED_EVENT_META_SUFFIXES = [
107
+ "p",
108
+ "pa",
109
+ "ri",
110
+ "ch",
111
+ "ce",
112
+ "f",
113
+ "fc",
114
+ "lc",
115
+ "lf",
116
+ "lfa",
117
+ "d",
118
+ "a",
119
+ "e"
120
+ ];
105
121
  /**
106
122
  * Upstash bills every call a script makes, so scripts read the counter and
107
123
  * split with one MGET, read metadata with one HMGET per hash (`read_fields`),
@@ -256,6 +272,32 @@ local function flush_queue()
256
272
  end
257
273
  `;
258
274
  /**
275
+ * Settles one unfinished event for scripts that include ENQUEUE_LUA: it
276
+ * leaves the pending and ready sets, and a lane head hands its lane to the
277
+ * next unfinished event. A dead-lettered successor stays out of the ready set.
278
+ */
279
+ const SETTLE_LUA = `
280
+ local function settle(field, lane, next_field)
281
+ redis.call('ZREM', PENDING, field)
282
+ redis.call('ZREM', READY, field)
283
+ if lane == '' then return end
284
+ local head_key = lane_field(lane, 'head')
285
+ if redis.call('HGET', META, head_key) ~= field then return end
286
+ while next_field do
287
+ local row = read_fields({ next_field }, { 'p', 'f', 'next' })[next_field]
288
+ if not row[1] then
289
+ redis.call('HSET', META, head_key, next_field)
290
+ if not row[2] then
291
+ redis.call('ZADD', READY, tonumber(next_field), next_field)
292
+ end
293
+ return
294
+ end
295
+ next_field = row[3]
296
+ end
297
+ clear_lane(lane)
298
+ end
299
+ `;
300
+ /**
259
301
  * Event-id ownership for scripts that add events. Each script reads the
260
302
  * counter, split, clear boundary, and per-id keys with one MGET and applies
261
303
  * the first three. Returns the owner record, or false, per event, and the
@@ -580,25 +622,7 @@ ${ENQUEUE_LUA}
580
622
  ${ID_OWNERS_LUA}
581
623
  ${RELEASE_PINS_LUA}
582
624
  ${WAKE_LUA}
583
- local function settle(field, lane, next_field)
584
- redis.call('ZREM', PENDING, field)
585
- redis.call('ZREM', READY, field)
586
- if lane == '' then return end
587
- local head_key = lane_field(lane, 'head')
588
- if redis.call('HGET', META, head_key) ~= field then return end
589
- while next_field do
590
- local row = read_fields({ next_field }, { 'p', 'f', 'next' })[next_field]
591
- if not row[1] then
592
- redis.call('HSET', META, head_key, next_field)
593
- if not row[2] then
594
- redis.call('ZADD', READY, tonumber(next_field), next_field)
595
- end
596
- return
597
- end
598
- next_field = row[3]
599
- end
600
- clear_lane(lane)
601
- end
625
+ ${SETTLE_LUA}
602
626
  local parent = ARGV[3]
603
627
  local attempt = tonumber(ARGV[4])
604
628
  local n = tonumber(ARGV[5])
@@ -684,6 +708,76 @@ if n > 0 then wake(ARGV[6 + n * 5], tostring(total + n), ARGV[2]) end
684
708
  return { 'ok', tostring(total + 1) }
685
709
  `;
686
710
  /**
711
+ * KEYS: counter, meta, pending, ready, metadata split.
712
+ * ARGV: idx, action ('retry' | 'skip'), nowMs.
713
+ * Returns [redriven, hasPending]. Acts only on an unfinished, dead-lettered
714
+ * event above the clear boundary. A retry keeps the event pending and makes
715
+ * it ready again when it is unlaned or its lane's head.
716
+ */
717
+ const REDRIVE_LUA = `
718
+ local COUNTER, META, SPLIT, PENDING, READY = KEYS[1], KEYS[2], KEYS[5], KEYS[3], KEYS[4]
719
+ ${META_LUA}
720
+ ${ENQUEUE_LUA}
721
+ ${SETTLE_LUA}
722
+ local function result(redriven)
723
+ return { redriven, redis.call('ZCARD', PENDING) > 0 and '1' or '0' }
724
+ end
725
+ local counts = redis.call('MGET', COUNTER, SPLIT, CLEAR)
726
+ local total = tonumber(counts[1] or '0')
727
+ use_split(counts[2], counts[1], counts[3])
728
+ local idx = tonumber(ARGV[1])
729
+ if idx <= boundary or idx > total then return result('0') end
730
+ local field = tostring(idx)
731
+ local row = read_fields({ field }, { 'p', 'f', 'l', 'next' })[field]
732
+ if row[1] or not row[2] then return result('0') end
733
+ local meta_key = mkey(field)
734
+ local lane = row[3] or ''
735
+ if ARGV[2] == 'skip' then
736
+ put(meta_key, field .. ':p', ARGV[3])
737
+ settle(field, lane, row[4])
738
+ flush_queue()
739
+ flush()
740
+ else
741
+ redis.call('HDEL', meta_key, field .. ':f', field .. ':a')
742
+ if lane == '' or redis.call('HGET', META, lane_field(lane, 'head')) == field then
743
+ redis.call('ZADD', READY, idx, field)
744
+ end
745
+ end
746
+ return result('1')
747
+ `;
748
+ /**
749
+ * KEYS: store, counter, meta, pending, metadata split.
750
+ * Returns the dead-lettered events above the clear boundary, oldest first,
751
+ * as pairs of a stream entry and its STORED_EVENT_META_SUFFIXES columns.
752
+ * Every unfinished event is in the pending set, so the scan is bounded by
753
+ * unfinished work, not history; only dead letters read their full columns.
754
+ */
755
+ const DEAD_LETTERS_LUA = `
756
+ local COUNTER, META, SPLIT = KEYS[2], KEYS[3], KEYS[5]
757
+ ${META_LUA}
758
+ local counts = redis.call('MGET', COUNTER, SPLIT, CLEAR)
759
+ use_split(counts[2], counts[1], counts[3])
760
+ local candidates = {}
761
+ for _, field in ipairs(redis.call('ZRANGE', KEYS[4], 0, -1)) do
762
+ if tonumber(field) > boundary then candidates[#candidates + 1] = field end
763
+ end
764
+ local status = read_fields(candidates, { 'p', 'f' })
765
+ local dead = {}
766
+ for _, field in ipairs(candidates) do
767
+ if status[field][2] and not status[field][1] then dead[#dead + 1] = field end
768
+ end
769
+ local rows = read_fields(dead, { ${STORED_EVENT_META_SUFFIXES.map((suffix) => `'${suffix}'`).join(", ")} })
770
+ local result = {}
771
+ for _, field in ipairs(dead) do
772
+ local entries = redis.call('XRANGE', KEYS[1], field .. '-0', field .. '-0')
773
+ if #entries > 0 then
774
+ result[#result + 1] = entries[1]
775
+ result[#result + 1] = rows[field]
776
+ end
777
+ end
778
+ return result
779
+ `;
780
+ /**
687
781
  * KEYS: head, history, event counter, event meta, pin counts, metadata split.
688
782
  * ARGV: nowMs, event pin key prefix, write count, then per write:
689
783
  * index, snapshot JSON, pin count, pin event indexes; finally the reducer
@@ -1070,21 +1164,6 @@ const fieldMap = (fields) => {
1070
1164
  for (let i = 0; i + 1 < fields.length; i += 2) map[fields[i]] = fields[i + 1];
1071
1165
  return map;
1072
1166
  };
1073
- const STORED_EVENT_META_SUFFIXES = [
1074
- "p",
1075
- "pa",
1076
- "ri",
1077
- "ch",
1078
- "ce",
1079
- "f",
1080
- "fc",
1081
- "lc",
1082
- "lf",
1083
- "lfa",
1084
- "d",
1085
- "a",
1086
- "e"
1087
- ];
1088
1167
  const entryIndex = (entryId) => Number(entryId.slice(0, entryId.indexOf("-")));
1089
1168
  const toEvent = (sessionId, entry) => {
1090
1169
  const fields = fieldMap(entry[1]);
@@ -1153,6 +1232,36 @@ const toCause = (raw) => {
1153
1232
  ...value.batchSize === void 0 ? {} : { batchSize: Number(value.batchSize) }
1154
1233
  };
1155
1234
  };
1235
+ /** A stream entry joined with its metadata columns; absent columns are unset. */
1236
+ const toStoredEvent = (sessionId, entry, column) => {
1237
+ const fields = fieldMap(entry[1]);
1238
+ const date = (suffix) => {
1239
+ const value = column(suffix);
1240
+ return value === void 0 ? null : new Date(Number(value));
1241
+ };
1242
+ const count = (suffix) => {
1243
+ const value = column(suffix);
1244
+ return value === void 0 ? null : Number(value);
1245
+ };
1246
+ const returnedEventIds = column("ri");
1247
+ return Object.assign(toEvent(sessionId, entry), {
1248
+ cause: toCause(fields["cause"]),
1249
+ lane: fields["lane"] === "" ? null : fields["lane"],
1250
+ processedAt: date("p"),
1251
+ processedByAttempt: count("pa"),
1252
+ returnedEventIds: returnedEventIds === void 0 ? null : decodeReturnedEventIds(returnedEventIds),
1253
+ firstClaimedAt: date("fc"),
1254
+ lastClaimedAt: date("lc"),
1255
+ attemptCount: count("d") ?? 0,
1256
+ claimHolder: column("ch") ?? null,
1257
+ claimExpiresAt: date("ce"),
1258
+ failureCount: count("a") ?? 0,
1259
+ lastFailedAt: date("lf"),
1260
+ lastFailedAttempt: count("lfa"),
1261
+ lastError: column("e") ?? null,
1262
+ failedAt: date("f")
1263
+ });
1264
+ };
1156
1265
  const toClaimAvailableResult = (sessionId, reply) => {
1157
1266
  const outcome = String(reply[0]);
1158
1267
  if (outcome === "claimed") {
@@ -1295,36 +1404,8 @@ function createRedisStoreCore(options) {
1295
1404
  }
1296
1405
  }
1297
1406
  return entries.map((entry) => {
1298
- const event = toEvent(sessionId, entry);
1299
- const idx = event.index;
1300
- const fields = fieldMap(entry[1]);
1301
- const processedAt = meta[`${idx}:p`];
1302
- const processedByAttempt = meta[`${idx}:pa`];
1303
- const returnedEventIds = meta[`${idx}:ri`];
1304
- const claimHolder = meta[`${idx}:ch`];
1305
- const claimExpiresAt = meta[`${idx}:ce`];
1306
- const failedAt = meta[`${idx}:f`];
1307
- const firstClaimedAt = meta[`${idx}:fc`];
1308
- const lastClaimedAt = meta[`${idx}:lc`];
1309
- const lastFailedAt = meta[`${idx}:lf`];
1310
- const lastFailedAttempt = meta[`${idx}:lfa`];
1311
- return Object.assign(event, {
1312
- cause: toCause(fields["cause"]),
1313
- lane: fields["lane"] === "" ? null : fields["lane"],
1314
- processedAt: processedAt === void 0 ? null : new Date(Number(processedAt)),
1315
- processedByAttempt: processedByAttempt === void 0 ? null : Number(processedByAttempt),
1316
- returnedEventIds: returnedEventIds === void 0 ? null : decodeReturnedEventIds(returnedEventIds),
1317
- firstClaimedAt: firstClaimedAt === void 0 ? null : new Date(Number(firstClaimedAt)),
1318
- lastClaimedAt: lastClaimedAt === void 0 ? null : new Date(Number(lastClaimedAt)),
1319
- attemptCount: Number(meta[`${idx}:d`] ?? 0),
1320
- claimHolder: claimHolder ?? null,
1321
- claimExpiresAt: claimExpiresAt === void 0 ? null : new Date(Number(claimExpiresAt)),
1322
- failureCount: Number(meta[`${idx}:a`] ?? 0),
1323
- lastFailedAt: lastFailedAt === void 0 ? null : new Date(Number(lastFailedAt)),
1324
- lastFailedAttempt: lastFailedAttempt === void 0 ? null : Number(lastFailedAttempt),
1325
- lastError: meta[`${idx}:e`] ?? null,
1326
- failedAt: failedAt === void 0 ? null : new Date(Number(failedAt))
1327
- });
1407
+ const idx = entryIndex(entry[0]);
1408
+ return toStoredEvent(sessionId, entry, (suffix) => meta[`${idx}:${suffix}`]);
1328
1409
  });
1329
1410
  };
1330
1411
  const readEventPages = async (sessionId, afterIndex, throughIndex) => {
@@ -1675,6 +1756,42 @@ function createRedisStoreCore(options) {
1675
1756
  };
1676
1757
  });
1677
1758
  },
1759
+ async deadLetters(sessionId) {
1760
+ return wrap(async () => {
1761
+ const reply = await evalScript(DEAD_LETTERS_LUA, [
1762
+ storeKey(sessionId),
1763
+ countKey(sessionId),
1764
+ metaKey(sessionId),
1765
+ pendingKey(sessionId),
1766
+ metaSplitKey(sessionId)
1767
+ ], []);
1768
+ const events = [];
1769
+ for (let i = 0; i + 1 < reply.length; i += 2) {
1770
+ const columns = reply[i + 1];
1771
+ events.push(toStoredEvent(sessionId, reply[i], (suffix) => columns[STORED_EVENT_META_SUFFIXES.indexOf(suffix)] ?? void 0));
1772
+ }
1773
+ return events;
1774
+ });
1775
+ },
1776
+ async redrive({ sessionId, index, action }) {
1777
+ return wrap(async () => {
1778
+ const [redriven, hasPending] = await evalScript(REDRIVE_LUA, [
1779
+ countKey(sessionId),
1780
+ metaKey(sessionId),
1781
+ pendingKey(sessionId),
1782
+ readyKey(sessionId),
1783
+ metaSplitKey(sessionId)
1784
+ ], [
1785
+ index,
1786
+ action,
1787
+ clock.now().getTime()
1788
+ ]);
1789
+ return {
1790
+ redriven: redriven === "1",
1791
+ hasPending: hasPending === "1"
1792
+ };
1793
+ });
1794
+ },
1678
1795
  async readState(sessionId, reducerName, stateOptions) {
1679
1796
  return wrap(() => retryAfterGap(async () => {
1680
1797
  const reply = await evalScript(READ_STATE_LUA, [
@@ -2084,6 +2201,8 @@ function createRedisNotifyStore(options) {
2084
2201
  putSnapshots: core.putSnapshots,
2085
2202
  clear: core.clear,
2086
2203
  reclaim: core.reclaim,
2204
+ deadLetters: core.deadLetters,
2205
+ redrive: core.redrive,
2087
2206
  presence: {
2088
2207
  async set(ns, participant, values, meta) {
2089
2208
  const patch = await core.presence.set(ns, participant, values, meta);
@@ -2371,4 +2490,4 @@ function createRedisNotifyStore(options) {
2371
2490
  //#endregion
2372
2491
  export { createRedisNotifyStore as t };
2373
2492
 
2374
- //# sourceMappingURL=store-redis-notify-tqWNnMRu.js.map
2493
+ //# sourceMappingURL=store-redis-notify-BRITti_o.js.map