@yielded/agent-platform-cloudflare 0.1.0-beta.166 → 0.1.0-beta.168
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/{Alarm-B0ODr1NE.mjs → Alarm-DxODgAZZ.mjs} +259 -159
- package/dist/Alarm-DxODgAZZ.mjs.map +1 -0
- package/dist/Alarm.d.mts +27 -36
- package/dist/Alarm.mjs +2 -2
- package/dist/BrowserCredentials-C5IWfi7f.d.mts +91 -0
- package/dist/BrowserCredentials.d.mts +2 -91
- package/dist/BrowserRestCapture.mjs +3 -3
- package/dist/BrowserRestCapture.mjs.map +1 -1
- package/dist/BrowserRestCrawl.mjs +6 -6
- package/dist/BrowserRestCrawl.mjs.map +1 -1
- package/dist/{BrowserSession-RfIzxGCH.mjs → BrowserSession-BfNmyayH.mjs} +5 -5
- package/dist/BrowserSession-BfNmyayH.mjs.map +1 -0
- package/dist/{BrowserSession-B969Xg-2.d.mts → BrowserSession-Bx0V1-6_.d.mts} +6 -3
- package/dist/BrowserSession.d.mts +1 -1
- package/dist/BrowserSession.mjs +2 -2
- package/dist/BrowserUse-AstK0jKX.mjs +1467 -0
- package/dist/BrowserUse-AstK0jKX.mjs.map +1 -0
- package/dist/BrowserUse.d.mts +83 -0
- package/dist/BrowserUse.mjs +2 -0
- package/dist/CloudflareBindings.d.mts +8 -1
- package/dist/CloudflareBindings.mjs +2 -2
- package/dist/CloudflareBindings.mjs.map +1 -1
- package/dist/{CloudflareBrowser-5an6m-Qb.mjs → CloudflareBrowser-Vt8qTdYW.mjs} +11 -13
- package/dist/CloudflareBrowser-Vt8qTdYW.mjs.map +1 -0
- package/dist/CloudflareBrowser.mjs +1 -1
- package/dist/CloudflareCodeMode.mjs +1 -1
- package/dist/CloudflareCodeMode.mjs.map +1 -1
- package/dist/{CloudflareConfig-qDrRLofL.d.mts → CloudflareConfig-C9MvPbX8.d.mts} +9 -11
- package/dist/CloudflareConfig.d.mts +1 -1
- package/dist/CloudflareConfig.mjs +9 -12
- package/dist/CloudflareConfig.mjs.map +1 -1
- package/dist/CloudflareMemory.d.mts +3 -25
- package/dist/CloudflareMemory.mjs +33 -31
- package/dist/CloudflareMemory.mjs.map +1 -1
- package/dist/CloudflareScheduling.mjs +8 -15
- package/dist/CloudflareScheduling.mjs.map +1 -1
- package/dist/CloudflareSubscriptions.mjs +9 -16
- package/dist/CloudflareSubscriptions.mjs.map +1 -1
- package/dist/CloudflareThreadClient-CZm0TvUe.mjs +588 -0
- package/dist/CloudflareThreadClient-CZm0TvUe.mjs.map +1 -0
- package/dist/CloudflareThreadClient-QsVzLGGZ.d.mts +2033 -0
- package/dist/CloudflareThreadClient.d.mts +2 -1796
- package/dist/CloudflareThreadClient.mjs +2 -382
- package/dist/{InteractiveBrowser-BbG3kGA9.mjs → InteractiveBrowser-LdKzQeLR.mjs} +18 -19
- package/dist/InteractiveBrowser-LdKzQeLR.mjs.map +1 -0
- package/dist/InteractiveBrowser.mjs +1 -1
- package/dist/{ThreadObject-BcfXbzSO.d.mts → ThreadObject-7s5XnlH-.d.mts} +103 -79
- package/dist/{ThreadObject-DHjrnlX4.mjs → ThreadObject-C0vrcDrw.mjs} +265 -66
- package/dist/ThreadObject-C0vrcDrw.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +2 -2
- package/dist/ThreadObject.mjs +2 -3
- package/dist/WakeScheduler.d.mts +6 -5
- package/dist/WakeScheduler.mjs +15 -11
- package/dist/WakeScheduler.mjs.map +1 -1
- package/dist/index.d.mts +7 -6
- package/dist/index.mjs +7 -6
- package/dist/page-function-CmUCcyNP.mjs +11 -0
- package/dist/page-function-CmUCcyNP.mjs.map +1 -0
- package/dist/{prepared-admission-DMX5p9dC.mjs → prepared-admission-3UUq0zth.mjs} +3 -3
- package/dist/{prepared-admission-DMX5p9dC.mjs.map → prepared-admission-3UUq0zth.mjs.map} +1 -1
- package/package.json +1 -1
- package/src/Alarm.ts +510 -279
- package/src/BrowserRestCapture.ts +2 -2
- package/src/BrowserRestCrawl.ts +10 -14
- package/src/BrowserSession.ts +10 -2
- package/src/BrowserUse.ts +1541 -0
- package/src/CloudflareBindings.ts +13 -4
- package/src/CloudflareCodeMode.ts +1 -1
- package/src/CloudflareConfig.ts +9 -12
- package/src/CloudflareMemory.ts +21 -28
- package/src/CloudflareScheduling.ts +7 -15
- package/src/CloudflareSubscriptions.ts +11 -20
- package/src/CloudflareThreadClient.ts +289 -9
- package/src/InteractiveBrowser.ts +325 -323
- package/src/ThreadObject.ts +172 -52
- package/src/WakeScheduler.ts +29 -18
- package/src/index.ts +1 -0
- package/src/internal/browser-dom.ts +845 -0
- package/src/internal/browser-file-selection.ts +8 -1
- package/src/internal/browser-jev-dom.ts +344 -0
- package/src/internal/browser-quick-action.ts +7 -8
- package/src/internal/browser-session-lifecycle.ts +2 -5
- package/src/internal/crypto.ts +41 -0
- package/src/internal/due-queue.ts +5 -69
- package/src/internal/layers.ts +100 -17
- package/src/internal/live-text-protocol.ts +51 -0
- package/src/internal/live-text.ts +256 -0
- package/src/internal/page-function.ts +12 -0
- package/src/internal/prepared-admission.ts +1 -1
- package/src/internal/progress-wait.ts +19 -24
- package/src/internal/transport.ts +1 -1
- package/dist/Alarm-B0ODr1NE.mjs.map +0 -1
- package/dist/BrowserSession-RfIzxGCH.mjs.map +0 -1
- package/dist/CloudflareBrowser-5an6m-Qb.mjs.map +0 -1
- package/dist/CloudflareThreadClient.mjs.map +0 -1
- package/dist/InteractiveBrowser-BbG3kGA9.mjs.map +0 -1
- package/dist/ThreadObject-DHjrnlX4.mjs.map +0 -1
package/src/ThreadObject.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
DurableAgentRuntime,
|
|
23
23
|
DurableRuntimeConfig,
|
|
24
24
|
RecoveryReport,
|
|
25
|
+
RecoveryBlocked,
|
|
25
26
|
type DurableSubmitAgent,
|
|
26
27
|
} from "@yielded/agent/durable-agent-runtime";
|
|
27
28
|
import { DurableRuntimeFailpointError } from "@yielded/agent/durable-failpoint";
|
|
@@ -48,18 +49,20 @@ import {
|
|
|
48
49
|
ThreadRead,
|
|
49
50
|
ThreadStore,
|
|
50
51
|
ThreadStoreError,
|
|
51
|
-
FenceRejected,
|
|
52
52
|
} from "@yielded/agent/thread-store";
|
|
53
|
+
import { WorkDiscoveryUnavailable } from "@yielded/agent/thread-work";
|
|
53
54
|
import { WakeScheduler } from "@yielded/agent/wake-scheduler";
|
|
54
|
-
import { Effect, Layer, Option, Schema, Stream } from "effect";
|
|
55
|
+
import { Effect, Layer, Option, Schema, Stream, type Scope } from "effect";
|
|
55
56
|
import {
|
|
56
57
|
DurableObject as EffectCfDurableObject,
|
|
58
|
+
DurableObjectAlarm,
|
|
57
59
|
DurableObjectState as EffectCfDurableObjectState,
|
|
58
60
|
WorkerEnvironment,
|
|
59
61
|
} from "effect-cf";
|
|
60
62
|
|
|
61
63
|
import {
|
|
62
64
|
ThreadMaintenance,
|
|
65
|
+
ThreadMaintenanceAlarm,
|
|
63
66
|
DurableAlarmError,
|
|
64
67
|
DurableAlarmService,
|
|
65
68
|
ThreadMutationGate,
|
|
@@ -84,6 +87,7 @@ import {
|
|
|
84
87
|
ProgressObserved,
|
|
85
88
|
ProgressCancelled,
|
|
86
89
|
SettlementReached,
|
|
90
|
+
SettlementRecordReached,
|
|
87
91
|
SubmissionStatusResponse,
|
|
88
92
|
SubmitSucceeded,
|
|
89
93
|
UnknownResolutionRecorded,
|
|
@@ -109,6 +113,8 @@ import {
|
|
|
109
113
|
type CloudflareDurableRuntimeServices,
|
|
110
114
|
type CloudflareBootstrapServices,
|
|
111
115
|
} from "./internal/layers.ts";
|
|
116
|
+
import { Request as WatchTextRequest } from "./internal/live-text-protocol.ts";
|
|
117
|
+
import { LiveTextHub } from "./internal/live-text.ts";
|
|
112
118
|
import { ProgressWaitRegistry } from "./internal/progress-wait.ts";
|
|
113
119
|
|
|
114
120
|
export {
|
|
@@ -146,11 +152,50 @@ export {
|
|
|
146
152
|
* `runRecovery({ threadId })` BEFORE that Thread's claim; old recovery cannot gate fresh dispatch.
|
|
147
153
|
*/
|
|
148
154
|
|
|
155
|
+
type ApplicationAlarmRegistration<ApplicationServices, EventServices, AlarmServices, AlarmError> =
|
|
156
|
+
DurableObjectAlarm.AlarmRegistration<
|
|
157
|
+
AlarmServices,
|
|
158
|
+
| RuntimeServices
|
|
159
|
+
| ApplicationServices
|
|
160
|
+
| EventServices
|
|
161
|
+
| NoInfer<AlarmServices>
|
|
162
|
+
| EffectCfDurableObjectState.DurableObjectState
|
|
163
|
+
| WorkerEnvironment
|
|
164
|
+
| Scope.Scope,
|
|
165
|
+
AlarmError
|
|
166
|
+
>;
|
|
167
|
+
|
|
149
168
|
/** Construction options for one deployed Thread Object class. */
|
|
150
|
-
export
|
|
169
|
+
export type Options<
|
|
151
170
|
ApplicationServices = never,
|
|
152
171
|
EventServices = never,
|
|
153
172
|
EventLayerError = never,
|
|
173
|
+
AlarmServices = never,
|
|
174
|
+
AlarmError = never,
|
|
175
|
+
> = BaseOptions<ApplicationServices, EventServices, EventLayerError, AlarmServices> &
|
|
176
|
+
([AlarmServices] extends [never]
|
|
177
|
+
? {
|
|
178
|
+
readonly alarms?: ApplicationAlarmRegistration<
|
|
179
|
+
ApplicationServices,
|
|
180
|
+
EventServices,
|
|
181
|
+
AlarmServices,
|
|
182
|
+
AlarmError
|
|
183
|
+
>;
|
|
184
|
+
}
|
|
185
|
+
: {
|
|
186
|
+
readonly alarms: ApplicationAlarmRegistration<
|
|
187
|
+
ApplicationServices,
|
|
188
|
+
EventServices,
|
|
189
|
+
AlarmServices,
|
|
190
|
+
AlarmError
|
|
191
|
+
>;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
interface BaseOptions<
|
|
195
|
+
ApplicationServices,
|
|
196
|
+
EventServices,
|
|
197
|
+
EventLayerError,
|
|
198
|
+
AlarmServices,
|
|
154
199
|
> extends CloudflareDurableRuntimeOptions {
|
|
155
200
|
/** Accept transient native RPC tracing through effect-cf; disabled by default. */
|
|
156
201
|
readonly rpcTracing?: boolean;
|
|
@@ -166,6 +211,7 @@ export interface Options<
|
|
|
166
211
|
EventLayerError,
|
|
167
212
|
| RuntimeServices
|
|
168
213
|
| ApplicationServices
|
|
214
|
+
| NoInfer<AlarmServices>
|
|
169
215
|
| EffectCfDurableObjectState.DurableObjectState
|
|
170
216
|
| WorkerEnvironment
|
|
171
217
|
>;
|
|
@@ -192,16 +238,28 @@ const isMutatingPortRequest = (request: PortRequest): boolean => {
|
|
|
192
238
|
case "LedgerRecordChildSettled":
|
|
193
239
|
case "StoreMaterialize":
|
|
194
240
|
case "StoreAppend":
|
|
241
|
+
case "StoreWorkRebuild":
|
|
242
|
+
case "StoreRangeSeal":
|
|
243
|
+
case "StoreRangeArchive":
|
|
244
|
+
case "SettlementPublish":
|
|
195
245
|
case "MessageDeliveryComplete":
|
|
196
246
|
return true;
|
|
197
247
|
case "LedgerInspectWorker":
|
|
198
248
|
case "LedgerLookup":
|
|
199
249
|
case "LedgerResolveAdmission":
|
|
250
|
+
case "LedgerFundingOwner":
|
|
200
251
|
case "StoreReadPage":
|
|
252
|
+
case "StoreReadPrompt":
|
|
253
|
+
case "StoreReadExecution":
|
|
254
|
+
case "StoreWorkPage":
|
|
201
255
|
case "StoreInspectTail":
|
|
202
256
|
case "StoreReadIdentity":
|
|
203
257
|
case "StoreCountPeerMessages":
|
|
204
258
|
case "StoreExport":
|
|
259
|
+
case "StoreRangePage":
|
|
260
|
+
case "StoreRangeVerify":
|
|
261
|
+
case "StoreVerification":
|
|
262
|
+
case "StoreWorkerCapacity":
|
|
205
263
|
case "MessageDeliveryList":
|
|
206
264
|
return false;
|
|
207
265
|
}
|
|
@@ -253,7 +311,7 @@ const utf8Bytes = (value: PersistedJson): number =>
|
|
|
253
311
|
* exempt: its accepted-work obligation already exists, and returning the original Receipt
|
|
254
312
|
* consumes no new quota. Refusals are typed `AdmissionLimitExceeded` and nothing is written.
|
|
255
313
|
*/
|
|
256
|
-
const gateAdmissionLimits = Effect.
|
|
314
|
+
const gateAdmissionLimits = Effect.fnUntraced(function* (
|
|
257
315
|
threadId: ThreadId,
|
|
258
316
|
request: {
|
|
259
317
|
readonly principal: SubmissionLookupByKey["principal"];
|
|
@@ -323,9 +381,7 @@ const passthroughSubmitAgent = (agentId: AgentId): DurableSubmitAgent<typeof Per
|
|
|
323
381
|
});
|
|
324
382
|
|
|
325
383
|
/** A physical owner may hold other Threads; an addressed request cannot act on their IDs. */
|
|
326
|
-
const lookupAddressedSubmission = Effect.
|
|
327
|
-
submissionId: SubmissionId,
|
|
328
|
-
) {
|
|
384
|
+
const lookupAddressedSubmission = Effect.fnUntraced(function* (submissionId: SubmissionId) {
|
|
329
385
|
const { threadId } = yield* ThreadObjectIdentity;
|
|
330
386
|
const ports = yield* ThreadObjectPorts;
|
|
331
387
|
const submission = yield* ports.lookupSubmission(submissionId);
|
|
@@ -336,9 +392,7 @@ const lookupAddressedSubmission = Effect.fn("ThreadObject.lookupAddressedSubmiss
|
|
|
336
392
|
return submission;
|
|
337
393
|
});
|
|
338
394
|
|
|
339
|
-
const requireSubmissionThread = Effect.
|
|
340
|
-
submissionId: SubmissionId,
|
|
341
|
-
) {
|
|
395
|
+
const requireSubmissionThread = Effect.fnUntraced(function* (submissionId: SubmissionId) {
|
|
342
396
|
const submission = yield* lookupAddressedSubmission(submissionId);
|
|
343
397
|
|
|
344
398
|
if (Option.isNone(submission))
|
|
@@ -348,9 +402,7 @@ const requireSubmissionThread = Effect.fn("ThreadObject.requireSubmissionThread"
|
|
|
348
402
|
});
|
|
349
403
|
});
|
|
350
404
|
|
|
351
|
-
const requireReceiptThread = Effect.
|
|
352
|
-
threadId: ThreadId,
|
|
353
|
-
) {
|
|
405
|
+
const requireReceiptThread = Effect.fnUntraced(function* (threadId: ThreadId) {
|
|
354
406
|
const identity = yield* ThreadObjectIdentity;
|
|
355
407
|
|
|
356
408
|
if (threadId !== identity.threadId)
|
|
@@ -359,6 +411,10 @@ const requireReceiptThread = Effect.fn("ThreadObject.requireReceiptThread")(func
|
|
|
359
411
|
|
|
360
412
|
const requirePortThread = (request: PortRequest) => {
|
|
361
413
|
switch (request._tag) {
|
|
414
|
+
case "SettlementPublish":
|
|
415
|
+
return requireReceiptThread(request.request.append.threadId).pipe(
|
|
416
|
+
Effect.andThen(requireSubmissionThread(request.request.submissionId)),
|
|
417
|
+
);
|
|
362
418
|
case "MessageDeliveryComplete":
|
|
363
419
|
return requireReceiptThread(request.key.ownerThreadId);
|
|
364
420
|
case "MessageDeliveryList":
|
|
@@ -372,6 +428,10 @@ const requirePortThread = (request: PortRequest) => {
|
|
|
372
428
|
return requireSubmissionThread(request.request.submissionId);
|
|
373
429
|
case "LedgerRecordChildSettled":
|
|
374
430
|
return requireSubmissionThread(request.request.parentSubmissionId);
|
|
431
|
+
case "LedgerFundingOwner":
|
|
432
|
+
return requireReceiptThread(request.request.threadId).pipe(
|
|
433
|
+
Effect.andThen(requireSubmissionThread(request.request.submissionId)),
|
|
434
|
+
);
|
|
375
435
|
case "LedgerInspectWorker":
|
|
376
436
|
case "LedgerStopWorker":
|
|
377
437
|
case "WorkerAdmit":
|
|
@@ -380,10 +440,20 @@ const requirePortThread = (request: PortRequest) => {
|
|
|
380
440
|
case "StoreMaterialize":
|
|
381
441
|
case "StoreAppend":
|
|
382
442
|
case "StoreReadPage":
|
|
443
|
+
case "StoreReadPrompt":
|
|
444
|
+
case "StoreReadExecution":
|
|
383
445
|
case "StoreInspectTail":
|
|
384
446
|
case "StoreReadIdentity":
|
|
385
447
|
case "StoreCountPeerMessages":
|
|
386
448
|
case "StoreExport":
|
|
449
|
+
case "StoreWorkPage":
|
|
450
|
+
case "StoreWorkRebuild":
|
|
451
|
+
case "StoreRangePage":
|
|
452
|
+
case "StoreRangeSeal":
|
|
453
|
+
case "StoreRangeArchive":
|
|
454
|
+
case "StoreRangeVerify":
|
|
455
|
+
case "StoreVerification":
|
|
456
|
+
case "StoreWorkerCapacity":
|
|
387
457
|
return requireReceiptThread(request.request.threadId);
|
|
388
458
|
}
|
|
389
459
|
request satisfies never;
|
|
@@ -392,22 +462,21 @@ const requirePortThread = (request: PortRequest) => {
|
|
|
392
462
|
/**
|
|
393
463
|
* Admit an already Schema-decoded request to a logical Thread in this physical owner.
|
|
394
464
|
* Custom hosts validate local placement before calling this Effect and provide their same
|
|
395
|
-
* runtime
|
|
465
|
+
* runtime, maintenance and alarm instances. The native endpoint uses this path too: queue limits,
|
|
396
466
|
* idempotent receipts and the pre-admission generation/alarm commit have one owner.
|
|
397
467
|
*/
|
|
398
|
-
export const submit = Effect.
|
|
399
|
-
threadId: ThreadId,
|
|
400
|
-
request: SubmitRequest,
|
|
401
|
-
) {
|
|
468
|
+
export const submit = Effect.fnUntraced(function* (threadId: ThreadId, request: SubmitRequest) {
|
|
402
469
|
const placement = yield* ThreadObjectPlacement;
|
|
403
470
|
|
|
404
471
|
if (!placement.ownsThread(threadId))
|
|
405
472
|
return yield* HostProtocolError.make({ message: "The Thread belongs to another Object" });
|
|
406
473
|
const mutations = yield* ThreadMutationGate;
|
|
407
474
|
const runtime = yield* DurableAgentRuntime;
|
|
475
|
+
const alarm = yield* DurableAlarmService;
|
|
408
476
|
|
|
409
477
|
yield* gateAdmissionLimits(threadId, request);
|
|
410
478
|
|
|
479
|
+
// The mutation gate pre-arms durable maintenance; publication keeps its own wake behavior.
|
|
411
480
|
return yield* mutations.withMutation(
|
|
412
481
|
runtime
|
|
413
482
|
.submit(passthroughSubmitAgent(request.agentId), request.inputPayload, {
|
|
@@ -424,7 +493,10 @@ export const submit = Effect.fn("ThreadObject.submit")(function* (
|
|
|
424
493
|
: { messageAdmission: request.messageAdmission }),
|
|
425
494
|
definitions: request.definitions,
|
|
426
495
|
})
|
|
427
|
-
.pipe(
|
|
496
|
+
.pipe(
|
|
497
|
+
alarm.withHintsDeferred,
|
|
498
|
+
Effect.tap(() => publishCommitted),
|
|
499
|
+
),
|
|
428
500
|
);
|
|
429
501
|
});
|
|
430
502
|
|
|
@@ -435,6 +507,18 @@ const submitEndpoint = (encoded: unknown): Effect.Effect<unknown, never, Endpoin
|
|
|
435
507
|
Effect.gen(function* () {
|
|
436
508
|
const identity = yield* ThreadObjectIdentity;
|
|
437
509
|
const receipt = yield* submit(identity.threadId, request);
|
|
510
|
+
const scheduling = yield* DurableObjectAlarm.ScheduleConfiguration;
|
|
511
|
+
|
|
512
|
+
if (scheduling.dispatchAfterEvent === true) {
|
|
513
|
+
const alarms = yield* DurableAlarmService;
|
|
514
|
+
|
|
515
|
+
// Make accepted work eligible for post-response dispatch without changing queue retry floors.
|
|
516
|
+
yield* alarms.scheduleNow.pipe(
|
|
517
|
+
Effect.catch((error) =>
|
|
518
|
+
Effect.logWarning("Post-admission maintenance hint failed", error),
|
|
519
|
+
),
|
|
520
|
+
);
|
|
521
|
+
}
|
|
438
522
|
|
|
439
523
|
return SubmitSucceeded.make({ receipt });
|
|
440
524
|
}),
|
|
@@ -472,6 +556,7 @@ const submissionStatusEndpoint = (
|
|
|
472
556
|
|
|
473
557
|
const awaitSettlementEndpoint = (
|
|
474
558
|
encoded: unknown,
|
|
559
|
+
includeRecord = false,
|
|
475
560
|
): Effect.Effect<unknown, never, EndpointServices> =>
|
|
476
561
|
decodeReceipt(encoded).pipe(
|
|
477
562
|
Effect.mapError(protocolFailure("The receipt could not be decoded")),
|
|
@@ -486,12 +571,23 @@ const awaitSettlementEndpoint = (
|
|
|
486
571
|
submissionId: receipt.submissionId,
|
|
487
572
|
}),
|
|
488
573
|
);
|
|
574
|
+
if (includeRecord)
|
|
575
|
+
yield* authorizer.authorize(
|
|
576
|
+
OperationAuthorizationRequest.make({
|
|
577
|
+
operation: "observe",
|
|
578
|
+
threadId: receipt.threadId,
|
|
579
|
+
}),
|
|
580
|
+
);
|
|
489
581
|
yield* requireReceiptThread(receipt.threadId);
|
|
490
582
|
yield* requireSubmissionThread(receipt.submissionId);
|
|
491
583
|
const runtime = yield* DurableAgentRuntime;
|
|
492
|
-
const settlement = yield* runtime.awaitSettlement(receipt);
|
|
493
584
|
|
|
494
|
-
|
|
585
|
+
if (includeRecord)
|
|
586
|
+
return SettlementRecordReached.make({
|
|
587
|
+
record: yield* runtime.awaitSettlementRecord(receipt),
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
return SettlementReached.make({ settlement: yield* runtime.awaitSettlement(receipt) });
|
|
495
591
|
}),
|
|
496
592
|
),
|
|
497
593
|
respond,
|
|
@@ -583,6 +679,25 @@ const observePageEndpoint = (encoded: unknown): Effect.Effect<unknown, never, En
|
|
|
583
679
|
Effect.flatMap(encodeResponse),
|
|
584
680
|
);
|
|
585
681
|
|
|
682
|
+
const watchTextEndpoint = (encoded: unknown): Effect.Effect<unknown, never, EndpointServices> =>
|
|
683
|
+
Schema.decodeUnknownEffect(WatchTextRequest)(encoded).pipe(
|
|
684
|
+
Effect.mapError(protocolFailure("The provisional text request could not be decoded")),
|
|
685
|
+
Effect.flatMap(() =>
|
|
686
|
+
Effect.gen(function* () {
|
|
687
|
+
const { threadId } = yield* ThreadObjectIdentity;
|
|
688
|
+
const authorizer = yield* OperationAuthorizer;
|
|
689
|
+
|
|
690
|
+
yield* authorizer.authorize(
|
|
691
|
+
OperationAuthorizationRequest.make({ operation: "observe", threadId }),
|
|
692
|
+
);
|
|
693
|
+
const hub = yield* LiveTextHub;
|
|
694
|
+
|
|
695
|
+
return yield* hub.open(threadId);
|
|
696
|
+
}),
|
|
697
|
+
),
|
|
698
|
+
Effect.catch((failure) => encodeResponse(HostFailed.make({ failure }))),
|
|
699
|
+
);
|
|
700
|
+
|
|
586
701
|
const abortEndpoint = (encoded: unknown): Effect.Effect<unknown, never, EndpointServices> =>
|
|
587
702
|
decodeAbortCommand(encoded).pipe(
|
|
588
703
|
Effect.mapError(protocolFailure("The abort command could not be decoded")),
|
|
@@ -696,10 +811,11 @@ export const AdminFailure = Schema.Union([
|
|
|
696
811
|
ThreadStoreError,
|
|
697
812
|
ThreadNotMaterialized,
|
|
698
813
|
AppendConflict,
|
|
699
|
-
FenceRejected,
|
|
700
814
|
DurableRuntimeFailpointError,
|
|
701
815
|
DurableAlarmError,
|
|
702
816
|
HostProtocolError,
|
|
817
|
+
RecoveryBlocked,
|
|
818
|
+
WorkDiscoveryUnavailable,
|
|
703
819
|
]);
|
|
704
820
|
|
|
705
821
|
export type AdminFailure = typeof AdminFailure.Type;
|
|
@@ -955,8 +1071,10 @@ export const ThreadRpcOperation = Schema.Literals([
|
|
|
955
1071
|
"submitEncoded",
|
|
956
1072
|
"submissionStatusEncoded",
|
|
957
1073
|
"awaitSettlementEncoded",
|
|
1074
|
+
"awaitSettlementRecordEncoded",
|
|
958
1075
|
"awaitProgressEncoded",
|
|
959
1076
|
"cancelProgressEncoded",
|
|
1077
|
+
"watchTextEncoded",
|
|
960
1078
|
"observePage",
|
|
961
1079
|
"abortEncoded",
|
|
962
1080
|
"resolveApprovalEncoded",
|
|
@@ -971,8 +1089,10 @@ const threadRpc = {
|
|
|
971
1089
|
submitEncoded: submitEndpoint,
|
|
972
1090
|
submissionStatusEncoded: submissionStatusEndpoint,
|
|
973
1091
|
awaitSettlementEncoded: awaitSettlementEndpoint,
|
|
1092
|
+
awaitSettlementRecordEncoded: (encoded) => awaitSettlementEndpoint(encoded, true),
|
|
974
1093
|
awaitProgressEncoded: awaitProgressEndpoint,
|
|
975
1094
|
cancelProgressEncoded: cancelProgressEndpoint,
|
|
1095
|
+
watchTextEncoded: watchTextEndpoint,
|
|
976
1096
|
observePage: observePageEndpoint,
|
|
977
1097
|
abortEncoded: abortEndpoint,
|
|
978
1098
|
resolveApprovalEncoded: resolveApprovalEndpoint,
|
|
@@ -991,7 +1111,7 @@ const threadRpc = {
|
|
|
991
1111
|
* The producer comes from the actual runtime configuration, never from caller input. These
|
|
992
1112
|
* guards supplement the existing current model/Tool and operation authorization policies.
|
|
993
1113
|
*/
|
|
994
|
-
export const handleRpc = Effect.
|
|
1114
|
+
export const handleRpc = Effect.fnUntraced(function* (
|
|
995
1115
|
threadId: ThreadId,
|
|
996
1116
|
operation: ThreadRpcOperation,
|
|
997
1117
|
encoded: unknown,
|
|
@@ -1007,15 +1127,11 @@ export const handleRpc = Effect.fn("ThreadObject.handleRpc")(function* (
|
|
|
1007
1127
|
);
|
|
1008
1128
|
});
|
|
1009
1129
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
// (at-least-once delivery), and the dirty generation retains a committed slot meanwhile.
|
|
1016
|
-
yield* maintenance.pass;
|
|
1017
|
-
},
|
|
1018
|
-
);
|
|
1130
|
+
/** Compose with application registrations on a shared physical object. */
|
|
1131
|
+
export const alarms = ThreadMaintenanceAlarm.handlers({
|
|
1132
|
+
"@yielded/agent/thread-maintenance": () =>
|
|
1133
|
+
Effect.flatMap(ThreadMaintenance, (maintenance) => Effect.asVoid(maintenance.pass)),
|
|
1134
|
+
});
|
|
1019
1135
|
|
|
1020
1136
|
const gateEndpoint: Effect.Effect<void, MaintenancePassFailure, EndpointServices> = Effect.gen(
|
|
1021
1137
|
function* () {
|
|
@@ -1067,13 +1183,18 @@ const effectCfPlatformLayer = (
|
|
|
1067
1183
|
|
|
1068
1184
|
/** The public endpoints and effect-cf invocation hook of one Thread Object instance. */
|
|
1069
1185
|
export interface Instance<EventServices = never> extends InstanceType<
|
|
1070
|
-
EffectCfDurableObject.DurableObjectClass<
|
|
1186
|
+
EffectCfDurableObject.DurableObjectClass<
|
|
1187
|
+
Record<never, never>,
|
|
1188
|
+
RuntimeServices | EventServices | ThreadMaintenanceAlarm
|
|
1189
|
+
>
|
|
1071
1190
|
> {
|
|
1072
1191
|
submitEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1073
1192
|
submissionStatusEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1074
1193
|
awaitSettlementEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1194
|
+
awaitSettlementRecordEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1075
1195
|
awaitProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1076
1196
|
cancelProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1197
|
+
watchTextEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1077
1198
|
observePage(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1078
1199
|
abortEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
1079
1200
|
resolveApprovalEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
@@ -1108,18 +1229,22 @@ export const make = <
|
|
|
1108
1229
|
ApplicationError,
|
|
1109
1230
|
EventServices = never,
|
|
1110
1231
|
EventLayerError = never,
|
|
1232
|
+
AlarmServices = never,
|
|
1233
|
+
AlarmError = never,
|
|
1111
1234
|
>(
|
|
1112
1235
|
applicationLayer: Layer.Layer<
|
|
1113
1236
|
CloudflareDurableRuntimeServices | ApplicationServices,
|
|
1114
1237
|
ApplicationError,
|
|
1115
1238
|
| CloudflareBootstrapServices
|
|
1239
|
+
| ThreadMaintenanceAlarm
|
|
1240
|
+
| NoInfer<AlarmServices>
|
|
1116
1241
|
| EffectCfDurableObjectState.DurableObjectState
|
|
1117
1242
|
| WorkerEnvironment
|
|
1118
1243
|
| DurableObjectContext
|
|
1119
1244
|
| ThreadObjectNamespace
|
|
1120
1245
|
>,
|
|
1121
|
-
options: Options<ApplicationServices, EventServices, EventLayerError>,
|
|
1122
|
-
): Class<ApplicationServices | EventServices> => {
|
|
1246
|
+
options: Options<ApplicationServices, EventServices, EventLayerError, AlarmServices, AlarmError>,
|
|
1247
|
+
): Class<ApplicationServices | EventServices | AlarmServices> => {
|
|
1123
1248
|
const application = applicationLayer.pipe(
|
|
1124
1249
|
Layer.provideMerge(layerConfig(options)),
|
|
1125
1250
|
Layer.provideMerge(effectCfPlatformLayer(options.namespaceBinding, options.rpcTracing)),
|
|
@@ -1131,7 +1256,10 @@ export const make = <
|
|
|
1131
1256
|
const runtime: Layer.Layer<
|
|
1132
1257
|
RuntimeServices | ApplicationServices,
|
|
1133
1258
|
ThreadObjectInitializationError | ApplicationError,
|
|
1134
|
-
EffectCfDurableObjectState.DurableObjectState
|
|
1259
|
+
| EffectCfDurableObjectState.DurableObjectState
|
|
1260
|
+
| WorkerEnvironment
|
|
1261
|
+
| ThreadMaintenanceAlarm
|
|
1262
|
+
| AlarmServices
|
|
1135
1263
|
> = Layer.effectContext(
|
|
1136
1264
|
Effect.gen(function* () {
|
|
1137
1265
|
const state = yield* EffectCfDurableObjectState.DurableObjectState;
|
|
@@ -1163,15 +1291,17 @@ export const make = <
|
|
|
1163
1291
|
RuntimeServices | ApplicationServices,
|
|
1164
1292
|
EventServices,
|
|
1165
1293
|
EventLayerError,
|
|
1166
|
-
typeof rpc
|
|
1294
|
+
typeof rpc,
|
|
1295
|
+
ThreadMaintenanceAlarm | AlarmServices
|
|
1167
1296
|
>;
|
|
1168
1297
|
|
|
1169
|
-
|
|
1298
|
+
return EffectCfDurableObject.make<
|
|
1170
1299
|
RuntimeServices | ApplicationServices,
|
|
1171
1300
|
ThreadObjectInitializationError | ApplicationError,
|
|
1172
1301
|
EventServices,
|
|
1173
1302
|
EventLayerError,
|
|
1174
|
-
typeof rpc
|
|
1303
|
+
typeof rpc,
|
|
1304
|
+
ThreadMaintenanceAlarm | AlarmServices
|
|
1175
1305
|
>(runtime, {
|
|
1176
1306
|
...(options.rpcTracing === true ? { rpcTracing: { service: options.namespaceBinding } } : {}),
|
|
1177
1307
|
...(options.eventLayer === undefined ? {} : { eventLayer: options.eventLayer }),
|
|
@@ -1179,20 +1309,10 @@ export const make = <
|
|
|
1179
1309
|
// in each bounded pass so cross-Object initialization cannot deadlock.
|
|
1180
1310
|
initialize: Effect.void,
|
|
1181
1311
|
rpc,
|
|
1182
|
-
|
|
1183
|
-
|
|
1312
|
+
alarms:
|
|
1313
|
+
options.alarms === undefined ? alarms : DurableObjectAlarm.mergeAll(options.alarms, alarms),
|
|
1314
|
+
// The named registration supplies its scheduler before the constructor builds application services.
|
|
1184
1315
|
// Upstream's conditional alarm-registration check cannot reduce over generic application
|
|
1185
1316
|
// services. Options and the rpc satisfies check above retain their Effect requirements.
|
|
1186
1317
|
} as NativeOptions);
|
|
1187
|
-
|
|
1188
|
-
// effect-cf's class type keeps `alarm` optional even when the handler option is present. This
|
|
1189
|
-
// concrete override reflects this factory's stronger contract while delegating execution to
|
|
1190
|
-
// the effect-cf runtime unchanged.
|
|
1191
|
-
class ThreadObject extends EffectCfThreadObject {
|
|
1192
|
-
override alarm(alarmInfo?: AlarmInvocationInfo): Promise<void> | void {
|
|
1193
|
-
return super.alarm?.(alarmInfo);
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
return ThreadObject;
|
|
1198
1318
|
};
|
package/src/WakeScheduler.ts
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
import { safeCauseMessage } from "./internal/boundary.ts";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Bounded in-memory wake buffer for same-incarnation
|
|
15
|
-
* hints are droppable by contract (consumers pair
|
|
14
|
+
* Bounded in-memory wake buffer for same-incarnation workers. Wake
|
|
15
|
+
* hints are droppable by contract (consumers pair hints with durable authority), so
|
|
16
16
|
* a full buffer slides out the oldest hint and an eviction simply loses the buffer — the
|
|
17
17
|
* poll interval and the persisted alarm keep liveness (persistence §14).
|
|
18
18
|
*/
|
|
@@ -28,14 +28,15 @@ class RemoteWakeDropped extends Schema.TaggedError<RemoteWakeDropped>()("RemoteW
|
|
|
28
28
|
/**
|
|
29
29
|
* The DC `WakeScheduler` (plan §1.4):
|
|
30
30
|
*
|
|
31
|
-
* - `notify(local)`
|
|
32
|
-
* earliest
|
|
31
|
+
* - `notify(local)` wakes progress and settlement registrations, publishes a worker hint, and
|
|
32
|
+
* schedules the earliest alarm. Processing an owned Thread defers immediate alarms until
|
|
33
|
+
* processing exits; pre-armed recovery deadlines remain active. Progress hints skip settlement
|
|
34
|
+
* registrations only.
|
|
33
35
|
* - `notify(remote)` → fire-and-forget `wake()` on the owning Object's stub with every error
|
|
34
36
|
* swallowed and logged: hints are droppable, and the target's own alarm/scan pairing (the
|
|
35
37
|
* maintenance pass re-polls unsettled children) guarantees liveness without this call.
|
|
36
|
-
* - `wakes` → the bounded sliding PubSub only.
|
|
37
|
-
*
|
|
38
|
-
* is the correctness path.
|
|
38
|
+
* - `wakes` → the bounded sliding PubSub only. Thread waiters use Scope-owned one-shot hubs;
|
|
39
|
+
* the settlement poll interval and persisted alarm retain liveness after hint loss.
|
|
39
40
|
*/
|
|
40
41
|
export const cloudflareWakeSchedulerLayer: Layer.Layer<
|
|
41
42
|
WakeScheduler,
|
|
@@ -48,18 +49,23 @@ export const cloudflareWakeSchedulerLayer: Layer.Layer<
|
|
|
48
49
|
const namespace = yield* ThreadObjectNamespace;
|
|
49
50
|
const hints = yield* PubSub.sliding<ThreadId>(WAKE_BUFFER_CAPACITY);
|
|
50
51
|
const progress = yield* makeWakeSubscriptionHub;
|
|
52
|
+
const settlements = yield* makeWakeSubscriptionHub;
|
|
51
53
|
|
|
52
54
|
yield* Effect.addFinalizer(() => PubSub.shutdown(hints));
|
|
53
55
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
+
const scheduleLocal = alarm.scheduleNow.pipe(
|
|
57
|
+
Effect.catch((error) =>
|
|
58
|
+
Effect.logWarning("CloudflareWakeScheduler: local alarm wake failed", error),
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const notifyLocal = (threadId: ThreadId, kind?: "progress") =>
|
|
63
|
+
(kind === "progress"
|
|
64
|
+
? progress.notify(threadId)
|
|
65
|
+
: settlements.notify(threadId).pipe(Effect.andThen(progress.notify(threadId)))
|
|
66
|
+
).pipe(
|
|
56
67
|
Effect.andThen(PubSub.publish(hints, threadId)),
|
|
57
|
-
Effect.andThen(
|
|
58
|
-
Effect.catch((error) =>
|
|
59
|
-
// `notify` never fails by contract; a failed alarm write degrades to "hint lost"
|
|
60
|
-
// and the pass re-arm (or the next entry point's pre-arm) restores the invariant.
|
|
61
|
-
Effect.logWarning("CloudflareWakeScheduler: local alarm wake failed", error),
|
|
62
|
-
),
|
|
68
|
+
Effect.andThen(scheduleLocal),
|
|
63
69
|
Effect.asVoid,
|
|
64
70
|
);
|
|
65
71
|
|
|
@@ -82,9 +88,14 @@ export const cloudflareWakeSchedulerLayer: Layer.Layer<
|
|
|
82
88
|
);
|
|
83
89
|
|
|
84
90
|
return WakeScheduler.of({
|
|
85
|
-
|
|
86
|
-
placement.ownsThread(threadId)
|
|
87
|
-
|
|
91
|
+
withProcessing: (threadId, body) =>
|
|
92
|
+
placement.ownsThread(threadId)
|
|
93
|
+
? alarm.withHintsDeferred(body).pipe(Effect.ensuring(scheduleLocal))
|
|
94
|
+
: body,
|
|
95
|
+
notify: (threadId, kind) =>
|
|
96
|
+
placement.ownsThread(threadId) ? notifyLocal(threadId, kind) : notifyRemote(threadId),
|
|
97
|
+
subscribe: (threadId, kind) =>
|
|
98
|
+
(kind === "settlement" ? settlements : progress).subscribe(threadId),
|
|
88
99
|
wakes: Stream.fromPubSub(hints),
|
|
89
100
|
});
|
|
90
101
|
}),
|
package/src/index.ts
CHANGED
|
@@ -12,3 +12,4 @@ export * as WakeScheduler from "./WakeScheduler.ts";
|
|
|
12
12
|
export * as CloudflareAiGateway from "./CloudflareAiGateway.ts";
|
|
13
13
|
export * as BrowserSession from "./BrowserSession.ts";
|
|
14
14
|
export * as BrowserCredentials from "./BrowserCredentials.ts";
|
|
15
|
+
export * as BrowserUse from "./BrowserUse.ts";
|