@animalabs/agent-framework 0.32.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -14
- package/dist/src/admission/admit.d.ts +18 -0
- package/dist/src/admission/admit.d.ts.map +1 -1
- package/dist/src/admission/admit.js +16 -3
- package/dist/src/admission/admit.js.map +1 -1
- package/dist/src/agent.d.ts +185 -78
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +279 -102
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.d.ts.map +1 -1
- package/dist/src/api/mcp-server-core.js +50 -12
- package/dist/src/api/mcp-server-core.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +4 -20
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/code-execution/isolate/scripts.d.ts +12 -8
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +24 -0
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/config-checks.d.ts +7 -0
- package/dist/src/config-checks.d.ts.map +1 -1
- package/dist/src/config-checks.js +14 -0
- package/dist/src/config-checks.js.map +1 -1
- package/dist/src/context-manager-errors.d.ts +25 -0
- package/dist/src/context-manager-errors.d.ts.map +1 -0
- package/dist/src/context-manager-errors.js +31 -0
- package/dist/src/context-manager-errors.js.map +1 -0
- package/dist/src/context-options.d.ts +67 -0
- package/dist/src/context-options.d.ts.map +1 -0
- package/dist/src/context-options.js +85 -0
- package/dist/src/context-options.js.map +1 -0
- package/dist/src/deferred-import.d.ts +93 -0
- package/dist/src/deferred-import.d.ts.map +1 -0
- package/dist/src/deferred-import.js +177 -0
- package/dist/src/deferred-import.js.map +1 -0
- package/dist/src/dendrite/audience.d.ts +223 -0
- package/dist/src/dendrite/audience.d.ts.map +1 -0
- package/dist/src/dendrite/audience.js +202 -0
- package/dist/src/dendrite/audience.js.map +1 -0
- package/dist/src/dendrite/index.d.ts +5 -3
- package/dist/src/dendrite/index.d.ts.map +1 -1
- package/dist/src/dendrite/index.js +2 -1
- package/dist/src/dendrite/index.js.map +1 -1
- package/dist/src/dendrite/presets.d.ts +10 -5
- package/dist/src/dendrite/presets.d.ts.map +1 -1
- package/dist/src/dendrite/presets.js +17 -5
- package/dist/src/dendrite/presets.js.map +1 -1
- package/dist/src/dendrite/registry.d.ts +62 -7
- package/dist/src/dendrite/registry.d.ts.map +1 -1
- package/dist/src/dendrite/registry.js +179 -10
- package/dist/src/dendrite/registry.js.map +1 -1
- package/dist/src/dendrite/types.d.ts +91 -8
- package/dist/src/dendrite/types.d.ts.map +1 -1
- package/dist/src/foreign-rounds.d.ts +69 -0
- package/dist/src/foreign-rounds.d.ts.map +1 -0
- package/dist/src/foreign-rounds.js +152 -0
- package/dist/src/foreign-rounds.js.map +1 -0
- package/dist/src/framework.d.ts +1976 -818
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +7566 -3422
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +85 -15
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +421 -47
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +1 -1
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/rule-scope.d.ts +30 -0
- package/dist/src/gate/rule-scope.d.ts.map +1 -0
- package/dist/src/gate/rule-scope.js +41 -0
- package/dist/src/gate/rule-scope.js.map +1 -0
- package/dist/src/gate/suppress-script-worker.d.ts +2 -0
- package/dist/src/gate/suppress-script-worker.d.ts.map +1 -0
- package/dist/src/gate/suppress-script-worker.js +103 -0
- package/dist/src/gate/suppress-script-worker.js.map +1 -0
- package/dist/src/gate/suppress-scripts.d.ts +105 -0
- package/dist/src/gate/suppress-scripts.d.ts.map +1 -0
- package/dist/src/gate/suppress-scripts.js +403 -0
- package/dist/src/gate/suppress-scripts.js.map +1 -0
- package/dist/src/gate/suppression-review.d.ts +143 -0
- package/dist/src/gate/suppression-review.d.ts.map +1 -0
- package/dist/src/gate/suppression-review.js +463 -0
- package/dist/src/gate/suppression-review.js.map +1 -0
- package/dist/src/gate/suppression-stats.d.ts +125 -0
- package/dist/src/gate/suppression-stats.d.ts.map +1 -0
- package/dist/src/gate/suppression-stats.js +214 -0
- package/dist/src/gate/suppression-stats.js.map +1 -0
- package/dist/src/gate/types.d.ts +135 -7
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/gate/wake-onboarding.d.ts +119 -0
- package/dist/src/gate/wake-onboarding.d.ts.map +1 -0
- package/dist/src/gate/wake-onboarding.js +277 -0
- package/dist/src/gate/wake-onboarding.js.map +1 -0
- package/dist/src/held-writes.d.ts +31 -23
- package/dist/src/held-writes.d.ts.map +1 -1
- package/dist/src/held-writes.js +44 -59
- package/dist/src/held-writes.js.map +1 -1
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/jsonrpc-envelope.d.ts +69 -0
- package/dist/src/jsonrpc-envelope.d.ts.map +1 -0
- package/dist/src/jsonrpc-envelope.js +66 -0
- package/dist/src/jsonrpc-envelope.js.map +1 -0
- package/dist/src/mcpl/channel-registry.d.ts +131 -15
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +290 -46
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +2 -4
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts +2 -4
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +2 -1
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
- package/dist/src/mcpl/inbound-source.js +2 -1
- package/dist/src/mcpl/inbound-source.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +6 -6
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +132 -22
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +105 -15
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +9 -7
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +13 -10
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +134 -56
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tool-classes.d.ts.map +1 -1
- package/dist/src/mcpl/tool-classes.js +3 -0
- package/dist/src/mcpl/tool-classes.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +8 -2
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +24 -1
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +68 -18
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts +45 -8
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +237 -86
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +59 -0
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +432 -67
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +6 -5
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +15 -22
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.d.ts +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +1 -1
- package/dist/src/modules/workspace/index.d.ts +28 -7
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +76 -22
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/notice-tags.d.ts +10 -1
- package/dist/src/notice-tags.d.ts.map +1 -1
- package/dist/src/notice-tags.js +10 -1
- package/dist/src/notice-tags.js.map +1 -1
- package/dist/src/operator-log.d.ts +36 -3
- package/dist/src/operator-log.d.ts.map +1 -1
- package/dist/src/operator-log.js +2 -2
- package/dist/src/operator-log.js.map +1 -1
- package/dist/src/presentation.d.ts +746 -0
- package/dist/src/presentation.d.ts.map +1 -0
- package/dist/src/presentation.js +1435 -0
- package/dist/src/presentation.js.map +1 -0
- package/dist/src/prose-drafts.d.ts +15 -11
- package/dist/src/prose-drafts.d.ts.map +1 -1
- package/dist/src/prose-drafts.js +16 -12
- package/dist/src/prose-drafts.js.map +1 -1
- package/dist/src/read-marks.d.ts +192 -0
- package/dist/src/read-marks.d.ts.map +1 -0
- package/dist/src/read-marks.js +242 -0
- package/dist/src/read-marks.js.map +1 -0
- package/dist/src/recovery/offline-branch.d.ts +91 -6
- package/dist/src/recovery/offline-branch.d.ts.map +1 -1
- package/dist/src/recovery/offline-branch.js +273 -29
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js +22 -3
- package/dist/src/recovery/recover-cli.js.map +1 -1
- package/dist/src/send-tools.d.ts +5 -9
- package/dist/src/send-tools.d.ts.map +1 -1
- package/dist/src/send-tools.js +40 -7
- package/dist/src/send-tools.js.map +1 -1
- package/dist/src/stored-injections.d.ts +2 -2
- package/dist/src/stored-injections.js +2 -2
- package/dist/src/stored-removal.d.ts +17 -4
- package/dist/src/stored-removal.d.ts.map +1 -1
- package/dist/src/stored-removal.js +29 -5
- package/dist/src/stored-removal.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +12 -1
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +42 -8
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +1159 -79
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +3709 -363
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +114 -17
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/tune-out/tools.js +87 -27
- package/dist/src/tune-out/tools.js.map +1 -1
- package/dist/src/tune-out/window-anchor.d.ts +143 -0
- package/dist/src/tune-out/window-anchor.d.ts.map +1 -0
- package/dist/src/tune-out/window-anchor.js +202 -0
- package/dist/src/tune-out/window-anchor.js.map +1 -0
- package/dist/src/types/agent.d.ts +82 -20
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +2 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +32 -12
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +180 -69
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +510 -36
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/admission/admit.ts +32 -3
- package/src/agent.ts +323 -120
- package/src/api/mcp-server-core.ts +54 -17
- package/src/api/server.ts +4 -23
- package/src/code-execution/isolate/scripts.ts +28 -3
- package/src/config-checks.ts +14 -0
- package/src/context-manager-errors.ts +37 -0
- package/src/context-options.ts +96 -0
- package/src/deferred-import.ts +239 -0
- package/src/dendrite/audience.ts +315 -0
- package/src/dendrite/index.ts +14 -2
- package/src/dendrite/presets.ts +18 -6
- package/src/dendrite/registry.ts +209 -12
- package/src/dendrite/types.ts +92 -8
- package/src/foreign-rounds.ts +199 -0
- package/src/framework.ts +7804 -3546
- package/src/gate/event-gate.ts +428 -65
- package/src/gate/index.ts +3 -0
- package/src/gate/rule-scope.ts +45 -0
- package/src/gate/suppress-script-worker.ts +89 -0
- package/src/gate/suppress-scripts.ts +414 -0
- package/src/gate/suppression-review.ts +517 -0
- package/src/gate/suppression-stats.ts +314 -0
- package/src/gate/types.ts +141 -7
- package/src/gate/wake-onboarding.ts +309 -0
- package/src/held-writes.ts +49 -60
- package/src/index.ts +9 -2
- package/src/jsonrpc-envelope.ts +81 -0
- package/src/mcpl/channel-registry.ts +353 -85
- package/src/mcpl/checkpoint-manager.ts +3 -2
- package/src/mcpl/feature-set-manager.ts +2 -1
- package/src/mcpl/inbound-source.ts +2 -1
- package/src/mcpl/inference-router.ts +7 -4
- package/src/mcpl/push-coalescer.ts +193 -34
- package/src/mcpl/push-handler.ts +10 -5
- package/src/mcpl/server-connection.ts +141 -60
- package/src/mcpl/tool-classes.ts +3 -0
- package/src/mcpl/types.ts +8 -2
- package/src/module-registry.ts +73 -17
- package/src/modules/discord/index.ts +229 -81
- package/src/modules/history/index.ts +439 -70
- package/src/modules/history/semantic.ts +15 -21
- package/src/modules/workspace/disk-agreement.ts +1 -1
- package/src/modules/workspace/index.ts +85 -23
- package/src/notice-tags.ts +10 -1
- package/src/operator-log.ts +36 -3
- package/src/presentation.ts +1676 -0
- package/src/prose-drafts.ts +16 -12
- package/src/read-marks.ts +328 -0
- package/src/recovery/offline-branch.ts +346 -32
- package/src/recovery/recover-cli.ts +21 -3
- package/src/send-tools.ts +40 -7
- package/src/stored-injections.ts +2 -2
- package/src/stored-removal.ts +31 -7
- package/src/tool-result-guard.ts +41 -7
- package/src/tune-out/coordinator.ts +4089 -457
- package/src/tune-out/tools.ts +160 -43
- package/src/tune-out/window-anchor.ts +277 -0
- package/src/types/agent.ts +86 -20
- package/src/types/events.ts +2 -0
- package/src/types/framework.ts +32 -12
- package/src/types/module.ts +181 -68
- package/src/types/trace.ts +542 -40
|
@@ -17,6 +17,7 @@ import { ensureStateRegistered } from '../module-registry.js';
|
|
|
17
17
|
import { boundedErrorMessage as thrownText } from '../error-message.js';
|
|
18
18
|
|
|
19
19
|
import type { StateCheckpoint, JsonPatchOperation } from './types.js';
|
|
20
|
+
import type { TraceEventInput } from '../types/trace.js';
|
|
20
21
|
|
|
21
22
|
// ============================================================================
|
|
22
23
|
// Constants
|
|
@@ -198,12 +199,12 @@ export function applyJsonPatch(doc: unknown, ops: JsonPatchOperation[]): unknown
|
|
|
198
199
|
|
|
199
200
|
export class CheckpointManager {
|
|
200
201
|
private store: JsStore;
|
|
201
|
-
private emitTraceFn: (event:
|
|
202
|
+
private emitTraceFn: (event: TraceEventInput) => void;
|
|
202
203
|
private trees = new Map<string, FeatureSetState>();
|
|
203
204
|
|
|
204
205
|
constructor(
|
|
205
206
|
store: JsStore,
|
|
206
|
-
emitTraceFn: (event:
|
|
207
|
+
emitTraceFn: (event: TraceEventInput) => void,
|
|
207
208
|
) {
|
|
208
209
|
this.store = store;
|
|
209
210
|
this.emitTraceFn = emitTraceFn;
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
UNKNOWN_FEATURE_SET,
|
|
20
20
|
} from './errors.js';
|
|
21
21
|
import { isKnownCapabilityPath } from './capability-grant.js';
|
|
22
|
+
import type { TraceEventInput } from '../types/trace.js';
|
|
22
23
|
import { logValue } from './log-value.js';
|
|
23
24
|
|
|
24
25
|
// ============================================================================
|
|
@@ -136,7 +137,7 @@ export class FeatureSetManager {
|
|
|
136
137
|
* turned off. Omit for scratch derivations (nothing is installed).
|
|
137
138
|
*/
|
|
138
139
|
constructor(
|
|
139
|
-
private readonly emitTrace?: (event:
|
|
140
|
+
private readonly emitTrace?: (event: TraceEventInput) => void,
|
|
140
141
|
) {}
|
|
141
142
|
|
|
142
143
|
/**
|
|
@@ -175,7 +175,8 @@ export function conversationKey(source: InboundSource): string | undefined {
|
|
|
175
175
|
* It expects JSON-shaped values, as the MCPL lanes deliver them (parsed from
|
|
176
176
|
* JSON). Any other object is written by its own enumerable entries, unlike
|
|
177
177
|
* JSON.stringify: a Date becomes `{}`, as the store's own write keeps it, but
|
|
178
|
-
* a
|
|
178
|
+
* a write an earlier release's deferral queue left in its JSON file, which
|
|
179
|
+
* the importer stores at boot, brings it back as a string.
|
|
179
180
|
*
|
|
180
181
|
* sourceBodyDigest's serializer, kept out of the package API: serialization
|
|
181
182
|
* and framing both stay inside sourceBodyDigest, so consumers don't
|
|
@@ -29,6 +29,7 @@ import { hostStoppedWhileHandling } from './errors.js';
|
|
|
29
29
|
import type { HookOrchestrator } from './hook-orchestrator.js';
|
|
30
30
|
import { logValue } from './log-value.js';
|
|
31
31
|
import { streamBounds, wholeCallMs, withHostSetting, type InferenceBounds } from '../inference-bounds.js';
|
|
32
|
+
import type { TraceEventInput } from '../types/trace.js';
|
|
32
33
|
|
|
33
34
|
// ============================================================================
|
|
34
35
|
// Responder interface
|
|
@@ -38,8 +39,10 @@ import { streamBounds, wholeCallMs, withHostSetting, type InferenceBounds } from
|
|
|
38
39
|
interface Responder {
|
|
39
40
|
respond(result: McplInferenceRequestResult): void;
|
|
40
41
|
respondError(code: number, message: string, data?: unknown): void;
|
|
41
|
-
/** The JSON-RPC request ID, used for streaming chunk correlation
|
|
42
|
-
|
|
42
|
+
/** The JSON-RPC request ID, used for streaming chunk correlation: a
|
|
43
|
+
* request sent with id null is answered whole, since its chunks couldn't
|
|
44
|
+
* name it. */
|
|
45
|
+
requestId?: string | number | null;
|
|
43
46
|
/** Sends a streamed chunk on the transport the request came on, as its
|
|
44
47
|
* answer is sent. */
|
|
45
48
|
sendChunk?(params: InferenceChunkParams): void;
|
|
@@ -184,7 +187,7 @@ export class InferenceRouter {
|
|
|
184
187
|
private hookOrchestrator: HookOrchestrator;
|
|
185
188
|
private featureSetManager: FeatureSetManager;
|
|
186
189
|
private routingPolicy: InferenceRoutingPolicy | null;
|
|
187
|
-
private emitTraceFn: (event:
|
|
190
|
+
private emitTraceFn: (event: TraceEventInput) => void;
|
|
188
191
|
/** The refusals for want of a model that have been logged: one line per
|
|
189
192
|
* server with no policy, and per server and feature set with one, so a
|
|
190
193
|
* server that keeps asking doesn't flood the operator's log. */
|
|
@@ -196,7 +199,7 @@ export class InferenceRouter {
|
|
|
196
199
|
hookOrchestrator: HookOrchestrator,
|
|
197
200
|
featureSetManager: FeatureSetManager,
|
|
198
201
|
routingPolicy: InferenceRoutingPolicy | null,
|
|
199
|
-
emitTraceFn: (event:
|
|
202
|
+
emitTraceFn: (event: TraceEventInput) => void,
|
|
200
203
|
private readonly inferenceBounds: () => InferenceBounds | null = () => null,
|
|
201
204
|
) {
|
|
202
205
|
this.membrane = membrane;
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* the ordinary channel/push path the moment it is admitted, exactly as today;
|
|
7
7
|
* the coalescer only remembers WHERE it landed. A later occurrence of the same
|
|
8
8
|
* subject edits or removes that message in place while it is still unread
|
|
9
|
-
* (above the agent's consumed watermark,
|
|
9
|
+
* (above the agent's consumed watermark, a position in the agent's window,
|
|
10
|
+
* #291: a row not in the window yet is unread wherever it is in the store;
|
|
11
|
+
* not folded by compression), and
|
|
10
12
|
* appends once it has been read. Nothing is staged, journaled or capped: on
|
|
11
13
|
* any uncertainty (restart, branch switch, eviction) the host behaves as an
|
|
12
14
|
* append-only host would, which is what the RFC's principle 4 asks for.
|
|
@@ -67,10 +69,16 @@ export class EmptyContentError extends CoalesceError {
|
|
|
67
69
|
/** Where an unread occurrence currently lives. */
|
|
68
70
|
export interface CoalescingPlacement {
|
|
69
71
|
agent: string;
|
|
70
|
-
/** Stored message id in that agent's context manager.
|
|
72
|
+
/** Stored message id in that agent's context manager. Absent for a write
|
|
73
|
+
* kept in memory (a surgery's hold, or the wait for its agent's
|
|
74
|
+
* recovery), which counts as read and can't be removed. */
|
|
71
75
|
messageId?: string;
|
|
72
|
-
/**
|
|
73
|
-
|
|
76
|
+
/** Set on a placement that wasn't presented to `agent` at its write: a
|
|
77
|
+
* write kept in memory (no `messageId` yet), or one stored while a turn
|
|
78
|
+
* of the agent's was alive, or with a presentation at rest that failed
|
|
79
|
+
* (#291's presentation index). No request of the agent's reads it until
|
|
80
|
+
* it is presented. */
|
|
81
|
+
presented?: false;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
export interface CoalescingScope { kind: 'featureSet' | 'channel'; id: string }
|
|
@@ -98,6 +106,20 @@ export interface CoalescedOccurrence<E = unknown> {
|
|
|
98
106
|
identity?: { messageId?: string; author?: { id: string; name: string }; threadId?: string };
|
|
99
107
|
/** The event the ordinary path would have queued — the host delivers it. */
|
|
100
108
|
event: E;
|
|
109
|
+
/**
|
|
110
|
+
* The suppress rule that took this occurrence, or null when none did, once
|
|
111
|
+
* the host has decided (its wake, or its row, whichever came first). Decided
|
|
112
|
+
* once for the occurrence, since a rule's script answers once per event:
|
|
113
|
+
* its row and any later wake read this. Snapshots carry it.
|
|
114
|
+
*/
|
|
115
|
+
suppressionDecision?: string | null;
|
|
116
|
+
/**
|
|
117
|
+
* Set on a batch's latest occurrence when the batch holds an occurrence
|
|
118
|
+
* addressed to the agent (`chat:addressed`), this one or an earlier one. An
|
|
119
|
+
* addressed event is never suppressed, so such a batch is shown, whatever its
|
|
120
|
+
* latest occurrence would be decided. Snapshots carry it.
|
|
121
|
+
*/
|
|
122
|
+
batchAddressed?: true;
|
|
101
123
|
/** Set by the coalescer: deliver into this agent only (§3.2 audience may
|
|
102
124
|
* narrow, never widen — a replacement or notice follows the prior delivery). */
|
|
103
125
|
deliverTo?: string;
|
|
@@ -135,8 +157,19 @@ interface DeferredBatch<E> {
|
|
|
135
157
|
* acceptance receipt, which doesn't carry it, reads as not all ambient:
|
|
136
158
|
* the side that keeps a wake. */
|
|
137
159
|
allAmbient?: true;
|
|
138
|
-
/** Set on a batch restored from a snapshot whose render may have started
|
|
160
|
+
/** Set on a batch restored from a snapshot whose render may have started,
|
|
161
|
+
* and on one whose render's delivery threw: its recovery delivers the
|
|
162
|
+
* fallback, without asking the server again. */
|
|
139
163
|
noRender?: boolean;
|
|
164
|
+
/** Set, to the event id the delivery carried, on a batch whose render's
|
|
165
|
+
* delivery threw where the host can't tell whether it stored the
|
|
166
|
+
* occurrence (CoalescerHost.mayHaveStored). Its recovery's delivery of
|
|
167
|
+
* that event says so (DeliveryOptions.mayBeStored), so the host stores the
|
|
168
|
+
* fallback only where it doesn't find the occurrence stored. A later
|
|
169
|
+
* notice that joins the batch moves its latest event on, and the recovery
|
|
170
|
+
* then delivers that one as any fallback. In memory only: a restored
|
|
171
|
+
* batch that may have rendered is settled by wasPublished. */
|
|
172
|
+
mayBeStored?: string;
|
|
140
173
|
/** The batch opened on the agent's first view of its subject: a fresh
|
|
141
174
|
* `initial` birth, or a first-view occupant it displaced that nothing had
|
|
142
175
|
* shown. Its first delivery is a first view too, while nothing of the
|
|
@@ -166,8 +199,8 @@ export interface AssemblyResult {
|
|
|
166
199
|
* read: empty or blank render (§5.2), cancelled, revoked before the render
|
|
167
200
|
* or at its response, a render-start that could not be made durable (the
|
|
168
201
|
* batch then waits for its recovery wake), a batch whose audience doesn't
|
|
169
|
-
* include the agent, a render placed
|
|
170
|
-
*
|
|
202
|
+
* include the agent, a render placed where the turn doesn't read it
|
|
203
|
+
* (stored for another agent whose turn is alive, not presented to it), a render
|
|
171
204
|
* another assembly froze (its compile may show it, as that request's), or
|
|
172
205
|
* a cause whose batch was withdrawn, displaced or consumed by another
|
|
173
206
|
* request. */
|
|
@@ -293,12 +326,31 @@ export interface CoalescingReceiptRecord {
|
|
|
293
326
|
batch: { latest: CoalescedOccurrence<unknown>; notices: DeferredNotice[]; dropped: number } | null;
|
|
294
327
|
}
|
|
295
328
|
|
|
329
|
+
export type CoalescingAudit = { subject: string; eventId: string } & (
|
|
330
|
+
| { kind: 'received'; retract: boolean; deferred: boolean }
|
|
331
|
+
| { kind: 'removed' }
|
|
332
|
+
| { kind: 'remove-failed' }
|
|
333
|
+
| { kind: 'revoked' }
|
|
334
|
+
| { kind: 'displaced'; batch?: true }
|
|
335
|
+
| { kind: 'render-cancelled'; reason?: 'suspend' }
|
|
336
|
+
| { kind: 'recovery-wake'; attempt: number; delayMs: number }
|
|
337
|
+
| { kind: 'rendered'; source: 'render' | 'fallback'; empty: boolean }
|
|
338
|
+
| { kind: 'render-failed'; serverId: string; field?: string; error: string }
|
|
339
|
+
/** The render's delivery threw, and its batch went to the recovery wake.
|
|
340
|
+
* `mayBeStored`: the host couldn't tell whether a row landed
|
|
341
|
+
* (CoalescerHost.mayHaveStored), so the recovery's delivery reads the
|
|
342
|
+
* store before it stores the fallback. */
|
|
343
|
+
| { kind: 'deliver-failed'; error: string; mayBeStored?: true }
|
|
344
|
+
| { kind: 'late-render'; discarded: true }
|
|
345
|
+
| { kind: 'persist-failed'; error: string }
|
|
346
|
+
| { kind: 'accepted-observer-failed'; error: string }
|
|
347
|
+
);
|
|
348
|
+
|
|
296
349
|
export interface CoalescerHost<E> {
|
|
297
350
|
/** True iff the placement still exists and, as far as the host can tell,
|
|
298
351
|
* no model request has included it. */
|
|
299
352
|
isUnread(placement: CoalescingPlacement): boolean;
|
|
300
|
-
/** True iff nothing may have shown the placement to the agent: it is
|
|
301
|
-
* in the deferred queue and known not to be stored already, or it is
|
|
353
|
+
/** True iff nothing may have shown the placement to the agent: it is
|
|
302
354
|
* stored where no committing compile has begun to assemble it and no tool
|
|
303
355
|
* boundary has injected past it. Unlike
|
|
304
356
|
* isUnread, a compile counts from its start: a doubt counts as shown. */
|
|
@@ -307,18 +359,36 @@ export interface CoalescerHost<E> {
|
|
|
307
359
|
remove(placement: CoalescingPlacement): boolean;
|
|
308
360
|
/** Ordinary delivery of the event; undefined when routing delivered nowhere.
|
|
309
361
|
* A replacement is delivered as a fresh message after the unread prior was
|
|
310
|
-
* removed: it lands where a fresh event lands (§4.1 allows either place).
|
|
311
|
-
|
|
362
|
+
* removed: it lands where a fresh event lands (§4.1 allows either place).
|
|
363
|
+
* A render's delivery that throws keeps its batch for its recovery wake,
|
|
364
|
+
* which delivers the fallback. A throw is taken as a delivery that stored
|
|
365
|
+
* none of the occurrence, unless mayHaveStored says the host can't tell:
|
|
366
|
+
* so a delivery whose store threw after rows of the occurrence landed
|
|
367
|
+
* answers as stored by them, as the framework's does (context-manager's
|
|
368
|
+
* WriteLandedError). Where the host can't tell (the framework: context-
|
|
369
|
+
* manager's WriteOutcomeUnknownError), the recovery's delivery carries
|
|
370
|
+
* `options.mayBeStored`: the host looks for the occurrence's rows first,
|
|
371
|
+
* by the delivery identity they carry (subject and event id, as
|
|
372
|
+
* wasPublished), and answers the placement of those it finds, storing
|
|
373
|
+
* nothing. It stores the fallback where it finds none, and also where
|
|
374
|
+
* its store can't be read to tell, saying it may then be stored twice. */
|
|
375
|
+
deliver(occurrence: CoalescedOccurrence<E>, materialized?: McplContentBlock[], assemblingFor?: string, options?: DeliveryOptions): Promise<CoalescingPlacement | undefined>;
|
|
376
|
+
/** Whether a delivery's throw leaves the host unable to tell whether it
|
|
377
|
+
* stored the occurrence (the framework's: context-manager's
|
|
378
|
+
* WriteOutcomeUnknownError). Absent, or false, a throw stored none of it. */
|
|
379
|
+
mayHaveStored?(error: unknown): boolean;
|
|
312
380
|
/** Queue a wake for a batch that has no model-visible content yet. */
|
|
313
381
|
wakeForBatch(occurrence: CoalescedOccurrence<E>): Promise<void>;
|
|
314
|
-
/** Queue an ordinary push wake for
|
|
315
|
-
* policy, to read a render of the batch
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
382
|
+
/** Queue an ordinary push wake for the placement's agent, under the
|
|
383
|
+
* batch's own wake policy, to read a render of the batch that no request
|
|
384
|
+
* consumed, placed where the turn being assembled doesn't read it: stored
|
|
385
|
+
* and not presented to the agent (`placement`, which the host reads the
|
|
386
|
+
* wake's mark from). It carries
|
|
387
|
+
* no subject, so the subject's other wakes, and later withdrawals of
|
|
388
|
+
* them, leave it. `allAmbient`: every notice the render stands for was
|
|
389
|
+
* tagged `chat:ambient` (DeferredBatch.allAmbient), so the wake is marked
|
|
320
390
|
* ambient. */
|
|
321
|
-
wakeToRead(occurrence: CoalescedOccurrence<E>,
|
|
391
|
+
wakeToRead(occurrence: CoalescedOccurrence<E>, placement: CoalescingPlacement, allAmbient?: boolean): Promise<void>;
|
|
322
392
|
/** Withdraw unstarted wakes whose sole cause is this subject. */
|
|
323
393
|
cancelWake(subject: string): void;
|
|
324
394
|
/** The batch's audience still holds the authority it was admitted under. */
|
|
@@ -326,7 +396,7 @@ export interface CoalescerHost<E> {
|
|
|
326
396
|
/** Agents that read this occurrence's delivery target (may spawn a fork). */
|
|
327
397
|
audience(occurrence: CoalescedOccurrence<E>): Promise<string[]>;
|
|
328
398
|
render(occurrence: CoalescedOccurrence<E>, params: PushRenderParams): Promise<PushRenderResult>;
|
|
329
|
-
audit(record:
|
|
399
|
+
audit(record: CoalescingAudit): void;
|
|
330
400
|
/** Persist (throttled by the host); the thunk builds the snapshot lazily. */
|
|
331
401
|
save(snapshot: () => CoalescingSnapshot): void;
|
|
332
402
|
/** Persist the snapshot NOW (a render is about to start: that boundary must
|
|
@@ -355,6 +425,15 @@ export interface CoalescerHost<E> {
|
|
|
355
425
|
now?(): number;
|
|
356
426
|
}
|
|
357
427
|
|
|
428
|
+
/** What the coalescer tells the host of a delivery (CoalescerHost.deliver). */
|
|
429
|
+
export interface DeliveryOptions {
|
|
430
|
+
/** A render's recovery, after a delivery of this event whose throw left the
|
|
431
|
+
* host unable to tell whether it stored the occurrence (mayHaveStored):
|
|
432
|
+
* the host looks for its rows first, and stores it where it finds none,
|
|
433
|
+
* or can't read its store to tell. */
|
|
434
|
+
mayBeStored?: true;
|
|
435
|
+
}
|
|
436
|
+
|
|
358
437
|
export interface CoalescerOptions {
|
|
359
438
|
retryWindowMs?: number;
|
|
360
439
|
/** Base delay before re-waking a batch whose render-start commit failed
|
|
@@ -738,8 +817,9 @@ export class PushCoalescer<E = unknown> {
|
|
|
738
817
|
/**
|
|
739
818
|
* Remove the occupant and the run it builds on, newest first, auditing each
|
|
740
819
|
* as `kind`. Every member is unread: refreshOccupant has just run, under the
|
|
741
|
-
* lock. The host's removal may still refuse one, such as a
|
|
742
|
-
*
|
|
820
|
+
* lock. The host's removal may still refuse one, such as a row a reader
|
|
821
|
+
* fork was handed as framing, or any while a surgery holds the store. A
|
|
822
|
+
* member the host
|
|
743
823
|
* can't remove may be read, so the walk stops there, leaving it and the
|
|
744
824
|
* renders it builds on, and the history becomes `unknown`.
|
|
745
825
|
* Returns false then. It doesn't touch `state.occupant`, which still names
|
|
@@ -937,6 +1017,7 @@ export class PushCoalescer<E = unknown> {
|
|
|
937
1017
|
// Later notices stay in the batch's audience.
|
|
938
1018
|
occurrence = { ...occurrence, deliverTo: state.batch.latest.deliverTo };
|
|
939
1019
|
}
|
|
1020
|
+
if (occurrence.tags?.includes('chat:addressed') || state.batch?.latest.batchAddressed) occurrence = { ...occurrence, batchAddressed: true };
|
|
940
1021
|
const notice: DeferredNotice = { eventId: occurrence.eventId, timestamp: occurrence.timestamp, ...(occurrence.data !== undefined ? { data: structuredClone(occurrence.data) } : {}) };
|
|
941
1022
|
if (state.batch) {
|
|
942
1023
|
state.batch.notices.push(notice);
|
|
@@ -1003,10 +1084,15 @@ export class PushCoalescer<E = unknown> {
|
|
|
1003
1084
|
* A batch that opened while another render ran is rendered once that
|
|
1004
1085
|
* render settles. Resolves whether it materialized.
|
|
1005
1086
|
*/
|
|
1006
|
-
async materializeNow(agentName: string, subject: string): Promise<boolean> {
|
|
1087
|
+
async materializeNow(agentName: string, subject: string, onlyIfLatest?: string): Promise<boolean> {
|
|
1007
1088
|
for (;;) {
|
|
1008
1089
|
if (this.suspended) return false;
|
|
1009
|
-
|
|
1090
|
+
// `onlyIfLatest`: a newer occurrence took the batch over while this
|
|
1091
|
+
// waited for the lock, and its own decision and wake are the batch's.
|
|
1092
|
+
const started = await this.locked(() => {
|
|
1093
|
+
if (onlyIfLatest !== undefined && this.subjects.get(subject)?.batch?.latest.eventId !== onlyIfLatest) return Promise.resolve(undefined);
|
|
1094
|
+
return this.freeze(subject, agentName);
|
|
1095
|
+
});
|
|
1010
1096
|
if (!started || started === 'declined') return false;
|
|
1011
1097
|
await started.done;
|
|
1012
1098
|
this.persist();
|
|
@@ -1086,7 +1172,7 @@ export class PushCoalescer<E = unknown> {
|
|
|
1086
1172
|
this.scheduleRecovery(subject, state);
|
|
1087
1173
|
return 'declined';
|
|
1088
1174
|
}
|
|
1089
|
-
this.
|
|
1175
|
+
this.disarmRecovery(subject);
|
|
1090
1176
|
if (this.suspended) { state.rendering = undefined; state.batch = batch; return undefined; }
|
|
1091
1177
|
// Authority (grant, registration, binding) is re-checked immediately
|
|
1092
1178
|
// before dispatch: a server id reassigned to another endpoint during the
|
|
@@ -1142,9 +1228,13 @@ export class PushCoalescer<E = unknown> {
|
|
|
1142
1228
|
}
|
|
1143
1229
|
// Settlement runs under the lock: between the RPC's return and the state
|
|
1144
1230
|
// update no admission can withdraw or replace what is being published.
|
|
1231
|
+
let failed = false;
|
|
1232
|
+
let current = false;
|
|
1233
|
+
let recover = false;
|
|
1145
1234
|
await this.locked(async () => {
|
|
1146
1235
|
try {
|
|
1147
1236
|
if (rendering.cancelled || state.rendering !== rendering || this.suspended) return;
|
|
1237
|
+
current = true;
|
|
1148
1238
|
// Rule 4: authority is re-checked at response.
|
|
1149
1239
|
if (!this.host.authorized(occurrence)) { this.host.audit({ kind: 'revoked', subject, eventId: occurrence.eventId }); return; }
|
|
1150
1240
|
const empty = isVisiblyEmptyContent(content);
|
|
@@ -1159,15 +1249,82 @@ export class PushCoalescer<E = unknown> {
|
|
|
1159
1249
|
// a run's oldest render can be one.
|
|
1160
1250
|
const run = state.occupant?.rendered ? state.occupant : undefined;
|
|
1161
1251
|
const firstView = batch.firstView === true && state.history === 'none' && !state.occupant;
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
//
|
|
1170
|
-
//
|
|
1252
|
+
let placement: CoalescingPlacement | undefined;
|
|
1253
|
+
try {
|
|
1254
|
+
placement = await this.deliver(
|
|
1255
|
+
{ ...occurrence, timestamp, content, ...(firstView ? { firstView: true } : {}) }, content, assemblingFor,
|
|
1256
|
+
batch.mayBeStored === occurrence.eventId ? { mayBeStored: true } : undefined,
|
|
1257
|
+
);
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
// A delivery that throws goes to its batch's recovery, which
|
|
1260
|
+
// delivers the fallback (the host's deliver). A store that threw
|
|
1261
|
+
// after the occurrence's rows landed (context-manager's own
|
|
1262
|
+
// listener failing after its append, or a sharded append whose
|
|
1263
|
+
// clean-up failed too) doesn't come here: context-manager names
|
|
1264
|
+
// the rows (WriteLandedError), and the framework's delivery takes
|
|
1265
|
+
// the occurrence as stored by them, placed, presented and woken
|
|
1266
|
+
// for, so no fallback is stored beside them. Any other throw stored
|
|
1267
|
+
// nothing, unless the host can't tell (mayHaveStored: the store
|
|
1268
|
+
// couldn't be read to tell whether a row landed). Then nothing is
|
|
1269
|
+
// stored at once: the recovery's delivery says the occurrence may
|
|
1270
|
+
// be stored (mayBeStored), and the host, its store read again,
|
|
1271
|
+
// places the rows it finds and stores no fallback, stores the
|
|
1272
|
+
// fallback where it finds none, and where its store still can't
|
|
1273
|
+
// be read, stores the fallback and logs that the agent may see the
|
|
1274
|
+
// occurrence twice. A batch opened while this render ran (§5.4
|
|
1275
|
+
// rule 1) has its own fallback delivered at the recovery, which
|
|
1276
|
+
// isn't this occurrence and isn't looked for: this render, if it
|
|
1277
|
+
// landed, stays where it is, unplaced. The mark stays on a batch
|
|
1278
|
+
// whose recovery's delivery throws again, so the next one looks
|
|
1279
|
+
// again.
|
|
1280
|
+
failed = true;
|
|
1281
|
+
const mayHaveStored = this.host.mayHaveStored?.(error) === true;
|
|
1282
|
+
this.host.audit({ kind: 'deliver-failed', subject, eventId: occurrence.eventId, error: String(error), ...(mayHaveStored ? { mayBeStored: true } : {}) });
|
|
1283
|
+
if (state.batch) state.batch.noRender = true;
|
|
1284
|
+
else {
|
|
1285
|
+
batch.noRender = true;
|
|
1286
|
+
if (mayHaveStored) batch.mayBeStored = occurrence.eventId;
|
|
1287
|
+
state.batch = batch;
|
|
1288
|
+
}
|
|
1289
|
+
// Saved now, not only once the assembly's other renders settle: a
|
|
1290
|
+
// newer batch's last snapshot, its acceptance's, has it rendering
|
|
1291
|
+
// as usual, and a restart before the next save would render it on
|
|
1292
|
+
// top of the render the agent never got. Written at once and made
|
|
1293
|
+
// durable, as the render-start boundary is (saveNow, then commit):
|
|
1294
|
+
// the host's throttled save would leave that window open for its
|
|
1295
|
+
// delay. Where either throws, the throttled save is asked for.
|
|
1296
|
+
let written = false;
|
|
1297
|
+
try {
|
|
1298
|
+
if (this.host.saveNow) { this.host.saveNow(this.snapshot()); written = true; }
|
|
1299
|
+
} catch (saveError) {
|
|
1300
|
+
this.host.audit({ kind: 'persist-failed', subject, eventId: occurrence.eventId, error: String(saveError) });
|
|
1301
|
+
}
|
|
1302
|
+
if (!written) this.persist();
|
|
1303
|
+
// Its recovery is armed once the render has settled (the finally
|
|
1304
|
+
// below): a recovery that fires while a render is under way does
|
|
1305
|
+
// nothing and isn't armed again, and the commit may outlast the
|
|
1306
|
+
// backoff.
|
|
1307
|
+
recover = true;
|
|
1308
|
+
if (written) {
|
|
1309
|
+
try {
|
|
1310
|
+
await this.host.commit?.();
|
|
1311
|
+
} catch (commitError) {
|
|
1312
|
+
this.host.audit({ kind: 'persist-failed', subject, eventId: occurrence.eventId, error: String(commitError) });
|
|
1313
|
+
this.persist();
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
if (placement?.presented === false) {
|
|
1319
|
+
// Not presented to its agent: stored for another agent, whose
|
|
1320
|
+
// turn is alive, or for the assembling agent after its assembly
|
|
1321
|
+
// ended, as any write during its turn; or kept in memory (no id)
|
|
1322
|
+
// until its hold's release stores it, which the host counts as
|
|
1323
|
+
// read and can't remove, so of what follows only the wake applies
|
|
1324
|
+
// to it. A stored one is still unread there, so it stays
|
|
1325
|
+
// replaceable and withdrawable. The turn being assembled
|
|
1326
|
+
// doesn't read it, so it isn't materialized for that turn, and no
|
|
1327
|
+
// request has consumed it. The freeze withdrew the placement's
|
|
1171
1328
|
// agent's wake for this batch; give it an ordinary wake, as any
|
|
1172
1329
|
// delivery during its turn has, beside any wakes a newer batch has,
|
|
1173
1330
|
// so that its next turn reads the content (the wake starts no turn
|
|
@@ -1187,7 +1344,7 @@ export class PushCoalescer<E = unknown> {
|
|
|
1187
1344
|
await this.host.wakeForBatch(state.batch.latest);
|
|
1188
1345
|
}
|
|
1189
1346
|
state.audienceAgent = placement.agent;
|
|
1190
|
-
await this.host.wakeToRead(occurrence, placement
|
|
1347
|
+
await this.host.wakeToRead(occurrence, placement, batch.allAmbient === true);
|
|
1191
1348
|
return;
|
|
1192
1349
|
}
|
|
1193
1350
|
// The materialized occurrence is consumed by the request being
|
|
@@ -1199,6 +1356,8 @@ export class PushCoalescer<E = unknown> {
|
|
|
1199
1356
|
if (placement) state.audienceAgent = placement.agent;
|
|
1200
1357
|
} finally {
|
|
1201
1358
|
if (state.rendering === rendering) state.rendering = undefined;
|
|
1359
|
+
if (current && !failed) this.clearRecovery(subject);
|
|
1360
|
+
if (recover) this.scheduleRecovery(subject, state);
|
|
1202
1361
|
}
|
|
1203
1362
|
});
|
|
1204
1363
|
}
|
package/src/mcpl/push-handler.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type { InboundSource } from './inbound-source.js';
|
|
|
22
22
|
import { isSilentHeartbeatMarker, isVisiblyEmptyConversion } from './visible-content.js';
|
|
23
23
|
import { logDetail, logValue } from './log-value.js';
|
|
24
24
|
import { RefusalLines } from './refusal-lines.js';
|
|
25
|
+
import type { TraceEventInput } from '../types/trace.js';
|
|
25
26
|
|
|
26
27
|
// ============================================================================
|
|
27
28
|
// McplPushEvent (the ProcessEvent shape pushed to the queue)
|
|
@@ -35,6 +36,8 @@ import { RefusalLines } from './refusal-lines.js';
|
|
|
35
36
|
* events.ts modification is deferred.
|
|
36
37
|
*/
|
|
37
38
|
export interface McplPushEvent {
|
|
39
|
+
/** The occurrence's suppression, when a coalesced push's wake decided it (CoalescedOccurrence.suppressionDecision). */
|
|
40
|
+
suppressionDecision?: string | null;
|
|
38
41
|
type: 'mcpl:push-event';
|
|
39
42
|
serverId: string;
|
|
40
43
|
featureSet: string;
|
|
@@ -48,13 +51,15 @@ export interface McplPushEvent {
|
|
|
48
51
|
targetAgents?: string[];
|
|
49
52
|
/** RFC-006: set when the coalescer delivers; rides the wake it queues. */
|
|
50
53
|
coalescingSubject?: string;
|
|
51
|
-
/** RFC-006 assembly: materialized for this agent's turn
|
|
52
|
-
*
|
|
53
|
-
* addMessage, bypassDeferralFor). */
|
|
54
|
+
/** RFC-006 assembly: materialized for this agent's turn while its
|
|
55
|
+
* assembly runs, which presents it before the turn's compile. */
|
|
54
56
|
assemblingFor?: string;
|
|
55
57
|
/** The coalescer's mark: the agent's first view of the subject (see
|
|
56
58
|
* CoalescedOccurrence.firstView). */
|
|
57
59
|
firstView?: boolean;
|
|
60
|
+
/** A batch's recovery after a delivery that may have stored the push (the
|
|
61
|
+
* framework's FrameworkWriteOptions.mayBeStored). */
|
|
62
|
+
mayBeStored?: true;
|
|
58
63
|
/** Host acceptance time (epoch ms), stamped where the push is admitted. */
|
|
59
64
|
acceptedAt?: number;
|
|
60
65
|
/** Framework-owned source envelope, frozen at a coalesced occurrence's
|
|
@@ -111,7 +116,7 @@ interface Responder {
|
|
|
111
116
|
export class PushHandler {
|
|
112
117
|
private featureSetManager: FeatureSetManager;
|
|
113
118
|
private pushEventFn: (event: McplPushEvent) => void;
|
|
114
|
-
private emitTraceFn: (event:
|
|
119
|
+
private emitTraceFn: (event: TraceEventInput) => void;
|
|
115
120
|
private shouldTriggerInference?: (content: string, metadata: Record<string, unknown>) => boolean;
|
|
116
121
|
private dedup = new DedupSet(1000);
|
|
117
122
|
/** Holds back repeats of the line for a channel-scoped push refused because
|
|
@@ -121,7 +126,7 @@ export class PushHandler {
|
|
|
121
126
|
constructor(
|
|
122
127
|
featureSetManager: FeatureSetManager,
|
|
123
128
|
pushEventFn: (event: McplPushEvent) => void,
|
|
124
|
-
emitTraceFn: (event:
|
|
129
|
+
emitTraceFn: (event: TraceEventInput) => void,
|
|
125
130
|
shouldTriggerInference?: (content: string, metadata: Record<string, unknown>) => boolean,
|
|
126
131
|
private readonly handleCoalesced?: (serverId: string, params: PushEventParams, event: McplPushEvent) => Promise<PushEventResult>,
|
|
127
132
|
/** An ordinary (uncoalesced) push was just admitted, before it is queued
|