effect-durable-agent-cloudflare 0.1.0-alpha.24 → 0.1.0-alpha.25
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/dist/{durable-object-BZJM3RyV.js → durable-object-CkrxIOH4.js} +190 -40
- package/dist/durable-object-CkrxIOH4.js.map +1 -0
- package/dist/{durable-object-storage-D3muKtpz.d.ts → durable-object-storage-BVsu5oAV.d.ts} +21 -1
- package/dist/{durable-object-store-Dct3N6rk.js → durable-object-store-Ca0pvpa_.js} +100 -17
- package/dist/durable-object-store-Ca0pvpa_.js.map +1 -0
- package/dist/durable-object.d.ts +41 -6
- package/dist/durable-object.js +1 -1
- package/dist/index.d.ts +54 -13
- package/dist/index.js +5 -5
- package/dist/{rpc-spec-B5cL83zd.js → rpc-spec-BupIkBhI.js} +125 -17
- package/dist/rpc-spec-BupIkBhI.js.map +1 -0
- package/dist/{rpc-spec-BTVd02Rg.d.ts → rpc-spec-CwehaLCd.d.ts} +191 -7
- package/dist/rpc.d.ts +2 -2
- package/dist/rpc.js +2 -2
- package/dist/{session-controller-D8uXDKVG.d.ts → session-controller-D0PxLJJ9.d.ts} +9 -4
- package/dist/{session-controller-DoUy9zR6.js → session-controller-KWMuKhlX.js} +3 -3
- package/dist/session-controller-KWMuKhlX.js.map +1 -0
- package/dist/session-controller.d.ts +1 -1
- package/dist/session-controller.js +1 -1
- package/dist/storage.d.ts +1 -1
- package/package.json +2 -2
- package/dist/durable-object-BZJM3RyV.js.map +0 -1
- package/dist/durable-object-store-Dct3N6rk.js.map +0 -1
- package/dist/rpc-spec-B5cL83zd.js.map +0 -1
- package/dist/session-controller-DoUy9zR6.js.map +0 -1
|
@@ -1,15 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { S as encodeEdaRpcDurableEvent, a as SessionOutcomeInput, d as SessionTranscriptWindowInput, g as DurableObjectTranscriptStore, h as DurableObjectTranscriptBuilder, i as SessionGrantRunInput, l as SessionTranscriptSummaryInput, m as edaSessionRpc, n as SessionBlockInput, o as SessionScopedInput, r as SessionCommandInput, s as SessionTranscriptBodyInput, t as SessionBatchInput } from "./rpc-spec-BupIkBhI.js";
|
|
2
|
+
import { i as readDurableObjectSummary, m as resolveTranscriptSliceLimits, r as readDurableObjectCommittedEvent, s as defaultTranscriptSliceLimits } from "./durable-object-store-Ca0pvpa_.js";
|
|
3
|
+
import { t as EDASessionController } from "./session-controller-KWMuKhlX.js";
|
|
3
4
|
import * as Effect from "effect/Effect";
|
|
4
5
|
import { durableEventEnvelope } from "effect-durable-agent/types/events/durable";
|
|
5
6
|
import * as Schema from "effect/Schema";
|
|
6
7
|
import { CommandId, RunRequestId, SequenceNumber, SessionId } from "effect-durable-agent/types/core";
|
|
7
8
|
import { EDATraceMetadata, makeEDATraceMetadataFromParent, makeRootEDATraceMetadata, parseEDATraceparent } from "effect-durable-agent/types/tracing";
|
|
8
9
|
import { EDASessionStoreError } from "effect-durable-agent/services/session-store";
|
|
9
|
-
import { defaultTranscriptWindowLimits, emptyTranscriptMappings, makeTranscriptDefinition } from "effect-durable-agent/transcript";
|
|
10
|
+
import { defaultTranscriptWindowLimits, emptyTranscriptMappings, makeTranscriptDefinition, resolveTranscriptBody } from "effect-durable-agent/transcript";
|
|
10
11
|
import * as Result from "effect/Result";
|
|
11
12
|
import { DurableObject } from "cloudflare:workers";
|
|
12
13
|
import { EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE } from "effect-durable-agent/websocket";
|
|
14
|
+
//#region src/durable-object-alarm-handler.ts
|
|
15
|
+
/** Longest delay between builder retries. */
|
|
16
|
+
const maxBuilderRetryDelayMs = 36e5;
|
|
17
|
+
/**
|
|
18
|
+
* Handle one platform alarm shared by the runtime and the transcript builder.
|
|
19
|
+
*
|
|
20
|
+
* - The runtime runs for its own wakeup, or for an alarm no owner recorded (one armed by a
|
|
21
|
+
* release before wakeups were recorded). The builder runs for its wakeup even when the runtime
|
|
22
|
+
* failed.
|
|
23
|
+
* - No wakeup is consumed up front: each owner's is cleared only after its work succeeds, so a
|
|
24
|
+
* handler that throws or dies mid-work leaves the platform's retry attributed to every owner it
|
|
25
|
+
* fired for.
|
|
26
|
+
* - A failed builder retries with exponential backoff; its consecutive failures persist with its
|
|
27
|
+
* wakeup, so eviction cannot reset them, and at `maxBuilderFailures` it stops scheduling itself.
|
|
28
|
+
* No failure re-arms the alarm sooner than the backoff, so no object can loop on its alarm.
|
|
29
|
+
* - A failed runtime rethrows without re-arming the platform alarm, so the platform's own retry
|
|
30
|
+
* (backoff and retry cap) applies; otherwise the alarm is pointed at the earliest wakeup left.
|
|
31
|
+
*/
|
|
32
|
+
const runEDASessionAlarm = async (participants) => {
|
|
33
|
+
const { alarms } = participants;
|
|
34
|
+
const now = participants.now();
|
|
35
|
+
const due = alarms.due(now);
|
|
36
|
+
let runtimeFailure;
|
|
37
|
+
if (due.has("runtime") || due.size === 0) try {
|
|
38
|
+
await participants.runRuntime();
|
|
39
|
+
alarms.clearThrough("runtime", due.get("runtime") ?? now);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
runtimeFailure = { error };
|
|
42
|
+
}
|
|
43
|
+
const builderWakeup = due.get("transcript");
|
|
44
|
+
if (builderWakeup !== void 0) {
|
|
45
|
+
const runBuilderSlices = participants.runBuilderSlices;
|
|
46
|
+
if (runBuilderSlices === void 0) alarms.clear("transcript");
|
|
47
|
+
else try {
|
|
48
|
+
if (await runBuilderSlices()) alarms.retain("transcript", participants.now());
|
|
49
|
+
else alarms.clearThrough("transcript", builderWakeup);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const consecutiveFailures = alarms.failures("transcript") + 1;
|
|
52
|
+
const stopped = consecutiveFailures >= participants.maxBuilderFailures;
|
|
53
|
+
if (stopped) alarms.clear("transcript");
|
|
54
|
+
else {
|
|
55
|
+
const delayMs = Math.min(participants.builderRetryDelayMs * 2 ** (consecutiveFailures - 1), maxBuilderRetryDelayMs);
|
|
56
|
+
alarms.retain("transcript", participants.now() + delayMs, consecutiveFailures);
|
|
57
|
+
}
|
|
58
|
+
participants.onBuilderFailure({
|
|
59
|
+
error,
|
|
60
|
+
consecutiveFailures,
|
|
61
|
+
stopped
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (runtimeFailure !== void 0) throw runtimeFailure.error;
|
|
66
|
+
await alarms.sync();
|
|
67
|
+
};
|
|
68
|
+
//#endregion
|
|
13
69
|
//#region src/durable-object-alarms.ts
|
|
14
70
|
/**
|
|
15
71
|
* Shares one Durable Object alarm between owners that each keep a durable wakeup.
|
|
@@ -63,8 +119,16 @@ var DurableObjectAlarmScheduler = class {
|
|
|
63
119
|
* caller syncs once at the end, or not at all when it rethrows, which leaves the platform's own
|
|
64
120
|
* retry (with its backoff and cap) in force.
|
|
65
121
|
*/
|
|
66
|
-
retain(owner, atMs) {
|
|
67
|
-
this.#write(owner, atMs);
|
|
122
|
+
retain(owner, atMs, failures = 0) {
|
|
123
|
+
this.#write(owner, atMs, failures);
|
|
124
|
+
}
|
|
125
|
+
/** Consecutive failures recorded with `owner`'s wakeup; 0 without one. */
|
|
126
|
+
failures(owner) {
|
|
127
|
+
return this.storage.sql.exec("SELECT failures FROM _eda_alarm_wakeups WHERE owner = ?", owner).toArray()[0]?.failures ?? 0;
|
|
128
|
+
}
|
|
129
|
+
/** Remove `owner`'s wakeup whatever its time; call `sync` afterwards. */
|
|
130
|
+
clear(owner) {
|
|
131
|
+
this.#write(owner, void 0);
|
|
68
132
|
}
|
|
69
133
|
/** Wake `owner` no later than `atMs`; an earlier wakeup it already holds is kept. */
|
|
70
134
|
request(owner, atMs) {
|
|
@@ -114,13 +178,19 @@ var DurableObjectAlarmScheduler = class {
|
|
|
114
178
|
#wakeup(owner) {
|
|
115
179
|
return this.storage.sql.exec("SELECT at_ms FROM _eda_alarm_wakeups WHERE owner = ?", owner).toArray()[0]?.at_ms;
|
|
116
180
|
}
|
|
117
|
-
|
|
181
|
+
/** Upsert or delete `owner`'s wakeup. Without `failures`, an existing count is kept. */
|
|
182
|
+
#write(owner, atMs, failures) {
|
|
118
183
|
if (atMs === void 0) {
|
|
119
184
|
this.storage.sql.exec("DELETE FROM _eda_alarm_wakeups WHERE owner = ?", owner);
|
|
120
185
|
return;
|
|
121
186
|
}
|
|
122
|
-
|
|
123
|
-
|
|
187
|
+
if (failures === void 0) {
|
|
188
|
+
this.storage.sql.exec(`INSERT INTO _eda_alarm_wakeups (owner, at_ms) VALUES (?, ?)
|
|
189
|
+
ON CONFLICT(owner) DO UPDATE SET at_ms = excluded.at_ms`, owner, atMs);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
this.storage.sql.exec(`INSERT INTO _eda_alarm_wakeups (owner, at_ms, failures) VALUES (?, ?, ?)
|
|
193
|
+
ON CONFLICT(owner) DO UPDATE SET at_ms = excluded.at_ms, failures = excluded.failures`, owner, atMs, failures);
|
|
124
194
|
}
|
|
125
195
|
};
|
|
126
196
|
const isOwner = (owner) => owner === "runtime" || owner === "transcript";
|
|
@@ -138,6 +208,8 @@ const transcriptStatusMaxSlices = 16;
|
|
|
138
208
|
/** Server bounds on one `readTranscriptWindow` RPC, whatever limits the caller asks for. */
|
|
139
209
|
const transcriptWindowMaxRecords = 500;
|
|
140
210
|
const transcriptWindowMaxBytes = 8388608;
|
|
211
|
+
/** First retry delay after a failed builder alarm; doubled per consecutive failure. */
|
|
212
|
+
const transcriptBuilderRetryDelayMs = 3e4;
|
|
141
213
|
/** Slices one builder alarm runs before rescheduling itself. */
|
|
142
214
|
const transcriptAlarmMaxSlices = 4;
|
|
143
215
|
/** Resolve a concrete EDA session Durable Object binding by domain session id. */
|
|
@@ -156,6 +228,7 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
156
228
|
#alarms;
|
|
157
229
|
#transcriptOptions;
|
|
158
230
|
#transcript;
|
|
231
|
+
#transcriptEnabled;
|
|
159
232
|
constructor(ctx, env, options) {
|
|
160
233
|
super(ctx, env);
|
|
161
234
|
this.#webSocketProjectionId = options.webSocketProjection?.id;
|
|
@@ -164,13 +237,15 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
164
237
|
const { transcript: transcriptHostOptions, transcriptSockets, ...controllerOptions } = options;
|
|
165
238
|
const transcript = this.transcriptOptions(transcriptHostOptions);
|
|
166
239
|
this.#transcriptOptions = transcript;
|
|
240
|
+
const enabled = transcriptHostOptions?.enabled ?? false;
|
|
241
|
+
this.#transcriptEnabled = typeof enabled === "function" ? enabled : () => enabled;
|
|
167
242
|
this.#controller = new EDASessionController({
|
|
168
243
|
...controllerOptions,
|
|
169
244
|
alarmStorage: this.#alarms.owner("runtime"),
|
|
170
245
|
background: this.ctx,
|
|
171
246
|
getWebSockets: () => this.ctx.getWebSockets(),
|
|
172
247
|
storage,
|
|
173
|
-
transcript,
|
|
248
|
+
transcript: ({ sessionId }) => this.transcriptFor(sessionId).enabled ? transcript : void 0,
|
|
174
249
|
...transcriptSockets === void 0 ? {} : { transcriptSockets: {
|
|
175
250
|
...transcriptSockets,
|
|
176
251
|
transcriptStore: transcriptSockets.transcriptStore ?? this.transcriptPort()
|
|
@@ -180,7 +255,7 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
180
255
|
this.ctx.blockConcurrencyWhile(async () => {
|
|
181
256
|
await Effect.runPromise(EDASessionController.migrate(storage));
|
|
182
257
|
await this.#alarms.adoptUnrecordedAlarm();
|
|
183
|
-
await this.
|
|
258
|
+
await this.maintainedTranscript()?.builder.start();
|
|
184
259
|
});
|
|
185
260
|
}
|
|
186
261
|
async fetch(request) {
|
|
@@ -193,11 +268,12 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
193
268
|
if (afterSeq !== void 0 && (!Number.isInteger(afterSeq) || afterSeq < 0)) return new Response("Invalid afterSeq.", { status: 400 });
|
|
194
269
|
const projectionId = request.headers.get("x-eda-websocket-projection") ?? void 0;
|
|
195
270
|
if (projectionId !== void 0 && projectionId !== this.#webSocketProjectionId) return new Response("Unsupported WebSocket projection.", { status: 400 });
|
|
196
|
-
const sessionId = this.
|
|
271
|
+
const sessionId = this.parseSessionIdOrUndefined(sessionIdRaw);
|
|
272
|
+
if (sessionId === void 0) return new Response("This object does not serve that session.", { status: 400 });
|
|
197
273
|
const trace = traceMetadataFromRequest(request);
|
|
198
274
|
if (url.searchParams.get("stream") === "transcript") {
|
|
199
275
|
if (projectionId !== void 0 || !this.#controller.supportsTranscriptWebSocket) return new Response("Unsupported transcript stream.", { status: 400 });
|
|
200
|
-
this.transcriptFor(sessionId);
|
|
276
|
+
if (!this.transcriptFor(sessionId).enabled) return new Response("Transcript is not enabled for this session.", { status: 409 });
|
|
201
277
|
const pair = new WebSocketPair();
|
|
202
278
|
this.ctx.acceptWebSocket(pair[1]);
|
|
203
279
|
this.#controller.acceptTranscriptWebSocket({
|
|
@@ -328,6 +404,7 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
328
404
|
async transcriptStatus(raw) {
|
|
329
405
|
const input = Schema.decodeUnknownSync(SessionScopedInput)(raw);
|
|
330
406
|
const transcript = this.transcriptFor(this.parseSessionId(input.sessionId));
|
|
407
|
+
if (!transcript.enabled) return Schema.encodeSync(edaSessionRpc.transcriptStatus.output)(transcript.store.disabledStatus());
|
|
331
408
|
await transcript.builder.run(transcriptStatusMaxSlices);
|
|
332
409
|
return Schema.encodeSync(edaSessionRpc.transcriptStatus.output)(transcript.store.status());
|
|
333
410
|
}
|
|
@@ -335,6 +412,7 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
335
412
|
async readTranscriptWindow(raw) {
|
|
336
413
|
const input = Schema.decodeUnknownSync(SessionTranscriptWindowInput)(raw);
|
|
337
414
|
const transcript = this.transcriptFor(this.parseSessionId(input.sessionId));
|
|
415
|
+
if (!transcript.enabled) return Schema.encodeSync(edaSessionRpc.readTranscriptWindow.output)({ _tag: "Disabled" });
|
|
338
416
|
const limits = input.limits ?? defaultTranscriptWindowLimits;
|
|
339
417
|
const window = transcript.store.readWindow(input.selector, {
|
|
340
418
|
...limits,
|
|
@@ -350,37 +428,75 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
350
428
|
});
|
|
351
429
|
}
|
|
352
430
|
/**
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
|
|
431
|
+
* Runtime-free read of the body a transcript `Ref` names, resolved through the immutable log
|
|
432
|
+
* (so it stays valid after projection rows are rebuilt). Hosts authorize the caller.
|
|
433
|
+
*/
|
|
434
|
+
async readTranscriptBody(raw) {
|
|
435
|
+
const input = Schema.decodeUnknownSync(SessionTranscriptBodyInput)(raw);
|
|
436
|
+
const scope = this.transcriptReadScope(input.sessionId, input.incarnation);
|
|
437
|
+
if (scope._tag !== "Scope") return Schema.encodeSync(edaSessionRpc.readTranscriptBody.output)(scope);
|
|
438
|
+
const sessionId = scope.sessionId;
|
|
439
|
+
const committed = readDurableObjectCommittedEvent(toEDASessionStorage(this.ctx.storage).sql, sessionId, input.source.seq);
|
|
440
|
+
const resolved = committed === void 0 ? {
|
|
441
|
+
_tag: "Missing",
|
|
442
|
+
message: `no event at seq ${input.source.seq}`
|
|
443
|
+
} : resolveTranscriptBody(committed.event, input.source.path);
|
|
444
|
+
return Schema.encodeSync(edaSessionRpc.readTranscriptBody.output)(resolved._tag === "Found" ? {
|
|
445
|
+
_tag: "Body",
|
|
446
|
+
value: resolved.value
|
|
447
|
+
} : {
|
|
448
|
+
_tag: "NotFound",
|
|
449
|
+
message: resolved.message
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
/** Runtime-free read of a compaction summary's text. Hosts authorize the caller. */
|
|
453
|
+
async readTranscriptSummary(raw) {
|
|
454
|
+
const input = Schema.decodeUnknownSync(SessionTranscriptSummaryInput)(raw);
|
|
455
|
+
const scope = this.transcriptReadScope(input.sessionId, input.incarnation);
|
|
456
|
+
if (scope._tag !== "Scope") return Schema.encodeSync(edaSessionRpc.readTranscriptSummary.output)(scope);
|
|
457
|
+
const artifact = readDurableObjectSummary(toEDASessionStorage(this.ctx.storage).sql, input.summaryId);
|
|
458
|
+
return Schema.encodeSync(edaSessionRpc.readTranscriptSummary.output)(artifact === void 0 ? {
|
|
459
|
+
_tag: "NotFound",
|
|
460
|
+
message: `no summary ${input.summaryId}`
|
|
461
|
+
} : {
|
|
462
|
+
_tag: "Summary",
|
|
463
|
+
summary: {
|
|
464
|
+
compactionId: artifact.compactionId,
|
|
465
|
+
summaryId: artifact.summaryId,
|
|
466
|
+
text: artifact.text,
|
|
467
|
+
sourceFromSeq: artifact.sourceFromSeq,
|
|
468
|
+
sourceToSeq: artifact.sourceToSeq
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* The alarm is shared by the runtime heartbeat and the transcript builder; see
|
|
474
|
+
* `runEDASessionAlarm`.
|
|
356
475
|
*/
|
|
357
476
|
async alarm() {
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
477
|
+
const transcript = this.maintainedTranscript();
|
|
478
|
+
await runEDASessionAlarm({
|
|
479
|
+
alarms: this.#alarms,
|
|
480
|
+
now: () => Date.now(),
|
|
481
|
+
runRuntime: async () => {
|
|
482
|
+
const sessionId = this.sessionIdFromObjectName();
|
|
364
483
|
await this.#controller.alarm(sessionId === void 0 ? void 0 : {
|
|
365
484
|
sessionId,
|
|
366
485
|
trace: makeRootEDATraceMetadata()
|
|
367
486
|
});
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
487
|
+
},
|
|
488
|
+
runBuilderSlices: transcript === void 0 ? void 0 : () => transcript.builder.runSlices(transcriptAlarmMaxSlices),
|
|
489
|
+
maxBuilderFailures: this.#transcriptOptions.sliceLimits?.maxSliceFailures ?? defaultTranscriptSliceLimits.maxSliceFailures,
|
|
490
|
+
builderRetryDelayMs: transcriptBuilderRetryDelayMs,
|
|
491
|
+
onBuilderFailure: ({ error, consecutiveFailures, stopped }) => {
|
|
492
|
+
console.error("EDA transcript builder slice failed", error);
|
|
493
|
+
if (stopped) transcript?.store.alert({
|
|
494
|
+
_tag: "BuilderStopped",
|
|
495
|
+
consecutiveFailures,
|
|
496
|
+
error: error instanceof Error ? error.message : String(error)
|
|
497
|
+
});
|
|
371
498
|
}
|
|
372
|
-
}
|
|
373
|
-
const builderWakeup = due.get("transcript");
|
|
374
|
-
if (builderWakeup !== void 0) try {
|
|
375
|
-
const builder = this.objectTranscript()?.builder;
|
|
376
|
-
if (builder === void 0 ? false : await builder.runSlices(transcriptAlarmMaxSlices)) this.#alarms.retain("transcript", Date.now());
|
|
377
|
-
else this.#alarms.clearThrough("transcript", builderWakeup);
|
|
378
|
-
} catch (error) {
|
|
379
|
-
console.error("EDA transcript builder slice failed", error);
|
|
380
|
-
this.#alarms.retain("transcript", Date.now() + 3e4);
|
|
381
|
-
}
|
|
382
|
-
if (runtimeFailure !== void 0) throw runtimeFailure.error;
|
|
383
|
-
await this.#alarms.sync();
|
|
499
|
+
});
|
|
384
500
|
}
|
|
385
501
|
transcriptOptions(options) {
|
|
386
502
|
const key = options?.adoptIncarnationKey;
|
|
@@ -390,12 +506,16 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
390
506
|
key,
|
|
391
507
|
kv: this.ctx.storage.kv
|
|
392
508
|
} },
|
|
509
|
+
sliceLimits: resolveTranscriptSliceLimits(options?.sliceLimits),
|
|
393
510
|
observer: {
|
|
394
511
|
onAlert: options?.onAlert ?? ((alert) => console.error("EDA transcript alert", JSON.stringify(alert))),
|
|
395
512
|
onPendingWork: () => {
|
|
396
|
-
const builder = this.
|
|
513
|
+
const builder = this.maintainedTranscript()?.builder;
|
|
397
514
|
if (builder !== void 0) this.ctx.waitUntil(builder.kick());
|
|
398
515
|
},
|
|
516
|
+
onServedChanged: () => {
|
|
517
|
+
this.ctx.waitUntil(this.#controller.refreshTranscriptWebSockets());
|
|
518
|
+
},
|
|
399
519
|
onActivated: (activation) => {
|
|
400
520
|
this.ctx.waitUntil(this.#controller.refreshTranscriptWebSockets());
|
|
401
521
|
options?.onActivated?.(activation);
|
|
@@ -405,13 +525,29 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
405
525
|
}
|
|
406
526
|
/** This object's transcript store as the port transcript sockets read, resolved per call. */
|
|
407
527
|
transcriptPort() {
|
|
408
|
-
const port = () => this.
|
|
409
|
-
const unbound = () => Effect.fail(new EDASessionStoreError({ message: "EDA transcript read before the session was named" }));
|
|
528
|
+
const port = () => this.maintainedTranscript()?.store.port;
|
|
529
|
+
const unbound = () => Effect.fail(new EDASessionStoreError({ message: this.objectTranscript()?.enabled === false ? "EDA transcript is not enabled for this session" : "EDA transcript read before the session was named" }));
|
|
410
530
|
return {
|
|
411
531
|
readTranscriptWindow: (selector, limits) => Effect.suspend(() => port()?.readTranscriptWindow(selector, limits) ?? unbound()),
|
|
412
532
|
readTranscriptMeta: Effect.suspend(() => port()?.readTranscriptMeta ?? unbound())
|
|
413
533
|
};
|
|
414
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* The session and log a body or summary read may serve: pinned to the session this object's
|
|
537
|
+
* transcript serves, like window reads, and to the incarnation the caller's reference names.
|
|
538
|
+
*/
|
|
539
|
+
transcriptReadScope(rawSessionId, incarnation) {
|
|
540
|
+
const transcript = this.transcriptFor(this.parseSessionId(rawSessionId));
|
|
541
|
+
if (!transcript.enabled) return { _tag: "Disabled" };
|
|
542
|
+
const current = transcript.store.incarnation();
|
|
543
|
+
return incarnation === current ? {
|
|
544
|
+
_tag: "Scope",
|
|
545
|
+
sessionId: transcript.sessionId
|
|
546
|
+
} : {
|
|
547
|
+
_tag: "IncarnationMismatch",
|
|
548
|
+
incarnation: current
|
|
549
|
+
};
|
|
550
|
+
}
|
|
415
551
|
/** The transcript service of this object's session, created on first use. */
|
|
416
552
|
transcriptFor(sessionId) {
|
|
417
553
|
if (this.#transcript !== void 0) {
|
|
@@ -426,17 +562,31 @@ var EDASessionDurableObject = class extends DurableObject {
|
|
|
426
562
|
const builder = new DurableObjectTranscriptBuilder(store, () => this.#alarms.request("transcript", Date.now()));
|
|
427
563
|
this.#transcript = {
|
|
428
564
|
sessionId,
|
|
565
|
+
enabled: this.#transcriptEnabled(sessionId),
|
|
429
566
|
store,
|
|
430
567
|
builder
|
|
431
568
|
};
|
|
432
569
|
return this.#transcript;
|
|
433
570
|
}
|
|
571
|
+
/** The transcript service when this object's session is enabled for maintenance. */
|
|
572
|
+
maintainedTranscript() {
|
|
573
|
+
const transcript = this.objectTranscript();
|
|
574
|
+
return transcript?.enabled === true ? transcript : void 0;
|
|
575
|
+
}
|
|
434
576
|
/** The transcript service when the object is named by a valid session id. */
|
|
435
577
|
objectTranscript() {
|
|
436
578
|
if (this.#transcript !== void 0) return this.#transcript;
|
|
437
579
|
const name = this.ctx.id.name;
|
|
438
580
|
return name !== void 0 && Schema.is(SessionId)(name) ? this.transcriptFor(SessionId.make(name)) : void 0;
|
|
439
581
|
}
|
|
582
|
+
/** `parseSessionId` for request paths that answer a foreign session with a 400. */
|
|
583
|
+
parseSessionIdOrUndefined(input) {
|
|
584
|
+
if (!Schema.is(SessionId)(input)) return void 0;
|
|
585
|
+
const name = this.ctx.id.name;
|
|
586
|
+
if (name !== void 0 && name !== input) return void 0;
|
|
587
|
+
const bound = this.#transcript?.sessionId;
|
|
588
|
+
return bound === void 0 || bound === input ? SessionId.make(input) : void 0;
|
|
589
|
+
}
|
|
440
590
|
parseSessionId(input) {
|
|
441
591
|
const sessionId = SessionId.make(input);
|
|
442
592
|
const objectName = this.ctx.id.name;
|
|
@@ -474,4 +624,4 @@ const encodeEdaRpcSessionSnapshot = Schema.encodeSync(edaSessionRpc.snapshot.out
|
|
|
474
624
|
//#endregion
|
|
475
625
|
export { encodeEdaRpcCommittedDurableEvent as a, DurableObjectAlarmScheduler as c, edaRuntimeConfig as i, EDA_WEB_SOCKET_PROJECTION_HEADER as n, encodeEdaRpcSessionSnapshot as o, EDA_WEB_SOCKET_STREAM_PARAM as r, getEDASessionDurableObjectByName as s, EDASessionDurableObject as t };
|
|
476
626
|
|
|
477
|
-
//# sourceMappingURL=durable-object-
|
|
627
|
+
//# sourceMappingURL=durable-object-CkrxIOH4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-object-CkrxIOH4.js","names":["#wakeup","#write","#rows","#tail","#controller","#webSocketProjectionId","#alarms","#transcriptOptions","#transcriptEnabled","#transcript"],"sources":["../src/durable-object-alarm-handler.ts","../src/durable-object-alarms.ts","../src/durable-object.ts"],"sourcesContent":["import type { DurableObjectAlarmScheduler } from \"./durable-object-alarms\";\n\n/** What one session object's alarm fires for, and the policy bounding builder retries. */\nexport interface EDASessionAlarmParticipants {\n readonly alarms: DurableObjectAlarmScheduler;\n readonly now: () => number;\n /** The runtime's alarm work: keep-alive and run-scheduling redelivery. */\n readonly runRuntime: () => Promise<void>;\n /**\n * Bounded builder slices; resolves whether work remains. Undefined when this object's session\n * maintains no transcript (it is not enabled, or the object is unnamed).\n */\n readonly runBuilderSlices: (() => Promise<boolean>) | undefined;\n /** Consecutive failed builder alarms before the builder stops scheduling itself. */\n readonly maxBuilderFailures: number;\n /** First retry delay after a failed builder alarm; doubled per consecutive failure. */\n readonly builderRetryDelayMs: number;\n readonly onBuilderFailure: (failure: {\n readonly error: unknown;\n readonly consecutiveFailures: number;\n readonly stopped: boolean;\n }) => void;\n}\n\n/** Longest delay between builder retries. */\nconst maxBuilderRetryDelayMs = 3_600_000;\n\n/**\n * Handle one platform alarm shared by the runtime and the transcript builder.\n *\n * - The runtime runs for its own wakeup, or for an alarm no owner recorded (one armed by a\n * release before wakeups were recorded). The builder runs for its wakeup even when the runtime\n * failed.\n * - No wakeup is consumed up front: each owner's is cleared only after its work succeeds, so a\n * handler that throws or dies mid-work leaves the platform's retry attributed to every owner it\n * fired for.\n * - A failed builder retries with exponential backoff; its consecutive failures persist with its\n * wakeup, so eviction cannot reset them, and at `maxBuilderFailures` it stops scheduling itself.\n * No failure re-arms the alarm sooner than the backoff, so no object can loop on its alarm.\n * - A failed runtime rethrows without re-arming the platform alarm, so the platform's own retry\n * (backoff and retry cap) applies; otherwise the alarm is pointed at the earliest wakeup left.\n */\nexport const runEDASessionAlarm = async (participants: EDASessionAlarmParticipants) => {\n const { alarms } = participants;\n const now = participants.now();\n const due = alarms.due(now);\n let runtimeFailure: { readonly error: unknown } | undefined;\n if (due.has(\"runtime\") || due.size === 0) {\n try {\n await participants.runRuntime();\n alarms.clearThrough(\"runtime\", due.get(\"runtime\") ?? now);\n } catch (error) {\n runtimeFailure = { error };\n }\n }\n const builderWakeup = due.get(\"transcript\");\n if (builderWakeup !== undefined) {\n const runBuilderSlices = participants.runBuilderSlices;\n if (runBuilderSlices === undefined) {\n alarms.clear(\"transcript\");\n } else {\n try {\n if (await runBuilderSlices()) alarms.retain(\"transcript\", participants.now());\n else alarms.clearThrough(\"transcript\", builderWakeup);\n } catch (error) {\n const consecutiveFailures = alarms.failures(\"transcript\") + 1;\n const stopped = consecutiveFailures >= participants.maxBuilderFailures;\n if (stopped) {\n alarms.clear(\"transcript\");\n } else {\n const delayMs = Math.min(\n participants.builderRetryDelayMs * 2 ** (consecutiveFailures - 1),\n maxBuilderRetryDelayMs,\n );\n alarms.retain(\"transcript\", participants.now() + delayMs, consecutiveFailures);\n }\n participants.onBuilderFailure({ error, consecutiveFailures, stopped });\n }\n }\n }\n if (runtimeFailure !== undefined) throw runtimeFailure.error;\n await alarms.sync();\n};\n","import type { DurableObjectAlarmStorage } from \"./durable-object-keepalive\";\nimport type { DurableObjectSessionStorage } from \"./durable-object-storage\";\n\n/**\n * The independent owners of a session object's single platform alarm.\n *\n * - `runtime`: the keep-alive heartbeat and run-scheduling redelivery; its wakeup builds the model\n * runtime.\n * - `transcript`: the transcript builder; its wakeup runs storage-only slices and must never\n * build the model runtime.\n */\nexport type DurableObjectAlarmOwner = \"runtime\" | \"transcript\";\n\n/**\n * Shares one Durable Object alarm between owners that each keep a durable wakeup.\n *\n * Each owner's wakeup is a row in `_eda_alarm_wakeups`, and the platform alarm is always set to the\n * earliest row, so no owner can delete or postpone another's wakeup, and after an eviction the\n * alarm handler still knows which owners it fired for. An owner's view (`owner(...)`) has the\n * same shape as the platform alarm API, so the keep-alive heartbeat uses it unchanged.\n */\nexport class DurableObjectAlarmScheduler {\n #tail: Promise<void> = Promise.resolve();\n\n constructor(\n private readonly storage: DurableObjectSessionStorage & DurableObjectAlarmStorage,\n private readonly now: () => number = () => Date.now(),\n ) {}\n\n /**\n * One owner's alarm, behaving like the platform alarm restricted to that owner. Like the\n * platform's, it reports no alarm once its time has passed: a wakeup left behind when the\n * platform gave up retrying a failed alarm must not look armed, or its owner would never re-arm.\n */\n owner(owner: DurableObjectAlarmOwner): DurableObjectAlarmStorage {\n return {\n getAlarm: () => {\n const wakeup = this.#wakeup(owner);\n return wakeup === undefined || wakeup < this.now() ? null : wakeup;\n },\n setAlarm: (scheduledTimeMs) => {\n this.#write(owner, scheduledTimeMs);\n return this.sync();\n },\n deleteAlarm: () => {\n this.#write(owner, undefined);\n return this.sync();\n },\n };\n }\n\n /**\n * Adopt a platform alarm no owner recorded (one armed before wakeups were recorded) as the\n * runtime's, so the first wakeup another owner requests cannot replace it. Call once at startup,\n * before any owner writes a wakeup.\n */\n async adoptUnrecordedAlarm(): Promise<void> {\n if (this.#rows().length > 0) return;\n const armed = await this.storage.getAlarm();\n if (armed !== null && this.#rows().length === 0) this.#write(\"runtime\", armed);\n }\n\n /**\n * Record `owner`'s wakeup without touching the platform alarm, for use inside `alarm()`: the\n * caller syncs once at the end, or not at all when it rethrows, which leaves the platform's own\n * retry (with its backoff and cap) in force.\n */\n retain(owner: DurableObjectAlarmOwner, atMs: number, failures = 0): void {\n this.#write(owner, atMs, failures);\n }\n\n /** Consecutive failures recorded with `owner`'s wakeup; 0 without one. */\n failures(owner: DurableObjectAlarmOwner): number {\n return (\n this.storage.sql\n .exec<{ readonly failures: number }>(\n \"SELECT failures FROM _eda_alarm_wakeups WHERE owner = ?\",\n owner,\n )\n .toArray()[0]?.failures ?? 0\n );\n }\n\n /** Remove `owner`'s wakeup whatever its time; call `sync` afterwards. */\n clear(owner: DurableObjectAlarmOwner): void {\n this.#write(owner, undefined);\n }\n\n /** Wake `owner` no later than `atMs`; an earlier wakeup it already holds is kept. */\n request(owner: DurableObjectAlarmOwner, atMs: number): Promise<void> {\n const current = this.#wakeup(owner);\n if (current !== undefined && current <= atMs) return this.sync();\n this.#write(owner, atMs);\n return this.sync();\n }\n\n /**\n * Called when the platform alarm fires: the owners it fired for (every wakeup at or before now,\n * and always the earliest one) and the wakeup each held. Nothing is consumed here: each owner\n * `clearThrough`s its wakeup only after its work succeeds, so a handler that throws, or dies\n * mid-work (eviction, deploy, a resource limit), leaves the platform's retry attributed to every\n * owner it fired for. An owner's view already reports a past wakeup as no alarm, so the runtime's\n * keep-alive re-arms during its work as it did with the platform alarm.\n */\n due(nowMs: number): ReadonlyMap<DurableObjectAlarmOwner, number> {\n const rows = this.#rows();\n const earliest = rows.reduce((min, row) => Math.min(min, row.at_ms), Number.POSITIVE_INFINITY);\n const threshold = Math.max(nowMs, earliest);\n const due = new Map<DurableObjectAlarmOwner, number>();\n for (const row of rows) {\n if (row.at_ms <= threshold && isOwner(row.owner)) due.set(row.owner, row.at_ms);\n }\n return due;\n }\n\n /**\n * Remove `owner`'s wakeup if it is still at or before `atMs`, once the work it fired for has\n * succeeded. A later wakeup the owner re-armed during that work is kept.\n */\n clearThrough(owner: DurableObjectAlarmOwner, atMs: number): void {\n this.storage.sql.exec(\n \"DELETE FROM _eda_alarm_wakeups WHERE owner = ? AND at_ms <= ?\",\n owner,\n atMs,\n );\n }\n\n /** Point the platform alarm at the earliest wakeup, or clear it when none remains. */\n sync(): Promise<void> {\n this.#tail = this.#tail\n .catch(() => undefined)\n .then(async () => {\n const earliest = this.storage.sql\n .exec<{ readonly at_ms: number | null }>(\n \"SELECT MIN(at_ms) AS at_ms FROM _eda_alarm_wakeups\",\n )\n .one().at_ms;\n if (earliest === null) {\n await this.storage.deleteAlarm();\n return;\n }\n if ((await this.storage.getAlarm()) !== earliest) {\n await this.storage.setAlarm(earliest);\n }\n });\n return this.#tail;\n }\n\n #rows(): ReadonlyArray<{ readonly owner: string; readonly at_ms: number }> {\n return this.storage.sql\n .exec<{ readonly owner: string; readonly at_ms: number }>(\n \"SELECT owner, at_ms FROM _eda_alarm_wakeups\",\n )\n .toArray();\n }\n\n #wakeup(owner: DurableObjectAlarmOwner): number | undefined {\n return this.storage.sql\n .exec<{ readonly at_ms: number }>(\n \"SELECT at_ms FROM _eda_alarm_wakeups WHERE owner = ?\",\n owner,\n )\n .toArray()[0]?.at_ms;\n }\n\n /** Upsert or delete `owner`'s wakeup. Without `failures`, an existing count is kept. */\n #write(owner: DurableObjectAlarmOwner, atMs: number | undefined, failures?: number): void {\n if (atMs === undefined) {\n this.storage.sql.exec(\"DELETE FROM _eda_alarm_wakeups WHERE owner = ?\", owner);\n return;\n }\n if (failures === undefined) {\n this.storage.sql.exec(\n `INSERT INTO _eda_alarm_wakeups (owner, at_ms) VALUES (?, ?)\n ON CONFLICT(owner) DO UPDATE SET at_ms = excluded.at_ms`,\n owner,\n atMs,\n );\n return;\n }\n this.storage.sql.exec(\n `INSERT INTO _eda_alarm_wakeups (owner, at_ms, failures) VALUES (?, ?, ?)\n ON CONFLICT(owner) DO UPDATE SET at_ms = excluded.at_ms, failures = excluded.failures`,\n owner,\n atMs,\n failures,\n );\n }\n}\n\nconst isOwner = (owner: string): owner is DurableObjectAlarmOwner =>\n owner === \"runtime\" || owner === \"transcript\";\n","import type { CommittedDurableEvent } from \"effect-durable-agent/services/session-store\";\nimport { durableEventEnvelope } from \"effect-durable-agent/types/events/durable\";\nimport { encodeEdaRpcDurableEvent } from \"./rpc-codec\";\nimport {\n SessionScopedInput,\n SessionTranscriptBodyInput,\n SessionTranscriptSummaryInput,\n SessionTranscriptWindowInput,\n SessionCommandInput,\n SessionGrantRunInput,\n SessionBatchInput,\n SessionBlockInput,\n SessionOutcomeInput,\n edaSessionRpc,\n} from \"./rpc-spec\";\nimport { DurableObject } from \"cloudflare:workers\";\nimport * as Effect from \"effect/Effect\";\nimport * as Schema from \"effect/Schema\";\n\nimport type { EDARuntimeConfig } from \"effect-durable-agent/services/runtime\";\nimport {\n CommandId,\n RunRequestId,\n SequenceNumber,\n SessionId,\n} from \"effect-durable-agent/types/core\";\nimport {\n EDATraceMetadata,\n makeEDATraceMetadataFromParent,\n makeRootEDATraceMetadata,\n parseEDATraceparent,\n} from \"effect-durable-agent/types/tracing\";\nimport {\n EDASessionController,\n type EDASessionControllerOptions,\n type EDASessionDurableObjectStorage,\n} from \"./session-controller\";\nimport {\n EDA_WEB_SOCKET_PING_MESSAGE,\n EDA_WEB_SOCKET_PONG_MESSAGE,\n} from \"effect-durable-agent/websocket\";\nimport * as Result from \"effect/Result\";\nimport {\n defaultTranscriptWindowLimits,\n emptyTranscriptMappings,\n makeTranscriptDefinition,\n resolveTranscriptBody,\n type EDATranscriptStoreShape,\n type TranscriptLogIncarnation,\n type TranscriptMaintenanceConfig,\n} from \"effect-durable-agent/transcript\";\nimport { runEDASessionAlarm } from \"./durable-object-alarm-handler\";\nimport { DurableObjectAlarmScheduler } from \"./durable-object-alarms\";\nimport { readDurableObjectCommittedEvent, readDurableObjectSummary } from \"./durable-object-store\";\nimport type { EDATranscriptSocketOptions } from \"./websocket/transcript-sockets\";\nimport { EDASessionStoreError } from \"effect-durable-agent/services/session-store\";\nimport {\n DurableObjectTranscriptBuilder,\n DurableObjectTranscriptStore,\n} from \"./durable-object-transcript\";\nimport {\n defaultTranscriptSliceLimits,\n resolveTranscriptSliceLimits,\n type DurableObjectTranscriptObserver,\n type DurableObjectTranscriptOptions,\n type DurableObjectTranscriptSliceLimits,\n type TranscriptAlert,\n} from \"./durable-object-transcript-maintenance\";\n\nexport type { EDAWebSocketProjection } from \"./websocket/projection\";\n/** Internal Worker-to-object header selecting an app-owned WebSocket projection. */\nexport const EDA_WEB_SOCKET_PROJECTION_HEADER = \"x-eda-websocket-projection\";\n\n/**\n * Upgrade query parameter selecting the socket's stream: `transcript` selects the ordered\n * transcript-window stream; anything else keeps the raw event stream.\n */\nexport const EDA_WEB_SOCKET_STREAM_PARAM = \"stream\";\n\n/** Encoded session RPC inputs, decoded before invoking the controller. */\nexport type EDASessionCommandRpcInput = typeof SessionCommandInput.Encoded;\nexport type EDASessionGrantRunRpcInput = typeof SessionGrantRunInput.Encoded;\nexport type EDASessionSubmitBatchRpcInput = typeof SessionBatchInput.Encoded;\nexport type EDASessionScopedRpcInput = typeof SessionScopedInput.Encoded;\nexport type EDASessionEventsRpcInput = EDASessionScopedRpcInput & { readonly afterSeq?: number };\nexport type EDASessionBlockOnCommandRpcInput = typeof SessionBlockInput.Encoded;\nexport type EDASessionRunRequestOutcomeRpcInput = typeof SessionOutcomeInput.Encoded;\n\n/** EDA RPC methods required by the session namespace helper. */\nexport interface EDASessionRpcSurface {\n readonly runRequestOutcome: (\n input: EDASessionRunRequestOutcomeRpcInput,\n ) => Promise<typeof edaSessionRpc.runRequestOutcome.output.Encoded>;\n readonly grantRun: (\n input: EDASessionGrantRunRpcInput,\n ) => Promise<typeof edaSessionRpc.grantRun.output.Encoded>;\n readonly submit: (\n input: EDASessionCommandRpcInput,\n ) => Promise<typeof edaSessionRpc.submit.output.Encoded>;\n readonly submitBatch: (\n input: EDASessionSubmitBatchRpcInput,\n ) => Promise<typeof edaSessionRpc.submitBatch.output.Encoded>;\n readonly submitAndBlock: (\n input: EDASessionCommandRpcInput,\n ) => Promise<typeof edaSessionRpc.submitAndBlock.output.Encoded>;\n readonly blockOnCommand: (\n input: EDASessionBlockOnCommandRpcInput,\n ) => Promise<typeof edaSessionRpc.blockOnCommand.output.Encoded>;\n readonly snapshot: (\n input: EDASessionScopedRpcInput,\n ) => Promise<typeof edaSessionRpc.snapshot.output.Encoded>;\n readonly messages: (\n input: EDASessionScopedRpcInput,\n ) => Promise<typeof edaSessionRpc.messages.output.Encoded>;\n readonly destroySession: (\n input: EDASessionScopedRpcInput,\n ) => Promise<typeof edaSessionRpc.destroySession.output.Encoded>;\n readonly transcriptStatus: (\n input: EDASessionScopedRpcInput,\n ) => Promise<typeof edaSessionRpc.transcriptStatus.output.Encoded>;\n readonly readTranscriptWindow: (\n input: typeof SessionTranscriptWindowInput.Encoded,\n ) => Promise<typeof edaSessionRpc.readTranscriptWindow.output.Encoded>;\n readonly readTranscriptBody: (\n input: typeof SessionTranscriptBodyInput.Encoded,\n ) => Promise<typeof edaSessionRpc.readTranscriptBody.output.Encoded>;\n readonly readTranscriptSummary: (\n input: typeof SessionTranscriptSummaryInput.Encoded,\n ) => Promise<typeof edaSessionRpc.readTranscriptSummary.output.Encoded>;\n}\n\n/** Host transcript configuration of a session Durable Object. */\nexport interface EDASessionTranscriptOptions {\n /**\n * Which sessions maintain a transcript; default **off**. A disabled session gets no transcript\n * writes, builder slices or wakeups: `readTranscriptWindow` answers `Disabled`,\n * `transcriptStatus` reports `Disabled` without writing, and transcript sockets are\n * refused. Enabling a session takes the first-introduction path (a build from the log, then\n * activation). Rollout rings use a deterministic predicate (for example a hash of the session\n * id under a percentage); disabling leaves a session's rows untouched, and re-enabling catches\n * an active version up to the head before it is served as current. Evaluated once per object\n * instance, so a change takes effect when the object next starts.\n */\n readonly enabled?: boolean | ((sessionId: SessionId) => boolean);\n /**\n * Builder bounds: events per slice, rows deleted per slice, and `maxSliceFailures` (default 5),\n * the consecutive failures after which a build freezes as failed and a failing builder alarm\n * stops scheduling itself.\n */\n readonly sliceLimits?: Partial<DurableObjectTranscriptSliceLimits>;\n /**\n * The transcript definitions this release carries. Defaults to the definition with no host\n * mappings. A host changing its mappings ships `current` = new, `previous` = old, then runs\n * its backfill through `transcriptStatus` (see `docs/transcript-history.md` §7).\n */\n readonly definitions?: TranscriptMaintenanceConfig;\n /**\n * Adopt the host's existing log incarnation from this `ctx.storage.kv` key (Gia:\n * `gia.log_incarnation`). Without it the adapter mints its own.\n */\n readonly adoptIncarnationKey?: string;\n /** Alerts (frozen versions, missed sessions). Defaults to `console.error`. */\n readonly onAlert?: (alert: TranscriptAlert) => void;\n /** A rebuilt version became active. */\n readonly onActivated?: DurableObjectTranscriptObserver[\"onActivated\"];\n}\n\n/** Constructor options for concrete app subclasses of the EDA Durable Object base. */\nexport type EDASessionDurableObjectOptions<ProjectionState extends object = never> = Omit<\n EDASessionControllerOptions<ProjectionState>,\n \"alarmStorage\" | \"background\" | \"getWebSockets\" | \"storage\" | \"transcript\" | \"transcriptSockets\"\n> & {\n readonly transcript?: EDASessionTranscriptOptions;\n /**\n * Serve transcript-window sockets (`?stream=transcript`). `transcriptStore` defaults to this\n * object's own runtime-free Durable Object transcript store.\n */\n readonly transcriptSockets?: Omit<EDATranscriptSocketOptions, \"transcriptStore\"> & {\n readonly transcriptStore?: EDATranscriptSocketOptions[\"transcriptStore\"];\n };\n};\n\n/** Slices a backfill `transcriptStatus` call may run before it returns. */\nconst transcriptStatusMaxSlices = 16;\n/** Server bounds on one `readTranscriptWindow` RPC, whatever limits the caller asks for. */\nconst transcriptWindowMaxRecords = 500;\nconst transcriptWindowMaxBytes = 8 * 1_048_576;\n/** First retry delay after a failed builder alarm; doubled per consecutive failure. */\nconst transcriptBuilderRetryDelayMs = 30_000;\n/** Slices one builder alarm runs before rescheduling itself. */\nconst transcriptAlarmMaxSlices = 4;\n\n/** Resolve a concrete EDA session Durable Object binding by domain session id. */\nexport const getEDASessionDurableObjectByName = <\n T extends Rpc.DurableObjectBranded & EDASessionRpcSurface,\n>(\n namespace: DurableObjectNamespace<T>,\n sessionId: string,\n): DurableObjectStub<T> => namespace.getByName(sessionId);\n\n/**\n * Base class for raw Cloudflare Durable Object hosts of one EDA session.\n *\n * Concrete products should extend this class, pass provider/tool layers from\n * their own constructor, and register only that subclass in Wrangler. The base\n * class is deliberately not exported from `workers/app.ts` and owns no binding\n * name or routing convention.\n */\nexport abstract class EDASessionDurableObject<\n EnvType extends object = object,\n ProjectionState extends object = never,\n> extends DurableObject<EnvType> {\n readonly #controller: EDASessionController<ProjectionState>;\n readonly #webSocketProjectionId: string | undefined;\n readonly #alarms: DurableObjectAlarmScheduler;\n readonly #transcriptOptions: DurableObjectTranscriptOptions;\n #transcript:\n | {\n readonly sessionId: SessionId;\n /** Whether the host enabled transcript maintenance for this session. */\n readonly enabled: boolean;\n readonly store: DurableObjectTranscriptStore;\n readonly builder: DurableObjectTranscriptBuilder;\n }\n | undefined;\n readonly #transcriptEnabled: (sessionId: SessionId) => boolean;\n\n protected constructor(\n ctx: DurableObjectState,\n env: EnvType,\n options: EDASessionDurableObjectOptions<ProjectionState>,\n ) {\n super(ctx, env);\n this.#webSocketProjectionId = options.webSocketProjection?.id;\n const storage = toEDASessionStorage(this.ctx.storage);\n this.#alarms = new DurableObjectAlarmScheduler(storage);\n const { transcript: transcriptHostOptions, transcriptSockets, ...controllerOptions } = options;\n const transcript = this.transcriptOptions(transcriptHostOptions);\n this.#transcriptOptions = transcript;\n const enabled = transcriptHostOptions?.enabled ?? false;\n this.#transcriptEnabled = typeof enabled === \"function\" ? enabled : () => enabled;\n this.#controller = new EDASessionController({\n ...controllerOptions,\n alarmStorage: this.#alarms.owner(\"runtime\"),\n background: this.ctx,\n getWebSockets: () => this.ctx.getWebSockets(),\n storage,\n // The runtime's store maintains the transcript only for an enabled session.\n transcript: ({ sessionId }) =>\n this.transcriptFor(sessionId).enabled ? transcript : undefined,\n ...(transcriptSockets === undefined\n ? {}\n : {\n transcriptSockets: {\n ...transcriptSockets,\n transcriptStore: transcriptSockets.transcriptStore ?? this.transcriptPort(),\n },\n }),\n });\n\n // Answer client liveness pings at the runtime layer so idle sockets never\n // wake a hibernated object.\n this.ctx.setWebSocketAutoResponse(\n new WebSocketRequestResponsePair(EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE),\n );\n\n this.ctx.blockConcurrencyWhile(async () => {\n await Effect.runPromise(EDASessionController.migrate(storage));\n // Keep an alarm armed by an earlier release for the runtime, then resume any builder\n // work an earlier instance left (or a new release created).\n await this.#alarms.adoptUnrecordedAlarm();\n await this.maintainedTranscript()?.builder.start();\n });\n }\n\n async fetch(request: Request): Promise<Response> {\n const upgrade = request.headers.get(\"Upgrade\");\n if (upgrade?.toLowerCase() !== \"websocket\") {\n return new Response(\"Expected WebSocket upgrade.\", { status: 426 });\n }\n\n const url = new URL(request.url);\n const sessionIdRaw = url.searchParams.get(\"sessionId\");\n if (sessionIdRaw === null) {\n return new Response(\"Missing sessionId.\", { status: 400 });\n }\n const afterSeqRaw = url.searchParams.get(\"afterSeq\");\n const afterSeq = afterSeqRaw === null ? undefined : Number(afterSeqRaw);\n if (afterSeq !== undefined && (!Number.isInteger(afterSeq) || afterSeq < 0)) {\n return new Response(\"Invalid afterSeq.\", { status: 400 });\n }\n const projectionId = request.headers.get(EDA_WEB_SOCKET_PROJECTION_HEADER) ?? undefined;\n if (projectionId !== undefined && projectionId !== this.#webSocketProjectionId) {\n return new Response(\"Unsupported WebSocket projection.\", { status: 400 });\n }\n\n const sessionId = this.parseSessionIdOrUndefined(sessionIdRaw);\n if (sessionId === undefined) {\n return new Response(\"This object does not serve that session.\", { status: 400 });\n }\n const trace = traceMetadataFromRequest(request);\n\n if (url.searchParams.get(EDA_WEB_SOCKET_STREAM_PARAM) === \"transcript\") {\n if (projectionId !== undefined || !this.#controller.supportsTranscriptWebSocket) {\n return new Response(\"Unsupported transcript stream.\", { status: 400 });\n }\n if (!this.transcriptFor(sessionId).enabled) {\n return new Response(\"Transcript is not enabled for this session.\", { status: 409 });\n }\n const pair = new WebSocketPair();\n this.ctx.acceptWebSocket(pair[1]);\n this.#controller.acceptTranscriptWebSocket({ sessionId, trace, webSocket: pair[1] });\n return new Response(null, { status: 101, webSocket: pair[0] });\n }\n\n const prepared = await this.#controller.prepareEventWebSocket({\n ...(afterSeq === undefined ? {} : { afterSeq: SequenceNumber.make(afterSeq) }),\n sessionId,\n trace,\n ...(projectionId === undefined ? {} : { projectionId }),\n });\n\n const pair = new WebSocketPair();\n const client = pair[0];\n const server = pair[1];\n this.ctx.acceptWebSocket(server);\n await this.#controller.acceptPreparedEventWebSocket({\n ...prepared,\n webSocket: server,\n });\n return new Response(null, { status: 101, webSocket: client });\n }\n\n async submit(\n raw: typeof SessionCommandInput.Encoded,\n ): Promise<typeof edaSessionRpc.submit.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionCommandInput)(raw);\n const committed = await this.#controller.submit({\n command: input.command,\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcCommittedDurableEvent(committed);\n }\n\n /** Reconcile lost grant replies from durable request and run facts. */\n async runRequestOutcome(\n raw: typeof SessionOutcomeInput.Encoded,\n ): Promise<typeof edaSessionRpc.runRequestOutcome.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionOutcomeInput)(raw);\n const result = await this.#controller.runRequestOutcome({\n requestId: Schema.decodeUnknownSync(RunRequestId)(input.requestId),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return Schema.encodeSync(edaSessionRpc.runRequestOutcome.output)(result);\n }\n\n /** Trusted RPC only: the calling Worker owns scheduler authentication and authorization. */\n async grantRun(\n raw: typeof SessionGrantRunInput.Encoded,\n ): Promise<typeof edaSessionRpc.grantRun.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionGrantRunInput)(raw);\n const command = input.command;\n const result = await this.#controller.grantRun({\n command,\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return Schema.encodeSync(edaSessionRpc.grantRun.output)(result);\n }\n\n async submitBatch(\n raw: typeof SessionBatchInput.Encoded,\n ): Promise<typeof edaSessionRpc.submitBatch.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionBatchInput)(raw);\n const committed = await this.#controller.submitBatch({\n items: input.items,\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return committed.map(encodeEdaRpcCommittedDurableEvent);\n }\n\n async submitAndBlock(\n raw: typeof SessionCommandInput.Encoded,\n ): Promise<typeof edaSessionRpc.submitAndBlock.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionCommandInput)(raw);\n const committed = await this.#controller.submitAndBlock({\n command: input.command,\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return Schema.encodeSync(edaSessionRpc.submitAndBlock.output)(committed);\n }\n\n async blockOnCommand(\n raw: typeof SessionBlockInput.Encoded,\n ): Promise<typeof edaSessionRpc.blockOnCommand.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionBlockInput)(raw);\n const committed = await this.#controller.blockOnCommand({\n ...(input.afterSeq === undefined ? {} : { afterSeq: SequenceNumber.make(input.afterSeq) }),\n commandId: CommandId.make(input.commandId),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return Schema.encodeSync(edaSessionRpc.blockOnCommand.output)(committed);\n }\n\n async snapshot(\n raw: typeof SessionScopedInput.Encoded,\n ): Promise<typeof edaSessionRpc.snapshot.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionScopedInput)(raw);\n const snapshot = await this.#controller.snapshot({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return Schema.encodeSync(edaSessionRpc.snapshot.output)(snapshot);\n }\n\n async messages(\n raw: typeof SessionScopedInput.Encoded,\n ): Promise<typeof edaSessionRpc.messages.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionScopedInput)(raw);\n const result = await this.#controller.messages({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return Schema.encodeSync(edaSessionRpc.messages.output)(result);\n }\n\n async webSocketMessage(webSocket: WebSocket, message: string | ArrayBuffer): Promise<void> {\n await this.#controller.webSocketMessage(webSocket, message);\n }\n\n async webSocketClose(\n webSocket: WebSocket,\n _code: number,\n _reason: string,\n _wasClean: boolean,\n ): Promise<void> {\n this.#controller.webSocketClose(webSocket);\n }\n\n async webSocketError(webSocket: WebSocket, _error: unknown): Promise<void> {\n this.#controller.webSocketError(webSocket);\n }\n\n async destroySession(\n raw: typeof SessionScopedInput.Encoded,\n ): Promise<typeof edaSessionRpc.destroySession.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionScopedInput)(raw);\n await this.#controller.destroy({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n await this.ctx.storage.deleteAlarm();\n await this.ctx.storage.deleteAll();\n // The wiped storage is a brand-new log: recreate the tables storage-only entry points read,\n // synchronously, so no other work observes the object without them.\n Effect.runSync(EDASessionController.migrate(toEDASessionStorage(this.ctx.storage)));\n }\n\n /**\n * Storage-only backfill entry point: advance pending transcript build work by bounded slices\n * and return the transcript status. It never starts the model runtime, reducers or sinks.\n */\n async transcriptStatus(\n raw: typeof SessionScopedInput.Encoded,\n ): Promise<typeof edaSessionRpc.transcriptStatus.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionScopedInput)(raw);\n const transcript = this.transcriptFor(this.parseSessionId(input.sessionId));\n if (!transcript.enabled) {\n return Schema.encodeSync(edaSessionRpc.transcriptStatus.output)(\n transcript.store.disabledStatus(),\n );\n }\n await transcript.builder.run(transcriptStatusMaxSlices);\n return Schema.encodeSync(edaSessionRpc.transcriptStatus.output)(transcript.store.status());\n }\n\n /** Runtime-free transcript window read for SSR and CLIs. Hosts authorize the caller. */\n async readTranscriptWindow(\n raw: typeof SessionTranscriptWindowInput.Encoded,\n ): Promise<typeof edaSessionRpc.readTranscriptWindow.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionTranscriptWindowInput)(raw);\n const transcript = this.transcriptFor(this.parseSessionId(input.sessionId));\n if (!transcript.enabled) {\n return Schema.encodeSync(edaSessionRpc.readTranscriptWindow.output)({ _tag: \"Disabled\" });\n }\n const limits = input.limits ?? defaultTranscriptWindowLimits;\n const window = transcript.store.readWindow(input.selector, {\n ...limits,\n maxRecords: Math.min(limits.maxRecords, transcriptWindowMaxRecords),\n maxBytes: Math.min(limits.maxBytes, transcriptWindowMaxBytes),\n });\n return Schema.encodeSync(edaSessionRpc.readTranscriptWindow.output)(\n Result.isSuccess(window)\n ? { _tag: \"Window\", window: window.success }\n : { _tag: \"CursorMismatch\", message: window.failure.message },\n );\n }\n\n /**\n * Runtime-free read of the body a transcript `Ref` names, resolved through the immutable log\n * (so it stays valid after projection rows are rebuilt). Hosts authorize the caller.\n */\n async readTranscriptBody(\n raw: typeof SessionTranscriptBodyInput.Encoded,\n ): Promise<typeof edaSessionRpc.readTranscriptBody.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionTranscriptBodyInput)(raw);\n const scope = this.transcriptReadScope(input.sessionId, input.incarnation);\n if (scope._tag !== \"Scope\") {\n return Schema.encodeSync(edaSessionRpc.readTranscriptBody.output)(scope);\n }\n const sessionId = scope.sessionId;\n const committed = readDurableObjectCommittedEvent(\n toEDASessionStorage(this.ctx.storage).sql,\n sessionId,\n input.source.seq,\n );\n const resolved =\n committed === undefined\n ? ({ _tag: \"Missing\", message: `no event at seq ${input.source.seq}` } as const)\n : resolveTranscriptBody(committed.event, input.source.path);\n return Schema.encodeSync(edaSessionRpc.readTranscriptBody.output)(\n resolved._tag === \"Found\"\n ? { _tag: \"Body\", value: resolved.value }\n : { _tag: \"NotFound\", message: resolved.message },\n );\n }\n\n /** Runtime-free read of a compaction summary's text. Hosts authorize the caller. */\n async readTranscriptSummary(\n raw: typeof SessionTranscriptSummaryInput.Encoded,\n ): Promise<typeof edaSessionRpc.readTranscriptSummary.output.Encoded> {\n const input = Schema.decodeUnknownSync(SessionTranscriptSummaryInput)(raw);\n const scope = this.transcriptReadScope(input.sessionId, input.incarnation);\n if (scope._tag !== \"Scope\") {\n return Schema.encodeSync(edaSessionRpc.readTranscriptSummary.output)(scope);\n }\n const artifact = readDurableObjectSummary(\n toEDASessionStorage(this.ctx.storage).sql,\n input.summaryId,\n );\n return Schema.encodeSync(edaSessionRpc.readTranscriptSummary.output)(\n artifact === undefined\n ? { _tag: \"NotFound\", message: `no summary ${input.summaryId}` }\n : {\n _tag: \"Summary\",\n summary: {\n compactionId: artifact.compactionId,\n summaryId: artifact.summaryId,\n text: artifact.text,\n sourceFromSeq: artifact.sourceFromSeq,\n sourceToSeq: artifact.sourceToSeq,\n },\n },\n );\n }\n\n /**\n * The alarm is shared by the runtime heartbeat and the transcript builder; see\n * `runEDASessionAlarm`.\n */\n async alarm(): Promise<void> {\n const transcript = this.maintainedTranscript();\n await runEDASessionAlarm({\n alarms: this.#alarms,\n now: () => Date.now(),\n runRuntime: async () => {\n const sessionId = this.sessionIdFromObjectName();\n await this.#controller.alarm(\n sessionId === undefined ? undefined : { sessionId, trace: makeRootEDATraceMetadata() },\n );\n },\n runBuilderSlices:\n transcript === undefined\n ? undefined\n : () => transcript.builder.runSlices(transcriptAlarmMaxSlices),\n maxBuilderFailures:\n this.#transcriptOptions.sliceLimits?.maxSliceFailures ??\n defaultTranscriptSliceLimits.maxSliceFailures,\n builderRetryDelayMs: transcriptBuilderRetryDelayMs,\n onBuilderFailure: ({ error, consecutiveFailures, stopped }) => {\n console.error(\"EDA transcript builder slice failed\", error);\n if (stopped) {\n transcript?.store.alert({\n _tag: \"BuilderStopped\",\n consecutiveFailures,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n },\n });\n }\n\n private transcriptOptions(\n options: EDASessionTranscriptOptions | undefined,\n ): DurableObjectTranscriptOptions {\n const key = options?.adoptIncarnationKey;\n return {\n definitions: options?.definitions ?? {\n current: Effect.runSync(makeTranscriptDefinition(emptyTranscriptMappings)),\n },\n ...(key === undefined ? {} : { adoptIncarnation: { key, kv: this.ctx.storage.kv } }),\n // Validated here so a misconfigured host fails at construction, not on a failing slice.\n sliceLimits: resolveTranscriptSliceLimits(options?.sliceLimits),\n observer: {\n onAlert:\n options?.onAlert ??\n ((alert) => console.error(\"EDA transcript alert\", JSON.stringify(alert))),\n onPendingWork: () => {\n const builder = this.maintainedTranscript()?.builder;\n if (builder !== undefined) this.ctx.waitUntil(builder.kick());\n },\n onServedChanged: () => {\n // A behind active version reached the head, or the active version froze: sockets re-read.\n this.ctx.waitUntil(this.#controller.refreshTranscriptWebSockets());\n },\n onActivated: (activation) => {\n // Connected transcript sockets get one complete replacement in the new version.\n this.ctx.waitUntil(this.#controller.refreshTranscriptWebSockets());\n options?.onActivated?.(activation);\n },\n },\n };\n }\n\n /** This object's transcript store as the port transcript sockets read, resolved per call. */\n private transcriptPort(): EDATranscriptStoreShape {\n const port = () => this.maintainedTranscript()?.store.port;\n const unbound = () =>\n Effect.fail(\n new EDASessionStoreError({\n message:\n this.objectTranscript()?.enabled === false\n ? \"EDA transcript is not enabled for this session\"\n : \"EDA transcript read before the session was named\",\n }),\n );\n return {\n readTranscriptWindow: (selector, limits) =>\n Effect.suspend(() => port()?.readTranscriptWindow(selector, limits) ?? unbound()),\n readTranscriptMeta: Effect.suspend(() => port()?.readTranscriptMeta ?? unbound()),\n };\n }\n\n /**\n * The session and log a body or summary read may serve: pinned to the session this object's\n * transcript serves, like window reads, and to the incarnation the caller's reference names.\n */\n private transcriptReadScope(\n rawSessionId: string,\n incarnation: TranscriptLogIncarnation,\n ):\n | { readonly _tag: \"Scope\"; readonly sessionId: SessionId }\n | { readonly _tag: \"Disabled\" }\n | { readonly _tag: \"IncarnationMismatch\"; readonly incarnation: TranscriptLogIncarnation } {\n const transcript = this.transcriptFor(this.parseSessionId(rawSessionId));\n // Outside the rollout nothing is initialized or minted, and no reference was ever served.\n if (!transcript.enabled) return { _tag: \"Disabled\" };\n const current = transcript.store.incarnation();\n return incarnation === current\n ? { _tag: \"Scope\", sessionId: transcript.sessionId }\n : { _tag: \"IncarnationMismatch\", incarnation: current };\n }\n\n /** The transcript service of this object's session, created on first use. */\n private transcriptFor(sessionId: SessionId) {\n if (this.#transcript !== undefined) {\n if (this.#transcript.sessionId !== sessionId) {\n throw new Error(\n `EDA transcript is scoped to session ${this.#transcript.sessionId}; received ${sessionId}`,\n );\n }\n return this.#transcript;\n }\n const store = new DurableObjectTranscriptStore({\n sessionId,\n storage: toEDASessionStorage(this.ctx.storage),\n transcript: this.#transcriptOptions,\n });\n const builder = new DurableObjectTranscriptBuilder(store, () =>\n this.#alarms.request(\"transcript\", Date.now()),\n );\n this.#transcript = { sessionId, enabled: this.#transcriptEnabled(sessionId), store, builder };\n return this.#transcript;\n }\n\n /** The transcript service when this object's session is enabled for maintenance. */\n private maintainedTranscript() {\n const transcript = this.objectTranscript();\n return transcript?.enabled === true ? transcript : undefined;\n }\n\n /** The transcript service when the object is named by a valid session id. */\n private objectTranscript() {\n if (this.#transcript !== undefined) return this.#transcript;\n const name = this.ctx.id.name;\n return name !== undefined && Schema.is(SessionId)(name)\n ? this.transcriptFor(SessionId.make(name))\n : undefined;\n }\n\n /** `parseSessionId` for request paths that answer a foreign session with a 400. */\n private parseSessionIdOrUndefined(input: string): SessionId | undefined {\n if (!Schema.is(SessionId)(input)) return undefined;\n const name = this.ctx.id.name;\n if (name !== undefined && name !== input) return undefined;\n const bound = this.#transcript?.sessionId;\n return bound === undefined || bound === input ? SessionId.make(input) : undefined;\n }\n\n private parseSessionId(input: string): SessionId {\n const sessionId = SessionId.make(input);\n const objectName = this.ctx.id.name;\n if (objectName !== undefined && objectName !== sessionId) {\n throw new Error(`EDA Durable Object name ${objectName} cannot serve session ${sessionId}`);\n }\n return sessionId;\n }\n\n private sessionIdFromObjectName(): SessionId | undefined {\n return this.ctx.id.name === undefined ? undefined : SessionId.make(this.ctx.id.name);\n }\n}\n\nconst toEDASessionStorage = (storage: DurableObjectStorage): EDASessionDurableObjectStorage => {\n // SAFETY: the adapter interface is the exact SQL, transaction, and alarm subset\n // of DurableObjectStorage; its looser row generic also supports test implementations.\n return storage as EDASessionDurableObjectStorage;\n};\n\nconst decodeTraceMetadata = (input: unknown): EDATraceMetadata =>\n Schema.decodeUnknownSync(EDATraceMetadata)(input);\n\nconst traceMetadataFromRequest = (request: Request): EDATraceMetadata => {\n const parent = parseEDATraceparent(\n request.headers.get(\"traceparent\"),\n request.headers.get(\"tracestate\"),\n );\n return parent === null ? makeRootEDATraceMetadata() : makeEDATraceMetadataFromParent(parent);\n};\n\n/** Convenience config constructor for subclasses that need only provider/model ids. */\nexport const edaRuntimeConfig = (input: {\n readonly maxToolCallsPerRun?: number;\n readonly modelId: string;\n readonly provider: string;\n readonly systemPrompt?: string;\n}): EDARuntimeConfig => ({\n modelSelection: { modelId: input.modelId, provider: input.provider },\n ...(input.maxToolCallsPerRun === undefined\n ? {}\n : { maxToolCallsPerRun: input.maxToolCallsPerRun }),\n ...(input.systemPrompt === undefined ? {} : { systemPrompt: input.systemPrompt }),\n});\n\n/** Encode committed events into the existing structured-clone envelope. */\nexport const encodeEdaRpcCommittedDurableEvent = (\n entry: CommittedDurableEvent,\n): typeof edaSessionRpc.submit.output.Encoded => ({\n position: entry.position,\n event: encodeEdaRpcDurableEvent(durableEventEnvelope(entry.event)),\n});\n/** Encode query snapshots without asserting that encoded command values are decoded classes. */\nexport const encodeEdaRpcSessionSnapshot = Schema.encodeSync(edaSessionRpc.snapshot.output);\n"],"mappings":"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"}
|
|
@@ -24,6 +24,10 @@ interface AdoptedLogIncarnation {
|
|
|
24
24
|
* active version keeps serving and the backfill reports it.
|
|
25
25
|
* - `MissedSession`: the active version is one this release no longer carries, so its history is
|
|
26
26
|
* frozen behind the log and `current` is being built at once (§7 safety net).
|
|
27
|
+
* - `BuilderStopped`: the builder alarm failed `consecutiveFailures` times in a row, typically
|
|
28
|
+
* for a reason no build is charged for (cleanup or reconciliation, or a failure record storage
|
|
29
|
+
* refused), so the builder stopped scheduling itself. A later object start, `transcriptStatus`
|
|
30
|
+
* call or pending-work kick tries again, bounded the same way, and may raise it again.
|
|
27
31
|
*/
|
|
28
32
|
type TranscriptAlert = {
|
|
29
33
|
readonly _tag: "ProjectionFailed";
|
|
@@ -35,6 +39,10 @@ type TranscriptAlert = {
|
|
|
35
39
|
readonly _tag: "MissedSession";
|
|
36
40
|
readonly activeVersion: TranscriptVersion;
|
|
37
41
|
readonly currentVersion: TranscriptVersion;
|
|
42
|
+
} | {
|
|
43
|
+
readonly _tag: "BuilderStopped";
|
|
44
|
+
readonly consecutiveFailures: number;
|
|
45
|
+
readonly error: string;
|
|
38
46
|
};
|
|
39
47
|
/**
|
|
40
48
|
* Host hooks for transcript maintenance. Every hook runs only after the transaction that caused
|
|
@@ -49,6 +57,12 @@ interface DurableObjectTranscriptObserver {
|
|
|
49
57
|
}) => void;
|
|
50
58
|
/** Bounded builder or cleanup work is pending; the host schedules a slice. */
|
|
51
59
|
readonly onPendingWork?: () => void;
|
|
60
|
+
/**
|
|
61
|
+
* What reads serve changed without a publication, so connected clients need a re-read: an
|
|
62
|
+
* active version that was behind the head (a re-enabled session) reached it (`UpdatingHistory`
|
|
63
|
+
* becomes `Ready`), or the active version froze (it becomes `Degraded`).
|
|
64
|
+
*/
|
|
65
|
+
readonly onServedChanged?: () => void;
|
|
52
66
|
}
|
|
53
67
|
/** Bounds on one builder slice. */
|
|
54
68
|
interface DurableObjectTranscriptSliceLimits {
|
|
@@ -58,6 +72,12 @@ interface DurableObjectTranscriptSliceLimits {
|
|
|
58
72
|
readonly pageEvents: number;
|
|
59
73
|
/** Rows of retired versions deleted per slice. */
|
|
60
74
|
readonly maxDeletedRows: number;
|
|
75
|
+
/**
|
|
76
|
+
* Consecutive slices that may throw while advancing one build target before it is frozen as
|
|
77
|
+
* failed (and so never retried). The session object also stops its builder alarm after this
|
|
78
|
+
* many consecutive failed alarms.
|
|
79
|
+
*/
|
|
80
|
+
readonly maxSliceFailures: number;
|
|
61
81
|
}
|
|
62
82
|
/** Transcript maintenance configuration of one session store. */
|
|
63
83
|
interface DurableObjectTranscriptOptions {
|
|
@@ -112,4 +132,4 @@ interface DurableObjectSessionStoreOptions {
|
|
|
112
132
|
}
|
|
113
133
|
//#endregion
|
|
114
134
|
export { DurableObjectTranscriptObserver as a, TranscriptAlert as c, DurableObjectSqlStorage as i, AdoptedLogIncarnation as l, DurableObjectSessionStoreOptions as n, DurableObjectTranscriptOptions as o, DurableObjectSqlCursor as r, DurableObjectTranscriptSliceLimits as s, DurableObjectSessionStorage as t };
|
|
115
|
-
//# sourceMappingURL=durable-object-storage-
|
|
135
|
+
//# sourceMappingURL=durable-object-storage-BVsu5oAV.d.ts.map
|