stitchkit 0.82.0 → 0.83.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.
package/CHANGELOG.md CHANGED
@@ -15,6 +15,86 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.83.1] — 2026-09-07
19
+
20
+ ### Fixed
21
+
22
+ - **The drain bound could not fire when it was the only thing left to fire.**
23
+ 0.83.0 unref'd the bound's timer, which reads like hygiene — a drain bound
24
+ should not keep a process alive while it is shutting down — and silently
25
+ destroyed the feature. A pending write holds nothing, so when the stuck write
26
+ is the last work in the process, an unref'd timer lets the event loop empty
27
+ and `close({ timeoutMs })` never settles at all. That is not an edge case, it
28
+ is the case the bound exists for; under Node the process exits 13 on the
29
+ unsettled await. The timer cannot outlive the deadline the caller asked for,
30
+ so it is ref'd again.
31
+
32
+ Every in-process test passed against the broken build, because a test runner
33
+ keeps the loop alive by itself. The consumer lane now runs the real failure
34
+ from the packed tarball under each runtime (`node: drain bound`), which is the
35
+ only place it was ever visible.
36
+
37
+ ## [0.83.0] — 2026-09-07
38
+
39
+ ### ⚠️ Breaking changes
40
+
41
+ **Who must act:** anyone who builds an `ObservabilityDrainReport` by hand (a
42
+ test double, a persisted report re-parsed through
43
+ `ObservabilityDrainReportSchema`), and anyone who types an observability
44
+ `flush()` as `Promise<void>`.
45
+
46
+ - **`ObservabilityDrainReport` gained a required `drained` field.** Returned by
47
+ the framework, so reading code is unaffected; a hand-built object stops
48
+ compiling, and `ObservabilityDrainReportSchema.parse` rejects a stored report
49
+ written by an older version until it carries the field.
50
+ `// before: { request, tools, total, durationMs }` →
51
+ `// after: { request, tools, total, durationMs, drained }`
52
+
53
+ - **Every observability `flush()` returns `Promise<boolean>` instead of
54
+ `Promise<void>`** — `true` when the generation admitted before the call
55
+ settled inside the bound. This is not only the audit sink: it redefines
56
+ `stitchkit/application` (`createApplicationEventSink`, and
57
+ `ApplicationSnapshot` delivery through `createApplicationSnapshotSink`, whose
58
+ `close` gained the same bound) and `stitchkit/agent-runtime`
59
+ (`createAgentRuntimeEventSink`, `createAgentObservability`). `await
60
+ sink.flush()` is unchanged; a declared `Promise<void>` is not. The outcome
61
+ could not be left to `getStatus()`: flush waits on a generation, the status
62
+ counts everything alive right now, so a complete flush and an expired one are
63
+ indistinguishable there.
64
+ `// before: const f: () => Promise<void> = sink.flush` →
65
+ `// after: const f: () => Promise<boolean> = sink.flush`
66
+
67
+ ### Added
68
+
69
+ - **Every observability drain takes a bound.** `flush(bound?)` and
70
+ `close(bound?)` accept `{ timeoutMs?, signal? }` on `createObservability`,
71
+ `createApplicationEventSink`, `createApplicationSnapshotSink`,
72
+ `createAgentRuntimeEventSink` and `createAgentObservability`. Unbounded, a
73
+ drain waits for every accepted event however long the sink takes: one write
74
+ that never settled held `close()` forever, and an application whose shutdown
75
+ graph gives every other step a deadline spent its entire budget here and
76
+ exited by force — measured by a consumer as five forced shutdowns in a week,
77
+ each burning 110 seconds with every application operation already completed
78
+ and the transport closed in 26 ms. A drain that cannot be bounded cannot take
79
+ part in a shutdown budget: it either fits, or it cancels the budget. New
80
+ export `ObservabilityDrainBound`.
81
+
82
+ - **`ObservabilityDrainReport.drained`** says whether every accepted event
83
+ settled. It is read from the counters rather than from which side of the race
84
+ won, so a bound that expires on a sink that has in fact finished — a shutdown
85
+ signal already aborted by an earlier step — reports `true` rather than a false
86
+ audit-loss alarm.
87
+
88
+ ### Fixed
89
+
90
+ - **Sink counters were readable mid-update.** `completed` was incremented one
91
+ microtask before the write left `pending`, and an id left `preparing` two
92
+ microtasks after `admit` had already put it in `writes` — so a status read
93
+ landing in either gap counted one event twice, or reported `accepted` below
94
+ `completed + pending`. Harmless while nothing read the counters at speed; a
95
+ caller-supplied abort signal fires synchronously and lands a bounded drain's
96
+ report exactly there. Each counter now moves in the same tick as its map.
97
+
18
98
  ## [0.82.0] — 2026-09-06
19
99
 
20
100
  ### ⚠️ Breaking changes
@@ -1,3 +1,4 @@
1
+ import { type ObservabilityDrainBound } from '../internal/observability-sink.js';
1
2
  import type { ObservabilitySinkStatus } from '../observability/status.js';
2
3
  import { type AgentRuntimeEvent, type AgentRuntimePublisher } from './event-schema.js';
3
4
  export * from './event-schema.js';
@@ -17,9 +18,15 @@ export interface AgentRuntimeEventSinkConfig {
17
18
  }
18
19
  export interface AgentRuntimeEventSink {
19
20
  publish: AgentRuntimePublisher;
20
- flush(): Promise<void>;
21
+ /** Whether the generation admitted before this call settled inside the bound. */
22
+ flush(bound?: ObservabilityDrainBound): Promise<boolean>;
21
23
  getStatus(): ObservabilitySinkStatus;
22
- close(): Promise<ObservabilitySinkStatus>;
24
+ /**
25
+ * Stop admission and drain. Bounded the same way as every other drain here:
26
+ * without a bound it waits however long the sink takes, which is what used to
27
+ * consume an entire application shutdown budget when a write never settled.
28
+ */
29
+ close(bound?: ObservabilityDrainBound): Promise<ObservabilitySinkStatus>;
23
30
  }
24
31
  /** Bounded, failure-isolated transport-neutral delivery lifecycle. */
25
32
  export declare function createAgentRuntimeEventSink(config: AgentRuntimeEventSinkConfig): AgentRuntimeEventSink;
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAExB,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,MAAM,CAAC,CAAC,KAAK,EAAE;QACb,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC;QAC9B,KAAK,EAAE,iBAAiB,CAAC;QACzB,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,SAAS,IAAI,uBAAuB,CAAC;IACrC,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC3C;AAED,sEAAsE;AACtE,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,2BAA2B,GAClC,qBAAqB,CAiBvB"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAExB,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,MAAM,CAAC,CAAC,KAAK,EAAE;QACb,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC;QAC9B,KAAK,EAAE,iBAAiB,CAAC;QACzB,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,iFAAiF;IACjF,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,SAAS,IAAI,uBAAuB,CAAC;IACrC;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC1E;AAED,sEAAsE;AACtE,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,2BAA2B,GAClC,qBAAqB,CAiBvB"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type ObservabilityDrainBound } from '../internal/observability-sink.js';
2
3
  import type { ObservabilitySinkStatus } from '../observability/status.js';
3
4
  export declare const AgentRunStartedEventSchema: z.ZodObject<{
4
5
  schemaVersion: z.ZodLiteral<1>;
@@ -448,9 +449,15 @@ export interface AgentObservability {
448
449
  parentSpanId?: string;
449
450
  };
450
451
  emit(event: AgentRunEvent): void;
451
- flush(): Promise<void>;
452
+ /** Whether the generation admitted before this call settled inside the bound. */
453
+ flush(bound?: ObservabilityDrainBound): Promise<boolean>;
452
454
  getStatus(): ObservabilitySinkStatus;
453
- close(): Promise<ObservabilitySinkStatus>;
455
+ /**
456
+ * Stop admission and drain. Bounded the same way as every other drain here:
457
+ * without a bound it waits however long the sink takes, which is what used to
458
+ * consume an entire application shutdown budget when a write never settled.
459
+ */
460
+ close(bound?: ObservabilityDrainBound): Promise<ObservabilitySinkStatus>;
454
461
  }
455
462
  export declare function createAgentObservability(config: AgentRunSinkConfig): AgentObservability;
456
463
  //# sourceMappingURL=observability.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/observability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AA2BvE,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;iBAIrC,CAAC;AAEH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAMvC,CAAC;AAEH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgBtC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAI9B,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAChF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,CAAC,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QACvD,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,SAAS,IAAI,uBAAuB,CAAC;IACrC,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC3C;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAkDvF"}
1
+ {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/observability.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,12 @@ import {
2
2
  composeAgentPrompt,
3
3
  createAgentRuntime,
4
4
  createAgentRuntimeEventSink
5
- } from "./index-1dd82z59.js";
5
+ } from "./index-st8v8739.js";
6
6
  import {
7
7
  AgentModelDescriptorSchema
8
8
  } from "./index-tg3m2ec5.js";
9
9
  import"./index-y2s6h5bf.js";
10
- import"./index-a59da114.js";
10
+ import"./index-wnfk50r0.js";
11
11
  import {
12
12
  walkContainedFiles
13
13
  } from "./index-rqpdar1e.js";
@@ -48,7 +48,7 @@ import {
48
48
  registerDeferredToolRepair,
49
49
  repairedSearchCall,
50
50
  selectAgentHistory
51
- } from "./index-1dd82z59.js";
51
+ } from "./index-st8v8739.js";
52
52
  import {
53
53
  AgentModelCapabilitySchema,
54
54
  AgentModelCatalogEntrySchema,
@@ -72,7 +72,7 @@ import {
72
72
  } from "./index-y2s6h5bf.js";
73
73
  import {
74
74
  createBoundedSinkManager
75
- } from "./index-a59da114.js";
75
+ } from "./index-wnfk50r0.js";
76
76
  import {
77
77
  AgentAdmissionEventSchema,
78
78
  AgentCheckpointEventSchema,
@@ -688,9 +688,9 @@ function createAgentObservability(config) {
688
688
  return AgentRunTerminalEventSchema.omit({ internalCause: true }).parse(parsed);
689
689
  });
690
690
  },
691
- flush: () => manager.flush(),
691
+ flush: (bound) => manager.flush(bound),
692
692
  getStatus: () => manager.getStatus(),
693
- close: () => manager.close()
693
+ close: (bound) => manager.close(bound)
694
694
  };
695
695
  }
696
696
  // src/agent-runtime/protocol.ts
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type ObservabilityDrainBound } from '../internal/observability-sink.js';
2
3
  import type { ObservabilitySinkStatus } from '../observability/status.js';
3
4
  import { type ApplicationSnapshot } from './schemas.js';
4
5
  export declare const ApplicationLifecycleEventSchema: z.ZodReadonly<z.ZodObject<{
@@ -56,9 +57,15 @@ export interface ApplicationEventSinkConfig {
56
57
  }
57
58
  export interface ApplicationEventSink {
58
59
  publish(snapshot: ApplicationSnapshot): void;
59
- flush(): Promise<void>;
60
+ /** Whether the generation admitted before this call settled inside the bound. */
61
+ flush(bound?: ObservabilityDrainBound): Promise<boolean>;
60
62
  getStatus(): ObservabilitySinkStatus;
61
- close(): Promise<ObservabilitySinkStatus>;
63
+ /**
64
+ * Stop admission and drain. Bounded the same way as every other drain here:
65
+ * without a bound it waits however long the sink takes, which is what used to
66
+ * consume an entire application shutdown budget when a write never settled.
67
+ */
68
+ close(bound?: ObservabilityDrainBound): Promise<ObservabilitySinkStatus>;
62
69
  }
63
70
  /** Failure-isolated operator event delivery; canonical truth remains the absolute snapshot. */
64
71
  export declare function createApplicationEventSink(config: ApplicationEventSinkConfig): ApplicationEventSink;
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/application/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,WAAW,CAAC;AAEnB,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAa/B,CAAC;AACd,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,mBAAmB,GAC5B,yBAAyB,CAY3B;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,KAAK,EAAE,yBAAyB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,CAAC,OAAO,EAAE;QACpB,KAAK,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,yBAAyB,CAAC;KACnC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,SAAS,IAAI,uBAAuB,CAAC;IACrC,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC3C;AAED,+FAA+F;AAC/F,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,0BAA0B,GACjC,oBAAoB,CActB"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/application/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,WAAW,CAAC;AAEnB,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAa/B,CAAC;AACd,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,mBAAmB,GAC5B,yBAAyB,CAY3B;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,KAAK,EAAE,yBAAyB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,CAAC,OAAO,EAAE;QACpB,KAAK,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,yBAAyB,CAAC;KACnC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC7C,iFAAiF;IACjF,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,SAAS,IAAI,uBAAuB,CAAC;IACrC;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC1E;AAED,+FAA+F;AAC/F,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,0BAA0B,GACjC,oBAAoB,CActB"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type ObservabilityDrainBound } from '../internal/observability-sink.js';
2
3
  export declare const ApplicationSnapshotSinkStatusSchema: z.ZodReadonly<z.ZodObject<{
3
4
  accepting: z.ZodBoolean;
4
5
  received: z.ZodNumber;
@@ -28,8 +29,17 @@ export interface ApplicationSnapshotSink<TSnapshot extends RevisionedApplication
28
29
  /** Admit a newer absolute snapshot. Returns false after close or for a stale revision. */
29
30
  publish(snapshot: TSnapshot): boolean;
30
31
  getStatus(): ApplicationSnapshotSinkStatus;
31
- /** Close admission and deliver the newest snapshot accepted before this boundary. */
32
- close(): Promise<ApplicationSnapshotSinkStatus>;
32
+ /**
33
+ * Close admission and deliver the newest snapshot accepted before this
34
+ * boundary.
35
+ *
36
+ * Bounded like every other drain here. The application-kernel guide puts this
37
+ * call in a cleanup path, which is the same position — and the same failure —
38
+ * as the shutdown that spent its whole budget waiting on a write that never
39
+ * settled. The status is read after the wait ends, so `inFlight` and the
40
+ * queue say what was not delivered.
41
+ */
42
+ close(bound?: ObservabilityDrainBound): Promise<ApplicationSnapshotSinkStatus>;
33
43
  }
34
44
  /**
35
45
  * Deliver absolute state without growing a queue: one write runs while one newer
@@ -1 +1 @@
1
- {"version":3,"file":"latest-sink.d.ts","sourceRoot":"","sources":["../../src/application/latest-sink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,mCAAmC;;;;;;;;;;;;mBAenC,CAAC;AACd,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CACjD,OAAO,mCAAmC,CAC3C,CAAC;AAEF,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B,CAAC,SAAS;IACrD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,6BAA6B,CAC5C,SAAS,SAAS,6BAA6B;IAE/C,KAAK,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,WAAW,CAAC,CAAC,OAAO,EAAE,4BAA4B,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,uBAAuB,CAAC,SAAS,SAAS,6BAA6B;IACtF,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IACtC,SAAS,IAAI,6BAA6B,CAAC;IAC3C,qFAAqF;IACrF,KAAK,IAAI,OAAO,CAAC,6BAA6B,CAAC,CAAC;CACjD;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,SAAS,6BAA6B,EAC3F,MAAM,EAAE,6BAA6B,CAAC,SAAS,CAAC,GAC/C,uBAAuB,CAAC,SAAS,CAAC,CAgHpC"}
1
+ {"version":3,"file":"latest-sink.d.ts","sourceRoot":"","sources":["../../src/application/latest-sink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,gCAAgC,CAAC;AAKxC,eAAO,MAAM,mCAAmC;;;;;;;;;;;;mBAenC,CAAC;AACd,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CACjD,OAAO,mCAAmC,CAC3C,CAAC;AAEF,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B,CAAC,SAAS;IACrD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,6BAA6B,CAC5C,SAAS,SAAS,6BAA6B;IAE/C,KAAK,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,WAAW,CAAC,CAAC,OAAO,EAAE,4BAA4B,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,uBAAuB,CAAC,SAAS,SAAS,6BAA6B;IACtF,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IACtC,SAAS,IAAI,6BAA6B,CAAC;IAC3C;;;;;;;;;OASG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAAC;CAChF;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,SAAS,6BAA6B,EAC3F,MAAM,EAAE,6BAA6B,CAAC,SAAS,CAAC,GAC/C,uBAAuB,CAAC,SAAS,CAAC,CAoHpC"}
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  ActivitySnapshotSchema,
3
3
  ManagedScheduleStatusSchema
4
- } from "./index-6s7n2v50.js";
4
+ } from "./index-9f8ztey6.js";
5
5
  import {
6
6
  ApplicationSnapshotSchema
7
7
  } from "./index-3gye9wzb.js";
8
+ import"./index-wnfk50r0.js";
8
9
  import"./index-7gnkny4z.js";
9
10
 
10
11
  // src/application/opentelemetry.ts
@@ -26,7 +26,7 @@ import {
26
26
  createActivityProjection,
27
27
  createApplicationSnapshotSink,
28
28
  createManagedSchedule
29
- } from "./index-6s7n2v50.js";
29
+ } from "./index-9f8ztey6.js";
30
30
  import {
31
31
  DiagnosticJournalCloseResultSchema,
32
32
  DiagnosticJournalFailurePhaseSchema,
@@ -60,7 +60,7 @@ import {
60
60
  import"./index-3z73fh2c.js";
61
61
  import {
62
62
  createBoundedSinkManager
63
- } from "./index-a59da114.js";
63
+ } from "./index-wnfk50r0.js";
64
64
  import {
65
65
  backoffDelay,
66
66
  createBackoff
@@ -576,9 +576,9 @@ function createApplicationEventSink(config) {
576
576
  publish(snapshot) {
577
577
  manager.submit(() => applicationLifecycleEvent(snapshot));
578
578
  },
579
- flush: () => manager.flush(),
579
+ flush: (bound) => manager.flush(bound),
580
580
  getStatus: () => manager.getStatus(),
581
- close: () => manager.close()
581
+ close: (bound) => manager.close(bound)
582
582
  };
583
583
  }
584
584
  // src/application/health.ts
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  ApplicationIdSchema
3
3
  } from "./index-3gye9wzb.js";
4
+ import {
5
+ assertDrainBound,
6
+ withinBound
7
+ } from "./index-wnfk50r0.js";
4
8
  import {
5
9
  createBoundedChannel
6
10
  } from "./index-7gnkny4z.js";
@@ -111,16 +115,17 @@ function createApplicationSnapshotSink(config) {
111
115
  return true;
112
116
  },
113
117
  getStatus,
114
- close() {
115
- if (closePromise)
116
- return closePromise;
117
- accepting = false;
118
- pending.close({ mode: "drain" });
119
- closePromise = new Promise((resolve) => {
120
- resolveClose = resolve;
121
- settleCloseIfIdle();
122
- });
123
- return closePromise;
118
+ close(bound) {
119
+ assertDrainBound(bound);
120
+ if (!closePromise) {
121
+ accepting = false;
122
+ pending.close({ mode: "drain" });
123
+ closePromise = new Promise((resolve) => {
124
+ resolveClose = resolve;
125
+ settleCloseIfIdle();
126
+ });
127
+ }
128
+ return withinBound(closePromise, bound).then(() => getStatus());
124
129
  }
125
130
  };
126
131
  }
@@ -8,7 +8,7 @@ import {
8
8
  } from "./index-y2s6h5bf.js";
9
9
  import {
10
10
  createBoundedSinkManager
11
- } from "./index-a59da114.js";
11
+ } from "./index-wnfk50r0.js";
12
12
  import {
13
13
  AgentRuntimeEventSchema,
14
14
  agentDurableEventId
@@ -201,9 +201,9 @@ function createAgentRuntimeEventSink(config) {
201
201
  if (projected)
202
202
  manager.submit(() => AgentRuntimeEventSchema.parse(projected));
203
203
  },
204
- flush: () => manager.flush(),
204
+ flush: (bound) => manager.flush(bound),
205
205
  getStatus: () => manager.getStatus(),
206
- close: () => manager.close()
206
+ close: (bound) => manager.close(bound)
207
207
  };
208
208
  }
209
209
 
@@ -21,7 +21,8 @@ var ObservabilityStatusObjectSchema = z.object({
21
21
  });
22
22
  var ObservabilityStatusSchema = ObservabilityStatusObjectSchema.readonly();
23
23
  var ObservabilityDrainReportSchema = ObservabilityStatusObjectSchema.extend({
24
- durationMs: z.number().nonnegative()
24
+ durationMs: z.number().nonnegative(),
25
+ drained: z.boolean()
25
26
  }).readonly();
26
27
  var SUMMED_STATUS_KEYS = [
27
28
  "capacity",
@@ -56,6 +57,41 @@ function aggregateObservabilityStatus(surfaces, closed) {
56
57
  }
57
58
 
58
59
  // src/internal/observability-sink.ts
60
+ function assertDrainBound(bound) {
61
+ const timeoutMs = bound?.timeoutMs;
62
+ if (timeoutMs === undefined)
63
+ return;
64
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
65
+ throw new TypeError("Observability drain timeoutMs must be a non-negative finite number");
66
+ }
67
+ }
68
+ function withinBound(work, bound) {
69
+ assertDrainBound(bound);
70
+ const { timeoutMs, signal } = bound ?? {};
71
+ if (timeoutMs === undefined && !signal)
72
+ return work.then(() => true);
73
+ let timer;
74
+ let onAbort;
75
+ const reached = new Promise((resolve) => {
76
+ const give = () => resolve(false);
77
+ if (signal?.aborted) {
78
+ give();
79
+ return;
80
+ }
81
+ if (signal) {
82
+ onAbort = give;
83
+ signal.addEventListener("abort", onAbort, { once: true });
84
+ }
85
+ if (timeoutMs !== undefined)
86
+ timer = setTimeout(give, timeoutMs);
87
+ });
88
+ return Promise.race([work.then(() => true), reached]).finally(() => {
89
+ if (timer !== undefined)
90
+ clearTimeout(timer);
91
+ if (onAbort && signal)
92
+ signal.removeEventListener("abort", onAbort);
93
+ });
94
+ }
59
95
  var DEFAULT_MAX_PENDING = 1000;
60
96
  function invokeIsolated(callback) {
61
97
  if (!callback)
@@ -76,7 +112,7 @@ function createBoundedSinkManager(config) {
76
112
  let dropped = 0;
77
113
  let failed = 0;
78
114
  let preparationFailed = 0;
79
- let closePromise;
115
+ let drain;
80
116
  const preparing = new Map;
81
117
  const writes = new Map;
82
118
  const reportError = (error, event) => {
@@ -102,12 +138,16 @@ function createBoundedSinkManager(config) {
102
138
  return;
103
139
  }
104
140
  accepted += 1;
105
- const write = Promise.resolve().then(() => config.write(event)).then(() => {
141
+ const settle = (record) => {
142
+ record();
143
+ writes.delete(id);
144
+ };
145
+ const write = Promise.resolve().then(() => config.write(event)).then(() => settle(() => {
106
146
  completed += 1;
107
- }).catch((error) => {
147
+ }), (error) => settle(() => {
108
148
  failed += 1;
109
149
  reportError(error, event);
110
- }).finally(() => writes.delete(id));
150
+ }));
111
151
  writes.set(id, write);
112
152
  };
113
153
  const awaitGeneration = async (boundary) => {
@@ -129,22 +169,26 @@ function createBoundedSinkManager(config) {
129
169
  });
130
170
  return;
131
171
  }
132
- const preparation = Promise.resolve().then(produce).then((event) => admit(id, event)).catch((error) => {
172
+ const preparation = Promise.resolve().then(produce).then((event) => {
173
+ preparing.delete(id);
174
+ admit(id, event);
175
+ }, (error) => {
176
+ preparing.delete(id);
133
177
  preparationFailed += 1;
134
178
  reportError(error);
135
- }).finally(() => preparing.delete(id));
179
+ });
136
180
  preparing.set(id, preparation);
137
181
  },
138
- flush() {
139
- return awaitGeneration(sequence);
182
+ flush(bound) {
183
+ assertDrainBound(bound);
184
+ return withinBound(awaitGeneration(sequence), bound);
140
185
  },
141
186
  getStatus,
142
- close() {
143
- if (closePromise)
144
- return closePromise;
187
+ close(bound) {
188
+ assertDrainBound(bound);
189
+ drain ??= awaitGeneration(sequence);
145
190
  closed = true;
146
- closePromise = awaitGeneration(sequence).then(getStatus);
147
- return closePromise;
191
+ return withinBound(drain, bound).then(getStatus);
148
192
  }
149
193
  };
150
194
  function getStatus() {
@@ -164,4 +208,4 @@ function createBoundedSinkManager(config) {
164
208
  }
165
209
  }
166
210
 
167
- export { ObservabilitySinkStatusSchema, ObservabilityStatusSchema, ObservabilityDrainReportSchema, aggregateObservabilityStatus, createBoundedSinkManager };
211
+ export { ObservabilitySinkStatusSchema, ObservabilityStatusSchema, ObservabilityDrainReportSchema, aggregateObservabilityStatus, assertDrainBound, withinBound, createBoundedSinkManager };
@@ -16,11 +16,66 @@ export interface BoundedSinkConfig<EVENT> {
16
16
  onSinkError?(failure: BoundedSinkError<EVENT>): void | Promise<void>;
17
17
  onDrop?(drop: BoundedSinkDrop<EVENT>): void | Promise<void>;
18
18
  }
19
+ /**
20
+ * A caller's limit on how long a drain may WAIT.
21
+ *
22
+ * Unbounded, a drain waits for every accepted event however long the sink
23
+ * takes — correct when the sink is healthy, fatal when it is not. One write
24
+ * that never settles, a database that has stopped answering, held `close()`
25
+ * forever; an application whose shutdown graph gives every other step a
26
+ * deadline then spent its whole budget here and exited by force. A drain that
27
+ * cannot be bounded cannot take part in a shutdown budget: it either fits, or
28
+ * it cancels the budget.
29
+ *
30
+ * The bound ends the WAITING, not the writes: a sink's `write` is handed no
31
+ * cancellation, so an outstanding one keeps running against whatever the caller
32
+ * closes next. And it is a bound on waiting for I/O, not on wall time — no
33
+ * bound can preempt a `write` that occupies the event loop.
34
+ */
35
+ export interface ObservabilityDrainBound {
36
+ /** Give up waiting after this many milliseconds. */
37
+ timeoutMs?: number;
38
+ /** Give up waiting when this signal aborts. */
39
+ signal?: AbortSignal;
40
+ }
41
+ /**
42
+ * Refuse a nonsensical bound BEFORE anything is mutated, so a call that throws
43
+ * has no effect — admission stays open rather than half-closed.
44
+ */
45
+ export declare function assertDrainBound(bound: ObservabilityDrainBound | undefined): void;
46
+ /**
47
+ * Wait for `work`, but not past `bound`. Resolves `true` when the work settled
48
+ * first, `false` when the bound did.
49
+ *
50
+ * Composed by hand rather than with `AbortSignal.any`, because the composite it
51
+ * returns registers itself on the caller's signal and is never released: on Bun
52
+ * that is a dependent leaked per drain against a process-lifetime signal, and
53
+ * `flush({ signal })` per batch is exactly the shape that accumulates them.
54
+ * A listener and a timer, both disposed here, cost nothing and leave nothing.
55
+ *
56
+ * The timer is REF'D, and that is the whole mechanism. Unref'ing it reads like
57
+ * good hygiene — a drain bound should not keep a process alive while it is
58
+ * shutting down — and it silently destroys the feature: a pending write holds
59
+ * nothing, so when the stuck write is the last thing left, an unref'd timer
60
+ * lets the loop empty and the bound never fires at all. That is not an edge
61
+ * case, it is the case this exists for. The timer cannot outlive the deadline
62
+ * the caller itself asked for, so holding the loop for exactly that long is
63
+ * what was wanted. Found only by installing the published package and running
64
+ * it under `node`, where the process exits 13 on an unsettled top-level await;
65
+ * every in-process test passes because the runner keeps the loop alive.
66
+ */
67
+ export declare function withinBound(work: Promise<unknown>, bound: ObservabilityDrainBound | undefined): Promise<boolean>;
19
68
  export interface BoundedSinkManager<EVENT> {
20
69
  submit(produce: () => EVENT | Promise<EVENT>): void;
21
- flush(): Promise<void>;
70
+ /** Whether the generation admitted before this call settled inside the bound. */
71
+ flush(bound?: ObservabilityDrainBound): Promise<boolean>;
22
72
  getStatus(): ObservabilitySinkStatus;
23
- close(): Promise<ObservabilitySinkStatus>;
73
+ /**
74
+ * Stop admission and drain. The status is read AFTER the wait ends, so
75
+ * `pending` and `preparing` say what the sink had not written — including
76
+ * when the bound ended the wait early.
77
+ */
78
+ close(bound?: ObservabilityDrainBound): Promise<ObservabilitySinkStatus>;
24
79
  }
25
80
  export declare function createBoundedSinkManager<EVENT>(config: BoundedSinkConfig<EVENT>): BoundedSinkManager<EVENT>;
26
81
  //# sourceMappingURL=observability-sink.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observability-sink.d.ts","sourceRoot":"","sources":["../../src/internal/observability-sink.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,yBAAyB,CAAC;AAEjC,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE1D,MAAM,WAAW,gBAAgB,CAAC,KAAK;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,eAAe,CAAC,KAAK;IACpC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB,CAAC,KAAK;IACtC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,CAAC,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,CAAC,IAAI,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,kBAAkB,CAAC,KAAK;IACvC,MAAM,CAAC,OAAO,EAAE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,SAAS,IAAI,uBAAuB,CAAC;IACrC,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC3C;AAaD,wBAAgB,wBAAwB,CAAC,KAAK,EAC5C,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAC/B,kBAAkB,CAAC,KAAK,CAAC,CA8H3B"}
1
+ {"version":3,"file":"observability-sink.d.ts","sourceRoot":"","sources":["../../src/internal/observability-sink.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,yBAAyB,CAAC;AAEjC,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE1D,MAAM,WAAW,gBAAgB,CAAC,KAAK;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,eAAe,CAAC,KAAK;IACpC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB,CAAC,KAAK;IACtC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,CAAC,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,CAAC,IAAI,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,GAAG,IAAI,CAMjF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EACtB,KAAK,EAAE,uBAAuB,GAAG,SAAS,GACzC,OAAO,CAAC,OAAO,CAAC,CA2BlB;AAED,MAAM,WAAW,kBAAkB,CAAC,KAAK;IACvC,MAAM,CAAC,OAAO,EAAE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACpD,iFAAiF;IACjF,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,SAAS,IAAI,uBAAuB,CAAC;IACrC;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC1E;AAaD,wBAAgB,wBAAwB,CAAC,KAAK,EAC5C,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAC/B,kBAAkB,CAAC,KAAK,CAAC,CAuJ3B"}
@@ -4,6 +4,7 @@
4
4
  * Both are normalised into `RequestEvent` without nested fetch wrappers.
5
5
  */
6
6
  import type { RuntimeContext } from '../contract/index.js';
7
+ import { type ObservabilityDrainBound } from '../internal/observability-sink.js';
7
8
  import type { MethodDef } from '../server/types.js';
8
9
  import type { ToolCallHooks } from '../tools/execute.js';
9
10
  import { type DimensionCollision, type RequestContext } from './context.js';
@@ -66,13 +67,28 @@ export interface HttpRequestObserver {
66
67
  export interface Observability {
67
68
  request?: HttpRequestObserver;
68
69
  toolCall: ToolCallHooks;
69
- /** Wait for events admitted before this call. */
70
- flush(): Promise<void>;
70
+ /**
71
+ * Wait for events admitted before this call; `true` when they settled inside
72
+ * the bound.
73
+ *
74
+ * The outcome is RETURNED rather than left to `getStatus()`, which cannot
75
+ * answer it: flush waits on the generation admitted before this call, while
76
+ * the status counts everything alive right now — so a nonzero `pending` after
77
+ * a complete flush and after an expired one look identical.
78
+ */
79
+ flush(bound?: ObservabilityDrainBound): Promise<boolean>;
71
80
  /** Read an immutable snapshot of each enabled sink and their aggregate. */
72
81
  getStatus(): ObservabilityStatus;
73
- /** Stop admission and drain every previously accepted event. Idempotent. */
74
- close(): Promise<ObservabilityDrainReport>;
82
+ /**
83
+ * Stop admission and drain every previously accepted event. Idempotent.
84
+ *
85
+ * With a bound, returns a report rather than waiting past it; `drained` says
86
+ * which happened. The drain itself is started once and shared, so a second
87
+ * call with a shorter bound observes the same drain under its own limit.
88
+ */
89
+ close(bound?: ObservabilityDrainBound): Promise<ObservabilityDrainReport>;
75
90
  }
91
+ export type { ObservabilityDrainBound };
76
92
  export type ProjectedDimensions = Readonly<Record<string, string | undefined>>;
77
93
  /** Typed, explicit attribution rules composed into an application's hooks. */
78
94
  export interface DimensionsProjectorConfig<TContext extends RuntimeContext, TResult> {
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  * framework-owned HTTP completion plus canonical tool hooks into independent
7
7
  * sinks.
8
8
  */
9
- export { createDimensionsProjector, createObservability, type DimensionsProjector, type DimensionsProjectorConfig, type HttpRequestCompletion, type HttpRequestObserver, type Observability, type ObservabilityConfig, type ProjectedDimensions, type RequestEventSinkConfig, type RequestObservabilityConfig, type SinkDrop, type SinkDropReason, type SinkError, } from './audit.js';
9
+ export { createDimensionsProjector, createObservability, type DimensionsProjector, type DimensionsProjectorConfig, type HttpRequestCompletion, type HttpRequestObserver, type Observability, type ObservabilityConfig, type ObservabilityDrainBound, type ProjectedDimensions, type RequestEventSinkConfig, type RequestObservabilityConfig, type SinkDrop, type SinkDropReason, type SinkError, } from './audit.js';
10
10
  export { type BoundedLoggerBounds, type BoundedLoggerOptions, createBoundedLogger, DEFAULT_REDACT_PATHS, } from './bounded-logger.js';
11
11
  export { type DimensionCollision, getRequestContext, getTraceId, getUserId, type RequestContext, runWithRequestContext, type SetRequestDimensionsOptions, setRequestDimensions, setRequestEndpoint, setRequestError, setRequestUser, type WrapRequestContextOptions, wrapInRequestContext, } from './context.js';
12
12
  export type { RequestEvent } from './event.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,SAAS,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,kBAAkB,EACvB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,KAAK,cAAc,EACnB,qBAAqB,EACrB,KAAK,2BAA2B,EAChC,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,KAAK,yBAAyB,EAC9B,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EACd,WAAW,EACX,MAAM,EACN,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,KAAK,mBAAmB,EACxB,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,SAAS,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,kBAAkB,EACvB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,KAAK,cAAc,EACnB,qBAAqB,EACrB,KAAK,2BAA2B,EAChC,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,KAAK,yBAAyB,EAC9B,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EACd,WAAW,EACX,MAAM,EACN,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,KAAK,mBAAmB,EACxB,yBAAyB,GAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
@@ -3,8 +3,10 @@ import {
3
3
  ObservabilitySinkStatusSchema,
4
4
  ObservabilityStatusSchema,
5
5
  aggregateObservabilityStatus,
6
- createBoundedSinkManager
7
- } from "../index-a59da114.js";
6
+ assertDrainBound,
7
+ createBoundedSinkManager,
8
+ withinBound
9
+ } from "../index-wnfk50r0.js";
8
10
  import {
9
11
  measureSize,
10
12
  redact,
@@ -115,7 +117,9 @@ function createObservability(config) {
115
117
  const requestManager = config.request ? createSinkManager(config.request) : undefined;
116
118
  const toolManager = config.tools ? createSinkManager(config.tools) : undefined;
117
119
  let closed = false;
118
- let closePromise;
120
+ let drain;
121
+ let drainStartedAt = 0;
122
+ let unbounded;
119
123
  const request = config.request ? {
120
124
  includePayload: config.request.includePayload ?? false,
121
125
  complete: ({ context, statusCode, durationMs, payload, outcome }) => {
@@ -232,23 +236,34 @@ function createObservability(config) {
232
236
  return {
233
237
  ...request && { request },
234
238
  toolCall: toolCall ?? {},
235
- async flush() {
236
- await Promise.all([requestManager?.flush(), toolManager?.flush()]);
239
+ flush(bound) {
240
+ assertDrainBound(bound);
241
+ return withinBound(Promise.all([requestManager?.flush(), toolManager?.flush()]), bound);
237
242
  },
238
243
  getStatus,
239
- close() {
240
- if (!closePromise) {
244
+ close(bound) {
245
+ assertDrainBound(bound);
246
+ if (!drain) {
241
247
  closed = true;
242
- const startedAt = performance.now();
243
- closePromise = Promise.all([requestManager?.close(), toolManager?.close()]).then(() => {
244
- const status = getStatus();
245
- return ObservabilityDrainReportSchema.parse({
246
- ...status,
247
- durationMs: performance.now() - startedAt
248
- });
248
+ drainStartedAt = performance.now();
249
+ drain = Promise.all([requestManager?.close(), toolManager?.close()]).then(() => {
250
+ return;
251
+ });
252
+ }
253
+ const startedAt = drainStartedAt;
254
+ const report = () => {
255
+ const status = getStatus();
256
+ return ObservabilityDrainReportSchema.parse({
257
+ ...status,
258
+ durationMs: performance.now() - startedAt,
259
+ drained: status.total.pending + status.total.preparing === 0
249
260
  });
261
+ };
262
+ if (!bound?.signal && bound?.timeoutMs === undefined) {
263
+ unbounded ??= withinBound(drain, undefined).then(report);
264
+ return unbounded;
250
265
  }
251
- return closePromise;
266
+ return withinBound(drain, bound).then(report);
252
267
  }
253
268
  };
254
269
  }
@@ -98,6 +98,7 @@ export declare const ObservabilityDrainReportSchema: z.ZodReadonly<z.ZodObject<{
98
98
  closed: z.ZodBoolean;
99
99
  }, z.core.$strip>>;
100
100
  durationMs: z.ZodNumber;
101
+ drained: z.ZodBoolean;
101
102
  }, z.core.$strip>>;
102
103
  export type ObservabilityDrainReport = z.infer<typeof ObservabilityDrainReportSchema>;
103
104
  /** Build and freeze the aggregate without exposing mutable counters. */
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/observability/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,yEAAyE;AACzE,eAAO,MAAM,6BAA6B;;;;;;;;;;;;kBAc7B,CAAC;AAEd,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AASpF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA6C,CAAC;AAEpF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,sFAAsF;AACtF,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAE9B,CAAC;AAEd,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAiBtF,wEAAwE;AACxE,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,MAAM,EAAE,OAAO,GACd,uBAAuB,CAiBzB"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/observability/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,yEAAyE;AACzE,eAAO,MAAM,6BAA6B;;;;;;;;;;;;kBAc7B,CAAC;AAEd,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AASpF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA6C,CAAC;AAEpF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,sFAAsF;AACtF,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAoB9B,CAAC;AAEd,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAiBtF,wEAAwE;AACxE,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,MAAM,EAAE,OAAO,GACd,uBAAuB,CAiBzB"}
package/llms-full.txt CHANGED
@@ -63,11 +63,11 @@ own, recorded as an ADR.
63
63
  | `stitchkit/tracking/server` | server (Bun or Node) | evolving | the decisions a tracking backend makes — dispositions, visit lease over an application-owned store, active intervals, presence; no database |
64
64
  | `stitchkit/release` | browser **and** server | evolving | a page follows the release it was built for — `createReleaseMarker` on the server, `createReleaseWatcher` in the browser, the `X-Build-Id` header and a socket event between them |
65
65
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
66
- | `stitchkit/observability` | server | stable | request/tool event projections — `createObservability`, trace context, sanitisation |
66
+ | `stitchkit/observability` | server | stable<br>_redefined in 1 of the 28 minors since 0.56.2, most recently 0.83.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
67
67
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the store and managed-resource conformance kits |
68
68
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
69
69
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
70
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 11 of the 27 minors since 0.56.2, most recently 0.75.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
70
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 12 of the 28 minors since 0.56.2, most recently 0.83.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
71
71
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
72
72
  | `stitchkit/agent-runtime/coding-tools` | server (Bun or Node) | evolving | bounded host-authorized direct file and shell tools; a root boundary, not an OS sandbox |
73
73
  | `stitchkit/agent-runtime/openrouter` | server | evolving | isolated OpenRouter language-model adapter |
@@ -75,7 +75,7 @@ own, recorded as an ADR.
75
75
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
76
76
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
77
77
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
78
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 6 of the 27 minors since 0.56.2, most recently 0.79.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
78
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 7 of the 28 minors since 0.56.2, most recently 0.83.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
79
79
  | `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
80
80
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
81
81
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -9486,7 +9486,7 @@ export const observability = createObservability({
9486
9486
  })
9487
9487
 
9488
9488
  const status = observability.getStatus()
9489
- const drained = await observability.close()
9489
+ const report = await observability.close({ timeoutMs: 5_000 })
9490
9490
  ```
9491
9491
 
9492
9492
  `maxPending` defaults to `1000` per sink and must be a positive safe integer.
@@ -9504,14 +9504,71 @@ without parsing callback logs.
9504
9504
 
9505
9505
  `flush()` snapshots the current generation and waits only for events admitted
9506
9506
  up to that call. `close()` atomically stops admission, drains every accepted
9507
- generation and returns the final counters plus `durationMs`; repeated calls
9508
- return the same report. Graceful shutdown order is therefore:
9507
+ generation and returns the final counters plus `durationMs`; repeated unbounded
9508
+ calls return the same report. Graceful shutdown order is therefore:
9509
9509
 
9510
9510
  1. stop HTTP/MCP admission;
9511
9511
  2. wait for active requests and tool calls;
9512
- 3. `await observability.close()`;
9512
+ 3. `await observability.close({ timeoutMs })`;
9513
9513
  4. close the database/storage connection used by the sinks.
9514
9514
 
9515
+ ### Give the drain a bound in a shutdown budget
9516
+
9517
+ Unbounded, `close()` waits for every accepted event however long the sink takes.
9518
+ That is right when the sink is healthy and fatal when it is not: one write that
9519
+ never settles — a database that has stopped answering — holds the drain forever,
9520
+ and a shutdown that gives every other step a deadline then spends its entire
9521
+ budget here and exits by force. A drain that cannot be bounded cannot take part
9522
+ in a shutdown budget: it either fits, or it cancels the budget.
9523
+
9524
+ Both `flush` and `close` take `{ timeoutMs?, signal? }`:
9525
+
9526
+ ```ts
9527
+ const report = await observability.close({ timeoutMs: 5_000 })
9528
+ if (!report.drained) {
9529
+ const t = report.total
9530
+ logger.warn('audit drain incomplete', {
9531
+ stillWriting: t.pending + t.preparing,
9532
+ neverWritten: t.received - t.filtered - t.completed,
9533
+ })
9534
+ }
9535
+ ```
9536
+
9537
+ Those are two different numbers and the difference matters. `pending +
9538
+ preparing` is what the drain was still waiting for when the bound expired —
9539
+ events handed to `write` that may yet succeed. `received - filtered -
9540
+ completed` is every event the sink has not written **for any reason**: those,
9541
+ plus the ones already lost to `failed`, `dropped` and `preparationFailed`. A
9542
+ shutdown log that wants to state how much audit was lost wants the second; one
9543
+ that wants to explain why the drain did not finish wants the first.
9544
+
9545
+ `drained` is read from the counters, not from which side of the race won, so a
9546
+ bound that expires on a sink that has in fact finished — a shutdown signal
9547
+ already aborted by an earlier step — reports `true` rather than a false alarm.
9548
+
9549
+ The bound ends the **waiting**, not the writes: a sink's `write` is handed no
9550
+ cancellation, so an outstanding one keeps running against whatever the consumer
9551
+ closes next. It is a bound on waiting for I/O, not on wall time — no bound can
9552
+ preempt a `write` that occupies the event loop. Racing `close()` against your
9553
+ own timer looks equivalent and is not: it discards the report along with the
9554
+ wait.
9555
+
9556
+ `flush(bound?)` takes the same bound and returns whether the generation it
9557
+ waited on settled. That outcome is returned rather than left to `getStatus()`,
9558
+ which cannot answer it: flush waits on the events admitted before the call while
9559
+ the status counts everything alive right now, so a complete flush and an expired
9560
+ one look identical there.
9561
+
9562
+ Every other sink in the framework takes the same bound —
9563
+ `createApplicationEventSink`, `createApplicationSnapshotSink`,
9564
+ `createAgentRuntimeEventSink`, `createAgentObservability`. The hazard was never
9565
+ specific to the audit sink; it was specific to waiting without a limit.
9566
+
9567
+ The drain itself is started once and shared, so a second `close` with a shorter
9568
+ bound observes the same drain under its own limit rather than starting another.
9569
+ Passing no bound keeps the previous behaviour exactly, including the identical
9570
+ returned promise.
9571
+
9515
9572
  Stitchkit manages only in-process delivery. If process-crash durability matters,
9516
9573
  make `write` enqueue into a consumer-owned durable outbox and let that adapter
9517
9574
  own retry, replay and storage policy:
@@ -10970,6 +11027,53 @@ makes one thing your job rather than the resolver's:
10970
11027
  The mechanical part is identical either way. Only the *noticing* differs, and an
10971
11028
  exact pin moves it onto you.
10972
11029
 
11030
+ ## Released migration: 0.83.0
11031
+
11032
+ Two things, both mechanical, and only if you touch an observability sink.
11033
+
11034
+ ```bash
11035
+ rg -n "createObservability|createApplicationEventSink|createApplicationSnapshotSink|createAgentRuntimeEventSink|createAgentObservability"
11036
+ ```
11037
+
11038
+ **1. `flush()` returns `Promise<boolean>`.** `await sink.flush()` needs no
11039
+ change. Only a declared type does:
11040
+
11041
+ ```ts
11042
+ // before
11043
+ const flushAudit: () => Promise<void> = observability.flush
11044
+ // after
11045
+ const flushAudit: () => Promise<boolean> = observability.flush
11046
+ ```
11047
+
11048
+ **2. `ObservabilityDrainReport` carries `drained`.** The framework builds it, so
11049
+ reading code is unchanged. A hand-built report — a test double, or one persisted
11050
+ and re-parsed through `ObservabilityDrainReportSchema` — needs the field:
11051
+
11052
+ ```ts
11053
+ // before
11054
+ { request, tools, total, durationMs }
11055
+ // after
11056
+ { request, tools, total, durationMs, drained: true }
11057
+ ```
11058
+
11059
+ Then take the thing this release exists for. Every drain now accepts
11060
+ `{ timeoutMs?, signal? }`, so a shutdown can give the audit drain the same
11061
+ deadline it gives every other step:
11062
+
11063
+ ```ts
11064
+ const report = await observability.close({ timeoutMs: 5_000 })
11065
+ if (!report.drained) {
11066
+ logger.warn('audit drain incomplete', {
11067
+ unwritten: report.total.received - report.total.filtered - report.total.completed,
11068
+ })
11069
+ }
11070
+ ```
11071
+
11072
+ The bound ends the **waiting**, not the writes: a sink's `write` is handed no
11073
+ cancellation, so an outstanding one keeps running against whatever you close
11074
+ next. Racing `close()` against your own timer looks equivalent and is not — it
11075
+ discards the report along with the wait.
11076
+
10973
11077
  ## Released migration: 0.82.0
10974
11078
 
10975
11079
  One thing, and only if you mount MCP or agent tools from a `createTrackingContract` contract.
@@ -15279,10 +15383,11 @@ audit event. See the [Observability guide](../guide/observability.md).
15279
15383
  | `BoundedLoggerBounds` / `BoundedLoggerOptions` | _type_ | per-value and total record ceilings plus sink/redaction configuration |
15280
15384
  | `RequestEvent` | _type_ | the normalised audit event handed to the sink; opt-in HTTP cancellation rows carry `outcome: 'cancelled'` |
15281
15385
  | `ObservabilityConfig` | _type_ | independent request and tool sink configuration |
15282
- | `Observability` | _type_ | `{ request?, toolCall, getStatus(), flush(), close() }` with bounded sink lifecycle |
15386
+ | `Observability` | _type_ | `{ request?, toolCall, getStatus(), flush(bound?): Promise<boolean>, close(bound?) }` with bounded sink lifecycle |
15387
+ | `ObservabilityDrainBound` | _type_ | `timeoutMs` and/or `signal` limiting how long `flush`/`close` wait; the wait ends, outstanding writes do not |
15283
15388
  | `ObservabilitySinkStatus` | _type_ | immutable counters for one bounded request/tool sink |
15284
15389
  | `ObservabilityStatus` | _type_ | per-surface plus aggregate operational snapshot |
15285
- | `ObservabilityDrainReport` | _type_ | final closed/drained snapshot plus duration |
15390
+ | `ObservabilityDrainReport` | _type_ | final closed/drained snapshot, `durationMs` (the shared drain's age, not this call's wait), and `drained` — read from the counters, `false` only when `total.pending + total.preparing` is nonzero |
15286
15391
  | `ObservabilitySinkStatusSchema` / `ObservabilityStatusSchema` / `ObservabilityDrainReportSchema` | schema | runtime schemas for status/report integration boundaries |
15287
15392
  | `RequestEventSinkConfig` | _type_ | `write`, filter/sanitisation, `maxPending`, `onSinkError` and `onDrop` |
15288
15393
  | `RequestObservabilityConfig` | _type_ | request sink plus opt-in payload capture and default-off `includeCancelled` rows |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.82.0",
3
+ "version": "0.83.1",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",