@yielded/agent-platform-cloudflare 0.1.0-beta.172 → 0.1.0-beta.174
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-BMEAZo3p.mjs → Alarm-4J9B8Fg8.mjs} +54 -32
- package/dist/Alarm-4J9B8Fg8.mjs.map +1 -0
- package/dist/Alarm.mjs +1 -1
- package/dist/BrowserRestCapture.mjs.map +1 -1
- package/dist/BrowserRestCrawl.mjs.map +1 -1
- package/dist/{BrowserSession-Bx0V1-6_.d.mts → BrowserSession-Cojp7nth.d.mts} +3 -2
- package/dist/{BrowserSession-BfNmyayH.mjs → BrowserSession-aovMjfL4.mjs} +14 -14
- package/dist/BrowserSession-aovMjfL4.mjs.map +1 -0
- package/dist/BrowserSession.d.mts +1 -1
- package/dist/BrowserSession.mjs +2 -2
- package/dist/BrowserUse.d.mts +40 -64
- package/dist/BrowserUse.mjs +223 -2
- package/dist/BrowserUse.mjs.map +1 -0
- package/dist/CloudflareBrowser-Vt8qTdYW.mjs.map +1 -1
- package/dist/CloudflareCodeMode.mjs.map +1 -1
- package/dist/CloudflareScheduling.mjs +9 -9
- package/dist/CloudflareScheduling.mjs.map +1 -1
- package/dist/CloudflareSubscriptions.mjs +15 -15
- package/dist/CloudflareSubscriptions.mjs.map +1 -1
- package/dist/{CloudflareThreadClient-DgZVrZzF.mjs → CloudflareThreadClient-DU548EEj.mjs} +42 -39
- package/dist/CloudflareThreadClient-DU548EEj.mjs.map +1 -0
- package/dist/{CloudflareThreadClient-DuRG0MLd.d.mts → CloudflareThreadClient-lTm6baDP.d.mts} +353 -353
- package/dist/CloudflareThreadClient.d.mts +1 -1
- package/dist/CloudflareThreadClient.mjs +1 -1
- package/dist/{InteractiveBrowser-LdKzQeLR.mjs → InteractiveBrowser-BHNlwjZL.mjs} +40 -31
- package/dist/InteractiveBrowser-BHNlwjZL.mjs.map +1 -0
- package/dist/InteractiveBrowser.mjs +1 -1
- package/dist/{ThreadObject-CugPnWsJ.d.mts → ThreadObject-BLYgzcdT.d.mts} +6 -6
- package/dist/{ThreadObject-CGIaz6Jv.mjs → ThreadObject-C2LF3btz.mjs} +13 -14
- package/dist/ThreadObject-C2LF3btz.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/ThreadObject.mjs +1 -1
- package/dist/WakeScheduler.d.mts +1 -1
- package/dist/WakeScheduler.mjs +8 -3
- package/dist/WakeScheduler.mjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +5 -5
- package/dist/{prepared-admission-CSdzFM_v.mjs → prepared-admission-BoUPHSNQ.mjs} +2 -2
- package/dist/{prepared-admission-CSdzFM_v.mjs.map → prepared-admission-BoUPHSNQ.mjs.map} +1 -1
- package/package.json +1 -1
- package/src/Alarm.ts +82 -39
- package/src/BrowserRestCapture.ts +1 -0
- package/src/BrowserRestCrawl.ts +1 -0
- package/src/BrowserSession.ts +3 -2
- package/src/BrowserUse.ts +287 -1504
- package/src/CloudflareCodeMode.ts +6 -0
- package/src/CloudflareScheduling.ts +103 -103
- package/src/CloudflareSubscriptions.ts +192 -199
- package/src/CloudflareThreadClient.ts +256 -268
- package/src/InteractiveBrowser.ts +43 -17
- package/src/ThreadObject.ts +35 -24
- package/src/WakeScheduler.ts +12 -9
- package/src/internal/browser-binding.ts +8 -0
- package/src/internal/browser-credentials.ts +18 -13
- package/src/internal/browser-file-selection.ts +13 -2
- package/src/internal/browser-quick-action.ts +1 -0
- package/src/internal/browser-session-lifecycle.ts +32 -36
- package/src/internal/due-queue.ts +38 -19
- package/src/internal/layers.ts +53 -53
- package/dist/Alarm-BMEAZo3p.mjs.map +0 -1
- package/dist/BrowserSession-BfNmyayH.mjs.map +0 -1
- package/dist/BrowserUse-Bc2JdLW2.mjs +0 -1528
- package/dist/BrowserUse-Bc2JdLW2.mjs.map +0 -1
- package/dist/CloudflareThreadClient-DgZVrZzF.mjs.map +0 -1
- package/dist/InteractiveBrowser-LdKzQeLR.mjs.map +0 -1
- package/dist/ThreadObject-CGIaz6Jv.mjs.map +0 -1
- package/dist/page-function-CmUCcyNP.mjs +0 -11
- package/dist/page-function-CmUCcyNP.mjs.map +0 -1
- package/src/internal/browser-dom.ts +0 -886
- package/src/internal/browser-jev-dom.ts +0 -362
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WakeScheduler.mjs","names":[],"sources":["../src/WakeScheduler.ts"],"sourcesContent":["import { type ThreadId } from \"@yielded/agent/identifiers\";\nimport { makeWakeSubscriptionHub, WakeScheduler } from \"@yielded/agent/wake-scheduler\";\nimport { Effect, Layer, PubSub, Schema, Stream } from \"effect\";\n\nimport { DurableAlarmService } from \"./Alarm.ts\";\nimport {\n callThreadObject,\n ThreadObjectPlacement,\n ThreadObjectNamespace,\n} from \"./CloudflareBindings.ts\";\nimport { safeCauseMessage } from \"./internal/boundary.ts\";\n\n/**\n * Bounded in-memory wake buffer for same-incarnation workers. Wake\n * hints are droppable by contract (consumers pair hints with durable authority), so\n * a full buffer slides out the oldest hint and an eviction simply loses the buffer — the\n * poll interval and the persisted alarm keep liveness (persistence §14).\n */\nconst WAKE_BUFFER_CAPACITY = 1_024;\n\n/** A remote wake stub call failed; always swallowed and logged (hints are droppable). */\nclass RemoteWakeDropped extends Schema.TaggedError<RemoteWakeDropped>()(\"RemoteWakeDropped\", {\n threadId: Schema.String,\n message: Schema.String,\n cause: Schema.optionalKey(Schema.Defect()),\n}) {}\n\n/**\n * The DC `WakeScheduler` (plan §1.4):\n *\n * - `notify(local)` wakes progress and settlement registrations, publishes a worker hint, and\n * schedules the earliest alarm. Processing an owned Thread defers immediate alarms until\n * processing exits; pre-armed recovery deadlines remain active. Progress hints skip settlement\n * registrations
|
|
1
|
+
{"version":3,"file":"WakeScheduler.mjs","names":[],"sources":["../src/WakeScheduler.ts"],"sourcesContent":["import { CurrentNativeSource } from \"@yielded/agent-storage-cloudflare/port-routing\";\nimport { type ThreadId } from \"@yielded/agent/identifiers\";\nimport { makeWakeSubscriptionHub, WakeScheduler } from \"@yielded/agent/wake-scheduler\";\nimport { Effect, Layer, PubSub, Schema, Stream } from \"effect\";\n\nimport { DurableAlarmService } from \"./Alarm.ts\";\nimport {\n callThreadObject,\n ThreadObjectPlacement,\n ThreadObjectNamespace,\n} from \"./CloudflareBindings.ts\";\nimport { safeCauseMessage } from \"./internal/boundary.ts\";\n\n/**\n * Bounded in-memory wake buffer for same-incarnation workers. Wake\n * hints are droppable by contract (consumers pair hints with durable authority), so\n * a full buffer slides out the oldest hint and an eviction simply loses the buffer — the\n * poll interval and the persisted alarm keep liveness (persistence §14).\n */\nconst WAKE_BUFFER_CAPACITY = 1_024;\n\n/** A remote wake stub call failed; always swallowed and logged (hints are droppable). */\nclass RemoteWakeDropped extends Schema.TaggedError<RemoteWakeDropped>()(\"RemoteWakeDropped\", {\n threadId: Schema.String,\n message: Schema.String,\n cause: Schema.optionalKey(Schema.Defect()),\n}) {}\n\n/**\n * The DC `WakeScheduler` (plan §1.4):\n *\n * - `notify(local)` wakes progress and settlement registrations, publishes a worker hint, and\n * schedules the earliest alarm. Processing an owned Thread defers immediate alarms until\n * processing exits; pre-armed recovery deadlines remain active. Progress hints skip settlement\n * registrations; single-Thread native progress also omits the redundant worker hint.\n * - `notify(remote)` → fire-and-forget `wake()` on the owning Object's stub with every error\n * swallowed and logged: hints are droppable, and the target's own alarm/scan pairing (the\n * maintenance pass re-polls unsettled children) guarantees liveness without this call.\n * - `wakes` → the bounded sliding PubSub only. Thread waiters use Scope-owned one-shot hubs;\n * the settlement poll interval and persisted alarm retain liveness after hint loss.\n */\nexport const cloudflareWakeSchedulerLayer: Layer.Layer<\n WakeScheduler,\n never,\n DurableAlarmService | ThreadObjectPlacement | ThreadObjectNamespace\n> = Layer.effect(WakeScheduler)(\n Effect.gen(function* () {\n const alarm = yield* DurableAlarmService;\n const placement = yield* ThreadObjectPlacement;\n const namespace = yield* ThreadObjectNamespace;\n const hints = yield* PubSub.sliding<ThreadId>(WAKE_BUFFER_CAPACITY);\n const progress = yield* makeWakeSubscriptionHub;\n const settlements = yield* makeWakeSubscriptionHub;\n\n yield* Effect.addFinalizer(() => PubSub.shutdown(hints));\n\n const scheduleLocal = alarm.scheduleNow.pipe(\n Effect.catch((error) =>\n Effect.logWarning(\"CloudflareWakeScheduler: local alarm wake failed\", error),\n ),\n );\n\n const notifyLocal = Effect.fnUntraced(function* (threadId: ThreadId, kind?: \"progress\") {\n yield* kind === \"progress\"\n ? progress.notify(threadId)\n : settlements.notify(threadId).pipe(Effect.andThen(progress.notify(threadId)));\n\n // Progress waiters still observe every hint. The single-Thread maintenance loop\n // already awaits the running native fiber; its source commits enroll auxiliary work.\n if (kind !== \"progress\" || placement.threadId === undefined || !(yield* CurrentNativeSource))\n yield* PubSub.publish(hints, threadId);\n yield* scheduleLocal;\n });\n\n const notifyRemote = (threadId: ThreadId) =>\n callThreadObject(\n threadId,\n (target) => target.wake(),\n (cause) =>\n RemoteWakeDropped.make({\n threadId,\n message: safeCauseMessage(cause, \"The remote wake failed without a diagnostic\"),\n cause,\n }),\n ).pipe(\n Effect.provideService(ThreadObjectNamespace, namespace),\n Effect.catch((error) =>\n Effect.logWarning(`CloudflareWakeScheduler: remote wake of ${threadId} dropped`, error),\n ),\n Effect.asVoid,\n );\n\n return WakeScheduler.of({\n withProcessing: (threadId, body) =>\n placement.ownsThread(threadId)\n ? alarm.withHintsDeferred(body).pipe(Effect.ensuring(scheduleLocal))\n : body,\n notify: (threadId, kind) =>\n placement.ownsThread(threadId) ? notifyLocal(threadId, kind) : notifyRemote(threadId),\n subscribe: (threadId, kind) =>\n (kind === \"settlement\" ? settlements : progress).subscribe(threadId),\n wakes: Stream.fromPubSub(hints),\n });\n }),\n);\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,MAAM,uBAAuB;;AAG7B,IAAM,oBAAN,cAAgC,OAAO,YAA+B,CAAC,CAAC,qBAAqB;CAC3F,UAAU,OAAO;CACjB,SAAS,OAAO;CAChB,OAAO,OAAO,YAAY,OAAO,OAAO,CAAC;AAC3C,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;;;;AAeJ,MAAa,+BAIT,MAAM,OAAO,aAAa,CAAC,CAC7B,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,OAAO;CACrB,MAAM,YAAY,OAAO;CACzB,MAAM,YAAY,OAAO;CACzB,MAAM,QAAQ,OAAO,OAAO,QAAkB,oBAAoB;CAClE,MAAM,WAAW,OAAO;CACxB,MAAM,cAAc,OAAO;CAE3B,OAAO,OAAO,mBAAmB,OAAO,SAAS,KAAK,CAAC;CAEvD,MAAM,gBAAgB,MAAM,YAAY,KACtC,OAAO,OAAO,UACZ,OAAO,WAAW,oDAAoD,KAAK,CAC7E,CACF;CAEA,MAAM,cAAc,OAAO,WAAW,WAAW,UAAoB,MAAmB;EACtF,OAAO,SAAS,aACZ,SAAS,OAAO,QAAQ,IACxB,YAAY,OAAO,QAAQ,CAAC,CAAC,KAAK,OAAO,QAAQ,SAAS,OAAO,QAAQ,CAAC,CAAC;EAI/E,IAAI,SAAS,cAAc,UAAU,aAAa,KAAA,KAAa,EAAE,OAAO,sBACtE,OAAO,OAAO,QAAQ,OAAO,QAAQ;EACvC,OAAO;CACT,CAAC;CAED,MAAM,gBAAgB,aACpB,iBACE,WACC,WAAW,OAAO,KAAK,IACvB,UACC,kBAAkB,KAAK;EACrB;EACA,SAAS,iBAAiB,OAAO,6CAA6C;EAC9E;CACF,CAAC,CACL,CAAC,CAAC,KACA,OAAO,eAAe,uBAAuB,SAAS,GACtD,OAAO,OAAO,UACZ,OAAO,WAAW,2CAA2C,SAAS,WAAW,KAAK,CACxF,GACA,OAAO,MACT;CAEF,OAAO,cAAc,GAAG;EACtB,iBAAiB,UAAU,SACzB,UAAU,WAAW,QAAQ,IACzB,MAAM,kBAAkB,IAAI,CAAC,CAAC,KAAK,OAAO,SAAS,aAAa,CAAC,IACjE;EACN,SAAS,UAAU,SACjB,UAAU,WAAW,QAAQ,IAAI,YAAY,UAAU,IAAI,IAAI,aAAa,QAAQ;EACtF,YAAY,UAAU,UACnB,SAAS,eAAe,cAAc,SAAA,CAAU,UAAU,QAAQ;EACrE,OAAO,OAAO,WAAW,KAAK;CAChC,CAAC;AACH,CAAC,CACH"}
|
package/dist/index.d.mts
CHANGED
|
@@ -3,14 +3,14 @@ import { o as CloudflareConfig_d_exports } from "./CloudflareConfig-C9MvPbX8.mjs
|
|
|
3
3
|
import { t as Alarm_d_exports } from "./Alarm.mjs";
|
|
4
4
|
import { r as BrowserCredentials_d_exports } from "./BrowserCredentials-C5IWfi7f.mjs";
|
|
5
5
|
import { t as CloudflareBrowser_d_exports } from "./CloudflareBrowser-gu7gma0Y.mjs";
|
|
6
|
-
import { a as BrowserSession_d_exports } from "./BrowserSession-
|
|
6
|
+
import { a as BrowserSession_d_exports } from "./BrowserSession-Cojp7nth.mjs";
|
|
7
7
|
import { t as BrowserUse_d_exports } from "./BrowserUse.mjs";
|
|
8
8
|
import { t as CloudflareAiGateway_d_exports } from "./CloudflareAiGateway.mjs";
|
|
9
9
|
import { t as CloudflareCodeMode_d_exports } from "./CloudflareCodeMode.mjs";
|
|
10
10
|
import { t as CloudflareMemory_d_exports } from "./CloudflareMemory.mjs";
|
|
11
11
|
import { t as CloudflareScheduling_d_exports } from "./CloudflareScheduling.mjs";
|
|
12
12
|
import { t as CloudflareSubscriptions_d_exports } from "./CloudflareSubscriptions.mjs";
|
|
13
|
-
import { m as CloudflareThreadClient_d_exports } from "./CloudflareThreadClient-
|
|
14
|
-
import { f as ThreadObject_d_exports } from "./ThreadObject-
|
|
13
|
+
import { m as CloudflareThreadClient_d_exports } from "./CloudflareThreadClient-lTm6baDP.mjs";
|
|
14
|
+
import { f as ThreadObject_d_exports } from "./ThreadObject-BLYgzcdT.mjs";
|
|
15
15
|
import { t as WakeScheduler_d_exports } from "./WakeScheduler.mjs";
|
|
16
16
|
export { Alarm_d_exports as Alarm, BrowserCredentials_d_exports as BrowserCredentials, BrowserSession_d_exports as BrowserSession, BrowserUse_d_exports as BrowserUse, CloudflareAiGateway_d_exports as CloudflareAiGateway, CloudflareBindings_d_exports as CloudflareBindings, CloudflareBrowser_d_exports as CloudflareBrowser, CloudflareCodeMode_d_exports as CloudflareCodeMode, CloudflareConfig_d_exports as CloudflareConfig, CloudflareMemory_d_exports as CloudflareMemory, CloudflareScheduling_d_exports as CloudflareScheduling, CloudflareSubscriptions_d_exports as CloudflareSubscriptions, CloudflareThreadClient_d_exports as CloudflareThreadClient, ThreadObject_d_exports as ThreadObject, WakeScheduler_d_exports as WakeScheduler };
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { t as CloudflareBindings_exports } from "./CloudflareBindings.mjs";
|
|
2
2
|
import { t as CloudflareConfig_exports } from "./CloudflareConfig.mjs";
|
|
3
|
-
import { t as Alarm_exports } from "./Alarm-
|
|
3
|
+
import { t as Alarm_exports } from "./Alarm-4J9B8Fg8.mjs";
|
|
4
4
|
import { t as CloudflareBrowser_exports } from "./CloudflareBrowser-Vt8qTdYW.mjs";
|
|
5
5
|
import { t as CloudflareCodeMode_exports } from "./CloudflareCodeMode.mjs";
|
|
6
6
|
import { t as CloudflareMemory_exports } from "./CloudflareMemory.mjs";
|
|
7
|
-
import { s as CloudflareThreadClient_exports } from "./CloudflareThreadClient-
|
|
7
|
+
import { s as CloudflareThreadClient_exports } from "./CloudflareThreadClient-DU548EEj.mjs";
|
|
8
8
|
import { t as CloudflareScheduling_exports } from "./CloudflareScheduling.mjs";
|
|
9
9
|
import { t as CloudflareSubscriptions_exports } from "./CloudflareSubscriptions.mjs";
|
|
10
10
|
import { t as WakeScheduler_exports } from "./WakeScheduler.mjs";
|
|
11
|
-
import { l as ThreadObject_exports } from "./ThreadObject-
|
|
11
|
+
import { l as ThreadObject_exports } from "./ThreadObject-C2LF3btz.mjs";
|
|
12
12
|
import { t as CloudflareAiGateway_exports } from "./CloudflareAiGateway.mjs";
|
|
13
13
|
import { t as BrowserCredentials_exports } from "./BrowserCredentials.mjs";
|
|
14
|
-
import { i as BrowserSession_exports } from "./BrowserSession-
|
|
15
|
-
import { t as BrowserUse_exports } from "./BrowserUse
|
|
14
|
+
import { i as BrowserSession_exports } from "./BrowserSession-aovMjfL4.mjs";
|
|
15
|
+
import { t as BrowserUse_exports } from "./BrowserUse.mjs";
|
|
16
16
|
export { Alarm_exports as Alarm, BrowserCredentials_exports as BrowserCredentials, BrowserSession_exports as BrowserSession, BrowserUse_exports as BrowserUse, CloudflareAiGateway_exports as CloudflareAiGateway, CloudflareBindings_exports as CloudflareBindings, CloudflareBrowser_exports as CloudflareBrowser, CloudflareCodeMode_exports as CloudflareCodeMode, CloudflareConfig_exports as CloudflareConfig, CloudflareMemory_exports as CloudflareMemory, CloudflareScheduling_exports as CloudflareScheduling, CloudflareSubscriptions_exports as CloudflareSubscriptions, CloudflareThreadClient_exports as CloudflareThreadClient, ThreadObject_exports as ThreadObject, WakeScheduler_exports as WakeScheduler };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as CloudflareThreadClient } from "./CloudflareThreadClient-
|
|
1
|
+
import { o as CloudflareThreadClient } from "./CloudflareThreadClient-DU548EEj.mjs";
|
|
2
2
|
import "@yielded/agent/durable-agent-runtime";
|
|
3
3
|
import "@yielded/agent/identifiers";
|
|
4
4
|
import { Effect, Layer } from "effect";
|
|
@@ -70,4 +70,4 @@ const cloudflareScheduledInputAdmissionLayer = Layer.effect(ScheduledInputAdmiss
|
|
|
70
70
|
//#endregion
|
|
71
71
|
export { cloudflareScheduledInputAdmissionLayer as n, cloudflarePreparedInputAdmissionLayer as t };
|
|
72
72
|
|
|
73
|
-
//# sourceMappingURL=prepared-admission-
|
|
73
|
+
//# sourceMappingURL=prepared-admission-BoUPHSNQ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepared-admission-
|
|
1
|
+
{"version":3,"file":"prepared-admission-BoUPHSNQ.mjs","names":[],"sources":["../src/internal/prepared-admission.ts"],"sourcesContent":["import { type DurableSubmitAgent } from \"@yielded/agent/durable-agent-runtime\";\nimport { type AgentId } from \"@yielded/agent/identifiers\";\nimport { PreparedInputAdmission } from \"@yielded/agent/prepared-input-admission\";\nimport { PersistedJson } from \"@yielded/agent/records\";\nimport {\n type ScheduledEnvelope,\n ScheduledInputAdmission,\n ScheduledInputRetryable,\n ScheduledInputRefused,\n ScheduleStorageError,\n} from \"@yielded/agent/schedule\";\nimport { type PreparedInput } from \"@yielded/agent/subscription\";\nimport { Effect, Layer } from \"effect\";\n\nimport { CloudflareThreadClient, type ThreadClientError } from \"../CloudflareThreadClient.ts\";\n\nconst passthroughAgent = (agentId: AgentId): DurableSubmitAgent<typeof PersistedJson> => ({\n definition: { id: agentId, input: PersistedJson },\n});\n\n/** Ordinary prepared admission through one freshly addressed Thread Object call. */\nexport const cloudflarePreparedInputAdmissionLayer: Layer.Layer<\n PreparedInputAdmission,\n never,\n CloudflareThreadClient\n> = Layer.effect(\n PreparedInputAdmission,\n Effect.gen(function* () {\n const client = yield* CloudflareThreadClient;\n\n return PreparedInputAdmission.of({\n submissionStatus: (receipt) =>\n client\n .submissionStatus(receipt)\n .pipe(Effect.mapError(() => ScheduledInputRetryable.make({ reason: \"storage\" }))),\n submit: (envelope) =>\n client\n .submit(passthroughAgent(envelope.agentId), envelope.input, {\n threadId: envelope.threadId,\n principal: envelope.deliveryPrincipal,\n idempotencyKey: envelope.admissionKey,\n ...(envelope.admissionGroup === undefined\n ? {}\n : { admissionGroup: envelope.admissionGroup }),\n ...(envelope.admissionFence === undefined\n ? {}\n : { admissionFence: envelope.admissionFence }),\n ...(envelope.workerAdmission === undefined\n ? {}\n : { workerAdmission: envelope.workerAdmission }),\n ...(envelope.messageAdmission === undefined\n ? {}\n : { messageAdmission: envelope.messageAdmission }),\n definitions: envelope.definitions,\n })\n .pipe(\n Effect.catchTags({\n AdmissionConflict: () =>\n ScheduleStorageError.make({ operation: \"prepared admission\", reason: \"corrupt\" }),\n AdmissionLimitExceeded: () => ScheduledInputRetryable.make({ reason: \"capacity\" }),\n ThreadClientError: (error: ThreadClientError) =>\n ScheduledInputRetryable.make({\n reason: error.overloaded === true ? \"capacity\" : \"transport\",\n }),\n HostProtocolError: () => ScheduledInputRetryable.make({ reason: \"ambiguous\" }),\n AdmissionPolicyError: (error) =>\n error.reason === \"refused\"\n ? ScheduledInputRefused.make({ code: error.code })\n : ScheduledInputRetryable.make({\n reason: error.reason === \"occupied\" ? \"capacity\" : \"storage\",\n }),\n LedgerError: () => ScheduledInputRetryable.make({ reason: \"storage\" }),\n ThreadStoreError: () => ScheduledInputRetryable.make({ reason: \"storage\" }),\n DurableAlarmError: () => ScheduledInputRetryable.make({ reason: \"storage\" }),\n AgentInputError: () =>\n ScheduleStorageError.make({ operation: \"prepared admission\", reason: \"corrupt\" }),\n DigestError: () =>\n ScheduleStorageError.make({ operation: \"prepared admission\", reason: \"corrupt\" }),\n ThreadNotMaterialized: () => ScheduledInputRetryable.make({ reason: \"storage\" }),\n AppendConflict: () => ScheduledInputRetryable.make({ reason: \"ambiguous\" }),\n OwnershipLost: () => ScheduledInputRetryable.make({ reason: \"ambiguous\" }),\n DurableRuntimeFailpointError: () =>\n ScheduledInputRetryable.make({ reason: \"ambiguous\" }),\n }),\n ),\n });\n }),\n);\n\nconst preparedFromSchedule = (envelope: ScheduledEnvelope): PreparedInput => ({\n schemaVersion: 1,\n threadId: envelope.threadId,\n deliveryPrincipal: envelope.deliveryPrincipal,\n ...(envelope.admissionGroup === undefined ? {} : { admissionGroup: envelope.admissionGroup }),\n ...(envelope.admissionFence === undefined ? {} : { admissionFence: envelope.admissionFence }),\n agentId: envelope.agentId,\n definitions: envelope.definitions,\n input: envelope.input,\n inputDigest: envelope.inputDigest,\n admissionKey: envelope.admissionKey,\n authorization: envelope.authorization,\n});\n\n/** Compatibility adapter retaining Scheduling's public admission port. */\nexport const cloudflareScheduledInputAdmissionLayer: Layer.Layer<\n ScheduledInputAdmission,\n never,\n PreparedInputAdmission\n> = Layer.effect(\n ScheduledInputAdmission,\n Effect.map(PreparedInputAdmission, (admission) =>\n ScheduledInputAdmission.of({\n submit: (envelope) => admission.submit(preparedFromSchedule(envelope)),\n }),\n ),\n);\n"],"mappings":";;;;;;;;;AAgBA,MAAM,oBAAoB,aAAgE,EACxF,YAAY;CAAE,IAAI;CAAS,OAAO;AAAc,EAClD;;AAGA,MAAa,wCAIT,MAAM,OACR,wBACA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO;CAEtB,OAAO,uBAAuB,GAAG;EAC/B,mBAAmB,YACjB,OACG,iBAAiB,OAAO,CAAC,CACzB,KAAK,OAAO,eAAe,wBAAwB,KAAK,EAAE,QAAQ,UAAU,CAAC,CAAC,CAAC;EACpF,SAAS,aACP,OACG,OAAO,iBAAiB,SAAS,OAAO,GAAG,SAAS,OAAO;GAC1D,UAAU,SAAS;GACnB,WAAW,SAAS;GACpB,gBAAgB,SAAS;GACzB,GAAI,SAAS,mBAAmB,KAAA,IAC5B,CAAC,IACD,EAAE,gBAAgB,SAAS,eAAe;GAC9C,GAAI,SAAS,mBAAmB,KAAA,IAC5B,CAAC,IACD,EAAE,gBAAgB,SAAS,eAAe;GAC9C,GAAI,SAAS,oBAAoB,KAAA,IAC7B,CAAC,IACD,EAAE,iBAAiB,SAAS,gBAAgB;GAChD,GAAI,SAAS,qBAAqB,KAAA,IAC9B,CAAC,IACD,EAAE,kBAAkB,SAAS,iBAAiB;GAClD,aAAa,SAAS;EACxB,CAAC,CAAC,CACD,KACC,OAAO,UAAU;GACf,yBACE,qBAAqB,KAAK;IAAE,WAAW;IAAsB,QAAQ;GAAU,CAAC;GAClF,8BAA8B,wBAAwB,KAAK,EAAE,QAAQ,WAAW,CAAC;GACjF,oBAAoB,UAClB,wBAAwB,KAAK,EAC3B,QAAQ,MAAM,eAAe,OAAO,aAAa,YACnD,CAAC;GACH,yBAAyB,wBAAwB,KAAK,EAAE,QAAQ,YAAY,CAAC;GAC7E,uBAAuB,UACrB,MAAM,WAAW,YACb,sBAAsB,KAAK,EAAE,MAAM,MAAM,KAAK,CAAC,IAC/C,wBAAwB,KAAK,EAC3B,QAAQ,MAAM,WAAW,aAAa,aAAa,UACrD,CAAC;GACP,mBAAmB,wBAAwB,KAAK,EAAE,QAAQ,UAAU,CAAC;GACrE,wBAAwB,wBAAwB,KAAK,EAAE,QAAQ,UAAU,CAAC;GAC1E,yBAAyB,wBAAwB,KAAK,EAAE,QAAQ,UAAU,CAAC;GAC3E,uBACE,qBAAqB,KAAK;IAAE,WAAW;IAAsB,QAAQ;GAAU,CAAC;GAClF,mBACE,qBAAqB,KAAK;IAAE,WAAW;IAAsB,QAAQ;GAAU,CAAC;GAClF,6BAA6B,wBAAwB,KAAK,EAAE,QAAQ,UAAU,CAAC;GAC/E,sBAAsB,wBAAwB,KAAK,EAAE,QAAQ,YAAY,CAAC;GAC1E,qBAAqB,wBAAwB,KAAK,EAAE,QAAQ,YAAY,CAAC;GACzE,oCACE,wBAAwB,KAAK,EAAE,QAAQ,YAAY,CAAC;EACxD,CAAC,CACH;CACN,CAAC;AACH,CAAC,CACH;AAEA,MAAM,wBAAwB,cAAgD;CAC5E,eAAe;CACf,UAAU,SAAS;CACnB,mBAAmB,SAAS;CAC5B,GAAI,SAAS,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,gBAAgB,SAAS,eAAe;CAC3F,GAAI,SAAS,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,gBAAgB,SAAS,eAAe;CAC3F,SAAS,SAAS;CAClB,aAAa,SAAS;CACtB,OAAO,SAAS;CAChB,aAAa,SAAS;CACtB,cAAc,SAAS;CACvB,eAAe,SAAS;AAC1B;;AAGA,MAAa,yCAIT,MAAM,OACR,yBACA,OAAO,IAAI,yBAAyB,cAClC,wBAAwB,GAAG,EACzB,SAAS,aAAa,UAAU,OAAO,qBAAqB,QAAQ,CAAC,EACvE,CAAC,CACH,CACF"}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@yielded/agent-platform-cloudflare","version":"0.1.0-beta.
|
|
1
|
+
{"name":"@yielded/agent-platform-cloudflare","version":"0.1.0-beta.174","dependencies":{"@effect/platform-browser":"4.0.3","@effect/sql-sqlite-do":"4.0.3","@yielded/agent":"0.1.0-beta.174","@yielded/agent-storage-cloudflare":"0.1.0-beta.174","@yielded/agent-storage-sql":"0.1.0-beta.174","puppeteer-core":"24.29.0"},"devDependencies":{"@cloudflare/vitest-plugin":"1.4.0","@cloudflare/workers-types":"5.20260926.1","@effect/platform-node":"4.0.3","@effect/sql-d1":"4.0.3","@effect/vitest":"4.0.3","@yielded/agent-testing":"0.1.0-beta.174","effect":"4.0.3","effect-cf":"0.56.2","esbuild":"0.28.1","miniflare":"5.20260811.1-alpha","typescript":"7.0.2","vite":"npm:@voidzero-dev/vite-plus-core@1.1.0","vite-plus":"1.1.0","vitest":"5.0.3"},"peerDependencies":{"effect":"^4.0.3","effect-cf":"^0.56.0"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./alarm":{"types":"./dist/Alarm.d.mts","default":"./dist/Alarm.mjs"},"./browser-rest-capture":{"types":"./dist/BrowserRestCapture.d.mts","default":"./dist/BrowserRestCapture.mjs"},"./browser-rest-crawl":{"types":"./dist/BrowserRestCrawl.d.mts","default":"./dist/BrowserRestCrawl.mjs"},"./cloudflare-bindings":{"types":"./dist/CloudflareBindings.d.mts","default":"./dist/CloudflareBindings.mjs"},"./cloudflare-browser":{"types":"./dist/CloudflareBrowser.d.mts","default":"./dist/CloudflareBrowser.mjs"},"./cloudflare-code-mode":{"types":"./dist/CloudflareCodeMode.d.mts","default":"./dist/CloudflareCodeMode.mjs"},"./cloudflare-config":{"types":"./dist/CloudflareConfig.d.mts","default":"./dist/CloudflareConfig.mjs"},"./cloudflare-memory":{"types":"./dist/CloudflareMemory.d.mts","default":"./dist/CloudflareMemory.mjs"},"./cloudflare-scheduling":{"types":"./dist/CloudflareScheduling.d.mts","default":"./dist/CloudflareScheduling.mjs"},"./cloudflare-subscriptions":{"types":"./dist/CloudflareSubscriptions.d.mts","default":"./dist/CloudflareSubscriptions.mjs"},"./cloudflare-thread-client":{"types":"./dist/CloudflareThreadClient.d.mts","default":"./dist/CloudflareThreadClient.mjs"},"./interactive-browser":{"types":"./dist/InteractiveBrowser.d.mts","default":"./dist/InteractiveBrowser.mjs"},"./browser-session":{"types":"./dist/BrowserSession.d.mts","default":"./dist/BrowserSession.mjs"},"./browser-credentials":{"types":"./dist/BrowserCredentials.d.mts","default":"./dist/BrowserCredentials.mjs"},"./thread-object":{"types":"./dist/ThreadObject.d.mts","default":"./dist/ThreadObject.mjs"},"./wake-scheduler":{"types":"./dist/WakeScheduler.d.mts","default":"./dist/WakeScheduler.mjs"},"./cloudflare-ai-gateway":{"types":"./dist/CloudflareAiGateway.d.mts","default":"./dist/CloudflareAiGateway.mjs"},"./browser-use":{"types":"./dist/BrowserUse.d.mts","default":"./dist/BrowserUse.mjs"}},"description":"Cloudflare Layer assembly for Effect Agent: Durable Objects and Browser Run adapters.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/yielded-dev/agent.git","directory":"packages/platform-cloudflare"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json"}}
|
package/src/Alarm.ts
CHANGED
|
@@ -47,7 +47,7 @@ import { DurableObjectAlarm, DurableObjectStorage } from "effect-cf";
|
|
|
47
47
|
import { SqlClient } from "effect/sql/SqlClient";
|
|
48
48
|
import { SqlError, UnknownError } from "effect/sql/SqlError";
|
|
49
49
|
|
|
50
|
-
import { DurableObjectContext } from "./CloudflareBindings.ts";
|
|
50
|
+
import { DurableObjectContext, ThreadObjectPlacement } from "./CloudflareBindings.ts";
|
|
51
51
|
import { AuxiliaryDispatchMillis, CloudflareDurableRuntimeConfig } from "./CloudflareConfig.ts";
|
|
52
52
|
import { safeCauseMessage } from "./internal/boundary.ts";
|
|
53
53
|
import * as DueQueue from "./internal/due-queue.ts";
|
|
@@ -283,7 +283,7 @@ const makeAlarmOperation = Effect.gen(function* () {
|
|
|
283
283
|
reason: UnknownError.make({
|
|
284
284
|
operation: "begin maintenance source transaction",
|
|
285
285
|
message: "Use ThreadMutationGate.withTransaction at the outermost source transaction",
|
|
286
|
-
cause:
|
|
286
|
+
cause: "Unwrapped SQL transaction cannot own buffered maintenance intent",
|
|
287
287
|
}),
|
|
288
288
|
}),
|
|
289
289
|
);
|
|
@@ -294,8 +294,15 @@ const makeAlarmOperation = Effect.gen(function* () {
|
|
|
294
294
|
|
|
295
295
|
return transaction(
|
|
296
296
|
Effect.flatMap(Effect.serviceOption(sql.transactionService), (transaction) =>
|
|
297
|
-
Effect.
|
|
298
|
-
if (transaction._tag === "None")
|
|
297
|
+
Effect.gen(function* () {
|
|
298
|
+
if (transaction._tag === "None")
|
|
299
|
+
return yield* SqlError.make({
|
|
300
|
+
reason: UnknownError.make({
|
|
301
|
+
operation: "begin maintenance source transaction",
|
|
302
|
+
message: "SQL transaction unavailable",
|
|
303
|
+
cause: "SQL transaction unavailable",
|
|
304
|
+
}),
|
|
305
|
+
});
|
|
299
306
|
frame = queue.begin(transaction.value[1]);
|
|
300
307
|
}),
|
|
301
308
|
).pipe(
|
|
@@ -334,9 +341,16 @@ const makeAlarmOperation = Effect.gen(function* () {
|
|
|
334
341
|
operation: string,
|
|
335
342
|
execute: (transaction: MaintenanceStorage, checkpoint: CheckpointIntent) => Promise<A>,
|
|
336
343
|
) => {
|
|
337
|
-
const commit = Effect.
|
|
344
|
+
const commit = Effect.withFiber((fiber) => {
|
|
345
|
+
const tx = fiber.getRef(CurrentCheckpoint);
|
|
346
|
+
|
|
338
347
|
if (tx === undefined)
|
|
339
|
-
return Effect.die(
|
|
348
|
+
return Effect.die(
|
|
349
|
+
DurableAlarmError.make({
|
|
350
|
+
operation: "checkpoint",
|
|
351
|
+
message: "Maintenance checkpoint transaction unavailable",
|
|
352
|
+
}),
|
|
353
|
+
);
|
|
340
354
|
|
|
341
355
|
return tx.requireActive.pipe(
|
|
342
356
|
Effect.andThen(
|
|
@@ -366,14 +380,14 @@ const makeAlarmOperation = Effect.gen(function* () {
|
|
|
366
380
|
);
|
|
367
381
|
});
|
|
368
382
|
|
|
369
|
-
return Effect.
|
|
370
|
-
const current =
|
|
383
|
+
return Effect.withFiber((fiber) => {
|
|
384
|
+
const current = fiber.getRef(CurrentCheckpoint);
|
|
371
385
|
|
|
372
|
-
if (current !== undefined) return
|
|
373
|
-
const ambient =
|
|
386
|
+
if (current !== undefined) return commit;
|
|
387
|
+
const ambient = Context.getOption(fiber.context, sql.transactionService);
|
|
374
388
|
|
|
375
389
|
// Preserve eager writes and invalidation for an unwrapped host SQL transaction.
|
|
376
|
-
return
|
|
390
|
+
return ambient._tag === "Some" && !DueQueue.buffering(ctx.storage)
|
|
377
391
|
? transaction(commit).pipe(
|
|
378
392
|
Effect.ensuring(Effect.sync(() => DueQueue.invalidate(ctx.storage))),
|
|
379
393
|
)
|
|
@@ -629,6 +643,7 @@ const decodeRecoveryFaultValue = Schema.decodeUnknownSync(ThreadRecoveryFault);
|
|
|
629
643
|
const decodeRecoveryFault = (threadId: ThreadId, encoded: unknown) => {
|
|
630
644
|
const fault = decodeRecoveryFaultValue(encoded);
|
|
631
645
|
|
|
646
|
+
// oxlint-disable-next-line effect/no-untyped-throw, effect/no-native-error -- Native Durable Object transaction decoder must abort before corrupt persisted data can be committed.
|
|
632
647
|
if (fault.threadId !== threadId) throw new Error("Recovery status does not match its Thread key");
|
|
633
648
|
|
|
634
649
|
return fault;
|
|
@@ -909,6 +924,8 @@ export class ThreadMutationGate extends Context.Service<
|
|
|
909
924
|
const { ctx } = yield* DurableObjectContext;
|
|
910
925
|
const sql = yield* SqlClient;
|
|
911
926
|
const dueQueue = DueQueue.make(ctx.storage);
|
|
927
|
+
const placement = yield* Effect.serviceOption(ThreadObjectPlacement);
|
|
928
|
+
const singleThread = Option.isSome(placement) && placement.value.threadId !== undefined;
|
|
912
929
|
|
|
913
930
|
const config = yield* CloudflareDurableRuntimeConfig;
|
|
914
931
|
const failpoint = yield* ThreadMaintenanceFailpoint;
|
|
@@ -1127,7 +1144,12 @@ export class ThreadMutationGate extends Context.Service<
|
|
|
1127
1144
|
yield* runAlarmTransaction(
|
|
1128
1145
|
"record maintenance producer progress",
|
|
1129
1146
|
async (transaction, checkpoint) => {
|
|
1130
|
-
for (const id of affected)
|
|
1147
|
+
for (const id of affected) {
|
|
1148
|
+
// Shared hosts need every revision to discover newly actionable sibling Threads.
|
|
1149
|
+
if (singleThread && nativeSource && id === DueQueue.Native)
|
|
1150
|
+
dueQueue.progressNative(now);
|
|
1151
|
+
else dueQueue.dirty(id, now, true);
|
|
1152
|
+
}
|
|
1131
1153
|
if (affected.has(DueQueue.Native)) {
|
|
1132
1154
|
const { state, initialized } = await readMaintenanceState(transaction);
|
|
1133
1155
|
|
|
@@ -1147,7 +1169,10 @@ export class ThreadMutationGate extends Context.Service<
|
|
|
1147
1169
|
checkpoint.earlier(now + minimumAlarmDelay);
|
|
1148
1170
|
},
|
|
1149
1171
|
);
|
|
1150
|
-
|
|
1172
|
+
// Maintenance already awaits this native fiber's completion. With one Thread,
|
|
1173
|
+
// native-only progress cannot free another dispatch slot or enroll an auxiliary lane.
|
|
1174
|
+
if (!(singleThread && nativeSource && affected.size === 1 && affected.has(DueQueue.Native)))
|
|
1175
|
+
yield* notify;
|
|
1151
1176
|
});
|
|
1152
1177
|
|
|
1153
1178
|
return ThreadMutationGate.of({
|
|
@@ -1471,14 +1496,15 @@ export class ThreadMaintenance extends Context.Service<
|
|
|
1471
1496
|
);
|
|
1472
1497
|
|
|
1473
1498
|
for (let sequence = range.next; sequence <= range.through; sequence++) {
|
|
1474
|
-
const event = yield* runTransaction("read recovery event", async () =>
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
if (event.sequence !== sequence)
|
|
1478
|
-
throw new Error("Recovery event does not match its sequence key");
|
|
1499
|
+
const event = yield* runTransaction("read recovery event", async () =>
|
|
1500
|
+
decodeRecoveryEvent(await ctx.storage.get(recoveryEventKey(sequence))),
|
|
1501
|
+
);
|
|
1479
1502
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1503
|
+
if (event.sequence !== sequence)
|
|
1504
|
+
return yield* DurableAlarmError.make({
|
|
1505
|
+
operation: "read recovery event",
|
|
1506
|
+
message: "Recovery event does not match its sequence key",
|
|
1507
|
+
});
|
|
1482
1508
|
|
|
1483
1509
|
// No storage reservation or source mutation gate is held during host delivery.
|
|
1484
1510
|
yield* recoveryEvents.publish(event);
|
|
@@ -1521,7 +1547,20 @@ export class ThreadMaintenance extends Context.Service<
|
|
|
1521
1547
|
for (const report of result.reports) threads.set(report.threadId, undefined);
|
|
1522
1548
|
for (const report of result.workReports ?? []) threads.set(report.threadId, undefined);
|
|
1523
1549
|
for (const blocked of result.blocked) threads.set(blocked.threadId, blocked.failure);
|
|
1524
|
-
|
|
1550
|
+
|
|
1551
|
+
// This pass owns fault writes. Known absence needs no second transaction;
|
|
1552
|
+
// blocked results and retained faults still commit their transitions.
|
|
1553
|
+
if (
|
|
1554
|
+
threads.size === 0 ||
|
|
1555
|
+
[...threads].every(
|
|
1556
|
+
([threadId, failure]) =>
|
|
1557
|
+
failure === undefined &&
|
|
1558
|
+
recovery.loaded.has(threadId) &&
|
|
1559
|
+
!recovery.faults.has(threadId),
|
|
1560
|
+
)
|
|
1561
|
+
)
|
|
1562
|
+
return new Map<ThreadId, ThreadRecoveryFault>();
|
|
1563
|
+
|
|
1525
1564
|
const now = yield* Clock.currentTimeMillis;
|
|
1526
1565
|
|
|
1527
1566
|
// Fault-only discovery uses control state even when retained payloads cannot decode.
|
|
@@ -1625,18 +1664,21 @@ export class ThreadMaintenance extends Context.Service<
|
|
|
1625
1664
|
threadId: ThreadId,
|
|
1626
1665
|
recovery: NativeRecovery,
|
|
1627
1666
|
) {
|
|
1628
|
-
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
if (retained === undefined) return undefined;
|
|
1632
|
-
const decoded = Schema.decodeUnknownSync(NativeWorkCursor)(retained);
|
|
1667
|
+
const retained = yield* runTransaction("read work recovery cursor", async () => {
|
|
1668
|
+
const value = await ctx.storage.get(workCursorKey(threadId));
|
|
1633
1669
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
return decoded.cursor;
|
|
1670
|
+
return value === undefined
|
|
1671
|
+
? undefined
|
|
1672
|
+
: Schema.decodeUnknownSync(NativeWorkCursor)(value);
|
|
1638
1673
|
});
|
|
1639
1674
|
|
|
1675
|
+
if (retained !== undefined && retained.threadId !== threadId)
|
|
1676
|
+
return yield* DurableAlarmError.make({
|
|
1677
|
+
operation: "read work recovery cursor",
|
|
1678
|
+
message: "Work cursor belongs to another Thread",
|
|
1679
|
+
});
|
|
1680
|
+
let cursor = retained?.cursor;
|
|
1681
|
+
|
|
1640
1682
|
do {
|
|
1641
1683
|
const result = yield* runtime
|
|
1642
1684
|
.runRecovery({ threadId, ...(cursor === undefined ? {} : { cursor }) })
|
|
@@ -1645,14 +1687,15 @@ export class ThreadMaintenance extends Context.Service<
|
|
|
1645
1687
|
// Persist each bounded pass before a claim; eviction resumes past consumed metadata.
|
|
1646
1688
|
const faults = yield* recordRecoveryFaults(result, recovery, undefined, threadId);
|
|
1647
1689
|
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1690
|
+
if (cursor !== undefined || result.cursor !== undefined)
|
|
1691
|
+
yield* runTransaction("checkpoint work recovery cursor", async () => {
|
|
1692
|
+
if (result.cursor === undefined) await ctx.storage.delete(workCursorKey(threadId));
|
|
1693
|
+
else
|
|
1694
|
+
await ctx.storage.put(
|
|
1695
|
+
workCursorKey(threadId),
|
|
1696
|
+
Schema.encodeSync(NativeWorkCursor)({ threadId, cursor: result.cursor }),
|
|
1697
|
+
);
|
|
1698
|
+
});
|
|
1656
1699
|
for (const report of result.reports) recovery.reports.set(report.submissionId, report);
|
|
1657
1700
|
recovery.faults.delete(threadId);
|
|
1658
1701
|
for (const [id, fault] of faults) recovery.faults.set(id, fault);
|
|
@@ -2277,7 +2320,7 @@ export class ThreadMaintenance extends Context.Service<
|
|
|
2277
2320
|
|
|
2278
2321
|
const next = selected.at(-1);
|
|
2279
2322
|
|
|
2280
|
-
if (next !== undefined) {
|
|
2323
|
+
if (next !== undefined && next !== state.lastServedThreadId) {
|
|
2281
2324
|
await transaction.put(
|
|
2282
2325
|
MAINTENANCE_STATE_KEY,
|
|
2283
2326
|
encodeMaintenanceState(
|
|
@@ -89,6 +89,7 @@ const privateResponseCause = (
|
|
|
89
89
|
const token = Redacted.value(apiToken);
|
|
90
90
|
const diagnostic = boundedDiagnostic(bodyText);
|
|
91
91
|
|
|
92
|
+
// oxlint-disable-next-line effect/no-native-error -- Bounded, redacted foreign response diagnostic retained as cause, never as the Effect failure.
|
|
92
93
|
return new Error(token.length === 0 ? diagnostic : diagnostic.replaceAll(token, "[REDACTED]"));
|
|
93
94
|
};
|
|
94
95
|
|
package/src/BrowserRestCrawl.ts
CHANGED
|
@@ -100,6 +100,7 @@ const privateCause = (value: unknown, apiToken: Redacted.Redacted<string>): Erro
|
|
|
100
100
|
if (raw.length === 0) return undefined;
|
|
101
101
|
const token = Redacted.value(apiToken);
|
|
102
102
|
|
|
103
|
+
// oxlint-disable-next-line effect/no-native-error -- Bounded, redacted foreign response diagnostic retained as cause, never as the Effect failure.
|
|
103
104
|
return new Error(token.length === 0 ? raw : raw.replaceAll(token, "[REDACTED]"));
|
|
104
105
|
};
|
|
105
106
|
|
package/src/BrowserSession.ts
CHANGED
|
@@ -95,7 +95,8 @@ export class BrowserSessionError extends Schema.TaggedError<BrowserSessionError>
|
|
|
95
95
|
* One local attachment to an application-owned browser. Scope release disconnects; it never
|
|
96
96
|
* transfers ownership or closes a healthy remote session. The owner serializes attachments,
|
|
97
97
|
* fences old attempts, authorizes human access, and calls BrowserSessions.close on expiry/stop.
|
|
98
|
-
* Native callbacks are trusted host code: never
|
|
98
|
+
* Native callbacks are trusted host code: never start unawaited work. Adapters retaining SDK
|
|
99
|
+
* handles must scope their release and use run for every operation on the retained page.
|
|
99
100
|
*/
|
|
100
101
|
export interface BrowserSession {
|
|
101
102
|
readonly reference: BrowserSessionReference;
|
|
@@ -147,7 +148,7 @@ const failure = (
|
|
|
147
148
|
reason: BrowserSessionError["reason"],
|
|
148
149
|
dispatch: BrowserSessionError["dispatch"] = "not-dispatched",
|
|
149
150
|
cleanup: BrowserSessionError["cleanup"] = "not-requested",
|
|
150
|
-
) =>
|
|
151
|
+
) => BrowserSessionError.make({ reason, dispatch, cleanup });
|
|
151
152
|
|
|
152
153
|
const decode = <A>(schema: Schema.Codec<A>, input: unknown) =>
|
|
153
154
|
Schema.decodeUnknownEffect(schema, { onExcessProperty: "error" })(input).pipe(
|