experimental-a2 0.18.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/{actor-Bjce9_5U.d.ts → actor-C7b-1arn.d.ts} +3 -3
  3. package/dist/{actor-Bjce9_5U.d.ts.map → actor-C7b-1arn.d.ts.map} +1 -1
  4. package/dist/actor-client.d.ts +1 -1
  5. package/dist/actor-react.d.ts +1 -1
  6. package/dist/actor-react.js +1 -1
  7. package/dist/actor.d.ts +1 -1
  8. package/dist/actor.js +2 -2
  9. package/dist/{ai-DrUaMyRt.d.ts → ai-BMLlI2Ff.d.ts} +2 -2
  10. package/dist/{ai-DrUaMyRt.d.ts.map → ai-BMLlI2Ff.d.ts.map} +1 -1
  11. package/dist/{ai-BpOGm-zB.js → ai-D6mGlO8j.js} +2 -2
  12. package/dist/{ai-BpOGm-zB.js.map → ai-D6mGlO8j.js.map} +1 -1
  13. package/dist/ai-client.d.ts +1 -1
  14. package/dist/ai-client.js +2 -2
  15. package/dist/ai-fx.d.ts +1 -1
  16. package/dist/ai-fx.d.ts.map +1 -1
  17. package/dist/ai-fx.js +92 -34
  18. package/dist/ai-fx.js.map +1 -1
  19. package/dist/{ai-harness-DvsPeJeB.d.ts → ai-harness-B2suP2Qh.d.ts} +2 -2
  20. package/dist/{ai-harness-DvsPeJeB.d.ts.map → ai-harness-B2suP2Qh.d.ts.map} +1 -1
  21. package/dist/ai-pi.d.ts +1 -1
  22. package/dist/ai-pi.d.ts.map +1 -1
  23. package/dist/ai-pi.js +59 -13
  24. package/dist/ai-pi.js.map +1 -1
  25. package/dist/ai-react.d.ts +1 -1
  26. package/dist/ai-react.js +2 -2
  27. package/dist/ai-server.d.ts +3 -3
  28. package/dist/ai-server.d.ts.map +1 -1
  29. package/dist/ai-server.js +10 -6
  30. package/dist/ai-server.js.map +1 -1
  31. package/dist/ai-tool-input-DmSqMUQi.js +40 -0
  32. package/dist/ai-tool-input-DmSqMUQi.js.map +1 -0
  33. package/dist/ai.d.ts +1 -1
  34. package/dist/ai.js +1 -1
  35. package/dist/{client-BReKqwZC.js → client-BhHLoNKF.js} +3 -3
  36. package/dist/{client-BReKqwZC.js.map → client-BhHLoNKF.js.map} +1 -1
  37. package/dist/client.js +1 -1
  38. package/dist/index.d.ts +2 -2
  39. package/dist/{internal-B2T3U4Rq.js → internal-CscSY7K_.js} +4 -2
  40. package/dist/{internal-B2T3U4Rq.js.map → internal-CscSY7K_.js.map} +1 -1
  41. package/dist/react.js +1 -1
  42. package/dist/scheduler-qstash.d.ts +2 -2
  43. package/dist/scheduler-qstash.js +3 -3
  44. package/dist/scheduler-qstash.js.map +1 -1
  45. package/dist/scheduler-vercel.d.ts +7 -7
  46. package/dist/scheduler-vercel.d.ts.map +1 -1
  47. package/dist/scheduler-vercel.js +9 -10
  48. package/dist/scheduler-vercel.js.map +1 -1
  49. package/dist/{server-Dw_k_7kO.js → server-D-Y4pqwu.js} +37 -11
  50. package/dist/server-D-Y4pqwu.js.map +1 -0
  51. package/dist/{server-En7cbi3v.d.ts → server-DYAATFuT.d.ts} +11 -5
  52. package/dist/server-DYAATFuT.d.ts.map +1 -0
  53. package/dist/server.d.ts +2 -2
  54. package/dist/server.js +1 -1
  55. package/dist/{session-clear-Bo-XTFEy.d.ts → session-clear-C-QwTXV1.d.ts} +2 -2
  56. package/dist/session-clear-C-QwTXV1.d.ts.map +1 -0
  57. package/dist/{store-Djdv0bnZ.d.ts → store-CyLUy86v.d.ts} +23 -4
  58. package/dist/store-CyLUy86v.d.ts.map +1 -0
  59. package/dist/store-N8PXxDAS.js.map +1 -1
  60. package/dist/store-memory.d.ts +1 -1
  61. package/dist/store-memory.js +25 -6
  62. package/dist/store-memory.js.map +1 -1
  63. package/dist/{store-polling-B5FcwJfo.js → store-polling-7WJV0YWP.js} +2 -2
  64. package/dist/{store-polling-B5FcwJfo.js.map → store-polling-7WJV0YWP.js.map} +1 -1
  65. package/dist/store-postgres.d.ts +1 -1
  66. package/dist/store-postgres.d.ts.map +1 -1
  67. package/dist/store-postgres.js +55 -25
  68. package/dist/store-postgres.js.map +1 -1
  69. package/dist/{store-presence-polling-BAwS8RWn.js → store-presence-polling-B1ixeqIS.js} +3 -3
  70. package/dist/{store-presence-polling-BAwS8RWn.js.map → store-presence-polling-B1ixeqIS.js.map} +1 -1
  71. package/dist/store-redis-http.d.ts +1 -1
  72. package/dist/store-redis-http.js +3 -3
  73. package/dist/{store-redis-notify-BRITti_o.js → store-redis-notify-CM0AfLY0.js} +88 -37
  74. package/dist/store-redis-notify-CM0AfLY0.js.map +1 -0
  75. package/dist/store-redis.d.ts +1 -1
  76. package/dist/store-redis.js +1 -1
  77. package/dist/store-sqlite.d.ts +1 -1
  78. package/dist/store-sqlite.js +35 -20
  79. package/dist/store-sqlite.js.map +1 -1
  80. package/dist/{wire-rSUglYo8.js → wire-BKxH1vbP.js} +2 -2
  81. package/dist/{wire-rSUglYo8.js.map → wire-BKxH1vbP.js.map} +1 -1
  82. package/docs/agents/03-conversations.mdx +2 -2
  83. package/docs/index.mdx +3 -3
  84. package/docs/reference/02-server.mdx +12 -7
  85. package/docs/reference/04-ai.mdx +16 -7
  86. package/docs/reference/05-adapters.mdx +4 -4
  87. package/docs/server/01-handlers.mdx +1 -1
  88. package/docs/server/03-cancellation.mdx +3 -2
  89. package/docs/server/05-production.mdx +2 -1
  90. package/docs/server/06-guarantees.mdx +20 -11
  91. package/examples/playground/DEPLOY.md +2 -4
  92. package/examples/playground/app/a0/[appId]/page.tsx +2 -8
  93. package/examples/playground/app/a0/devtools.tsx +2 -8
  94. package/examples/playground/app/a0/server.ts +8 -19
  95. package/examples/playground/package.json +1 -1
  96. package/package.json +1 -1
  97. package/src/ai-agent.ts +10 -3
  98. package/src/ai-fx.ts +126 -47
  99. package/src/ai-pi.ts +74 -15
  100. package/src/ai-tool-input.ts +45 -0
  101. package/src/internal.ts +6 -0
  102. package/src/scheduler-qstash.ts +5 -5
  103. package/src/scheduler-vercel.ts +14 -15
  104. package/src/server.ts +58 -12
  105. package/src/store-memory.ts +50 -9
  106. package/src/store-postgres.ts +79 -28
  107. package/src/store-redis-core.ts +112 -31
  108. package/src/store-sqlite.ts +61 -28
  109. package/src/store.ts +22 -3
  110. package/dist/server-Dw_k_7kO.js.map +0 -1
  111. package/dist/server-En7cbi3v.d.ts.map +0 -1
  112. package/dist/session-clear-Bo-XTFEy.d.ts.map +0 -1
  113. package/dist/store-Djdv0bnZ.d.ts.map +0 -1
  114. package/dist/store-redis-notify-BRITti_o.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { i as POLL_TIMINGS } from "./internal-B2T3U4Rq.js";
2
- import { t as defaultSleep } from "./store-polling-B5FcwJfo.js";
1
+ import { i as POLL_TIMINGS } from "./internal-CscSY7K_.js";
2
+ import { t as defaultSleep } from "./store-polling-7WJV0YWP.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-BAwS8RWn.js.map
94
+ //# sourceMappingURL=store-presence-polling-B1ixeqIS.js.map
@@ -1 +1 @@
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
+ {"version":3,"file":"store-presence-polling-B1ixeqIS.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-Djdv0bnZ.js";
1
+ import { r as Clock, s as IdSource, t as A2Store } from "./store-CyLUy86v.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-B2T3U4Rq.js";
3
- import { t as defaultSleep } from "./store-polling-B5FcwJfo.js";
2
+ import { r as NOTIFY_TIMINGS } from "./internal-CscSY7K_.js";
3
+ import { t as defaultSleep } from "./store-polling-7WJV0YWP.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-BRITti_o.js";
5
+ import { t as createRedisNotifyStore } from "./store-redis-notify-CM0AfLY0.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 { _ 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";
2
+ import { _ as nullProtoRecord, r as NOTIFY_TIMINGS, y as stateTailReaders } from "./internal-CscSY7K_.js";
3
+ import { i as streamActivities, n as pollingStream, r as createStreamActivity, t as defaultSleep } from "./store-polling-7WJV0YWP.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-BAwS8RWn.js";
7
+ import { t as pollPresence } from "./store-presence-polling-B1ixeqIS.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 }) {
@@ -464,7 +464,7 @@ return { 'ok', tostring(base + 1), pending_after }
464
464
  /**
465
465
  * KEYS: counter, meta, pending, ready, event snapshot pins, snapshot pin counts,
466
466
  * metadata split.
467
- * ARGV: idx, attempt, error, maxFailures, nowMs.
467
+ * ARGV: idx, attempt, error, maxFailures, nowMs, retryAtMs ('' for none).
468
468
  */
469
469
  const FAIL_ATTEMPT_LUA = `
470
470
  local COUNTER, META, SPLIT = KEYS[1], KEYS[2], KEYS[7]
@@ -496,8 +496,14 @@ put(meta_key, field .. ':e', ARGV[3])
496
496
  put(meta_key, field .. ':lf', ARGV[5])
497
497
  put(meta_key, field .. ':lfa', ARGV[2])
498
498
  if dead then put(meta_key, field .. ':f', ARGV[5]) end
499
+ local hold = not dead and ARGV[6] ~= ''
500
+ if hold then put(meta_key, field .. ':ce', ARGV[6]) end
499
501
  flush()
500
- redis.call('HDEL', meta_key, field .. ':ch', field .. ':ce')
502
+ if hold then
503
+ redis.call('HDEL', meta_key, field .. ':ch')
504
+ else
505
+ redis.call('HDEL', meta_key, field .. ':ch', field .. ':ce')
506
+ end
501
507
  if dead then
502
508
  redis.call('ZREM', KEYS[4], field)
503
509
  release_snapshot_pins(KEYS[5], KEYS[6])
@@ -506,23 +512,29 @@ end
506
512
  return { 'failed', tostring(failures) }
507
513
  `;
508
514
  /**
509
- * KEYS: store, counter, meta, pending, ready, metadata split.
510
- * ARGV: holder, nowMs, expiresAtMs, excludedCount, excluded indexes.
515
+ * KEYS: store, counter, meta, pending, ready, metadata split, snapshot pin
516
+ * counts.
517
+ * ARGV: holder, nowMs, expiresAtMs, excludedCount, excluded indexes, then
518
+ * maxFailures ('' for none) and the event snapshot-pins key prefix.
511
519
  * The ready set is a subset of the pending set, so an empty ready set is
512
520
  * enough to report 'settled'. A claimed result ends with the clear boundary.
513
521
  */
514
522
  const CLAIM_AVAILABLE_LUA = `
515
523
  local COUNTER, META, SPLIT = KEYS[2], KEYS[3], KEYS[6]
516
524
  ${META_LUA}
525
+ ${RELEASE_PINS_LUA}
517
526
  local ready = redis.call('ZRANGE', KEYS[5], 0, -1)
518
527
  local excluded = {}
528
+ local tail = 5 + tonumber(ARGV[4])
519
529
  for i = 1, tonumber(ARGV[4]) do excluded[ARGV[4 + i]] = true end
530
+ local max_failures = tonumber(ARGV[tail])
520
531
  local candidates = {}
521
532
  for _, field in ipairs(ready) do
522
533
  if not excluded[field] then candidates[#candidates + 1] = field end
523
534
  end
524
535
  local rows = read_fields(candidates, { 'ce', 'd', 'fc', 'a', 'e', 'lf', 'lfa' })
525
536
  local claimed = {}
537
+ local dead = {}
526
538
  local retry_at = nil
527
539
  for _, field in ipairs(candidates) do
528
540
  local row = rows[field]
@@ -532,29 +544,63 @@ for _, field in ipairs(candidates) do
532
544
  else
533
545
  local entries = redis.call('XRANGE', KEYS[1], field .. '-0', field .. '-0')
534
546
  if #entries == 0 then return { 'missing', field } end
535
- local attempt = tostring(tonumber(row[2] or '0') + 1)
536
- local first_claimed = row[3] or ARGV[2]
537
547
  local meta_key = mkey(field)
548
+ local attempt = tostring(tonumber(row[2] or '0') + 1)
549
+ -- An expiry that is not the current attempt's retry hold is a lapsed
550
+ -- claim: its attempt never finished.
551
+ local lapsed = row[1] and row[7] ~= row[2]
552
+ local failures = tonumber(row[4] or '0')
553
+ if lapsed then
554
+ failures = failures + 1
555
+ put(meta_key, field .. ':a', tostring(failures))
556
+ end
538
557
  put(meta_key, field .. ':d', attempt)
539
- put(meta_key, field .. ':fc', first_claimed)
540
- put(meta_key, field .. ':lc', ARGV[2])
541
- put(meta_key, field .. ':ch', ARGV[1])
542
- put(meta_key, field .. ':ce', ARGV[3])
543
- claimed[#claimed + 1] = {
544
- attempt,
545
- row[4] or '0',
546
- row[5] or false,
547
- first_claimed,
548
- ARGV[2],
549
- row[6] or false,
550
- row[7] or false,
551
- ARGV[1],
552
- ARGV[3],
553
- entries[1]
554
- }
558
+ if lapsed and max_failures and failures >= max_failures then
559
+ -- That takeover spent the last failed attempt: dead-letter, unclaimed.
560
+ -- Advancing the attempt fences the lapsed one; nothing runs.
561
+ put(meta_key, field .. ':f', ARGV[2])
562
+ dead[#dead + 1] = field
563
+ claimed[#claimed + 1] = {
564
+ attempt,
565
+ tostring(failures),
566
+ row[5] or false,
567
+ row[3] or false,
568
+ read_fields({ field }, { 'lc' })[field][1] or false,
569
+ row[6] or false,
570
+ row[7] or false,
571
+ false,
572
+ false,
573
+ entries[1],
574
+ ARGV[2]
575
+ }
576
+ else
577
+ local first_claimed = row[3] or ARGV[2]
578
+ put(meta_key, field .. ':fc', first_claimed)
579
+ put(meta_key, field .. ':lc', ARGV[2])
580
+ put(meta_key, field .. ':ch', ARGV[1])
581
+ put(meta_key, field .. ':ce', ARGV[3])
582
+ claimed[#claimed + 1] = {
583
+ attempt,
584
+ tostring(failures),
585
+ row[5] or false,
586
+ first_claimed,
587
+ ARGV[2],
588
+ row[6] or false,
589
+ row[7] or false,
590
+ ARGV[1],
591
+ ARGV[3],
592
+ entries[1],
593
+ false
594
+ }
595
+ end
555
596
  end
556
597
  end
557
598
  flush()
599
+ for _, field in ipairs(dead) do
600
+ redis.call('HDEL', mkey(field), field .. ':ch', field .. ':ce')
601
+ redis.call('ZREM', KEYS[5], field)
602
+ release_snapshot_pins(ARGV[tail + 1] .. field, KEYS[7])
603
+ end
558
604
  if #claimed > 0 then
559
605
  local result = { 'claimed', tostring(#claimed) }
560
606
  for _, claim in ipairs(claimed) do
@@ -1268,7 +1314,7 @@ const toClaimAvailableResult = (sessionId, reply) => {
1268
1314
  const count = Number(reply[1]);
1269
1315
  const events = [];
1270
1316
  for (let i = 0; i < count; i++) {
1271
- const offset = 2 + i * 10;
1317
+ const offset = 2 + i * 11;
1272
1318
  const entry = reply[offset + 9];
1273
1319
  const event = toEvent(sessionId, entry);
1274
1320
  const fields = fieldMap(entry[1]);
@@ -1279,18 +1325,18 @@ const toClaimAvailableResult = (sessionId, reply) => {
1279
1325
  processedByAttempt: null,
1280
1326
  returnedEventIds: null,
1281
1327
  firstClaimedAt: reply[offset + 3] === null ? null : new Date(Number(reply[offset + 3])),
1282
- lastClaimedAt: new Date(Number(reply[offset + 4])),
1328
+ lastClaimedAt: reply[offset + 4] === null ? null : new Date(Number(reply[offset + 4])),
1283
1329
  attemptCount: Number(reply[offset]),
1284
- claimHolder: String(reply[offset + 7]),
1285
- claimExpiresAt: new Date(Number(reply[offset + 8])),
1330
+ claimHolder: reply[offset + 7] === null ? null : String(reply[offset + 7]),
1331
+ claimExpiresAt: reply[offset + 8] === null ? null : new Date(Number(reply[offset + 8])),
1286
1332
  failureCount: Number(reply[offset + 1]),
1287
1333
  lastFailedAt: reply[offset + 5] === null ? null : new Date(Number(reply[offset + 5])),
1288
1334
  lastFailedAttempt: reply[offset + 6] === null ? null : Number(reply[offset + 6]),
1289
1335
  lastError: reply[offset + 2] === null ? null : String(reply[offset + 2]),
1290
- failedAt: null
1336
+ failedAt: reply[offset + 10] === null ? null : new Date(Number(reply[offset + 10]))
1291
1337
  }));
1292
1338
  }
1293
- const clearIndex = Number(reply[2 + count * 10] ?? 0);
1339
+ const clearIndex = Number(reply[2 + count * 11] ?? 0);
1294
1340
  if (clearIndex > 0) for (const event of events) event.clearIndex = clearIndex;
1295
1341
  return {
1296
1342
  outcome: "claimed",
@@ -1599,7 +1645,7 @@ function createRedisStoreCore(options) {
1599
1645
  return readEventRange(sessionId, Math.max(after, clearIndex - 1), Math.min(opts?.throughIndex ?? frontier, frontier));
1600
1646
  }));
1601
1647
  },
1602
- async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes }) {
1648
+ async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes, maxFailures }) {
1603
1649
  return wrap(async () => {
1604
1650
  const now = clock.now().getTime();
1605
1651
  const expiresAt = expiresAtMs ?? now + ttlMs;
@@ -1610,13 +1656,16 @@ function createRedisStoreCore(options) {
1610
1656
  metaKey(sessionId),
1611
1657
  pendingKey(sessionId),
1612
1658
  readyKey(sessionId),
1613
- metaSplitKey(sessionId)
1659
+ metaSplitKey(sessionId),
1660
+ snapshotPinCountsKey(sessionId)
1614
1661
  ], [
1615
1662
  holder,
1616
1663
  now,
1617
1664
  expiresAt,
1618
1665
  excluded.length,
1619
- ...excluded
1666
+ ...excluded,
1667
+ maxFailures ?? "",
1668
+ snapshotEventPinsPrefix(sessionId)
1620
1669
  ]);
1621
1670
  return toClaimAvailableResult(sessionId, reply);
1622
1671
  });
@@ -1727,8 +1776,9 @@ function createRedisStoreCore(options) {
1727
1776
  throw new TypeError(`unexpected completion outcome '${outcome}'`);
1728
1777
  });
1729
1778
  },
1730
- async failAttempt({ sessionId, index, attempt, error, maxFailures }) {
1779
+ async failAttempt({ sessionId, index, attempt, error, maxFailures, retryAfterMs }) {
1731
1780
  return wrap(async () => {
1781
+ const now = clock.now().getTime();
1732
1782
  const reply = await evalScript(FAIL_ATTEMPT_LUA, [
1733
1783
  countKey(sessionId),
1734
1784
  metaKey(sessionId),
@@ -1742,7 +1792,8 @@ function createRedisStoreCore(options) {
1742
1792
  attempt,
1743
1793
  error,
1744
1794
  maxFailures,
1745
- clock.now().getTime()
1795
+ now,
1796
+ retryAfterMs === void 0 ? "" : now + retryAfterMs
1746
1797
  ]);
1747
1798
  const outcome = reply[0];
1748
1799
  if (outcome === "cleared") return {
@@ -2490,4 +2541,4 @@ function createRedisNotifyStore(options) {
2490
2541
  //#endregion
2491
2542
  export { createRedisNotifyStore as t };
2492
2543
 
2493
- //# sourceMappingURL=store-redis-notify-BRITti_o.js.map
2544
+ //# sourceMappingURL=store-redis-notify-CM0AfLY0.js.map