agentfootprint 11.1.0 → 11.2.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 (99) hide show
  1. package/CAPABILITIES.md +1 -0
  2. package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js +35 -4
  3. package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
  4. package/dist/adapters/mcp/agentcoreRuntime/transport.js +48 -22
  5. package/dist/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  6. package/dist/adapters/observability/cloudwatch.js +101 -110
  7. package/dist/adapters/observability/cloudwatch.js.map +1 -1
  8. package/dist/adapters/observability/file.js +48 -71
  9. package/dist/adapters/observability/file.js.map +1 -1
  10. package/dist/adapters/observability/outbox.js +217 -0
  11. package/dist/adapters/observability/outbox.js.map +1 -0
  12. package/dist/adapters/observability/xray.js +56 -70
  13. package/dist/adapters/observability/xray.js.map +1 -1
  14. package/dist/core/RunnerBase.js +20 -1
  15. package/dist/core/RunnerBase.js.map +1 -1
  16. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +6 -2
  17. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js +35 -4
  18. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
  19. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.d.ts +38 -12
  20. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js +47 -21
  21. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  22. package/dist/esm/adapters/observability/cloudwatch.d.ts +6 -2
  23. package/dist/esm/adapters/observability/cloudwatch.js +101 -110
  24. package/dist/esm/adapters/observability/cloudwatch.js.map +1 -1
  25. package/dist/esm/adapters/observability/file.d.ts +8 -4
  26. package/dist/esm/adapters/observability/file.js +48 -71
  27. package/dist/esm/adapters/observability/file.js.map +1 -1
  28. package/dist/esm/adapters/observability/outbox.d.ts +104 -0
  29. package/dist/esm/adapters/observability/outbox.js +213 -0
  30. package/dist/esm/adapters/observability/outbox.js.map +1 -0
  31. package/dist/esm/adapters/observability/xray.js +56 -70
  32. package/dist/esm/adapters/observability/xray.js.map +1 -1
  33. package/dist/esm/core/RunnerBase.d.ts +18 -0
  34. package/dist/esm/core/RunnerBase.js +20 -1
  35. package/dist/esm/core/RunnerBase.js.map +1 -1
  36. package/dist/esm/core/runner.d.ts +1 -0
  37. package/dist/esm/lib/longTimeout.d.ts +34 -0
  38. package/dist/esm/lib/longTimeout.js +56 -0
  39. package/dist/esm/lib/longTimeout.js.map +1 -0
  40. package/dist/esm/lib/sleep.d.ts +2 -0
  41. package/dist/esm/lib/sleep.js +2 -0
  42. package/dist/esm/lib/sleep.js.map +1 -1
  43. package/dist/esm/mcp/backoff.d.ts +81 -0
  44. package/dist/esm/mcp/backoff.js +158 -0
  45. package/dist/esm/mcp/backoff.js.map +1 -0
  46. package/dist/esm/mcp/mcpClient.js +3 -1
  47. package/dist/esm/mcp/mcpClient.js.map +1 -1
  48. package/dist/esm/mcp/throttleRetry.d.ts +198 -28
  49. package/dist/esm/mcp/throttleRetry.js +428 -83
  50. package/dist/esm/mcp/throttleRetry.js.map +1 -1
  51. package/dist/esm/mcp/types.d.ts +16 -7
  52. package/dist/esm/strategies/attach.js +3 -0
  53. package/dist/esm/strategies/attach.js.map +1 -1
  54. package/dist/esm/strategies/flushDeadline.d.ts +47 -0
  55. package/dist/esm/strategies/flushDeadline.js +96 -0
  56. package/dist/esm/strategies/flushDeadline.js.map +1 -0
  57. package/dist/lib/longTimeout.js +60 -0
  58. package/dist/lib/longTimeout.js.map +1 -0
  59. package/dist/lib/sleep.js +2 -0
  60. package/dist/lib/sleep.js.map +1 -1
  61. package/dist/mcp/backoff.js +163 -0
  62. package/dist/mcp/backoff.js.map +1 -0
  63. package/dist/mcp/mcpClient.js +3 -1
  64. package/dist/mcp/mcpClient.js.map +1 -1
  65. package/dist/mcp/throttleRetry.js +432 -85
  66. package/dist/mcp/throttleRetry.js.map +1 -1
  67. package/dist/strategies/attach.js +3 -0
  68. package/dist/strategies/attach.js.map +1 -1
  69. package/dist/strategies/flushDeadline.js +102 -0
  70. package/dist/strategies/flushDeadline.js.map +1 -0
  71. package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +6 -2
  72. package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts.map +1 -1
  73. package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts +38 -12
  74. package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
  75. package/dist/types/adapters/observability/cloudwatch.d.ts +6 -2
  76. package/dist/types/adapters/observability/cloudwatch.d.ts.map +1 -1
  77. package/dist/types/adapters/observability/file.d.ts +8 -4
  78. package/dist/types/adapters/observability/file.d.ts.map +1 -1
  79. package/dist/types/adapters/observability/outbox.d.ts +105 -0
  80. package/dist/types/adapters/observability/outbox.d.ts.map +1 -0
  81. package/dist/types/adapters/observability/xray.d.ts.map +1 -1
  82. package/dist/types/core/RunnerBase.d.ts +18 -0
  83. package/dist/types/core/RunnerBase.d.ts.map +1 -1
  84. package/dist/types/core/runner.d.ts +1 -0
  85. package/dist/types/core/runner.d.ts.map +1 -1
  86. package/dist/types/lib/longTimeout.d.ts +35 -0
  87. package/dist/types/lib/longTimeout.d.ts.map +1 -0
  88. package/dist/types/lib/sleep.d.ts +2 -0
  89. package/dist/types/lib/sleep.d.ts.map +1 -1
  90. package/dist/types/mcp/backoff.d.ts +82 -0
  91. package/dist/types/mcp/backoff.d.ts.map +1 -0
  92. package/dist/types/mcp/throttleRetry.d.ts +198 -28
  93. package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
  94. package/dist/types/mcp/types.d.ts +16 -7
  95. package/dist/types/mcp/types.d.ts.map +1 -1
  96. package/dist/types/strategies/attach.d.ts.map +1 -1
  97. package/dist/types/strategies/flushDeadline.d.ts +48 -0
  98. package/dist/types/strategies/flushDeadline.d.ts.map +1 -0
  99. package/package.json +2 -2
@@ -37,10 +37,14 @@
37
37
  * `exportEvent` is sync and never touches the disk: it serializes, buffers, and
38
38
  * returns. Batches are appended asynchronously on a size trigger
39
39
  * (`maxBufferEvents` / `maxBufferBytes`), on a timer (`flushIntervalMs`), and on
40
- * `flush()`. A hard kill therefore loses at most the buffer — the price of not
41
- * making telemetry a term in agent-loop latency. Call `flush()` (or
42
- * `agent.shutdown()`, which does) at process end; see the 8.12.0 lifecycle laws
43
- * on {@link BaseStrategy.flush}.
40
+ * `flush()` — all through ONE queue (`outbox.ts`), so one append is in flight
41
+ * at a time: this sink's appends never interleave, and lines land in dispatch
42
+ * order (#223). That is not atomicity — a disk that fails partway through an
43
+ * append (ENOSPC) can still leave part of a line behind, and that batch is
44
+ * reported dropped. A hard kill loses the buffer and can interrupt the append
45
+ * in flight — the price of not making telemetry a term in agent-loop latency.
46
+ * Call `flush()` (or `agent.shutdown()`, which does) at process end; see the
47
+ * 8.12.0 lifecycle laws on {@link BaseStrategy.flush}.
44
48
  *
45
49
  * ## Rotation is ONE generation, and that is deliberate
46
50
  *
@@ -75,6 +79,7 @@ exports.fileObservability = void 0;
75
79
  const lazyRequire_js_1 = require("../../lib/lazyRequire.js");
76
80
  const wireJson_js_1 = require("../../lib/wireJson.js");
77
81
  const deliveryErrors_js_1 = require("./deliveryErrors.js");
82
+ const outbox_js_1 = require("./outbox.js");
78
83
  // ─── Public factory ──────────────────────────────────────────────────
79
84
  /**
80
85
  * NDJSON-to-a-local-file observability strategy. See
@@ -120,27 +125,13 @@ function fileObservability(opts) {
120
125
  `Fix the path or the permission — this sink refuses to start rather than ` +
121
126
  `silently drop every event.`);
122
127
  }
123
- // Buffered lines — drained by `flush()` / size trigger / timer.
124
- const buffer = [];
125
- let bufferBytes = 0;
126
- let lastFlushPromise = Promise.resolve();
127
- let timer;
128
+ // Set by `stop()`: this sink accepts no more events. It is NOT a guard on
129
+ // writing — `stop()` does not authorise throwing away what was already
130
+ // accepted, and `flush()` after it still writes (the 8.11.1 stance).
128
131
  let stopped = false;
129
132
  // The fallback when the consumer wires nothing. Rate-limited; a
130
133
  // consumer-supplied sink is not.
131
134
  const consoleSink = (0, deliveryErrors_js_1.rateLimitedConsoleSink)(strategyName);
132
- function scheduleTimedFlush() {
133
- if (timer || flushIntervalMs <= 0 || stopped)
134
- return;
135
- timer = setTimeout(() => {
136
- timer = undefined;
137
- void doFlush();
138
- }, flushIntervalMs);
139
- // Never hold the process open for telemetry. `unref` exists on Node's
140
- // Timeout and not on the browser's number — feature-detected, so a
141
- // non-Node runtime with an injected `_fs` still works.
142
- timer.unref?.();
143
- }
144
135
  /** Route a delivery failure through whatever `_onError` IS RIGHT NOW —
145
136
  * reading it at call time, so a consumer who assigns `_onError` after
146
137
  * construction still receives them (the 8.11.0 fix). */
@@ -162,28 +153,29 @@ function fileObservability(opts) {
162
153
  await fs.rename(path, rotatedPath);
163
154
  bytesOnDisk = 0;
164
155
  }
165
- async function doFlush() {
166
- // `stopped` is deliberately NOT a guard here — same stance as the AWS
167
- // adapters since 8.11.1. `stop()` stops this strategy ACCEPTING events; it
168
- // does not authorise throwing away events already accepted, and a `flush()`
169
- // that cannot make progress after a `stop()` is how shutdown spins forever.
170
- if (buffer.length === 0)
171
- return;
172
- // Snapshot + clear so events emitted during the in-flight write accumulate
173
- // into the next batch.
174
- const batch = buffer.splice(0);
175
- const batchBytes = bufferBytes;
176
- bufferBytes = 0;
177
- const data = `${batch.join('\n')}\n`;
178
- try {
179
- await rotateIfNeeded(batchBytes);
180
- await fs.appendFile(path, data);
181
- bytesOnDisk += byteLength(data);
182
- }
183
- catch (err) {
184
- reportFailure(new Error(`${batch.length} event(s) dropped writing to '${path}': ${errorMessage(err)}`));
185
- }
156
+ /** One batch: rotate if it would cross the ceiling, then append. Runs
157
+ * inside the outbox's queue, so no other batch — and no other rotation —
158
+ * can start until this one settles. */
159
+ async function writeBatch(lines) {
160
+ const data = `${lines.join('\n')}\n`;
161
+ const bytes = byteLength(data);
162
+ await rotateIfNeeded(bytes);
163
+ await fs.appendFile(path, data);
164
+ bytesOnDisk += bytes;
186
165
  }
166
+ // Buffered lines, and the one queue every write goes through — size
167
+ // trigger, timer, `flush()`, shutdown (outbox.ts; #223).
168
+ const outbox = (0, outbox_js_1.createOutbox)({
169
+ name: strategyName,
170
+ maxItems: maxBufferEvents,
171
+ maxBytes: maxBufferBytes,
172
+ flushIntervalMs,
173
+ // Never hold the process open for a local write.
174
+ timerHoldsProcess: false,
175
+ deliver: writeBatch,
176
+ dropped: (lines, cause) => new Error(`${lines.length} event(s) dropped writing to '${path}': ${errorMessage(cause)}`),
177
+ report: reportFailure,
178
+ });
187
179
  function enqueue(event) {
188
180
  if (stopped)
189
181
  return;
@@ -205,17 +197,11 @@ function fileObservability(opts) {
205
197
  // write, and a bare "undefined" line would corrupt the NDJSON stream.
206
198
  if (line === undefined)
207
199
  return;
208
- buffer.push(line);
209
- bufferBytes += byteLength(line) + 1; // + the newline this line will carry
210
- if (buffer.length >= maxBufferEvents || bufferBytes >= maxBufferBytes) {
211
- // Size trigger. Chain onto the last write rather than racing it — the
212
- // file's line order IS the dispatch order, and that is the only thing an
213
- // offline reader can rely on.
214
- lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
215
- }
216
- else {
217
- scheduleTimedFlush();
218
- }
200
+ // + the newline this line will carry. The outbox decides when it is
201
+ // written, and writes it after every line accepted before it — the file's
202
+ // line order IS the dispatch order, the one thing an offline reader can
203
+ // rely on.
204
+ outbox.add(line, byteLength(line) + 1);
219
205
  }
220
206
  const strategy = {
221
207
  name: strategyName,
@@ -226,31 +212,22 @@ function fileObservability(opts) {
226
212
  * Write what is buffered. Called for you on shutdown (8.12.0) — by the
227
213
  * handle `enable.observability()` returns, by `agent.shutdown()`, and by a
228
214
  * `standingAgent` closing. Safe at any time, including after `stop()`.
215
+ *
216
+ * Resolves once every event accepted before the call is written (or
217
+ * dropped and reported) — one a timed write already had in flight
218
+ * included — after one more sweep of what arrived while it waited. It
219
+ * does not chase a producer that keeps emitting. A write that never
220
+ * settles holds it: nothing can cancel an in-flight write.
229
221
  */
230
- async flush() {
231
- // BOUNDED BY CONSTRUCTION — every pass must remove at least one buffered
232
- // line; a pass that removes none ends the drain instead of retrying. The
233
- // shape the CloudWatch adapter arrived at in 8.11.1, for the same reason:
234
- // a drain that cannot finish must return, never spin.
235
- for (;;) {
236
- const pending = buffer.length;
237
- lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
238
- await lastFlushPromise;
239
- if (buffer.length === 0)
240
- return;
241
- if (buffer.length >= pending)
242
- return;
243
- }
222
+ flush() {
223
+ return outbox.drain();
244
224
  },
245
225
  /** Clear the timer and stop accepting events. Terminal: there is no
246
226
  * restart. What it does NOT do is discard the buffer — `flush()` after
247
227
  * `stop()` still writes it. */
248
228
  stop() {
249
229
  stopped = true;
250
- if (timer) {
251
- clearTimeout(timer);
252
- timer = undefined;
253
- }
230
+ outbox.stop();
254
231
  },
255
232
  /** Where failures go. Two callers: the dispatch layer (when `exportEvent`
256
233
  * itself throws) and this adapter's own write path. */
@@ -1 +1 @@
1
- {"version":3,"file":"file.js","sourceRoot":"","sources":["../../../src/adapters/observability/file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file.js","sourceRoot":"","sources":["../../../src/adapters/observability/file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ /**
3
+ * outbox — the one owner of buffered, timed, serialized delivery for the
4
+ * batching sinks in this folder: `file`, `cloudwatch` (and `agentcore`, which
5
+ * is built on it) and `xray`.
6
+ *
7
+ * A sink accepts items on its hot path (sync, no I/O) and hands them here. The
8
+ * outbox decides WHEN a batch leaves and makes sure only one leaves at a time:
9
+ *
10
+ * - **One queue, every trigger.** The size trigger, the timer and `drain()`
11
+ * (the sink's `flush()`, and so every shutdown) all queue a delivery on
12
+ * the same promise chain. Nothing calls `deliver` any other way.
13
+ * - **One delivery in flight.** A delivery starts only after the previous
14
+ * one settled. A large `fs.promises.appendFile` is several `write()`s; two
15
+ * of them interleaved tear NDJSON lines (reported in #223, where the timer
16
+ * bypassed the chain). Whatever a sink does around its write — the file
17
+ * sink's rotation — runs inside its delivery, so it can never be skipped
18
+ * or reordered either. This keeps ONE sink's deliveries from interleaving;
19
+ * it does not make a delivery atomic (a disk that fails partway through an
20
+ * append can still leave part of it behind).
21
+ * - **Oldest first, within the receiver's limits.** A delivery takes the
22
+ * oldest waiting items, so items leave in the order they were accepted.
23
+ * When a receiving service limits one request (`maxItemsPerDelivery`,
24
+ * `maxBytesPerDelivery` — CloudWatch's PutLogEvents), a delivery takes
25
+ * only the oldest items that fit, and the rest go in the next delivery,
26
+ * right after. That matters BECAUSE of the queue: a slow delivery lets a
27
+ * backlog build behind it, which one request may not be allowed to carry.
28
+ * Each part settles on its own — shipped, or dropped and reported.
29
+ * - **`drain()` is bounded.** It resolves once every item accepted BEFORE
30
+ * the call has settled (delivered, or dropped and reported), one the timer
31
+ * already had in flight included — then makes one sweep of what arrived
32
+ * while it waited. It never chases a producer that keeps emitting: under
33
+ * steady traffic it ends after about two deliveries.
34
+ * - **A failed batch is dropped and reported once**, then the next batch
35
+ * goes. It is never requeued, so a full disk or an outage cannot grow the
36
+ * outbox. The queue never rejects: a failure report that itself throws is
37
+ * said on the console instead of becoming an unhandled rejection.
38
+ *
39
+ * What it does NOT do: bound a delivery that never settles. A hung disk holds
40
+ * the queue, and `drain()` with it; nothing can cancel an in-flight write.
41
+ *
42
+ * Role: leaf helper for `adapters/observability/*`. Composed as a field,
43
+ * never inherited.
44
+ */
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.createOutbox = void 0;
47
+ const deliveryErrors_js_1 = require("./deliveryErrors.js");
48
+ /** Build the outbox one sink owns. A per-delivery cap that could take
49
+ * nothing is refused: such a delivery could never finish a drain. */
50
+ function createOutbox(opts) {
51
+ if (opts.maxItemsPerDelivery !== undefined) {
52
+ requireDeliveryCap(opts.name, opts.maxItemsPerDelivery);
53
+ }
54
+ const waiting = [];
55
+ let waitingBytes = 0;
56
+ // Running counts. Deliveries take the oldest items, one delivery at a time,
57
+ // so once `settled` reaches n the first n accepted items are done.
58
+ let accepted = 0; // handed to `add`
59
+ let taken = 0; // taken by a delivery (in flight or settled)
60
+ let settled = 0; // delivered, or dropped and reported
61
+ // The ONE queue. Every delivery is chained here; `deliverNext` never
62
+ // rejects, so the chain never does either.
63
+ let queue = Promise.resolve();
64
+ // A delivery is queued and has not started yet. It will take everything
65
+ // waiting when it starts, so another trigger meanwhile has nothing to add —
66
+ // and the chain cannot grow while a delivery hangs.
67
+ let deliveryQueued = false;
68
+ let timer;
69
+ let stopped = false;
70
+ const consoleFallback = (0, deliveryErrors_js_1.rateLimitedConsoleSink)(opts.name);
71
+ function add(item, bytes = 0) {
72
+ waiting.push({ item, bytes });
73
+ waitingBytes += bytes;
74
+ accepted++;
75
+ if (atSizeTrigger())
76
+ queueDelivery();
77
+ else
78
+ armTimer();
79
+ }
80
+ function atSizeTrigger() {
81
+ if (waiting.length >= opts.maxItems)
82
+ return true;
83
+ return opts.maxBytes !== undefined && waitingBytes >= opts.maxBytes;
84
+ }
85
+ function armTimer() {
86
+ if (timer !== undefined || stopped || opts.flushIntervalMs <= 0)
87
+ return;
88
+ timer = setTimeout(onTimer, opts.flushIntervalMs);
89
+ // `unref` exists on Node's Timeout and not on a browser's number, so it
90
+ // is feature-detected.
91
+ if (!opts.timerHoldsProcess)
92
+ timer.unref?.();
93
+ }
94
+ function onTimer() {
95
+ timer = undefined;
96
+ queueDelivery();
97
+ }
98
+ function queueDelivery() {
99
+ if (deliveryQueued)
100
+ return;
101
+ deliveryQueued = true;
102
+ queue = queue.then(deliverNext);
103
+ }
104
+ async function deliverNext() {
105
+ deliveryQueued = false;
106
+ const { batch, leftBehind } = takeOldest();
107
+ if (batch.length === 0)
108
+ return;
109
+ try {
110
+ await opts.deliver(batch);
111
+ }
112
+ catch (cause) {
113
+ reportDropped(batch, cause);
114
+ }
115
+ // Settled exactly once, whatever happened to the deliveries around it.
116
+ settled += batch.length;
117
+ // Items a per-delivery limit left behind were already due when this
118
+ // delivery started — deliver them now rather than on the next timer.
119
+ if (leftBehind)
120
+ queueDelivery();
121
+ }
122
+ /** Take the oldest waiting items one delivery may carry. `leftBehind` says
123
+ * a per-delivery limit stopped the take with items still waiting. */
124
+ function takeOldest() {
125
+ const entries = waiting.splice(0, countForOneDelivery());
126
+ for (const entry of entries)
127
+ waitingBytes -= entry.bytes;
128
+ if (waiting.length === 0)
129
+ waitingBytes = 0;
130
+ taken += entries.length;
131
+ return { batch: entries.map((entry) => entry.item), leftBehind: waiting.length > 0 };
132
+ }
133
+ /** How many of the oldest waiting items fit one delivery: at least one,
134
+ * then as many as the item and byte limits allow, in order. */
135
+ function countForOneDelivery() {
136
+ const most = Math.min(waiting.length, opts.maxItemsPerDelivery ?? waiting.length);
137
+ const byteLimit = opts.maxBytesPerDelivery;
138
+ if (byteLimit === undefined)
139
+ return most;
140
+ const overhead = opts.overheadBytesPerItem ?? 0;
141
+ let count = 0;
142
+ let bytes = 0;
143
+ while (count < most) {
144
+ const entry = waiting[count];
145
+ if (entry === undefined)
146
+ break;
147
+ const cost = entry.bytes + overhead;
148
+ if (count > 0 && bytes + cost > byteLimit)
149
+ break;
150
+ bytes += cost;
151
+ count++;
152
+ }
153
+ return count;
154
+ }
155
+ function reportDropped(batch, cause) {
156
+ let error;
157
+ try {
158
+ error = opts.dropped(batch, cause);
159
+ }
160
+ catch {
161
+ error = new Error(`${batch.length} item(s) dropped: ${messageOf(cause)}`);
162
+ }
163
+ try {
164
+ opts.report(error);
165
+ }
166
+ catch (hookError) {
167
+ // The consumer's own `onError` threw. Say both things on the console
168
+ // rather than letting the queue reject (an unhandled rejection, which
169
+ // ends a Node process by default).
170
+ consoleFallback(new Error(`${error.message} — and the onError hook threw: ${messageOf(hookError)}`));
171
+ }
172
+ }
173
+ async function drain() {
174
+ // Everything accepted before this call, in flight or waiting…
175
+ await settleThrough(accepted);
176
+ // …then ONE sweep of what arrived while it waited, so a `stop()` right
177
+ // after `flush()` strands nothing that was already here. The target is
178
+ // fixed when the sweep starts, so steady traffic cannot keep it going.
179
+ await settleThrough(accepted);
180
+ }
181
+ /** Wait until the first `target` accepted items have settled. */
182
+ async function settleThrough(target) {
183
+ while (settled < target) {
184
+ const before = settled;
185
+ // Some of them still wait: make sure a delivery will take them.
186
+ if (taken < target)
187
+ queueDelivery();
188
+ await queue;
189
+ // A pass that settled nothing would repeat forever. With the cap
190
+ // refused above it cannot happen; if it ever does, end — never spin.
191
+ if (settled === before)
192
+ return;
193
+ }
194
+ }
195
+ function stop() {
196
+ stopped = true;
197
+ if (timer !== undefined) {
198
+ clearTimeout(timer);
199
+ timer = undefined;
200
+ }
201
+ }
202
+ return { add, drain, stop };
203
+ }
204
+ exports.createOutbox = createOutbox;
205
+ /** The sink-to-outbox contract: a cap is a whole number above zero. Sinks
206
+ * map and refuse their own options first (X-Ray: `segmentsPerPut`), so this
207
+ * guards the contract rather than a caller's input. */
208
+ function requireDeliveryCap(name, cap) {
209
+ if (Number.isInteger(cap) && cap > 0)
210
+ return;
211
+ throw new TypeError(`[${name}Observability] the outbox was given a per-delivery cap of ${String(cap)}; ` +
212
+ `a delivery that may take no items could never finish a drain.`);
213
+ }
214
+ function messageOf(err) {
215
+ return err instanceof Error ? err.message : String(err);
216
+ }
217
+ //# sourceMappingURL=outbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../../src/adapters/observability/outbox.ts"],"names":[],"mappings":"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"}
@@ -73,6 +73,7 @@ exports.xrayObservability = void 0;
73
73
  const lazyRequire_js_1 = require("../../lib/lazyRequire.js");
74
74
  const wireJson_js_1 = require("../../lib/wireJson.js");
75
75
  const deliveryErrors_js_1 = require("./deliveryErrors.js");
76
+ const outbox_js_1 = require("./outbox.js");
76
77
  // ─── Strategy factory ────────────────────────────────────────────────
77
78
  function xrayObservability(opts) {
78
79
  if (!opts.serviceName) {
@@ -86,11 +87,8 @@ function xrayObservability(opts) {
86
87
  // multiple in-flight turns; we key the active stack by the run
87
88
  // anchor (`meta.runId` — see anchorRunId).
88
89
  const activeTurns = new Map();
89
- // Outbound segment buffer (flat list of closed segments ready for
90
- // PutTraceSegments). Drained by flush() / size-trigger / time-trigger.
91
- const outbox = [];
92
- let lastFlushPromise = Promise.resolve();
93
- let timer;
90
+ // Set by `stop()`: no more events accepted. NOT a guard on delivery (8.11.1):
91
+ // segments already closed and queued are still owed to X-Ray.
94
92
  let stopped = false;
95
93
  // The fallback when the consumer wires nothing. Rate-limited; a
96
94
  // consumer-supplied sink is not.
@@ -103,44 +101,32 @@ function xrayObservability(opts) {
103
101
  client = createXRayClient(opts.region, opts._sdk);
104
102
  return client;
105
103
  }
106
- function scheduleTimedFlush() {
107
- if (timer || flushIntervalMs <= 0 || stopped)
108
- return;
109
- timer = setTimeout(() => {
110
- timer = undefined;
111
- void doFlush();
112
- }, flushIntervalMs);
113
- }
114
- async function doFlush() {
115
- // `stopped` is deliberately NOT a guard here (8.11.1). `stop()` stops this
116
- // strategy ACCEPTING events and cancels its timer; segments already closed
117
- // and queued are still owed to X-Ray. Guarding here made `flush()` after
118
- // `stop()` loop forever waiting for a drain that could no longer happen —
119
- // an infinite microtask spin that starved the event loop of the process
120
- // trying to shut down.
121
- if (outbox.length === 0)
122
- return;
123
- const batch = outbox.splice(0, maxBatchSegments);
124
- try {
125
- await ensureClient().putTraceSegments({
126
- TraceSegmentDocuments: batch.map((s) => (0, wireJson_js_1.toWireJson)(s)),
127
- });
128
- }
129
- catch (err) {
130
- // Routed through the strategy's CURRENT `_onError` (not a hook captured
131
- // at construction), so a consumer who assigns it — or passes `onError`
132
- // — actually receives delivery failures. Before 8.11.0 the hook was
133
- // installed lazily inside `_onError` itself and was `undefined` here,
134
- // so every failed put was silent.
135
- strategy._onError?.(new Error(`${batch.length} segment(s) dropped shipping to X-Ray: ` +
136
- (err instanceof Error ? err.message : String(err))));
137
- }
138
- // If outbox grew during the put (size > maxBatchSegments emits
139
- // arrived), chain another flush.
140
- if (outbox.length > 0 && !stopped) {
141
- lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
142
- }
104
+ /** Ship one batch of closed segments. Throws when it could not — the
105
+ * outbox drops the batch and reports it once. */
106
+ async function shipSegments(batch) {
107
+ await ensureClient().putTraceSegments({
108
+ TraceSegmentDocuments: batch.map((s) => (0, wireJson_js_1.toWireJson)(s)),
109
+ });
143
110
  }
111
+ // Closed segments ready for PutTraceSegments, and the one queue every put
112
+ // goes through — size trigger, timer, `flush()`, shutdown (outbox.ts;
113
+ // #223). A put takes at most `maxBatchSegments` (its whole part); what the
114
+ // cap leaves behind ships right after it.
115
+ const outbox = (0, outbox_js_1.createOutbox)({
116
+ name: 'xray',
117
+ // The trigger compares the value as given, as before the outbox.
118
+ maxItems: maxBatchSegments,
119
+ maxItemsPerDelivery: segmentsPerPut(maxBatchSegments),
120
+ flushIntervalMs,
121
+ timerHoldsProcess: true,
122
+ deliver: shipSegments,
123
+ dropped: (batch, cause) => new Error(`${batch.length} segment(s) dropped shipping to X-Ray: ` +
124
+ (cause instanceof Error ? cause.message : String(cause))),
125
+ // Routed through the strategy's CURRENT `_onError` (not a hook captured
126
+ // at construction), so a consumer who assigns it — or passes `onError`
127
+ // — actually receives delivery failures (8.11.0).
128
+ report: (err) => strategy._onError?.(err),
129
+ });
144
130
  function pushSegment(turnState, name) {
145
131
  const parent = turnState.stack[turnState.stack.length - 1];
146
132
  const seg = {
@@ -199,15 +185,10 @@ function xrayObservability(opts) {
199
185
  seg.metadata = { default: { ...(seg.metadata?.default ?? {}), ...extra.metadata } };
200
186
  if (turnState.sampled) {
201
187
  turnState.closed.push(seg);
202
- // Once the root closes, the whole turn graduates to outbox.
188
+ // Once the root closes, the whole turn graduates to the outbox.
203
189
  if (turnState.stack.length === 0) {
204
- outbox.push(...turnState.closed);
205
- if (outbox.length >= maxBatchSegments) {
206
- lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
207
- }
208
- else {
209
- scheduleTimedFlush();
210
- }
190
+ for (const closed of turnState.closed)
191
+ outbox.add(closed);
211
192
  }
212
193
  }
213
194
  }
@@ -415,22 +396,11 @@ function xrayObservability(opts) {
415
396
  while (t.stack.length > 0)
416
397
  closeSegment(t, undefined);
417
398
  }
418
- // BOUNDED BY CONSTRUCTION — every pass must shrink the outbox (a put
419
- // takes up to `maxBatchSegments` at a time, so several passes are
420
- // normal); a pass that shrinks nothing ends the drain instead of trying
421
- // again. The previous `while (outbox.length > 0)` trusted `doFlush()` to
422
- // make progress, which it stopped doing after `stop()` — an infinite
423
- // microtask spin that starved the event loop during shutdown.
424
- for (;;) {
425
- const pending = outbox.length;
426
- // Chain, never replace: segments ship in the order they closed.
427
- lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
428
- await lastFlushPromise;
429
- if (outbox.length === 0)
430
- return;
431
- if (outbox.length >= pending)
432
- return;
433
- }
399
+ // Segments ship in the order they closed, one put at a time, behind the
400
+ // put in flight — one the timer started included. Resolves once every
401
+ // segment closed before this call is shipped (or dropped and reported),
402
+ // after one more sweep; a put that never settles holds it.
403
+ await outbox.drain();
434
404
  },
435
405
  /** Stop the flush timer and stop accepting events. **The framework does
436
406
  * not call this for you** — an un-stopped strategy keeps a `setTimeout`
@@ -441,10 +411,7 @@ function xrayObservability(opts) {
441
411
  * (8.11.1). */
442
412
  stop() {
443
413
  stopped = true;
444
- if (timer) {
445
- clearTimeout(timer);
446
- timer = undefined;
447
- }
414
+ outbox.stop();
448
415
  },
449
416
  /**
450
417
  * Where errors go. Two callers: the dispatch layer (when `exportEvent`
@@ -460,6 +427,25 @@ function xrayObservability(opts) {
460
427
  return strategy;
461
428
  }
462
429
  exports.xrayObservability = xrayObservability;
430
+ // ─── Batch size ──────────────────────────────────────────────────────
431
+ /**
432
+ * How many segments one put takes — exactly what `outbox.splice(0,
433
+ * maxBatchSegments)` took before the shared queue: the whole part of the
434
+ * value, and every segment for `Infinity` (returned as no cap).
435
+ *
436
+ * A value whose whole part is below 1 — `0`, a fraction under 1, a negative
437
+ * number, `NaN` — took NO segments, so every put was empty and the queue
438
+ * never emptied: puts in a loop, forever. That never worked, so it is refused
439
+ * here, by the option's name; every value that did work keeps its meaning.
440
+ */
441
+ function segmentsPerPut(maxBatchSegments) {
442
+ const whole = Math.trunc(maxBatchSegments);
443
+ if (whole === Infinity)
444
+ return undefined;
445
+ if (whole >= 1)
446
+ return whole;
447
+ throw new TypeError(`[xrayObservability] \`maxBatchSegments\` must be at least 1 (got ${String(maxBatchSegments)}): ` + `it is how many segments one put takes, and a put of none never empties the queue.`);
448
+ }
463
449
  // ─── ID + time helpers ───────────────────────────────────────────────
464
450
  /**
465
451
  * Generate an X-Ray trace ID. Format: