effect-durable-agent-cloudflare 0.1.0-alpha.10

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Advait Shinde
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # effect-durable-agent-cloudflare
2
+
3
+ Cloudflare Durable Objects host for [`effect-durable-agent`](../effect-durable-agent/README.md).
4
+
5
+ Executable examples live in the repository's shared [`examples`](../../examples) directory.
6
+
7
+ ```sh
8
+ pnpm add effect-durable-agent@alpha effect-durable-agent-cloudflare@alpha
9
+ ```
10
+
11
+ ```ts
12
+ import { EDASessionDurableObject } from "effect-durable-agent-cloudflare";
13
+
14
+ export class AgentSession extends EDASessionDurableObject<Env> {
15
+ constructor(ctx: DurableObjectState, env: Env) {
16
+ super(ctx, env, makeAgentOptions(env));
17
+ }
18
+ }
19
+ ```
20
+
21
+ The adapter maps one session to one SQLite-backed Durable Object. Ownership is
22
+ split deliberately:
23
+
24
+ - `EDASessionDurableObject` maps Cloudflare callbacks and object identity.
25
+ - `EDASessionController` coordinates the session use cases.
26
+ - `EDASessionRuntime` owns the disposable Effect runtime and keep-alive.
27
+ - `EDAWebSocketConnectionManager` owns accepted sockets and hibernation state.
28
+
29
+ Idle sockets use `ctx.acceptWebSocket`, Effect Schema attachments, and
30
+ automatic ping responses. They own no timer or resident subscriber fiber.
31
+ Register only the concrete application subclass in `wrangler.jsonc`.
32
+
33
+ Apps with an existing client wire contract may register one
34
+ `EDAWebSocketProjection`. An authenticated Worker selects it on the internal
35
+ request with `EDA_WEB_SOCKET_PROJECTION_HEADER`; the Durable Object still owns
36
+ the accepted socket and persists the app projection state through hibernation.
37
+ Do not build a Worker-side WebSocket bridge for protocol translation.
38
+
39
+ Durable Object RPC uses structured clone. Encode Schema class instances before
40
+ passing commands or batches across the Worker-to-object boundary with
41
+ `encodeEdaRpcCommand` or `encodeEdaRpcSubmittables`.
42
+
43
+ The core and host packages are published in lockstep and must use the same
44
+ version.
45
+
46
+ Public subpaths are `/durable-object`, `/session-controller`, `/openai`,
47
+ `/rpc`, and `/storage`. The WebSocket protocol and pure delivery machine
48
+ are exported from `effect-durable-agent/websocket`.
@@ -0,0 +1,41 @@
1
+ import { SessionId } from "effect-durable-agent/types/core";
2
+
3
+ //#region src/durable-object-storage.d.ts
4
+ /**
5
+ * Minimal synchronous SQL cursor surface used by Cloudflare Durable Object storage.
6
+ *
7
+ * This type is host-only on purpose: framework services should depend on
8
+ * `EDASessionStore`, not Cloudflare's SQL API.
9
+ */
10
+ interface DurableObjectSqlCursor<Row> {
11
+ readonly one: () => Row;
12
+ readonly toArray: () => Array<Row>;
13
+ }
14
+ /** Minimal synchronous SQL executor surface used by Cloudflare Durable Object storage. */
15
+ interface DurableObjectSqlStorage {
16
+ readonly exec: <Row = Record<string, unknown>>(query: string, ...bindings: ReadonlyArray<unknown>) => DurableObjectSqlCursor<Row>;
17
+ }
18
+ /**
19
+ * Durable Object storage capabilities needed by EDA SQL adapters.
20
+ *
21
+ * The `transactionSync` callback must remain purely synchronous. Adapter code
22
+ * may run SQL here; model calls, live publish, and Effect suspension must stay
23
+ * outside the callback.
24
+ */
25
+ interface DurableObjectSessionStorage {
26
+ readonly sql: DurableObjectSqlStorage;
27
+ readonly transactionSync: <A>(closure: () => A) => A;
28
+ }
29
+ /**
30
+ * Construction options for one session-scoped Durable Object session store.
31
+ *
32
+ * One Durable Object SQLite database represents one EDA session, so `sessionId`
33
+ * is fixed at layer construction and not a method parameter.
34
+ */
35
+ interface DurableObjectSessionStoreOptions {
36
+ readonly sessionId: SessionId;
37
+ readonly storage: DurableObjectSessionStorage;
38
+ }
39
+ //#endregion
40
+ export { DurableObjectSqlStorage as i, DurableObjectSessionStoreOptions as n, DurableObjectSqlCursor as r, DurableObjectSessionStorage as t };
41
+ //# sourceMappingURL=durable-object-storage-DCL2IyUe.d.ts.map
@@ -0,0 +1,111 @@
1
+ import { i as EDASessionControllerOptions, l as EDAWebSocketProjection } from "./session-controller-DibIX-C0.js";
2
+ import { DurableObject } from "cloudflare:workers";
3
+ import { CommittedCommandTerminalEvent, EDARuntimeConfig } from "effect-durable-agent/services/runtime";
4
+ import { CommittedDurableEvent } from "effect-durable-agent/services/session-store";
5
+ import { DurableTranscriptMessage } from "effect-durable-agent/domain/message-transcript";
6
+ import { EDASessionSnapshot } from "effect-durable-agent/services/session-query";
7
+ import { RunGrantResult, RunRequestOutcome } from "effect-durable-agent/domain/run-scheduling";
8
+
9
+ //#region src/durable-object.d.ts
10
+ /** Internal Worker-to-object header selecting an app-owned WebSocket projection. */
11
+ declare const EDA_WEB_SOCKET_PROJECTION_HEADER = "x-eda-websocket-projection";
12
+ /** Raw RPC shape decoded before admitting one session command. */
13
+ interface EDASessionCommandRpcInput {
14
+ readonly command: unknown;
15
+ readonly sessionId: string;
16
+ readonly trace: unknown;
17
+ }
18
+ /** Trusted Worker-to-object scheduler callback; never exposed as ordinary user ingress. */
19
+ interface EDASessionGrantRunRpcInput {
20
+ readonly command: unknown;
21
+ readonly sessionId: string;
22
+ readonly trace: unknown;
23
+ }
24
+ /** Raw RPC shape decoded before admitting a durable command/app-event batch. */
25
+ interface EDASessionSubmitBatchRpcInput {
26
+ readonly items: ReadonlyArray<unknown>;
27
+ readonly sessionId: string;
28
+ readonly trace: unknown;
29
+ }
30
+ /** Raw RPC shape for read/destroy operations that only need a session id. */
31
+ interface EDASessionScopedRpcInput {
32
+ readonly sessionId: string;
33
+ readonly trace: unknown;
34
+ }
35
+ /** Raw RPC shape for WebSocket event-stream upgrades with a resume cursor. */
36
+ interface EDASessionEventsRpcInput {
37
+ readonly afterSeq?: number;
38
+ readonly sessionId: string;
39
+ readonly trace: unknown;
40
+ }
41
+ /** Raw RPC shape decoded before blocking on one command terminal event. */
42
+ interface EDASessionBlockOnCommandRpcInput {
43
+ readonly afterSeq?: number;
44
+ readonly commandId: string;
45
+ readonly sessionId: string;
46
+ readonly trace: unknown;
47
+ }
48
+ /** Raw identity decoded at the trusted reconciliation RPC boundary. */
49
+ interface EDASessionRunRequestOutcomeRpcInput extends EDASessionScopedRpcInput {
50
+ readonly requestId: string;
51
+ }
52
+ /** EDA RPC methods required by the session namespace helper. */
53
+ interface EDASessionRpcSurface {
54
+ readonly runRequestOutcome: (input: EDASessionRunRequestOutcomeRpcInput) => Promise<RunRequestOutcome>;
55
+ readonly grantRun: (input: EDASessionGrantRunRpcInput) => Promise<RunGrantResult>;
56
+ readonly submit: (input: EDASessionCommandRpcInput) => Promise<CommittedDurableEvent>;
57
+ readonly submitBatch: (input: EDASessionSubmitBatchRpcInput) => Promise<ReadonlyArray<CommittedDurableEvent>>;
58
+ readonly submitAndBlock: (input: EDASessionCommandRpcInput) => Promise<CommittedCommandTerminalEvent>;
59
+ readonly blockOnCommand: (input: EDASessionBlockOnCommandRpcInput) => Promise<CommittedCommandTerminalEvent>;
60
+ readonly snapshot: (input: EDASessionScopedRpcInput) => Promise<EDASessionSnapshot>;
61
+ readonly messages: (input: EDASessionScopedRpcInput) => Promise<ReadonlyArray<DurableTranscriptMessage>>;
62
+ readonly destroySession: (input: EDASessionScopedRpcInput) => Promise<void>;
63
+ }
64
+ /** Constructor options for concrete app subclasses of the EDA Durable Object base. */
65
+ type EDASessionDurableObjectOptions<ProjectionState extends object = never> = Omit<EDASessionControllerOptions<ProjectionState>, "background" | "getWebSockets" | "storage">;
66
+ /** Resolve a concrete EDA session Durable Object binding by domain session id. */
67
+ declare const getEDASessionDurableObjectByName: <T extends Rpc.DurableObjectBranded & EDASessionRpcSurface>(namespace: DurableObjectNamespace<T>, sessionId: string) => DurableObjectStub<T>;
68
+ /**
69
+ * Base class for raw Cloudflare Durable Object hosts of one EDA session.
70
+ *
71
+ * Concrete products should extend this class, pass provider/tool layers from
72
+ * their own constructor, and register only that subclass in Wrangler. The base
73
+ * class is deliberately not exported from `workers/app.ts` and owns no binding
74
+ * name or routing convention.
75
+ */
76
+ declare abstract class EDASessionDurableObject<EnvType extends object = object, ProjectionState extends object = never> extends DurableObject<EnvType> {
77
+ #private;
78
+ protected constructor(ctx: DurableObjectState, env: EnvType, options: EDASessionDurableObjectOptions<ProjectionState>);
79
+ fetch(request: Request): Promise<Response>;
80
+ submit(input: EDASessionCommandRpcInput): Promise<CommittedDurableEvent>;
81
+ /** Reconcile lost grant replies from durable request and run facts. */
82
+ runRequestOutcome(input: EDASessionRunRequestOutcomeRpcInput): Promise<RunRequestOutcome>;
83
+ /** Trusted RPC only: the calling Worker owns scheduler authentication and authorization. */
84
+ grantRun(input: EDASessionGrantRunRpcInput): Promise<RunGrantResult>;
85
+ submitBatch(input: EDASessionSubmitBatchRpcInput): Promise<ReadonlyArray<CommittedDurableEvent>>;
86
+ submitAndBlock(input: EDASessionCommandRpcInput): Promise<CommittedCommandTerminalEvent>;
87
+ blockOnCommand(input: EDASessionBlockOnCommandRpcInput): Promise<CommittedCommandTerminalEvent>;
88
+ snapshot(input: EDASessionScopedRpcInput): Promise<EDASessionSnapshot>;
89
+ messages(input: EDASessionScopedRpcInput): Promise<ReadonlyArray<DurableTranscriptMessage>>;
90
+ webSocketMessage(webSocket: WebSocket, message: string | ArrayBuffer): Promise<void>;
91
+ webSocketClose(webSocket: WebSocket, _code: number, _reason: string, _wasClean: boolean): Promise<void>;
92
+ webSocketError(webSocket: WebSocket, _error: unknown): Promise<void>;
93
+ destroySession(input: EDASessionScopedRpcInput): Promise<void>;
94
+ alarm(): Promise<void>;
95
+ private parseSessionId;
96
+ private sessionIdFromObjectName;
97
+ }
98
+ /** Convenience config constructor for subclasses that need only provider/model ids. */
99
+ declare const edaRuntimeConfig: (input: {
100
+ readonly maxToolCallsPerRun?: number;
101
+ readonly modelId: string;
102
+ readonly provider: string;
103
+ readonly systemPrompt?: string;
104
+ }) => EDARuntimeConfig;
105
+ /** Encode committed events into structured-clone-safe Durable Object RPC payloads. */
106
+ declare const encodeEdaRpcCommittedDurableEvent: (event: CommittedDurableEvent) => CommittedDurableEvent;
107
+ /** Encode runtime snapshots into structured-clone-safe Durable Object RPC payloads. */
108
+ declare const encodeEdaRpcSessionSnapshot: (snapshot: EDASessionSnapshot) => EDASessionSnapshot;
109
+ //#endregion
110
+ export { EDASessionBlockOnCommandRpcInput, EDASessionCommandRpcInput, EDASessionDurableObject, EDASessionDurableObjectOptions, EDASessionEventsRpcInput, EDASessionGrantRunRpcInput, EDASessionRpcSurface, EDASessionRunRequestOutcomeRpcInput, EDASessionScopedRpcInput, EDASessionSubmitBatchRpcInput, type EDAWebSocketProjection, EDA_WEB_SOCKET_PROJECTION_HEADER, edaRuntimeConfig, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, getEDASessionDurableObjectByName };
111
+ //# sourceMappingURL=durable-object.d.ts.map
@@ -0,0 +1,210 @@
1
+ import { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcDurableEvent } from "./rpc.js";
2
+ import { t as EDASessionController } from "./session-controller-C31DhUNp.js";
3
+ import { DurableObject } from "cloudflare:workers";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Schema from "effect/Schema";
6
+ import { EDACommand, GrantRunCommand } from "effect-durable-agent/types/commands";
7
+ import { CommandId, RunRequestId, SequenceNumber, SessionId } from "effect-durable-agent/types/core";
8
+ import { EDATraceMetadata, makeEDATraceMetadataFromParent, makeRootEDATraceMetadata, parseEDATraceparent } from "effect-durable-agent/types/tracing";
9
+ import { EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE } from "effect-durable-agent/websocket";
10
+ //#region src/durable-object.ts
11
+ /** Internal Worker-to-object header selecting an app-owned WebSocket projection. */
12
+ const EDA_WEB_SOCKET_PROJECTION_HEADER = "x-eda-websocket-projection";
13
+ /** Resolve a concrete EDA session Durable Object binding by domain session id. */
14
+ const getEDASessionDurableObjectByName = (namespace, sessionId) => namespace.getByName(sessionId);
15
+ /**
16
+ * Base class for raw Cloudflare Durable Object hosts of one EDA session.
17
+ *
18
+ * Concrete products should extend this class, pass provider/tool layers from
19
+ * their own constructor, and register only that subclass in Wrangler. The base
20
+ * class is deliberately not exported from `workers/app.ts` and owns no binding
21
+ * name or routing convention.
22
+ */
23
+ var EDASessionDurableObject = class extends DurableObject {
24
+ #controller;
25
+ #webSocketProjectionId;
26
+ constructor(ctx, env, options) {
27
+ super(ctx, env);
28
+ this.#webSocketProjectionId = options.webSocketProjection?.id;
29
+ const storage = toEDASessionStorage(this.ctx.storage);
30
+ this.#controller = new EDASessionController({
31
+ ...options,
32
+ background: this.ctx,
33
+ getWebSockets: () => this.ctx.getWebSockets(),
34
+ storage
35
+ });
36
+ this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair(EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE));
37
+ this.ctx.blockConcurrencyWhile(async () => {
38
+ await Effect.runPromise(EDASessionController.migrate(storage));
39
+ });
40
+ }
41
+ async fetch(request) {
42
+ if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") return new Response("Expected WebSocket upgrade.", { status: 426 });
43
+ const url = new URL(request.url);
44
+ const sessionIdRaw = url.searchParams.get("sessionId");
45
+ if (sessionIdRaw === null) return new Response("Missing sessionId.", { status: 400 });
46
+ const afterSeqRaw = url.searchParams.get("afterSeq");
47
+ const afterSeq = afterSeqRaw === null ? void 0 : Number(afterSeqRaw);
48
+ if (afterSeq !== void 0 && (!Number.isInteger(afterSeq) || afterSeq < 0)) return new Response("Invalid afterSeq.", { status: 400 });
49
+ const projectionId = request.headers.get("x-eda-websocket-projection") ?? void 0;
50
+ if (projectionId !== void 0 && projectionId !== this.#webSocketProjectionId) return new Response("Unsupported WebSocket projection.", { status: 400 });
51
+ const sessionId = this.parseSessionId(sessionIdRaw);
52
+ const trace = traceMetadataFromRequest(request);
53
+ const prepared = await this.#controller.prepareEventWebSocket({
54
+ ...afterSeq === void 0 ? {} : { afterSeq: SequenceNumber.make(afterSeq) },
55
+ sessionId,
56
+ trace,
57
+ ...projectionId === void 0 ? {} : { projectionId }
58
+ });
59
+ const pair = new WebSocketPair();
60
+ const client = pair[0];
61
+ const server = pair[1];
62
+ this.ctx.acceptWebSocket(server);
63
+ await this.#controller.acceptPreparedEventWebSocket({
64
+ ...prepared,
65
+ webSocket: server
66
+ });
67
+ return new Response(null, {
68
+ status: 101,
69
+ webSocket: client
70
+ });
71
+ }
72
+ async submit(input) {
73
+ return encodeEdaRpcCommittedDurableEvent(await this.#controller.submit({
74
+ command: await decodeCommand(input.command),
75
+ sessionId: this.parseSessionId(input.sessionId),
76
+ trace: decodeTraceMetadata(input.trace)
77
+ }));
78
+ }
79
+ /** Reconcile lost grant replies from durable request and run facts. */
80
+ async runRequestOutcome(input) {
81
+ return await this.#controller.runRequestOutcome({
82
+ requestId: Schema.decodeUnknownSync(RunRequestId)(input.requestId),
83
+ sessionId: this.parseSessionId(input.sessionId),
84
+ trace: decodeTraceMetadata(input.trace)
85
+ });
86
+ }
87
+ /** Trusted RPC only: the calling Worker owns scheduler authentication and authorization. */
88
+ async grantRun(input) {
89
+ const command = Schema.decodeUnknownSync(GrantRunCommand)(input.command);
90
+ return await this.#controller.grantRun({
91
+ command,
92
+ sessionId: this.parseSessionId(input.sessionId),
93
+ trace: decodeTraceMetadata(input.trace)
94
+ });
95
+ }
96
+ async submitBatch(input) {
97
+ return (await this.#controller.submitBatch({
98
+ items: await decodeSubmittables(input.items),
99
+ sessionId: this.parseSessionId(input.sessionId),
100
+ trace: decodeTraceMetadata(input.trace)
101
+ })).map(encodeEdaRpcCommittedDurableEvent);
102
+ }
103
+ async submitAndBlock(input) {
104
+ return encodeEdaRpcCommittedDurableEvent(await this.#controller.submitAndBlock({
105
+ command: await decodeCommand(input.command),
106
+ sessionId: this.parseSessionId(input.sessionId),
107
+ trace: decodeTraceMetadata(input.trace)
108
+ }));
109
+ }
110
+ async blockOnCommand(input) {
111
+ return encodeEdaRpcCommittedDurableEvent(await this.#controller.blockOnCommand({
112
+ ...input.afterSeq === void 0 ? {} : { afterSeq: SequenceNumber.make(input.afterSeq) },
113
+ commandId: CommandId.make(input.commandId),
114
+ sessionId: this.parseSessionId(input.sessionId),
115
+ trace: decodeTraceMetadata(input.trace)
116
+ }));
117
+ }
118
+ async snapshot(input) {
119
+ return encodeEdaRpcSessionSnapshot(await this.#controller.snapshot({
120
+ sessionId: this.parseSessionId(input.sessionId),
121
+ trace: decodeTraceMetadata(input.trace)
122
+ }));
123
+ }
124
+ async messages(input) {
125
+ return await this.#controller.messages({
126
+ sessionId: this.parseSessionId(input.sessionId),
127
+ trace: decodeTraceMetadata(input.trace)
128
+ });
129
+ }
130
+ async webSocketMessage(webSocket, message) {
131
+ await this.#controller.webSocketMessage(webSocket, message);
132
+ }
133
+ async webSocketClose(webSocket, _code, _reason, _wasClean) {
134
+ this.#controller.webSocketClose(webSocket);
135
+ }
136
+ async webSocketError(webSocket, _error) {
137
+ this.#controller.webSocketError(webSocket);
138
+ }
139
+ async destroySession(input) {
140
+ await this.#controller.destroy({
141
+ sessionId: this.parseSessionId(input.sessionId),
142
+ trace: decodeTraceMetadata(input.trace)
143
+ });
144
+ await this.ctx.storage.deleteAlarm();
145
+ await this.ctx.storage.deleteAll();
146
+ }
147
+ async alarm() {
148
+ const sessionId = this.sessionIdFromObjectName();
149
+ await this.#controller.alarm(sessionId === void 0 ? void 0 : {
150
+ sessionId,
151
+ trace: makeRootEDATraceMetadata()
152
+ });
153
+ }
154
+ parseSessionId(input) {
155
+ const sessionId = SessionId.make(input);
156
+ const objectName = this.ctx.id.name;
157
+ if (objectName !== void 0 && objectName !== sessionId) throw new Error(`EDA Durable Object name ${objectName} cannot serve session ${sessionId}`);
158
+ return sessionId;
159
+ }
160
+ sessionIdFromObjectName() {
161
+ return this.ctx.id.name === void 0 ? void 0 : SessionId.make(this.ctx.id.name);
162
+ }
163
+ };
164
+ const toEDASessionStorage = (storage) => {
165
+ return storage;
166
+ };
167
+ const decodeTraceMetadata = (input) => Schema.decodeUnknownSync(EDATraceMetadata)(input);
168
+ const traceMetadataFromRequest = (request) => {
169
+ const parent = parseEDATraceparent(request.headers.get("traceparent"), request.headers.get("tracestate"));
170
+ return parent === null ? makeRootEDATraceMetadata() : makeEDATraceMetadataFromParent(parent);
171
+ };
172
+ /** Convenience config constructor for subclasses that need only provider/model ids. */
173
+ const edaRuntimeConfig = (input) => ({
174
+ modelSelection: {
175
+ modelId: input.modelId,
176
+ provider: input.provider
177
+ },
178
+ ...input.maxToolCallsPerRun === void 0 ? {} : { maxToolCallsPerRun: input.maxToolCallsPerRun },
179
+ ...input.systemPrompt === void 0 ? {} : { systemPrompt: input.systemPrompt }
180
+ });
181
+ /** Encode committed events into structured-clone-safe Durable Object RPC payloads. */
182
+ const encodeEdaRpcCommittedDurableEvent = (event) => ({
183
+ position: event.position,
184
+ event: encodeEdaRpcDurableEvent(event.event)
185
+ });
186
+ /** Encode runtime snapshots into structured-clone-safe Durable Object RPC payloads. */
187
+ const encodeEdaRpcSessionSnapshot = (snapshot) => ({
188
+ ...snapshot,
189
+ reducerStates: new Map(snapshot.reducerStates),
190
+ state: {
191
+ ...snapshot.state,
192
+ commands: new Map(Array.from(snapshot.state.commands, ([commandId, record]) => [commandId, encodeCommandCarrierForRpc(record)])),
193
+ commandQueues: {
194
+ ...snapshot.state.commandQueues,
195
+ activeControlCommands: snapshot.state.commandQueues.activeControlCommands.map(encodeCommandCarrierForRpc),
196
+ pendingCommands: snapshot.state.commandQueues.pendingCommands.map(encodeCommandCarrierForRpc),
197
+ queuedCommands: snapshot.state.commandQueues.queuedCommands.map(encodeCommandCarrierForRpc)
198
+ }
199
+ }
200
+ });
201
+ const encodeCommandCarrierForRpc = (value) => value.command === void 0 ? value : {
202
+ ...value,
203
+ command: Schema.encodeSync(EDACommand)(value.command)
204
+ };
205
+ const decodeCommand = (input) => Effect.runPromise(decodeEdaRpcCommand(input));
206
+ const decodeSubmittables = (input) => Effect.runPromise(decodeEdaRpcSubmittables(input));
207
+ //#endregion
208
+ export { EDASessionDurableObject, EDA_WEB_SOCKET_PROJECTION_HEADER, edaRuntimeConfig, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, getEDASessionDurableObjectByName };
209
+
210
+ //# sourceMappingURL=durable-object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-object.js","names":["#controller","#webSocketProjectionId"],"sources":["../src/durable-object.ts"],"sourcesContent":["import { DurableObject } from \"cloudflare:workers\";\nimport * as Effect from \"effect/Effect\";\nimport * as Schema from \"effect/Schema\";\n\nimport type { DurableTranscriptMessage } from \"effect-durable-agent/domain/message-transcript\";\nimport {\n decodeEdaRpcCommand,\n decodeEdaRpcSubmittables,\n encodeEdaRpcDurableEvent,\n} from \"./rpc-codec\";\nimport type { EDASessionSnapshot } from \"effect-durable-agent/services/session-query\";\nimport type { CommittedDurableEvent as CommittedDurableEventValue } from \"effect-durable-agent/services/session-store\";\nimport type {\n CommittedCommandTerminalEvent,\n EDARuntimeConfig,\n} from \"effect-durable-agent/services/runtime\";\nimport {\n EDACommand,\n GrantRunCommand,\n type EDACommand as EDACommandValue,\n} from \"effect-durable-agent/types/commands\";\nimport type { RunGrantResult, RunRequestOutcome } from \"effect-durable-agent/domain/run-scheduling\";\nimport type { EDASubmittable } from \"effect-durable-agent/services/session-state\";\nimport {\n CommandId,\n RunRequestId,\n SequenceNumber,\n SessionId,\n} from \"effect-durable-agent/types/core\";\nimport {\n EDATraceMetadata,\n makeEDATraceMetadataFromParent,\n makeRootEDATraceMetadata,\n parseEDATraceparent,\n} from \"effect-durable-agent/types/tracing\";\nimport {\n EDASessionController,\n type EDASessionControllerOptions,\n type EDASessionDurableObjectStorage,\n} from \"./session-controller\";\nimport {\n EDA_WEB_SOCKET_PING_MESSAGE,\n EDA_WEB_SOCKET_PONG_MESSAGE,\n} from \"effect-durable-agent/websocket\";\nexport type { EDAWebSocketProjection } from \"./websocket/projection\";\n/** Internal Worker-to-object header selecting an app-owned WebSocket projection. */\nexport const EDA_WEB_SOCKET_PROJECTION_HEADER = \"x-eda-websocket-projection\";\n\n/** Raw RPC shape decoded before admitting one session command. */\nexport interface EDASessionCommandRpcInput {\n readonly command: unknown;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Trusted Worker-to-object scheduler callback; never exposed as ordinary user ingress. */\nexport interface EDASessionGrantRunRpcInput {\n readonly command: unknown;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape decoded before admitting a durable command/app-event batch. */\nexport interface EDASessionSubmitBatchRpcInput {\n readonly items: ReadonlyArray<unknown>;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape for read/destroy operations that only need a session id. */\nexport interface EDASessionScopedRpcInput {\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape for WebSocket event-stream upgrades with a resume cursor. */\nexport interface EDASessionEventsRpcInput {\n readonly afterSeq?: number;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape decoded before blocking on one command terminal event. */\nexport interface EDASessionBlockOnCommandRpcInput {\n readonly afterSeq?: number;\n readonly commandId: string;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw identity decoded at the trusted reconciliation RPC boundary. */\nexport interface EDASessionRunRequestOutcomeRpcInput extends EDASessionScopedRpcInput {\n readonly requestId: string;\n}\n\n/** EDA RPC methods required by the session namespace helper. */\nexport interface EDASessionRpcSurface {\n readonly runRequestOutcome: (\n input: EDASessionRunRequestOutcomeRpcInput,\n ) => Promise<RunRequestOutcome>;\n readonly grantRun: (input: EDASessionGrantRunRpcInput) => Promise<RunGrantResult>;\n readonly submit: (input: EDASessionCommandRpcInput) => Promise<CommittedDurableEventValue>;\n readonly submitBatch: (\n input: EDASessionSubmitBatchRpcInput,\n ) => Promise<ReadonlyArray<CommittedDurableEventValue>>;\n readonly submitAndBlock: (\n input: EDASessionCommandRpcInput,\n ) => Promise<CommittedCommandTerminalEvent>;\n readonly blockOnCommand: (\n input: EDASessionBlockOnCommandRpcInput,\n ) => Promise<CommittedCommandTerminalEvent>;\n readonly snapshot: (input: EDASessionScopedRpcInput) => Promise<EDASessionSnapshot>;\n readonly messages: (\n input: EDASessionScopedRpcInput,\n ) => Promise<ReadonlyArray<DurableTranscriptMessage>>;\n readonly destroySession: (input: EDASessionScopedRpcInput) => Promise<void>;\n}\n\n/** Constructor options for concrete app subclasses of the EDA Durable Object base. */\nexport type EDASessionDurableObjectOptions<ProjectionState extends object = never> = Omit<\n EDASessionControllerOptions<ProjectionState>,\n \"background\" | \"getWebSockets\" | \"storage\"\n>;\n\n/** Resolve a concrete EDA session Durable Object binding by domain session id. */\nexport const getEDASessionDurableObjectByName = <\n T extends Rpc.DurableObjectBranded & EDASessionRpcSurface,\n>(\n namespace: DurableObjectNamespace<T>,\n sessionId: string,\n): DurableObjectStub<T> => namespace.getByName(sessionId);\n\n/**\n * Base class for raw Cloudflare Durable Object hosts of one EDA session.\n *\n * Concrete products should extend this class, pass provider/tool layers from\n * their own constructor, and register only that subclass in Wrangler. The base\n * class is deliberately not exported from `workers/app.ts` and owns no binding\n * name or routing convention.\n */\nexport abstract class EDASessionDurableObject<\n EnvType extends object = object,\n ProjectionState extends object = never,\n> extends DurableObject<EnvType> {\n readonly #controller: EDASessionController<ProjectionState>;\n readonly #webSocketProjectionId: string | undefined;\n\n protected constructor(\n ctx: DurableObjectState,\n env: EnvType,\n options: EDASessionDurableObjectOptions<ProjectionState>,\n ) {\n super(ctx, env);\n this.#webSocketProjectionId = options.webSocketProjection?.id;\n const storage = toEDASessionStorage(this.ctx.storage);\n this.#controller = new EDASessionController({\n ...options,\n background: this.ctx,\n getWebSockets: () => this.ctx.getWebSockets(),\n storage,\n });\n\n // Answer client liveness pings at the runtime layer so idle sockets never\n // wake a hibernated object.\n this.ctx.setWebSocketAutoResponse(\n new WebSocketRequestResponsePair(EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE),\n );\n\n this.ctx.blockConcurrencyWhile(async () => {\n await Effect.runPromise(EDASessionController.migrate(storage));\n });\n }\n\n async fetch(request: Request): Promise<Response> {\n const upgrade = request.headers.get(\"Upgrade\");\n if (upgrade?.toLowerCase() !== \"websocket\") {\n return new Response(\"Expected WebSocket upgrade.\", { status: 426 });\n }\n\n const url = new URL(request.url);\n const sessionIdRaw = url.searchParams.get(\"sessionId\");\n if (sessionIdRaw === null) {\n return new Response(\"Missing sessionId.\", { status: 400 });\n }\n const afterSeqRaw = url.searchParams.get(\"afterSeq\");\n const afterSeq = afterSeqRaw === null ? undefined : Number(afterSeqRaw);\n if (afterSeq !== undefined && (!Number.isInteger(afterSeq) || afterSeq < 0)) {\n return new Response(\"Invalid afterSeq.\", { status: 400 });\n }\n const projectionId = request.headers.get(EDA_WEB_SOCKET_PROJECTION_HEADER) ?? undefined;\n if (projectionId !== undefined && projectionId !== this.#webSocketProjectionId) {\n return new Response(\"Unsupported WebSocket projection.\", { status: 400 });\n }\n\n const sessionId = this.parseSessionId(sessionIdRaw);\n const trace = traceMetadataFromRequest(request);\n const prepared = await this.#controller.prepareEventWebSocket({\n ...(afterSeq === undefined ? {} : { afterSeq: SequenceNumber.make(afterSeq) }),\n sessionId,\n trace,\n ...(projectionId === undefined ? {} : { projectionId }),\n });\n\n const pair = new WebSocketPair();\n const client = pair[0];\n const server = pair[1];\n this.ctx.acceptWebSocket(server);\n await this.#controller.acceptPreparedEventWebSocket({\n ...prepared,\n webSocket: server,\n });\n return new Response(null, { status: 101, webSocket: client });\n }\n\n async submit(input: EDASessionCommandRpcInput): Promise<CommittedDurableEventValue> {\n const committed = await this.#controller.submit({\n command: await decodeCommand(input.command),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcCommittedDurableEvent(committed);\n }\n\n /** Reconcile lost grant replies from durable request and run facts. */\n async runRequestOutcome(input: EDASessionRunRequestOutcomeRpcInput): Promise<RunRequestOutcome> {\n return await this.#controller.runRequestOutcome({\n requestId: Schema.decodeUnknownSync(RunRequestId)(input.requestId),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n }\n\n /** Trusted RPC only: the calling Worker owns scheduler authentication and authorization. */\n async grantRun(input: EDASessionGrantRunRpcInput): Promise<RunGrantResult> {\n const command = Schema.decodeUnknownSync(GrantRunCommand)(input.command);\n const result = await this.#controller.grantRun({\n command,\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return result;\n }\n\n async submitBatch(\n input: EDASessionSubmitBatchRpcInput,\n ): Promise<ReadonlyArray<CommittedDurableEventValue>> {\n const committed = await this.#controller.submitBatch({\n items: await decodeSubmittables(input.items),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return committed.map(encodeEdaRpcCommittedDurableEvent);\n }\n\n async submitAndBlock(input: EDASessionCommandRpcInput): Promise<CommittedCommandTerminalEvent> {\n const committed = await this.#controller.submitAndBlock({\n command: await decodeCommand(input.command),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcCommittedDurableEvent(committed) as CommittedCommandTerminalEvent;\n }\n\n async blockOnCommand(\n input: EDASessionBlockOnCommandRpcInput,\n ): Promise<CommittedCommandTerminalEvent> {\n const committed = await this.#controller.blockOnCommand({\n ...(input.afterSeq === undefined ? {} : { afterSeq: SequenceNumber.make(input.afterSeq) }),\n commandId: CommandId.make(input.commandId),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcCommittedDurableEvent(committed) as CommittedCommandTerminalEvent;\n }\n\n async snapshot(input: EDASessionScopedRpcInput): Promise<EDASessionSnapshot> {\n const snapshot = await this.#controller.snapshot({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcSessionSnapshot(snapshot);\n }\n\n async messages(\n input: EDASessionScopedRpcInput,\n ): Promise<ReadonlyArray<DurableTranscriptMessage>> {\n return await this.#controller.messages({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n }\n\n async webSocketMessage(webSocket: WebSocket, message: string | ArrayBuffer): Promise<void> {\n await this.#controller.webSocketMessage(webSocket, message);\n }\n\n async webSocketClose(\n webSocket: WebSocket,\n _code: number,\n _reason: string,\n _wasClean: boolean,\n ): Promise<void> {\n this.#controller.webSocketClose(webSocket);\n }\n\n async webSocketError(webSocket: WebSocket, _error: unknown): Promise<void> {\n this.#controller.webSocketError(webSocket);\n }\n\n async destroySession(input: EDASessionScopedRpcInput): Promise<void> {\n await this.#controller.destroy({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n await this.ctx.storage.deleteAlarm();\n await this.ctx.storage.deleteAll();\n }\n\n async alarm(): Promise<void> {\n const sessionId = this.sessionIdFromObjectName();\n await this.#controller.alarm(\n sessionId === undefined ? undefined : { sessionId, trace: makeRootEDATraceMetadata() },\n );\n }\n\n private parseSessionId(input: string): SessionId {\n const sessionId = SessionId.make(input);\n const objectName = this.ctx.id.name;\n if (objectName !== undefined && objectName !== sessionId) {\n throw new Error(`EDA Durable Object name ${objectName} cannot serve session ${sessionId}`);\n }\n return sessionId;\n }\n\n private sessionIdFromObjectName(): SessionId | undefined {\n return this.ctx.id.name === undefined ? undefined : SessionId.make(this.ctx.id.name);\n }\n}\n\nconst toEDASessionStorage = (storage: DurableObjectStorage): EDASessionDurableObjectStorage => {\n // SAFETY: the adapter interface is the exact SQL, transaction, and alarm subset\n // of DurableObjectStorage; its looser row generic also supports test implementations.\n return storage as EDASessionDurableObjectStorage;\n};\n\nconst decodeTraceMetadata = (input: unknown): EDATraceMetadata =>\n Schema.decodeUnknownSync(EDATraceMetadata)(input);\n\nconst traceMetadataFromRequest = (request: Request): EDATraceMetadata => {\n const parent = parseEDATraceparent(\n request.headers.get(\"traceparent\"),\n request.headers.get(\"tracestate\"),\n );\n return parent === null ? makeRootEDATraceMetadata() : makeEDATraceMetadataFromParent(parent);\n};\n\n/** Convenience config constructor for subclasses that need only provider/model ids. */\nexport const edaRuntimeConfig = (input: {\n readonly maxToolCallsPerRun?: number;\n readonly modelId: string;\n readonly provider: string;\n readonly systemPrompt?: string;\n}): EDARuntimeConfig => ({\n modelSelection: { modelId: input.modelId, provider: input.provider },\n ...(input.maxToolCallsPerRun === undefined\n ? {}\n : { maxToolCallsPerRun: input.maxToolCallsPerRun }),\n ...(input.systemPrompt === undefined ? {} : { systemPrompt: input.systemPrompt }),\n});\n\n/** Encode committed events into structured-clone-safe Durable Object RPC payloads. */\nexport const encodeEdaRpcCommittedDurableEvent = (\n event: CommittedDurableEventValue,\n): CommittedDurableEventValue => ({\n position: event.position,\n event: encodeEdaRpcDurableEvent(event.event),\n});\n\n/** Encode runtime snapshots into structured-clone-safe Durable Object RPC payloads. */\nexport const encodeEdaRpcSessionSnapshot = (snapshot: EDASessionSnapshot): EDASessionSnapshot => ({\n ...snapshot,\n reducerStates: new Map(snapshot.reducerStates),\n state: {\n ...snapshot.state,\n commands: new Map(\n Array.from(snapshot.state.commands, ([commandId, record]) => [\n commandId,\n encodeCommandCarrierForRpc(record),\n ]),\n ),\n commandQueues: {\n ...snapshot.state.commandQueues,\n activeControlCommands: snapshot.state.commandQueues.activeControlCommands.map(\n encodeCommandCarrierForRpc,\n ),\n pendingCommands: snapshot.state.commandQueues.pendingCommands.map(encodeCommandCarrierForRpc),\n queuedCommands: snapshot.state.commandQueues.queuedCommands.map(encodeCommandCarrierForRpc),\n },\n },\n});\n\nconst encodeCommandCarrierForRpc = <A extends { readonly command?: EDACommandValue }>(\n value: A,\n): A =>\n value.command === undefined\n ? value\n : {\n ...value,\n command: Schema.encodeSync(EDACommand)(value.command) as EDACommandValue,\n };\n\nconst decodeCommand = (input: unknown): Promise<EDACommandValue> =>\n Effect.runPromise(decodeEdaRpcCommand(input));\n\nconst decodeSubmittables = (\n input: ReadonlyArray<unknown>,\n): Promise<ReadonlyArray<EDASubmittable>> => Effect.runPromise(decodeEdaRpcSubmittables(input));\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { i as DurableObjectSqlStorage, n as DurableObjectSessionStoreOptions, r as DurableObjectSqlCursor, t as DurableObjectSessionStorage } from "./durable-object-storage-DCL2IyUe.js";
2
+ import { a as EDASessionEventsInput, c as EDASessionSubmitInput, d as EDAWebSocketProjectionInitial, f as EDAWebSocketProjectionResult, h as makeEDADurableObjectRuntimeLayer, i as EDASessionControllerOptions, l as EDAWebSocketProjection, m as EDASessionDurableObjectStorage, n as EDASessionBlockOnCommandInput, o as EDASessionScopedInput, p as EDADurableObjectRuntimeLayerOptions, r as EDASessionController, s as EDASessionSubmitBatchInput, t as EDAPreparedEventWebSocket, u as EDAWebSocketProjectionId } from "./session-controller-DibIX-C0.js";
3
+ import { EDASessionBlockOnCommandRpcInput, EDASessionCommandRpcInput, EDASessionDurableObject, EDASessionDurableObjectOptions, EDASessionEventsRpcInput, EDASessionGrantRunRpcInput, EDASessionRpcSurface, EDASessionRunRequestOutcomeRpcInput, EDASessionScopedRpcInput, EDASessionSubmitBatchRpcInput, EDA_WEB_SOCKET_PROJECTION_HEADER, edaRuntimeConfig, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, getEDASessionDurableObjectByName } from "./durable-object.js";
4
+ import { EDAOpenAiModelConfig, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectOpenAiModelResolverLayer } from "./openai.js";
5
+ import { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcCommand, encodeEdaRpcDurableEvent, encodeEdaRpcSubmittables } from "./rpc.js";
6
+ export { DurableObjectSessionStorage, DurableObjectSessionStoreOptions, DurableObjectSqlCursor, DurableObjectSqlStorage, EDADurableObjectRuntimeLayerOptions, EDAOpenAiModelConfig, EDAPreparedEventWebSocket, EDASessionBlockOnCommandInput, EDASessionBlockOnCommandRpcInput, EDASessionCommandRpcInput, EDASessionController, EDASessionControllerOptions, EDASessionDurableObject, EDASessionDurableObjectOptions, EDASessionDurableObjectStorage, EDASessionEventsInput, EDASessionEventsRpcInput, EDASessionGrantRunRpcInput, EDASessionRpcSurface, EDASessionRunRequestOutcomeRpcInput, EDASessionScopedInput, EDASessionScopedRpcInput, EDASessionSubmitBatchInput, EDASessionSubmitBatchRpcInput, EDASessionSubmitInput, EDAWebSocketProjection, EDAWebSocketProjectionId, EDAWebSocketProjectionInitial, EDAWebSocketProjectionResult, EDA_WEB_SOCKET_PROJECTION_HEADER, decodeEdaRpcCommand, decodeEdaRpcSubmittables, edaRuntimeConfig, encodeEdaRpcCommand, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcDurableEvent, encodeEdaRpcSessionSnapshot, encodeEdaRpcSubmittables, getEDASessionDurableObjectByName, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectOpenAiModelResolverLayer, makeEDADurableObjectRuntimeLayer };
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcCommand, encodeEdaRpcDurableEvent, encodeEdaRpcSubmittables } from "./rpc.js";
2
+ import { n as EDAWebSocketProjectionId, r as makeEDADurableObjectRuntimeLayer, t as EDASessionController } from "./session-controller-C31DhUNp.js";
3
+ import { EDASessionDurableObject, EDA_WEB_SOCKET_PROJECTION_HEADER, edaRuntimeConfig, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, getEDASessionDurableObjectByName } from "./durable-object.js";
4
+ import "./storage.js";
5
+ import { makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectOpenAiModelResolverLayer } from "./openai.js";
6
+ export { EDASessionController, EDASessionDurableObject, EDAWebSocketProjectionId, EDA_WEB_SOCKET_PROJECTION_HEADER, decodeEdaRpcCommand, decodeEdaRpcSubmittables, edaRuntimeConfig, encodeEdaRpcCommand, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcDurableEvent, encodeEdaRpcSessionSnapshot, encodeEdaRpcSubmittables, getEDASessionDurableObjectByName, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectOpenAiModelResolverLayer, makeEDADurableObjectRuntimeLayer };
@@ -0,0 +1,30 @@
1
+ import { ModelSelectionPayload } from "effect-durable-agent/types/events";
2
+ import * as Layer from "effect/Layer";
3
+ import { OpenAiLanguageModel } from "@effect/ai-openai";
4
+ import { ModelResolver } from "effect-durable-agent/services/model-resolver";
5
+ import * as LanguageModel from "effect/unstable/ai/LanguageModel";
6
+
7
+ //#region src/providers/openai.d.ts
8
+ /** Provider request defaults accepted by EDA's OpenAI Responses model adapter. */
9
+ type EDAOpenAiModelConfig = Omit<typeof OpenAiLanguageModel.Config.Service, "model">;
10
+ /** Resolves each durable selection while sharing the provider transport for the runtime lifetime. */
11
+ declare const makeEDADurableObjectOpenAiModelResolverLayer: (options: {
12
+ readonly aiGateway?: AiGateway;
13
+ readonly apiKey?: string;
14
+ readonly apiUrl?: string;
15
+ readonly resolve: (selection: ModelSelectionPayload | undefined) => {
16
+ readonly modelId: string;
17
+ readonly config: EDAOpenAiModelConfig;
18
+ };
19
+ }) => Layer.Layer<ModelResolver>;
20
+ /** OpenAI-backed Effect AI model layer for a Cloudflare Worker. */
21
+ declare const makeEDADurableObjectOpenAiModelLayer: (options: {
22
+ readonly aiGateway?: AiGateway;
23
+ readonly apiKey?: string;
24
+ readonly apiUrl?: string;
25
+ readonly config?: EDAOpenAiModelConfig;
26
+ readonly modelId: string;
27
+ }) => Layer.Layer<LanguageModel.LanguageModel>;
28
+ //#endregion
29
+ export { EDAOpenAiModelConfig, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectOpenAiModelResolverLayer };
30
+ //# sourceMappingURL=openai.d.ts.map
package/dist/openai.js ADDED
@@ -0,0 +1,80 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai";
4
+ import { ModelResolver } from "effect-durable-agent/services/model-resolver";
5
+ import * as Redacted from "effect/Redacted";
6
+ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
7
+ import "effect/unstable/http/HttpBody";
8
+ import * as HttpClient from "effect/unstable/http/HttpClient";
9
+ import * as HttpClientError from "effect/unstable/http/HttpClientError";
10
+ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
11
+ //#region src/providers/openai.ts
12
+ /** Resolves each durable selection while sharing the provider transport for the runtime lifetime. */
13
+ const makeEDADurableObjectOpenAiModelResolverLayer = (options) => {
14
+ const apiKey = optionalNonEmptyString(options.apiKey);
15
+ if (apiKey === void 0 && options.aiGateway === void 0) throw new Error("OPENAI_API_KEY is required unless the EDA OpenAI model resolver is backed by a Cloudflare AI Gateway binding.");
16
+ const transport = options.aiGateway === void 0 ? FetchHttpClient.layer : Layer.succeed(HttpClient.HttpClient, makeAiGatewayHttpClient(options.aiGateway));
17
+ const client = OpenAiClient.layer({
18
+ ...apiKey === void 0 ? {} : { apiKey: Redacted.make(apiKey) },
19
+ ...options.apiUrl === void 0 ? {} : { apiUrl: options.apiUrl }
20
+ }).pipe(Layer.provide(transport));
21
+ return Layer.effect(ModelResolver, Effect.gen(function* () {
22
+ const provider = yield* OpenAiClient.OpenAiClient;
23
+ return { resolve: Effect.fn("OpenAiModelResolver.resolve")(function* (selection) {
24
+ const resolved = options.resolve(selection);
25
+ return yield* OpenAiLanguageModel.make({
26
+ model: resolved.modelId,
27
+ config: resolved.config
28
+ }).pipe(Effect.provideService(OpenAiClient.OpenAiClient, provider));
29
+ }) };
30
+ })).pipe(Layer.provide(client));
31
+ };
32
+ /** OpenAI-backed Effect AI model layer for a Cloudflare Worker. */
33
+ const makeEDADurableObjectOpenAiModelLayer = (options) => {
34
+ const apiKey = optionalNonEmptyString(options.apiKey);
35
+ if (apiKey === void 0 && options.aiGateway === void 0) throw new Error("OPENAI_API_KEY is required unless the EDA OpenAI model layer is backed by a Cloudflare AI Gateway binding.");
36
+ const httpClient = options.aiGateway === void 0 ? FetchHttpClient.layer : Layer.succeed(HttpClient.HttpClient, makeAiGatewayHttpClient(options.aiGateway));
37
+ const client = OpenAiClient.layer({
38
+ ...apiKey === void 0 ? {} : { apiKey: Redacted.make(apiKey) },
39
+ ...options.apiUrl === void 0 ? {} : { apiUrl: options.apiUrl }
40
+ }).pipe(Layer.provide(httpClient));
41
+ return OpenAiLanguageModel.layer({
42
+ model: options.modelId,
43
+ ...options.config === void 0 ? {} : { config: options.config }
44
+ }).pipe(Layer.provide(client));
45
+ };
46
+ const makeAiGatewayHttpClient = (gateway) => HttpClient.make((request, url, signal) => Effect.tryPromise({
47
+ try: async () => {
48
+ const response = await gateway.run({
49
+ endpoint: endpointFromUrl(url),
50
+ headers: Object.fromEntries(Object.entries(request.headers).filter((entry) => typeof entry[1] === "string" && entry[0].toLowerCase() !== "host")),
51
+ provider: "openai",
52
+ query: requestBody(request.body)
53
+ }, { signal });
54
+ return HttpClientResponse.fromWeb(request, response);
55
+ },
56
+ catch: (cause) => new HttpClientError.HttpClientError({ reason: new HttpClientError.TransportError({
57
+ request,
58
+ cause,
59
+ description: "Cloudflare AI Gateway request failed"
60
+ }) })
61
+ }));
62
+ const endpointFromUrl = (url) => {
63
+ return `${url.pathname.startsWith("/v1/") ? url.pathname.slice(4) : url.pathname.replace(/^\/+/, "")}${url.search}`;
64
+ };
65
+ const requestBody = (body) => {
66
+ switch (body._tag) {
67
+ case "Empty": return;
68
+ case "Raw": return body.body;
69
+ case "Uint8Array": return JSON.parse(new TextDecoder().decode(body.body));
70
+ default: throw new Error(`Unsupported OpenAI request body for Cloudflare AI Gateway: ${body._tag}`);
71
+ }
72
+ };
73
+ const optionalNonEmptyString = (value) => {
74
+ const trimmed = value?.trim();
75
+ return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
76
+ };
77
+ //#endregion
78
+ export { makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectOpenAiModelResolverLayer };
79
+
80
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","names":[],"sources":["../src/providers/openai.ts"],"sourcesContent":["import { OpenAiClient, OpenAiLanguageModel } from \"@effect/ai-openai\";\nimport { ModelResolver } from \"effect-durable-agent/services/model-resolver\";\nimport type { ModelSelectionPayload } from \"effect-durable-agent/types/events\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Redacted from \"effect/Redacted\";\nimport type * as LanguageModel from \"effect/unstable/ai/LanguageModel\";\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\";\nimport * as HttpBody from \"effect/unstable/http/HttpBody\";\nimport * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport * as HttpClientError from \"effect/unstable/http/HttpClientError\";\nimport * as HttpClientResponse from \"effect/unstable/http/HttpClientResponse\";\n\n/** Provider request defaults accepted by EDA's OpenAI Responses model adapter. */\nexport type EDAOpenAiModelConfig = Omit<typeof OpenAiLanguageModel.Config.Service, \"model\">;\n\n/** Resolves each durable selection while sharing the provider transport for the runtime lifetime. */\nexport const makeEDADurableObjectOpenAiModelResolverLayer = (options: {\n readonly aiGateway?: AiGateway;\n readonly apiKey?: string;\n readonly apiUrl?: string;\n readonly resolve: (selection: ModelSelectionPayload | undefined) => {\n readonly modelId: string;\n readonly config: EDAOpenAiModelConfig;\n };\n}): Layer.Layer<ModelResolver> => {\n const apiKey = optionalNonEmptyString(options.apiKey);\n if (apiKey === undefined && options.aiGateway === undefined) {\n throw new Error(\n \"OPENAI_API_KEY is required unless the EDA OpenAI model resolver is backed by a Cloudflare AI Gateway binding.\",\n );\n }\n const transport =\n options.aiGateway === undefined\n ? FetchHttpClient.layer\n : Layer.succeed(HttpClient.HttpClient, makeAiGatewayHttpClient(options.aiGateway));\n const client = OpenAiClient.layer({\n ...(apiKey === undefined ? {} : { apiKey: Redacted.make(apiKey) }),\n ...(options.apiUrl === undefined ? {} : { apiUrl: options.apiUrl }),\n }).pipe(Layer.provide(transport));\n return Layer.effect(\n ModelResolver,\n Effect.gen(function* () {\n const provider = yield* OpenAiClient.OpenAiClient;\n return {\n resolve: Effect.fn(\"OpenAiModelResolver.resolve\")(function* (selection) {\n const resolved = options.resolve(selection);\n return yield* OpenAiLanguageModel.make({\n model: resolved.modelId,\n config: resolved.config,\n }).pipe(Effect.provideService(OpenAiClient.OpenAiClient, provider));\n }),\n };\n }),\n ).pipe(Layer.provide(client));\n};\n\n/** OpenAI-backed Effect AI model layer for a Cloudflare Worker. */\nexport const makeEDADurableObjectOpenAiModelLayer = (options: {\n readonly aiGateway?: AiGateway;\n readonly apiKey?: string;\n readonly apiUrl?: string;\n readonly config?: EDAOpenAiModelConfig;\n readonly modelId: string;\n}): Layer.Layer<LanguageModel.LanguageModel> => {\n const apiKey = optionalNonEmptyString(options.apiKey);\n if (apiKey === undefined && options.aiGateway === undefined) {\n throw new Error(\n \"OPENAI_API_KEY is required unless the EDA OpenAI model layer is backed by a Cloudflare AI Gateway binding.\",\n );\n }\n\n const httpClient =\n options.aiGateway === undefined\n ? FetchHttpClient.layer\n : Layer.succeed(HttpClient.HttpClient, makeAiGatewayHttpClient(options.aiGateway));\n const client = OpenAiClient.layer({\n ...(apiKey === undefined ? {} : { apiKey: Redacted.make(apiKey) }),\n ...(options.apiUrl === undefined ? {} : { apiUrl: options.apiUrl }),\n }).pipe(Layer.provide(httpClient));\n\n return OpenAiLanguageModel.layer({\n model: options.modelId,\n ...(options.config === undefined ? {} : { config: options.config }),\n }).pipe(Layer.provide(client));\n};\n\nconst makeAiGatewayHttpClient = (gateway: AiGateway): HttpClient.HttpClient =>\n HttpClient.make((request, url, signal) =>\n Effect.tryPromise({\n try: async () => {\n const response = await gateway.run(\n {\n endpoint: endpointFromUrl(url),\n headers: Object.fromEntries(\n Object.entries(request.headers).filter(\n (entry): entry is [string, string] =>\n typeof entry[1] === \"string\" && entry[0].toLowerCase() !== \"host\",\n ),\n ),\n provider: \"openai\",\n query: requestBody(request.body),\n },\n { signal },\n );\n return HttpClientResponse.fromWeb(request, response);\n },\n catch: (cause) =>\n new HttpClientError.HttpClientError({\n reason: new HttpClientError.TransportError({\n request,\n cause,\n description: \"Cloudflare AI Gateway request failed\",\n }),\n }),\n }),\n );\n\nconst endpointFromUrl = (url: URL): string => {\n const path = url.pathname.startsWith(\"/v1/\")\n ? url.pathname.slice(\"/v1/\".length)\n : url.pathname.replace(/^\\/+/, \"\");\n return `${path}${url.search}`;\n};\n\nconst requestBody = (body: HttpBody.HttpBody): unknown => {\n switch (body._tag) {\n case \"Empty\":\n return undefined;\n case \"Raw\":\n return body.body;\n case \"Uint8Array\":\n return JSON.parse(new TextDecoder().decode(body.body)) as unknown;\n default:\n throw new Error(`Unsupported OpenAI request body for Cloudflare AI Gateway: ${body._tag}`);\n }\n};\n\nconst optionalNonEmptyString = (value: string | undefined): string | undefined => {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;\n};\n"],"mappings":"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"}