@effect-agent/platform-cloudflare 0.1.0-beta.8 → 0.1.0-beta.80
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.d.mts +183 -0
- package/dist/Alarm.mjs +454 -0
- package/dist/Alarm.mjs.map +1 -0
- package/dist/BrowserRestCapture.d.mts +35 -0
- package/dist/BrowserRestCapture.mjs +238 -0
- package/dist/BrowserRestCapture.mjs.map +1 -0
- package/dist/BrowserRestCrawl.d.mts +17 -0
- package/dist/BrowserRestCrawl.mjs +244 -0
- package/dist/BrowserRestCrawl.mjs.map +1 -0
- package/dist/CloudflareAiGateway.d.mts +64 -0
- package/dist/CloudflareAiGateway.mjs +70 -0
- package/dist/CloudflareAiGateway.mjs.map +1 -0
- package/dist/CloudflareBindings.d.mts +112 -0
- package/dist/CloudflareBindings.mjs +101 -0
- package/dist/CloudflareBindings.mjs.map +1 -0
- package/dist/CloudflareBrowser-D4YqaTAl.mjs +495 -0
- package/dist/CloudflareBrowser-D4YqaTAl.mjs.map +1 -0
- package/dist/CloudflareBrowser-DJq53niJ.d.mts +80 -0
- package/dist/CloudflareBrowser.d.mts +2 -0
- package/dist/CloudflareBrowser.mjs +2 -0
- package/dist/CloudflareCodeMode.d.mts +44 -0
- package/dist/CloudflareCodeMode.mjs +616 -0
- package/dist/CloudflareCodeMode.mjs.map +1 -0
- package/dist/CloudflareConfig-f3CqTel1.d.mts +101 -0
- package/dist/CloudflareConfig.d.mts +2 -0
- package/dist/CloudflareConfig.mjs +122 -0
- package/dist/CloudflareConfig.mjs.map +1 -0
- package/dist/CloudflareMemory.d.mts +142 -0
- package/dist/CloudflareMemory.mjs +195 -0
- package/dist/CloudflareMemory.mjs.map +1 -0
- package/dist/CloudflareScheduling.d.mts +52 -0
- package/dist/CloudflareScheduling.mjs +389 -0
- package/dist/CloudflareScheduling.mjs.map +1 -0
- package/dist/CloudflareSubscriptions.d.mts +87 -0
- package/dist/CloudflareSubscriptions.mjs +524 -0
- package/dist/CloudflareSubscriptions.mjs.map +1 -0
- package/dist/CloudflareThreadClient.d.mts +1286 -0
- package/dist/CloudflareThreadClient.mjs +386 -0
- package/dist/CloudflareThreadClient.mjs.map +1 -0
- package/dist/InteractiveBrowser.d.mts +144 -0
- package/dist/InteractiveBrowser.mjs +1090 -0
- package/dist/InteractiveBrowser.mjs.map +1 -0
- package/dist/ProtectedBrowser.d.mts +65 -0
- package/dist/ProtectedBrowser.mjs +859 -0
- package/dist/ProtectedBrowser.mjs.map +1 -0
- package/dist/ThreadObject-3uDpKfB3.d.mts +816 -0
- package/dist/ThreadObject-DGkXv41C.mjs +836 -0
- package/dist/ThreadObject-DGkXv41C.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +2 -0
- package/dist/ThreadObject.mjs +3 -0
- package/dist/WakeScheduler.d.mts +23 -0
- package/dist/WakeScheduler.mjs +60 -0
- package/dist/WakeScheduler.mjs.map +1 -0
- package/dist/boundary-BguazVkh.mjs +42 -0
- package/dist/boundary-BguazVkh.mjs.map +1 -0
- package/dist/browser-session-lifecycle-CUbVfEgP.mjs +84 -0
- package/dist/browser-session-lifecycle-CUbVfEgP.mjs.map +1 -0
- package/dist/browser-session-lifecycle-DJpqLXy_.d.mts +21 -0
- package/dist/index.d.mts +13 -1547
- package/dist/index.mjs +13 -1636
- package/dist/prepared-admission-DSi55W-7.mjs +73 -0
- package/dist/prepared-admission-DSi55W-7.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +1 -53
- package/src/Alarm.ts +953 -0
- package/src/BrowserRestCapture.ts +477 -0
- package/src/BrowserRestCrawl.ts +540 -0
- package/src/CloudflareAiGateway.ts +170 -0
- package/src/CloudflareBindings.ts +180 -0
- package/src/CloudflareBrowser.ts +15 -0
- package/src/{code-mode-executor.ts → CloudflareCodeMode.ts} +372 -204
- package/src/{config.ts → CloudflareConfig.ts} +9 -8
- package/src/CloudflareMemory.ts +406 -0
- package/src/CloudflareScheduling.ts +733 -0
- package/src/CloudflareSubscriptions.ts +1045 -0
- package/src/CloudflareThreadClient.ts +807 -0
- package/src/InteractiveBrowser.ts +2357 -0
- package/src/ProtectedBrowser.ts +2 -0
- package/src/ThreadObject.ts +1179 -0
- package/src/{wake-scheduler.ts → WakeScheduler.ts} +23 -23
- package/src/index.ts +12 -23
- package/src/internal/boundary.ts +55 -0
- package/src/internal/browser-quick-action.ts +857 -0
- package/src/internal/browser-session-lifecycle.ts +160 -0
- package/src/internal/layers.ts +751 -0
- package/src/internal/message-delivery.ts +148 -0
- package/src/internal/prepared-admission.ts +116 -0
- package/src/internal/progress-wait.ts +121 -0
- package/src/internal/transport.ts +42 -0
- package/src/protected-browser/binding.ts +187 -0
- package/src/protected-browser/inspect-frame.ts +125 -0
- package/src/protected-browser/native.ts +429 -0
- package/src/protected-browser/policy.ts +719 -0
- package/dist/index.mjs.map +0 -1
- package/src/alarm.ts +0 -334
- package/src/bindings.ts +0 -145
- package/src/client.ts +0 -646
- package/src/conversation-object.ts +0 -768
- package/src/layers.ts +0 -376
- package/src/transport.ts +0 -38
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DEFAULT_MAX_STORED_VALUE_BYTES } from "@effect-agent/storage-cloudflare/DoStorageConfig";
|
|
2
|
+
import { DeploymentId } from "@effect-agent/thread/Records";
|
|
3
|
+
import { DEFAULT_OWNERSHIP_LEASE_DURATION } from "@effect-agent/thread/SubmissionLedger";
|
|
3
4
|
import { Context, Duration, Schema } from "effect";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -25,7 +26,7 @@ export class CloudflarePlatformConfigError extends Schema.TaggedError<Cloudflare
|
|
|
25
26
|
* (deployment spec §8, DEPLOY-007: "Admission has explicit bounded quota and overload
|
|
26
27
|
* behavior... a typed rejection"). This is the DC analogue of `NodeDurableHost`'s
|
|
27
28
|
* `AdmissionClosed` host gate: the port surface stays untouched, the refusal happens in the
|
|
28
|
-
*
|
|
29
|
+
* Thread Object's submit entry point before `DurableAgentRuntime.submit` runs, and
|
|
29
30
|
* nothing was admitted or written.
|
|
30
31
|
*/
|
|
31
32
|
export class AdmissionLimitExceeded extends Schema.TaggedError<AdmissionLimitExceeded>()(
|
|
@@ -52,13 +53,13 @@ export const CLOUDFLARE_DATABASE_CAP_BYTES = 10_000_000_000;
|
|
|
52
53
|
export const DEFAULT_MAX_DATABASE_BYTES = 9_000_000_000;
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
|
-
* Explicit bounded admission quotas checked by the
|
|
56
|
+
* Explicit bounded admission quotas checked by the Thread Object BEFORE admission
|
|
56
57
|
* (exit gate "resource limits are checked before admission").
|
|
57
58
|
*/
|
|
58
59
|
export class CloudflareAdmissionLimitsValue extends Schema.Class<CloudflareAdmissionLimitsValue>(
|
|
59
60
|
"@effect-agent/platform-cloudflare/CloudflareAdmissionLimitsValue",
|
|
60
61
|
)({
|
|
61
|
-
/** Maximum nonterminal Submissions per
|
|
62
|
+
/** Maximum nonterminal Submissions per Thread lane before new admissions refuse. */
|
|
62
63
|
maxQueueDepthPerLane: Schema.Int.check(
|
|
63
64
|
Schema.isGreaterThan(0),
|
|
64
65
|
Schema.isLessThanOrEqualTo(100_000),
|
|
@@ -74,7 +75,7 @@ export class CloudflareAdmissionLimitsValue extends Schema.Class<CloudflareAdmis
|
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
77
|
* Validated Cloudflare durable runtime configuration. The producer identity of one
|
|
77
|
-
*
|
|
78
|
+
* Thread Object is `{producerPrefix}:{threadId}` — stable across incarnations of
|
|
78
79
|
* the same deployment, distinct across deployments — and producer-epoch fencing (not the
|
|
79
80
|
* producer name) remains the correctness authority (DUR-006).
|
|
80
81
|
*/
|
|
@@ -82,7 +83,7 @@ export class CloudflareDurableRuntimeConfigValue extends Schema.Class<Cloudflare
|
|
|
82
83
|
"@effect-agent/platform-cloudflare/CloudflareDurableRuntimeConfigValue",
|
|
83
84
|
)({
|
|
84
85
|
deploymentId: DeploymentId,
|
|
85
|
-
/** Head of the minted producer identity `{producerPrefix}:{
|
|
86
|
+
/** Head of the minted producer identity `{producerPrefix}:{threadId}`. */
|
|
86
87
|
producerPrefix: Schema.NonEmptyString.check(Schema.isMaxLength(256)),
|
|
87
88
|
/** Submission ownership lease duration (D5); fences work across Object incarnations. */
|
|
88
89
|
ownershipLeaseDuration: PositiveMillis,
|
|
@@ -119,7 +120,7 @@ export class CloudflareDurableRuntimeConfig extends Context.Service<
|
|
|
119
120
|
CloudflareDurableRuntimeConfigValue
|
|
120
121
|
>()("@effect-agent/platform-cloudflare/CloudflareDurableRuntimeConfig") {}
|
|
121
122
|
|
|
122
|
-
/** Documented production defaults applied by `
|
|
123
|
+
/** Documented production defaults applied by `ThreadObject.layer`. */
|
|
123
124
|
export const CLOUDFLARE_RUNTIME_DEFAULTS = {
|
|
124
125
|
ownershipLeaseDuration: Duration.toMillis(DEFAULT_OWNERSHIP_LEASE_DURATION),
|
|
125
126
|
alarmBackoffBase: 100,
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import * as Memory from "@effect-agent/core/Memory";
|
|
2
|
+
import * as MemoryNamespace from "@effect-agent/core/MemoryNamespace";
|
|
3
|
+
import { MemoryNamespaceAddress } from "@effect-agent/core/MemoryNamespace";
|
|
4
|
+
import { type MemoryLookup, MemoryRecallLimits } from "@effect-agent/core/MemoryReference";
|
|
5
|
+
import { MemoryAccess } from "@effect-agent/core/MemoryRevalidation";
|
|
6
|
+
import {
|
|
7
|
+
type MemoryReader,
|
|
8
|
+
type MemoryWrite,
|
|
9
|
+
MemoryKey,
|
|
10
|
+
MemoryDocument,
|
|
11
|
+
MemoryMutationFailpoint,
|
|
12
|
+
MemoryStorageError,
|
|
13
|
+
MemoryWriter,
|
|
14
|
+
} from "@effect-agent/core/MemoryStore";
|
|
15
|
+
import {
|
|
16
|
+
type MemoryIndexSearch,
|
|
17
|
+
type SemanticMemoryProfile,
|
|
18
|
+
} from "@effect-agent/core/SemanticMemoryIndex";
|
|
19
|
+
import { type SemanticCandidateLimits } from "@effect-agent/core/SemanticMemoryRevalidation";
|
|
20
|
+
import {
|
|
21
|
+
type DoMemoryStorageLimits,
|
|
22
|
+
defaultDoMemoryStorageLimits,
|
|
23
|
+
doMemoryStoreLayerWithFailpoints,
|
|
24
|
+
} from "@effect-agent/storage-cloudflare/DoMemoryStore";
|
|
25
|
+
import {
|
|
26
|
+
type MemoryOwnerAuthorizer,
|
|
27
|
+
decodeMemoryWire,
|
|
28
|
+
defaultMemoryRpcLimits,
|
|
29
|
+
encodeMemoryWire,
|
|
30
|
+
handleMemoryOwnerRequest,
|
|
31
|
+
MemoryOwnerIdentity,
|
|
32
|
+
MemoryOwnerRequest,
|
|
33
|
+
MemoryOwnerResponse,
|
|
34
|
+
MemoryRpcError,
|
|
35
|
+
MemoryRpcLimits,
|
|
36
|
+
type MemoryOwnerFailure,
|
|
37
|
+
} from "@effect-agent/storage-cloudflare/MemoryProtocol";
|
|
38
|
+
import { Principal } from "@effect-agent/thread/SubmissionLedger";
|
|
39
|
+
import { Clock, Context, Effect, Layer, Schema } from "effect";
|
|
40
|
+
import {
|
|
41
|
+
DurableObject as EffectCfDurableObject,
|
|
42
|
+
DurableObjectState,
|
|
43
|
+
type WorkerEnvironment,
|
|
44
|
+
} from "effect-cf";
|
|
45
|
+
|
|
46
|
+
export interface MemoryObjectRpc extends Rpc.DurableObjectBranded {
|
|
47
|
+
memory(encoded: string): Promise<string>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class MemoryObjectNamespace extends Context.Service<
|
|
51
|
+
MemoryObjectNamespace,
|
|
52
|
+
{
|
|
53
|
+
readonly namespace: DurableObjectNamespace<MemoryObjectRpc>;
|
|
54
|
+
}
|
|
55
|
+
>()("@effect-agent/platform-cloudflare/MemoryObjectNamespace") {}
|
|
56
|
+
|
|
57
|
+
/** Namespace version and identity are already canonicalized by MemoryNamespace. */
|
|
58
|
+
export const memoryObjectName = (namespace: MemoryNamespace.Any): string => namespace.address;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Effect-native, host-bound memory client. Recall revalidates the entire admitted lookup
|
|
62
|
+
* in one RPC and renders it locally. No retries or per-source splitting occur here.
|
|
63
|
+
* Interrupted callers stop waiting; the owner has its own deadline. A timed-out write
|
|
64
|
+
* may have committed: reconcile by sending the identical operation ID and command.
|
|
65
|
+
*/
|
|
66
|
+
const makeMemoryClient = Effect.fn("CloudflareMemoryClient.make")(function* <
|
|
67
|
+
Namespace extends MemoryNamespace.Any,
|
|
68
|
+
>(
|
|
69
|
+
access: MemoryAccess<Namespace>,
|
|
70
|
+
principal: Principal,
|
|
71
|
+
rpcLimits: MemoryRpcLimits = defaultMemoryRpcLimits,
|
|
72
|
+
) {
|
|
73
|
+
const validated = yield* Schema.decodeUnknownEffect(MemoryRpcLimits)(rpcLimits).pipe(
|
|
74
|
+
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const bound = yield* Schema.decodeUnknownEffect(MemoryAccess.Wire)(access).pipe(
|
|
78
|
+
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
principal = yield* Schema.decodeUnknownEffect(Principal)(principal).pipe(
|
|
82
|
+
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
83
|
+
);
|
|
84
|
+
const { namespace } = yield* MemoryObjectNamespace;
|
|
85
|
+
|
|
86
|
+
const call = Effect.fn("CloudflareMemoryClient.call")(function* (request: MemoryOwnerRequest) {
|
|
87
|
+
const decoded = yield* Schema.decodeUnknownEffect(MemoryOwnerRequest)(request).pipe(
|
|
88
|
+
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const encoded = yield* encodeMemoryWire(MemoryOwnerRequest, decoded, validated.maxRequestBytes);
|
|
92
|
+
|
|
93
|
+
const raw = yield* Effect.tryPromise({
|
|
94
|
+
try: () =>
|
|
95
|
+
namespace.get(namespace.idFromName(memoryObjectName(bound.namespace))).memory(encoded),
|
|
96
|
+
catch: () => MemoryRpcError.make({ reason: "unavailable" }),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const response = yield* decodeMemoryWire(MemoryOwnerResponse, raw, validated.maxResponseBytes);
|
|
100
|
+
|
|
101
|
+
if (response._tag === "Failed") return yield* response.failure;
|
|
102
|
+
if (
|
|
103
|
+
!MemoryNamespace.equals(response.access.namespace, bound.namespace) ||
|
|
104
|
+
response.access.scope !== bound.scope
|
|
105
|
+
)
|
|
106
|
+
return yield* MemoryRpcError.make({ reason: "protocol" });
|
|
107
|
+
|
|
108
|
+
return response;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const withinDeadline = <A, E, R>(effect: Effect.Effect<A, E, R>, timeoutMillis: number) =>
|
|
112
|
+
effect.pipe(
|
|
113
|
+
Effect.timeoutOrElse({
|
|
114
|
+
duration: timeoutMillis,
|
|
115
|
+
orElse: () => Effect.fail(MemoryRpcError.make({ reason: "timeout" })),
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const revalidate = Effect.fn("CloudflareMemoryClient.revalidate")(function* (
|
|
120
|
+
lookup: MemoryLookup,
|
|
121
|
+
limits: MemoryRecallLimits,
|
|
122
|
+
) {
|
|
123
|
+
limits = yield* Schema.decodeUnknownEffect(MemoryRecallLimits)(limits).pipe(
|
|
124
|
+
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
125
|
+
);
|
|
126
|
+
const timeoutMillis = Math.min(validated.timeoutMillis, limits.timeoutMillis);
|
|
127
|
+
|
|
128
|
+
return yield* Effect.gen(function* () {
|
|
129
|
+
const response = yield* call({
|
|
130
|
+
_tag: "Revalidate",
|
|
131
|
+
version: 1,
|
|
132
|
+
access: bound,
|
|
133
|
+
principal,
|
|
134
|
+
lookup,
|
|
135
|
+
limits,
|
|
136
|
+
deadlineMillis: (yield* Clock.currentTimeMillis) + timeoutMillis,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (response._tag !== "Lookup") return yield* MemoryRpcError.make({ reason: "protocol" });
|
|
140
|
+
|
|
141
|
+
return response.lookup;
|
|
142
|
+
}).pipe((effect) => withinDeadline(effect, timeoutMillis));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const change = Effect.fn("CloudflareMemoryClient.change")(function* (
|
|
146
|
+
write: MemoryWrite<Namespace>,
|
|
147
|
+
) {
|
|
148
|
+
if (!MemoryNamespace.equals(write.key.namespace, bound.namespace))
|
|
149
|
+
return yield* MemoryRpcError.make({ reason: "denied" });
|
|
150
|
+
|
|
151
|
+
return yield* Effect.gen(function* () {
|
|
152
|
+
const response = yield* call({
|
|
153
|
+
_tag: "Change",
|
|
154
|
+
version: 1,
|
|
155
|
+
access: bound,
|
|
156
|
+
principal,
|
|
157
|
+
write,
|
|
158
|
+
deadlineMillis: (yield* Clock.currentTimeMillis) + validated.timeoutMillis,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (response._tag !== "Changed" || response.document.key.id !== write.key.id)
|
|
162
|
+
return yield* MemoryRpcError.make({ reason: "protocol" });
|
|
163
|
+
|
|
164
|
+
return yield* MemoryDocument.restore(access.namespace, response.document);
|
|
165
|
+
}).pipe((effect) => withinDeadline(effect, validated.timeoutMillis));
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Read one exact current document in one owner RPC. Null means absent; withdrawals return
|
|
170
|
+
* tombstones. Denial, unavailable storage and deadlines fail typed, never become absence.
|
|
171
|
+
* Reads begun after an acknowledged write observe it or a later revision. The owner checks
|
|
172
|
+
* exact-key authority and active document scopes; source-dependent provenance policy remains
|
|
173
|
+
* application-owned. No extraction, job draining, embedding, discovery or rendering occurs.
|
|
174
|
+
*/
|
|
175
|
+
const get = Effect.fn("CloudflareMemoryClient.get")(function* (key: MemoryKey<Namespace>) {
|
|
176
|
+
const decodedKey = yield* Schema.decodeUnknownEffect(MemoryKey.Wire)(key).pipe(
|
|
177
|
+
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (!MemoryNamespace.equals(decodedKey.namespace, bound.namespace))
|
|
181
|
+
return yield* MemoryRpcError.make({ reason: "denied" });
|
|
182
|
+
|
|
183
|
+
return yield* Effect.gen(function* () {
|
|
184
|
+
const response = yield* call({
|
|
185
|
+
_tag: "Get",
|
|
186
|
+
version: 1,
|
|
187
|
+
access: bound,
|
|
188
|
+
principal,
|
|
189
|
+
key: decodedKey,
|
|
190
|
+
deadlineMillis: (yield* Clock.currentTimeMillis) + validated.timeoutMillis,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (
|
|
194
|
+
response._tag !== "Document" ||
|
|
195
|
+
!MemoryNamespace.equals(response.key.namespace, bound.namespace) ||
|
|
196
|
+
response.key.id !== decodedKey.id
|
|
197
|
+
)
|
|
198
|
+
return yield* MemoryRpcError.make({ reason: "protocol" });
|
|
199
|
+
if (response.document === null) return null;
|
|
200
|
+
if (
|
|
201
|
+
response.document.key.id !== decodedKey.id ||
|
|
202
|
+
response.document.source.id !== decodedKey.id ||
|
|
203
|
+
(response.document._tag === "ActiveMemoryDocument" &&
|
|
204
|
+
!response.document.scopes.includes(bound.scope))
|
|
205
|
+
)
|
|
206
|
+
return yield* MemoryRpcError.make({ reason: "protocol" });
|
|
207
|
+
|
|
208
|
+
yield* encodeMemoryWire(MemoryDocument.Wire, response.document, validated.maxSourceBytes);
|
|
209
|
+
|
|
210
|
+
return yield* MemoryDocument.restore(access.namespace, response.document);
|
|
211
|
+
}).pipe((effect) => withinDeadline(effect, validated.timeoutMillis));
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const revalidateSemantic = Effect.fn("CloudflareMemoryClient.revalidateSemantic")(function* (
|
|
215
|
+
found: MemoryIndexSearch<Namespace>,
|
|
216
|
+
profile: SemanticMemoryProfile,
|
|
217
|
+
limits: SemanticCandidateLimits,
|
|
218
|
+
) {
|
|
219
|
+
return yield* Effect.gen(function* () {
|
|
220
|
+
const response = yield* call({
|
|
221
|
+
_tag: "RevalidateSemantic",
|
|
222
|
+
version: 1,
|
|
223
|
+
access: bound,
|
|
224
|
+
principal,
|
|
225
|
+
found,
|
|
226
|
+
profile,
|
|
227
|
+
limits,
|
|
228
|
+
deadlineMillis: (yield* Clock.currentTimeMillis) + validated.timeoutMillis,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (response._tag !== "Semantic") return yield* MemoryRpcError.make({ reason: "protocol" });
|
|
232
|
+
|
|
233
|
+
return response.result;
|
|
234
|
+
}).pipe((effect) => withinDeadline(effect, validated.timeoutMillis));
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Revalidate in one owner RPC, then render whole passages within the caller's budget.
|
|
239
|
+
* The bound source is essential: unavailable/stale results and matches that cannot fit
|
|
240
|
+
* fail instead of silently producing empty context. No-match remains successful.
|
|
241
|
+
* The single outcome has sourceId "memory". No embedding or candidate search is performed.
|
|
242
|
+
* Use revalidate with Memory.recall for multiple readers sharing one output budget.
|
|
243
|
+
*/
|
|
244
|
+
const recall = Effect.fn("CloudflareMemoryClient.recall")(function* (
|
|
245
|
+
lookup: MemoryLookup,
|
|
246
|
+
limits: MemoryRecallLimits,
|
|
247
|
+
estimateTokens?: (text: string) => number,
|
|
248
|
+
) {
|
|
249
|
+
return yield* Memory.recall(
|
|
250
|
+
[{ id: "memory", essential: true, read: revalidate(lookup, limits) }],
|
|
251
|
+
limits,
|
|
252
|
+
estimateTokens,
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return { get, recall, revalidate, revalidateSemantic, change };
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export const CloudflareMemoryClient = {
|
|
260
|
+
/** Bind access and principal using the MemoryObjectNamespace supplied by the application. */
|
|
261
|
+
make: makeMemoryClient,
|
|
262
|
+
/** Use a resolved Worker or Durable Object binding without manual service provisioning. */
|
|
263
|
+
fromBinding: Effect.fn("CloudflareMemoryClient.fromBinding")(function* <
|
|
264
|
+
Namespace extends MemoryNamespace.Any,
|
|
265
|
+
>(
|
|
266
|
+
binding: DurableObjectNamespace<MemoryObjectRpc>,
|
|
267
|
+
options: {
|
|
268
|
+
readonly access: MemoryAccess<Namespace>;
|
|
269
|
+
readonly principal: Principal;
|
|
270
|
+
readonly rpcLimits?: MemoryRpcLimits;
|
|
271
|
+
},
|
|
272
|
+
) {
|
|
273
|
+
return yield* makeMemoryClient(options.access, options.principal, options.rpcLimits).pipe(
|
|
274
|
+
Effect.provideService(MemoryObjectNamespace, { namespace: binding }),
|
|
275
|
+
);
|
|
276
|
+
}),
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Optional activity-processor destination. Keeps domain write errors intact; transport,
|
|
281
|
+
* authorization and deadline failures become the existing MemoryStorageError contract.
|
|
282
|
+
* Receipts remain authoritative, including after caller interruption or lost replies.
|
|
283
|
+
*/
|
|
284
|
+
export const cloudflareMemoryWriterLayer = (
|
|
285
|
+
access: MemoryAccess,
|
|
286
|
+
principal: Principal,
|
|
287
|
+
limits: MemoryRpcLimits = defaultMemoryRpcLimits,
|
|
288
|
+
) =>
|
|
289
|
+
Layer.effect(
|
|
290
|
+
MemoryWriter,
|
|
291
|
+
Effect.gen(function* () {
|
|
292
|
+
const client = yield* CloudflareMemoryClient.make(access, principal, limits);
|
|
293
|
+
|
|
294
|
+
return MemoryWriter.fromAdapter({
|
|
295
|
+
change: (write) =>
|
|
296
|
+
client.change(write).pipe(
|
|
297
|
+
Effect.catchTag("MemoryRpcError", (error) =>
|
|
298
|
+
Effect.fail(
|
|
299
|
+
MemoryStorageError.make({
|
|
300
|
+
operation: `memory RPC ${error.reason}`,
|
|
301
|
+
reason:
|
|
302
|
+
error.reason === "unavailable" || error.reason === "timeout"
|
|
303
|
+
? "unavailable"
|
|
304
|
+
: "invalid-input",
|
|
305
|
+
}),
|
|
306
|
+
),
|
|
307
|
+
),
|
|
308
|
+
Effect.catchTag(["MemoryRecallError", "MemoryIndexError", "SemanticMemoryError"], () =>
|
|
309
|
+
Effect.fail(
|
|
310
|
+
MemoryStorageError.make({ operation: "memory RPC response", reason: "corrupt" }),
|
|
311
|
+
),
|
|
312
|
+
),
|
|
313
|
+
),
|
|
314
|
+
});
|
|
315
|
+
}),
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
type OwnerServices = MemoryReader | MemoryWriter | MemoryOwnerAuthorizer | MemoryOwnerIdentity;
|
|
319
|
+
|
|
320
|
+
export interface MemoryObjectInstance extends InstanceType<
|
|
321
|
+
EffectCfDurableObject.DurableObjectClass<Record<never, never>, OwnerServices>
|
|
322
|
+
> {
|
|
323
|
+
memory(encoded: string): Promise<string>;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface MemoryObjectClass {
|
|
327
|
+
new (ctx: globalThis.DurableObjectState, env: Cloudflare.Env): MemoryObjectInstance;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Dedicated SQLite owner, independent of Thread lifetimes. The host binds authorization
|
|
332
|
+
* after restoring its namespace definition from MemoryOwnerIdentity. Do not retain
|
|
333
|
+
* cleanup-scoped resources in the host Layer; it lives for the DO incarnation.
|
|
334
|
+
*/
|
|
335
|
+
const makeMemoryObject = <E>(
|
|
336
|
+
host: Layer.Layer<
|
|
337
|
+
MemoryOwnerAuthorizer,
|
|
338
|
+
E,
|
|
339
|
+
MemoryOwnerIdentity | DurableObjectState.DurableObjectState | WorkerEnvironment
|
|
340
|
+
>,
|
|
341
|
+
options: {
|
|
342
|
+
readonly storageLimits?: DoMemoryStorageLimits;
|
|
343
|
+
readonly rpcLimits?: MemoryRpcLimits;
|
|
344
|
+
readonly failpoints?: Layer.Layer<
|
|
345
|
+
MemoryMutationFailpoint,
|
|
346
|
+
never,
|
|
347
|
+
DurableObjectState.DurableObjectState
|
|
348
|
+
>;
|
|
349
|
+
} = {},
|
|
350
|
+
): MemoryObjectClass => {
|
|
351
|
+
const identity = Layer.effect(
|
|
352
|
+
MemoryOwnerIdentity,
|
|
353
|
+
Effect.gen(function* () {
|
|
354
|
+
const state = yield* DurableObjectState.DurableObjectState;
|
|
355
|
+
|
|
356
|
+
const address = yield* Schema.decodeUnknownEffect(MemoryNamespaceAddress)(
|
|
357
|
+
state.raw.id.name,
|
|
358
|
+
).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "denied" })));
|
|
359
|
+
|
|
360
|
+
return { namespace: MemoryNamespace.Any.make({ address }) };
|
|
361
|
+
}),
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
const store = Layer.unwrap(
|
|
365
|
+
Effect.map(DurableObjectState.DurableObjectState, (state) =>
|
|
366
|
+
doMemoryStoreLayerWithFailpoints(
|
|
367
|
+
state.raw.storage,
|
|
368
|
+
options.storageLimits ?? defaultDoMemoryStorageLimits,
|
|
369
|
+
),
|
|
370
|
+
),
|
|
371
|
+
).pipe(Layer.provide(options.failpoints ?? MemoryMutationFailpoint.layer));
|
|
372
|
+
|
|
373
|
+
const application = Layer.merge(store, host).pipe(Layer.provideMerge(identity));
|
|
374
|
+
|
|
375
|
+
const runtime: Layer.Layer<
|
|
376
|
+
OwnerServices,
|
|
377
|
+
E | MemoryOwnerFailure,
|
|
378
|
+
DurableObjectState.DurableObjectState | WorkerEnvironment
|
|
379
|
+
> = Layer.effectContext(
|
|
380
|
+
Effect.gen(function* () {
|
|
381
|
+
const state = yield* DurableObjectState.DurableObjectState;
|
|
382
|
+
const scope = yield* Effect.scope;
|
|
383
|
+
|
|
384
|
+
yield* Schema.decodeUnknownEffect(MemoryRpcLimits)(
|
|
385
|
+
options.rpcLimits ?? defaultMemoryRpcLimits,
|
|
386
|
+
).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })));
|
|
387
|
+
|
|
388
|
+
return yield* state.blockConcurrencyWhile(Layer.buildWithScope(application, scope));
|
|
389
|
+
}),
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
const rpc = { memory: (encoded: string) => handleMemoryOwnerRequest(encoded, options.rpcLimits) };
|
|
393
|
+
|
|
394
|
+
return EffectCfDurableObject.make<
|
|
395
|
+
OwnerServices,
|
|
396
|
+
E | MemoryOwnerFailure,
|
|
397
|
+
never,
|
|
398
|
+
never,
|
|
399
|
+
typeof rpc
|
|
400
|
+
>(runtime, { rpc });
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
export const MemoryObject = {
|
|
404
|
+
/** Build the SQLite Durable Object class with the application's owner authorization Layer. */
|
|
405
|
+
make: makeMemoryObject,
|
|
406
|
+
};
|