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 +80 -0
- package/dist/agent-runtime/events.d.ts +9 -2
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/observability.d.ts +9 -2
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/agent-runtime-harness.js +2 -2
- package/dist/agent-runtime.js +4 -4
- package/dist/application/events.d.ts +9 -2
- package/dist/application/events.d.ts.map +1 -1
- package/dist/application/latest-sink.d.ts +12 -2
- package/dist/application/latest-sink.d.ts.map +1 -1
- package/dist/application-opentelemetry.js +2 -1
- package/dist/application.js +4 -4
- package/dist/{index-6s7n2v50.js → index-9f8ztey6.js} +15 -10
- package/dist/{index-1dd82z59.js → index-st8v8739.js} +3 -3
- package/dist/{index-a59da114.js → index-wnfk50r0.js} +59 -15
- package/dist/internal/observability-sink.d.ts +57 -2
- package/dist/internal/observability-sink.d.ts.map +1 -1
- package/dist/observability/audit.d.ts +20 -4
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +30 -15
- package/dist/observability/status.d.ts +1 -0
- package/dist/observability/status.d.ts.map +1 -1
- package/llms-full.txt +114 -9
- package/package.json +1 -1
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
|
-
|
|
21
|
+
/** Whether the generation admitted before this call settled inside the bound. */
|
|
22
|
+
flush(bound?: ObservabilityDrainBound): Promise<boolean>;
|
|
21
23
|
getStatus(): ObservabilitySinkStatus;
|
|
22
|
-
|
|
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":"
|
|
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
|
-
|
|
452
|
+
/** Whether the generation admitted before this call settled inside the bound. */
|
|
453
|
+
flush(bound?: ObservabilityDrainBound): Promise<boolean>;
|
|
452
454
|
getStatus(): ObservabilitySinkStatus;
|
|
453
|
-
|
|
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;
|
|
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-
|
|
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-
|
|
10
|
+
import"./index-wnfk50r0.js";
|
|
11
11
|
import {
|
|
12
12
|
walkContainedFiles
|
|
13
13
|
} from "./index-rqpdar1e.js";
|
package/dist/agent-runtime.js
CHANGED
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
registerDeferredToolRepair,
|
|
49
49
|
repairedSearchCall,
|
|
50
50
|
selectAgentHistory
|
|
51
|
-
} from "./index-
|
|
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-
|
|
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
|
-
|
|
60
|
+
/** Whether the generation admitted before this call settled inside the bound. */
|
|
61
|
+
flush(bound?: ObservabilityDrainBound): Promise<boolean>;
|
|
60
62
|
getStatus(): ObservabilitySinkStatus;
|
|
61
|
-
|
|
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;
|
|
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
|
-
/**
|
|
32
|
-
|
|
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;
|
|
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-
|
|
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
|
package/dist/application.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
createActivityProjection,
|
|
27
27
|
createApplicationSnapshotSink,
|
|
28
28
|
createManagedSchedule
|
|
29
|
-
} from "./index-
|
|
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-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
})
|
|
147
|
+
}), (error) => settle(() => {
|
|
108
148
|
failed += 1;
|
|
109
149
|
reportError(error, event);
|
|
110
|
-
})
|
|
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) =>
|
|
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
|
-
})
|
|
179
|
+
});
|
|
136
180
|
preparing.set(id, preparation);
|
|
137
181
|
},
|
|
138
|
-
flush() {
|
|
139
|
-
|
|
182
|
+
flush(bound) {
|
|
183
|
+
assertDrainBound(bound);
|
|
184
|
+
return withinBound(awaitGeneration(sequence), bound);
|
|
140
185
|
},
|
|
141
186
|
getStatus,
|
|
142
|
-
close() {
|
|
143
|
-
|
|
144
|
-
|
|
187
|
+
close(bound) {
|
|
188
|
+
assertDrainBound(bound);
|
|
189
|
+
drain ??= awaitGeneration(sequence);
|
|
145
190
|
closed = true;
|
|
146
|
-
|
|
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
|
-
|
|
70
|
+
/** Whether the generation admitted before this call settled inside the bound. */
|
|
71
|
+
flush(bound?: ObservabilityDrainBound): Promise<boolean>;
|
|
22
72
|
getStatus(): ObservabilitySinkStatus;
|
|
23
|
-
|
|
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,
|
|
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
|
-
/**
|
|
70
|
-
|
|
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
|
-
/**
|
|
74
|
-
|
|
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":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
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
|
-
|
|
7
|
-
|
|
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
|
|
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
|
-
|
|
236
|
-
|
|
239
|
+
flush(bound) {
|
|
240
|
+
assertDrainBound(bound);
|
|
241
|
+
return withinBound(Promise.all([requestManager?.flush(), toolManager?.flush()]), bound);
|
|
237
242
|
},
|
|
238
243
|
getStatus,
|
|
239
|
-
close() {
|
|
240
|
-
|
|
244
|
+
close(bound) {
|
|
245
|
+
assertDrainBound(bound);
|
|
246
|
+
if (!drain) {
|
|
241
247
|
closed = true;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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()
|
|
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
|
|
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