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 +1 @@
1
- {"version":3,"file":"store-polling-B5FcwJfo.js","names":[],"sources":["../src/stream-activity.ts","../src/store-polling.ts"],"sourcesContent":["export type StreamActivity = {\n readonly idle: boolean\n setIdle(idle: boolean): void\n activate(): void\n subscribe(listener: (idle: boolean) => void): () => void\n}\n\nexport const streamActivities: WeakMap<object, StreamActivity> = new WeakMap()\n\nexport function createStreamActivity(wake: () => void): StreamActivity {\n let idle = false\n const listeners = new Set<(idle: boolean) => void>()\n const setIdle = (next: boolean): void => {\n if (idle === next) return\n idle = next\n for (const listener of listeners) listener(idle)\n }\n return {\n get idle() {\n return idle\n },\n setIdle,\n activate() {\n setIdle(false)\n wake()\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n }\n}\n","/**\n * The shared polling stream — how poll-based backends (sqlite,\n * postgres) implement `A2Store.stream`.\n *\n * The cadence is activity-adaptive: while events are flowing, polls\n * run at a tight floor (25ms) so streams feel pushed — a token\n * generation reads smoothly instead of clumping at the idle interval;\n * every empty poll doubles the gap until the idle ceiling. The cadence\n * is owned (POLL_TIMINGS in internal.ts), not a public option: the\n * floor already keeps active streams smooth, so tuning it is a cost\n * decision a2 makes, not one it delegates. Correctness never depends\n * on the cadence — each poll is a catch-up read (`index > last`, in\n * order), so nothing can be missed, only arrive a beat later.\n */\n\nimport { POLL_TIMINGS } from './internal.ts'\nimport type { Event } from './store.ts'\nimport { createStreamActivity, streamActivities } from './stream-activity.ts'\n\n/** One cancellable sleep — the injection seam for cadence tests. */\nexport type Sleeper = { promise: Promise<void>; cancel: () => void }\n\n/** Unref'd cancellable timer — shared with the redis safety read. */\nexport const defaultSleep = (ms: number): Sleeper => {\n let timer: ReturnType<typeof setTimeout> | undefined\n let wake: (() => void) | undefined\n const promise = new Promise<void>((resolve) => {\n wake = resolve // assigned synchronously by the executor\n timer = setTimeout(resolve, ms)\n // Don't hold the process open for an idle poll.\n ;(timer as { unref?: () => void }).unref?.()\n })\n return {\n promise,\n cancel: () => {\n clearTimeout(timer)\n wake?.()\n },\n }\n}\n\n// Read the seam lazily — tests shrink POLL_TIMINGS in their setup.\nconst ceiling = (): number => POLL_TIMINGS.idleCeilingMs\nconst floor = (): number =>\n Math.min(POLL_TIMINGS.activeFloorMs, POLL_TIMINGS.idleCeilingMs)\n\nexport type PollingStreamOptions = {\n /** Deliver events strictly after this index (exclusive). */\n startAfter?: number\n /** Injectable sleep — tests assert the cadence without wall-clock waits. */\n sleep?: (ms: number) => Sleeper\n}\n\n/**\n * Build the `stream()` iterable from a backend's catch-up read. The\n * iterable ends when the consumer calls `return()` (a disconnecting\n * SSE client), waking any pending sleep immediately.\n */\nexport function pollingStream(\n readAfter: (afterIndex: number) => Event[] | Promise<Event[]>,\n options: PollingStreamOptions,\n): AsyncIterable<Event> {\n const startAfter = options.startAfter ?? 0\n const sleep = options.sleep ?? defaultSleep\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n let last = startAfter\n let buffer: Array<Event | undefined> = []\n let position = 0\n let closed = false\n let pending: Sleeper | null = null\n let interval = floor()\n let activityVersion = 0\n const activity = createStreamActivity(() => {\n if (closed) return\n activityVersion += 1\n interval = floor()\n pending?.cancel()\n })\n\n const iterator: AsyncIterator<Event> = {\n async next(): Promise<IteratorResult<Event>> {\n for (;;) {\n if (closed) return { value: undefined, done: true }\n try {\n if (position === buffer.length) {\n // oxlint-disable-next-line no-await-in-loop -- poll loop\n const rows = await readAfter(last)\n if (closed) return { value: undefined, done: true }\n buffer = rows\n position = 0\n }\n } catch (error) {\n if (closed) return { value: undefined, done: true }\n throw error\n }\n if (closed) return { value: undefined, done: true }\n const row = buffer[position]\n if (row) {\n // Activity: tighten back to the floor.\n activity.activate()\n last = row.index\n buffer[position] = undefined\n position += 1\n if (position === buffer.length) {\n buffer = []\n position = 0\n }\n return { value: row, done: false }\n }\n // Empty poll: wait, then back off toward the idle ceiling.\n activity.setIdle(interval === ceiling())\n const version = activityVersion\n pending = sleep(interval)\n // oxlint-disable-next-line no-await-in-loop -- poll loop\n await pending.promise\n pending = null\n if (version === activityVersion) {\n interval = Math.min(interval * 2, ceiling())\n }\n }\n },\n async return(): Promise<IteratorResult<Event>> {\n closed = true\n buffer = []\n position = 0\n pending?.cancel()\n return { value: undefined, done: true }\n },\n }\n streamActivities.set(iterator, activity)\n return iterator\n },\n }\n}\n"],"mappings":";;AAOA,MAAa,mCAAoD,IAAI,QAAQ;AAE7E,SAAgB,qBAAqB,MAAkC;CACrE,IAAI,OAAO;CACX,MAAM,4BAAY,IAAI,IAA6B;CACnD,MAAM,WAAW,SAAwB;EACvC,IAAI,SAAS,MAAM;EACnB,OAAO;EACP,KAAK,MAAM,YAAY,WAAW,SAAS,IAAI;CACjD;CACA,OAAO;EACL,IAAI,OAAO;GACT,OAAO;EACT;EACA;EACA,WAAW;GACT,QAAQ,KAAK;GACb,KAAK;EACP;EACA,UAAU,UAAU;GAClB,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;CACF;AACF;;;;;;;;;;;;;;;;;;ACRA,MAAa,gBAAgB,OAAwB;CACnD,IAAI;CACJ,IAAI;CAOJ,OAAO;EACL,SAAA,IAPkB,SAAe,YAAY;GAC7C,OAAO;GACP,QAAQ,WAAW,SAAS,EAAE;GAE7B,MAAkC,QAAQ;EAC7C,CAEQ;EACN,cAAc;GACZ,aAAa,KAAK;GAClB,OAAO;EACT;CACF;AACF;AAGA,MAAM,gBAAwB,aAAa;AAC3C,MAAM,cACJ,KAAK,IAAI,aAAa,eAAe,aAAa,aAAa;;;;;;AAcjE,SAAgB,cACd,WACA,SACsB;CACtB,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,QAAQ,QAAQ,SAAS;CAE/B,OAAO,EACL,CAAC,OAAO,iBAAuC;EAC7C,IAAI,OAAO;EACX,IAAI,SAAmC,CAAC;EACxC,IAAI,WAAW;EACf,IAAI,SAAS;EACb,IAAI,UAA0B;EAC9B,IAAI,WAAW,MAAM;EACrB,IAAI,kBAAkB;EACtB,MAAM,WAAW,2BAA2B;GAC1C,IAAI,QAAQ;GACZ,mBAAmB;GACnB,WAAW,MAAM;GACjB,SAAS,OAAO;EAClB,CAAC;EAED,MAAM,WAAiC;GACrC,MAAM,OAAuC;IAC3C,SAAS;KACP,IAAI,QAAQ,OAAO;MAAE,OAAO,KAAA;MAAW,MAAM;KAAK;KAClD,IAAI;MACF,IAAI,aAAa,OAAO,QAAQ;OAE9B,MAAM,OAAO,MAAM,UAAU,IAAI;OACjC,IAAI,QAAQ,OAAO;QAAE,OAAO,KAAA;QAAW,MAAM;OAAK;OAClD,SAAS;OACT,WAAW;MACb;KACF,SAAS,OAAO;MACd,IAAI,QAAQ,OAAO;OAAE,OAAO,KAAA;OAAW,MAAM;MAAK;MAClD,MAAM;KACR;KACA,IAAI,QAAQ,OAAO;MAAE,OAAO,KAAA;MAAW,MAAM;KAAK;KAClD,MAAM,MAAM,OAAO;KACnB,IAAI,KAAK;MAEP,SAAS,SAAS;MAClB,OAAO,IAAI;MACX,OAAO,YAAY,KAAA;MACnB,YAAY;MACZ,IAAI,aAAa,OAAO,QAAQ;OAC9B,SAAS,CAAC;OACV,WAAW;MACb;MACA,OAAO;OAAE,OAAO;OAAK,MAAM;MAAM;KACnC;KAEA,SAAS,QAAQ,aAAa,QAAQ,CAAC;KACvC,MAAM,UAAU;KAChB,UAAU,MAAM,QAAQ;KAExB,MAAM,QAAQ;KACd,UAAU;KACV,IAAI,YAAY,iBACd,WAAW,KAAK,IAAI,WAAW,GAAG,QAAQ,CAAC;IAE/C;GACF;GACA,MAAM,SAAyC;IAC7C,SAAS;IACT,SAAS,CAAC;IACV,WAAW;IACX,SAAS,OAAO;IAChB,OAAO;KAAE,OAAO,KAAA;KAAW,MAAM;IAAK;GACxC;EACF;EACA,iBAAiB,IAAI,UAAU,QAAQ;EACvC,OAAO;CACT,EACF;AACF"}
1
+ {"version":3,"file":"store-polling-7WJV0YWP.js","names":[],"sources":["../src/stream-activity.ts","../src/store-polling.ts"],"sourcesContent":["export type StreamActivity = {\n readonly idle: boolean\n setIdle(idle: boolean): void\n activate(): void\n subscribe(listener: (idle: boolean) => void): () => void\n}\n\nexport const streamActivities: WeakMap<object, StreamActivity> = new WeakMap()\n\nexport function createStreamActivity(wake: () => void): StreamActivity {\n let idle = false\n const listeners = new Set<(idle: boolean) => void>()\n const setIdle = (next: boolean): void => {\n if (idle === next) return\n idle = next\n for (const listener of listeners) listener(idle)\n }\n return {\n get idle() {\n return idle\n },\n setIdle,\n activate() {\n setIdle(false)\n wake()\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n }\n}\n","/**\n * The shared polling stream — how poll-based backends (sqlite,\n * postgres) implement `A2Store.stream`.\n *\n * The cadence is activity-adaptive: while events are flowing, polls\n * run at a tight floor (25ms) so streams feel pushed — a token\n * generation reads smoothly instead of clumping at the idle interval;\n * every empty poll doubles the gap until the idle ceiling. The cadence\n * is owned (POLL_TIMINGS in internal.ts), not a public option: the\n * floor already keeps active streams smooth, so tuning it is a cost\n * decision a2 makes, not one it delegates. Correctness never depends\n * on the cadence — each poll is a catch-up read (`index > last`, in\n * order), so nothing can be missed, only arrive a beat later.\n */\n\nimport { POLL_TIMINGS } from './internal.ts'\nimport type { Event } from './store.ts'\nimport { createStreamActivity, streamActivities } from './stream-activity.ts'\n\n/** One cancellable sleep — the injection seam for cadence tests. */\nexport type Sleeper = { promise: Promise<void>; cancel: () => void }\n\n/** Unref'd cancellable timer — shared with the redis safety read. */\nexport const defaultSleep = (ms: number): Sleeper => {\n let timer: ReturnType<typeof setTimeout> | undefined\n let wake: (() => void) | undefined\n const promise = new Promise<void>((resolve) => {\n wake = resolve // assigned synchronously by the executor\n timer = setTimeout(resolve, ms)\n // Don't hold the process open for an idle poll.\n ;(timer as { unref?: () => void }).unref?.()\n })\n return {\n promise,\n cancel: () => {\n clearTimeout(timer)\n wake?.()\n },\n }\n}\n\n// Read the seam lazily — tests shrink POLL_TIMINGS in their setup.\nconst ceiling = (): number => POLL_TIMINGS.idleCeilingMs\nconst floor = (): number =>\n Math.min(POLL_TIMINGS.activeFloorMs, POLL_TIMINGS.idleCeilingMs)\n\nexport type PollingStreamOptions = {\n /** Deliver events strictly after this index (exclusive). */\n startAfter?: number\n /** Injectable sleep — tests assert the cadence without wall-clock waits. */\n sleep?: (ms: number) => Sleeper\n}\n\n/**\n * Build the `stream()` iterable from a backend's catch-up read. The\n * iterable ends when the consumer calls `return()` (a disconnecting\n * SSE client), waking any pending sleep immediately.\n */\nexport function pollingStream(\n readAfter: (afterIndex: number) => Event[] | Promise<Event[]>,\n options: PollingStreamOptions,\n): AsyncIterable<Event> {\n const startAfter = options.startAfter ?? 0\n const sleep = options.sleep ?? defaultSleep\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n let last = startAfter\n let buffer: Array<Event | undefined> = []\n let position = 0\n let closed = false\n let pending: Sleeper | null = null\n let interval = floor()\n let activityVersion = 0\n const activity = createStreamActivity(() => {\n if (closed) return\n activityVersion += 1\n interval = floor()\n pending?.cancel()\n })\n\n const iterator: AsyncIterator<Event> = {\n async next(): Promise<IteratorResult<Event>> {\n for (;;) {\n if (closed) return { value: undefined, done: true }\n try {\n if (position === buffer.length) {\n // oxlint-disable-next-line no-await-in-loop -- poll loop\n const rows = await readAfter(last)\n if (closed) return { value: undefined, done: true }\n buffer = rows\n position = 0\n }\n } catch (error) {\n if (closed) return { value: undefined, done: true }\n throw error\n }\n if (closed) return { value: undefined, done: true }\n const row = buffer[position]\n if (row) {\n // Activity: tighten back to the floor.\n activity.activate()\n last = row.index\n buffer[position] = undefined\n position += 1\n if (position === buffer.length) {\n buffer = []\n position = 0\n }\n return { value: row, done: false }\n }\n // Empty poll: wait, then back off toward the idle ceiling.\n activity.setIdle(interval === ceiling())\n const version = activityVersion\n pending = sleep(interval)\n // oxlint-disable-next-line no-await-in-loop -- poll loop\n await pending.promise\n pending = null\n if (version === activityVersion) {\n interval = Math.min(interval * 2, ceiling())\n }\n }\n },\n async return(): Promise<IteratorResult<Event>> {\n closed = true\n buffer = []\n position = 0\n pending?.cancel()\n return { value: undefined, done: true }\n },\n }\n streamActivities.set(iterator, activity)\n return iterator\n },\n }\n}\n"],"mappings":";;AAOA,MAAa,mCAAoD,IAAI,QAAQ;AAE7E,SAAgB,qBAAqB,MAAkC;CACrE,IAAI,OAAO;CACX,MAAM,4BAAY,IAAI,IAA6B;CACnD,MAAM,WAAW,SAAwB;EACvC,IAAI,SAAS,MAAM;EACnB,OAAO;EACP,KAAK,MAAM,YAAY,WAAW,SAAS,IAAI;CACjD;CACA,OAAO;EACL,IAAI,OAAO;GACT,OAAO;EACT;EACA;EACA,WAAW;GACT,QAAQ,KAAK;GACb,KAAK;EACP;EACA,UAAU,UAAU;GAClB,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;CACF;AACF;;;;;;;;;;;;;;;;;;ACRA,MAAa,gBAAgB,OAAwB;CACnD,IAAI;CACJ,IAAI;CAOJ,OAAO;EACL,SAAA,IAPkB,SAAe,YAAY;GAC7C,OAAO;GACP,QAAQ,WAAW,SAAS,EAAE;GAE7B,MAAkC,QAAQ;EAC7C,CAEQ;EACN,cAAc;GACZ,aAAa,KAAK;GAClB,OAAO;EACT;CACF;AACF;AAGA,MAAM,gBAAwB,aAAa;AAC3C,MAAM,cACJ,KAAK,IAAI,aAAa,eAAe,aAAa,aAAa;;;;;;AAcjE,SAAgB,cACd,WACA,SACsB;CACtB,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,QAAQ,QAAQ,SAAS;CAE/B,OAAO,EACL,CAAC,OAAO,iBAAuC;EAC7C,IAAI,OAAO;EACX,IAAI,SAAmC,CAAC;EACxC,IAAI,WAAW;EACf,IAAI,SAAS;EACb,IAAI,UAA0B;EAC9B,IAAI,WAAW,MAAM;EACrB,IAAI,kBAAkB;EACtB,MAAM,WAAW,2BAA2B;GAC1C,IAAI,QAAQ;GACZ,mBAAmB;GACnB,WAAW,MAAM;GACjB,SAAS,OAAO;EAClB,CAAC;EAED,MAAM,WAAiC;GACrC,MAAM,OAAuC;IAC3C,SAAS;KACP,IAAI,QAAQ,OAAO;MAAE,OAAO,KAAA;MAAW,MAAM;KAAK;KAClD,IAAI;MACF,IAAI,aAAa,OAAO,QAAQ;OAE9B,MAAM,OAAO,MAAM,UAAU,IAAI;OACjC,IAAI,QAAQ,OAAO;QAAE,OAAO,KAAA;QAAW,MAAM;OAAK;OAClD,SAAS;OACT,WAAW;MACb;KACF,SAAS,OAAO;MACd,IAAI,QAAQ,OAAO;OAAE,OAAO,KAAA;OAAW,MAAM;MAAK;MAClD,MAAM;KACR;KACA,IAAI,QAAQ,OAAO;MAAE,OAAO,KAAA;MAAW,MAAM;KAAK;KAClD,MAAM,MAAM,OAAO;KACnB,IAAI,KAAK;MAEP,SAAS,SAAS;MAClB,OAAO,IAAI;MACX,OAAO,YAAY,KAAA;MACnB,YAAY;MACZ,IAAI,aAAa,OAAO,QAAQ;OAC9B,SAAS,CAAC;OACV,WAAW;MACb;MACA,OAAO;OAAE,OAAO;OAAK,MAAM;MAAM;KACnC;KAEA,SAAS,QAAQ,aAAa,QAAQ,CAAC;KACvC,MAAM,UAAU;KAChB,UAAU,MAAM,QAAQ;KAExB,MAAM,QAAQ;KACd,UAAU;KACV,IAAI,YAAY,iBACd,WAAW,KAAK,IAAI,WAAW,GAAG,QAAQ,CAAC;IAE/C;GACF;GACA,MAAM,SAAyC;IAC7C,SAAS;IACT,SAAS,CAAC;IACV,WAAW;IACX,SAAS,OAAO;IAChB,OAAO;KAAE,OAAO,KAAA;KAAW,MAAM;IAAK;GACxC;EACF;EACA,iBAAiB,IAAI,UAAU,QAAQ;EACvC,OAAO;CACT,EACF;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-postgres.d.ts
3
3
  /** The result shape this backend reads: just rows. */
4
4
  type PostgresQueryResult = {
@@ -1 +1 @@
1
- {"version":3,"file":"store-postgres.d.ts","names":[],"sources":["../src/store-postgres.ts"],"mappings":";;;KAuDY;EAAwB,MAAM;;;KAG9B;EACV,MAAM,cAAc,qBAAqB,QAAQ;EACjD;;;;;;;;KASU;EACV,MAAM,cAAc,qBAAqB,QAAQ;EACjD,YAAY,QAAQ;EACpB,QAAQ;;KAGE;;EAEV;;EAEA;;EAEA,SAAS;;EAET,QAAQ;;EAER,MAAM;;KAGI,gBAAgB;;;EAG1B,SAAS;;iBAqQK,SAAS,UAAS,uBAA4B"}
1
+ {"version":3,"file":"store-postgres.d.ts","names":[],"sources":["../src/store-postgres.ts"],"mappings":";;;KAuDY;EAAwB,MAAM;;;KAG9B;EACV,MAAM,cAAc,qBAAqB,QAAQ;EACjD;;;;;;;;KASU;EACV,MAAM,cAAc,qBAAqB,QAAQ;EACjD,YAAY,QAAQ;EACpB,QAAQ;;KAGE;;EAEV;;EAEA;;EAEA,SAAS;;EAET,QAAQ;;EAER,MAAM;;KAGI,gBAAgB;;;EAG1B,SAAS;;iBAiSK,SAAS,UAAS,uBAA4B"}
@@ -1,12 +1,12 @@
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 } from "./internal-B2T3U4Rq.js";
3
- import { n as pollingStream, t as defaultSleep } from "./store-polling-B5FcwJfo.js";
2
+ import { _ as nullProtoRecord, r as NOTIFY_TIMINGS } from "./internal-CscSY7K_.js";
3
+ import { n as pollingStream, t as defaultSleep } from "./store-polling-7WJV0YWP.js";
4
4
  import { r as platformWaitUntil } from "./platform-B4TnJtWu.js";
5
5
  import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
6
6
  import { t as idempotentReplay } from "./idempotent-replay-JdgMQRRm.js";
7
7
  import { n as SYSTEM_CLOCK, t as RANDOM_IDS } from "./store-N8PXxDAS.js";
8
8
  import { t as decodeReturnedEventIds } from "./store-codec-DTG0Ftek.js";
9
- import { t as pollPresence } from "./store-presence-polling-BAwS8RWn.js";
9
+ import { t as pollPresence } from "./store-presence-polling-B1ixeqIS.js";
10
10
  import { createHash } from "node:crypto";
11
11
  //#region src/postgres-notifications.ts
12
12
  const postgresNotificationChannel = ({ kind, sessionId }) => `a2_${kind}_${createHash("md5").update(sessionId).digest("hex")}`;
@@ -694,6 +694,19 @@ const createListener = ({ pg, connectionString }) => postgresNotifications(async
694
694
  }
695
695
  };
696
696
  });
697
+ /** A dead-lettered event's snapshot pins, and history no pin keeps. */
698
+ const releasePins = async ({ query, sessionId, index }) => {
699
+ await query(`delete from a2_snapshot_pins
700
+ where session_id = $1 and event_index = $2`, [sessionId, index]);
701
+ await query(`delete from a2_snapshot_history history
702
+ where history.session_id = $1
703
+ and not exists (
704
+ select 1 from a2_snapshot_pins pin
705
+ where pin.session_id = history.session_id
706
+ and pin.reducer_name = history.reducer_name
707
+ and pin.up_to_index = history.up_to_index
708
+ )`, [sessionId]);
709
+ };
697
710
  function postgres(options = {}) {
698
711
  const clock = options.clock ?? SYSTEM_CLOCK;
699
712
  const generateId = options.ids ?? RANDOM_IDS;
@@ -1095,10 +1108,12 @@ function postgres(options = {}) {
1095
1108
  return rows.map(toEvent);
1096
1109
  });
1097
1110
  },
1098
- async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes = [] }) {
1111
+ async claimAvailable({ sessionId, holder, ttlMs, expiresAtMs, excludeIndexes = [], maxFailures }) {
1099
1112
  return wrap(() => withTx(async (query) => {
1100
1113
  const now = clock.now().getTime();
1101
1114
  const expiresAt = expiresAtMs ?? now + ttlMs;
1115
+ const exhausted = `(claimed.claim_holder is not null
1116
+ and claimed.failure_count + 1 >= $6::int)`;
1102
1117
  const { rows } = await query(`with eligible as materialized (
1103
1118
  select event.idx
1104
1119
  from a2_events event
@@ -1113,10 +1128,16 @@ function postgres(options = {}) {
1113
1128
  )
1114
1129
  update a2_events claimed set
1115
1130
  attempt_count = claimed.attempt_count + 1,
1116
- first_claimed_at = coalesce(claimed.first_claimed_at, $2),
1117
- last_claimed_at = $2,
1118
- claim_holder = $3,
1119
- claim_expires_at = $4
1131
+ failure_count = claimed.failure_count
1132
+ + case when claimed.claim_holder is null then 0 else 1 end,
1133
+ failed_at = case when ${exhausted} then $2::bigint else claimed.failed_at end,
1134
+ first_claimed_at = case when ${exhausted}
1135
+ then claimed.first_claimed_at
1136
+ else coalesce(claimed.first_claimed_at, $2::bigint) end,
1137
+ last_claimed_at = case when ${exhausted}
1138
+ then claimed.last_claimed_at else $2::bigint end,
1139
+ claim_holder = case when ${exhausted} then null else $3::text end,
1140
+ claim_expires_at = case when ${exhausted} then null else $4::bigint end
1120
1141
  from eligible
1121
1142
  where claimed.session_id = $1 and claimed.idx = eligible.idx
1122
1143
  returning claimed.*, ${clearIndexOf("$1")} as clear_index`, [
@@ -1124,8 +1145,14 @@ function postgres(options = {}) {
1124
1145
  now,
1125
1146
  holder,
1126
1147
  expiresAt,
1127
- [...excludeIndexes]
1148
+ [...excludeIndexes],
1149
+ maxFailures ?? null
1128
1150
  ]);
1151
+ for (const row of rows) if (row["failed_at"] !== null) await releasePins({
1152
+ query,
1153
+ sessionId,
1154
+ index: asNumber(row["idx"])
1155
+ });
1129
1156
  if (rows.length > 0) return {
1130
1157
  outcome: "claimed",
1131
1158
  events: rows.map((row) => {
@@ -1142,7 +1169,12 @@ function postgres(options = {}) {
1142
1169
  and failed_at is null
1143
1170
  and lane_ready
1144
1171
  and idx > ${clearIndexOf("$1")}
1145
- and claim_expires_at > $2`, [sessionId, now]);
1172
+ and claim_expires_at > $2
1173
+ and not (idx = any($3::bigint[]))`, [
1174
+ sessionId,
1175
+ now,
1176
+ [...excludeIndexes]
1177
+ ]);
1146
1178
  const retryAt = activeRows[0]?.["retry_at"];
1147
1179
  return retryAt === null || retryAt === void 0 ? { outcome: "settled" } : {
1148
1180
  outcome: "busy",
@@ -1336,7 +1368,8 @@ function postgres(options = {}) {
1336
1368
  };
1337
1369
  }));
1338
1370
  },
1339
- async failAttempt({ sessionId, index, attempt, error, maxFailures }) {
1371
+ async failAttempt({ sessionId, index, attempt, error, maxFailures, retryAfterMs }) {
1372
+ const now = clock.now().getTime();
1340
1373
  return wrap(() => withTx(async (query) => {
1341
1374
  const { rows } = await query(`with current as materialized (
1342
1375
  select e.* from a2_events e
@@ -1354,7 +1387,10 @@ function postgres(options = {}) {
1354
1387
  else e.failed_at
1355
1388
  end,
1356
1389
  claim_holder = null,
1357
- claim_expires_at = null
1390
+ claim_expires_at = case
1391
+ when e.failure_count + 1 >= $5 then null
1392
+ else $7::bigint
1393
+ end
1358
1394
  from current
1359
1395
  where e.session_id = $1
1360
1396
  and e.idx = $2
@@ -1393,7 +1429,8 @@ function postgres(options = {}) {
1393
1429
  attempt,
1394
1430
  error,
1395
1431
  maxFailures,
1396
- clock.now().getTime()
1432
+ now,
1433
+ retryAfterMs === void 0 ? null : now + retryAfterMs
1397
1434
  ]);
1398
1435
  const row = rows[0];
1399
1436
  if (row?.["outcome"] === "cleared") return {
@@ -1411,18 +1448,11 @@ function postgres(options = {}) {
1411
1448
  };
1412
1449
  throw new TypeError(`no event at index ${index} in session '${sessionId}'`);
1413
1450
  }
1414
- if (row["outcome"] === "dead_lettered") {
1415
- await query(`delete from a2_snapshot_pins
1416
- where session_id = $1 and event_index = $2`, [sessionId, index]);
1417
- await query(`delete from a2_snapshot_history history
1418
- where history.session_id = $1
1419
- and not exists (
1420
- select 1 from a2_snapshot_pins pin
1421
- where pin.session_id = history.session_id
1422
- and pin.reducer_name = history.reducer_name
1423
- and pin.up_to_index = history.up_to_index
1424
- )`, [sessionId]);
1425
- }
1451
+ if (row["outcome"] === "dead_lettered") await releasePins({
1452
+ query,
1453
+ sessionId,
1454
+ index
1455
+ });
1426
1456
  return {
1427
1457
  outcome: row["outcome"],
1428
1458
  failureCount: asNumber(row["failure_count"])