@effect-agent/platform-cloudflare 0.1.0-beta.23 → 0.1.0-beta.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +36 -32
- package/dist/index.mjs +234 -171
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
- package/src/alarm.ts +2 -1
- package/src/bindings.ts +31 -30
- package/src/boundary.ts +51 -0
- package/src/client.ts +86 -187
- package/src/code-mode-executor.ts +128 -62
- package/src/conversation-object.ts +46 -20
- package/src/layers.ts +17 -33
- package/src/wake-scheduler.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-agent/platform-cloudflare",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.25",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"types": "./dist/index.d.mts",
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@effect-agent/core": "0.1.0-beta.
|
|
12
|
-
"@effect-agent/engine": "0.1.0-beta.
|
|
13
|
-
"@effect-agent/sandbox": "0.1.0-beta.
|
|
14
|
-
"@effect-agent/session": "0.1.0-beta.
|
|
15
|
-
"@effect-agent/storage-cloudflare": "0.1.0-beta.
|
|
11
|
+
"@effect-agent/core": "0.1.0-beta.25",
|
|
12
|
+
"@effect-agent/engine": "0.1.0-beta.25",
|
|
13
|
+
"@effect-agent/sandbox": "0.1.0-beta.25",
|
|
14
|
+
"@effect-agent/session": "0.1.0-beta.25",
|
|
15
|
+
"@effect-agent/storage-cloudflare": "0.1.0-beta.25",
|
|
16
16
|
"@effect/platform-browser": "4.0.0-rc.110",
|
|
17
17
|
"@effect/sql-sqlite-do": "4.0.0-rc.110",
|
|
18
18
|
"effect": "4.0.0-rc.110"
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@cloudflare/vitest-pool-workers": "0.21.3",
|
|
45
45
|
"@cloudflare/workers-types": "5.20260813.1",
|
|
46
|
-
"@effect-agent/capabilities": "0.1.0-beta.
|
|
47
|
-
"@effect-agent/testing": "0.1.0-beta.
|
|
46
|
+
"@effect-agent/capabilities": "0.1.0-beta.23",
|
|
47
|
+
"@effect-agent/testing": "0.1.0-beta.23",
|
|
48
48
|
"@effect/vitest": "4.0.0-rc.110",
|
|
49
49
|
"effect-cf": "0.27.0",
|
|
50
50
|
"esbuild": "0.28.1",
|
package/src/alarm.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "effect";
|
|
22
22
|
|
|
23
23
|
import { ConversationObjectIdentity, DurableObjectContext } from "./bindings.ts";
|
|
24
|
+
import { safeCauseMessage } from "./boundary.ts";
|
|
24
25
|
import { CloudflareDurableRuntimeConfig } from "./config.ts";
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -50,7 +51,7 @@ const alarmFailure =
|
|
|
50
51
|
(cause: unknown): DurableAlarmError =>
|
|
51
52
|
DurableAlarmError.make({
|
|
52
53
|
operation,
|
|
53
|
-
message: cause
|
|
54
|
+
message: safeCauseMessage(cause, "The Cloudflare alarm API failed without a diagnostic"),
|
|
54
55
|
cause,
|
|
55
56
|
});
|
|
56
57
|
|
package/src/bindings.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ConversationId } from "@effect-agent/core";
|
|
2
2
|
import type { ProducerId } from "@effect-agent/session";
|
|
3
|
-
import { Context, Effect, Layer, Schema } from "effect";
|
|
3
|
+
import { Context, Effect, Layer, Predicate, Schema } from "effect";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Cloudflare platform bindings as Effect services (DEPLOY-010: "Cloudflare platform bindings
|
|
@@ -73,41 +73,42 @@ export class ConversationObjectNamespace extends Context.Service<
|
|
|
73
73
|
* narrowest-boundary check (structural probe for the namespace surface the transport uses);
|
|
74
74
|
* a missing or misshaped binding fails typed before any Layer is built.
|
|
75
75
|
*/
|
|
76
|
-
export const conversationNamespaceFromEnv = (
|
|
76
|
+
export const conversationNamespaceFromEnv = Effect.fn("conversationNamespaceFromEnv")(function* (
|
|
77
77
|
env: unknown,
|
|
78
78
|
binding: string,
|
|
79
|
-
): Effect.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"get" in candidate &&
|
|
96
|
-
typeof candidate.get === "function"
|
|
97
|
-
) {
|
|
98
|
-
// The structural probe above is the entire runtime contract this package relies on;
|
|
99
|
-
// the assertion records that `idFromName`/`get` name a DurableObjectNamespace.
|
|
100
|
-
return Effect.succeed(candidate as DurableObjectNamespace<ConversationObjectRpc>);
|
|
101
|
-
}
|
|
102
|
-
return Effect.fail(
|
|
79
|
+
): Effect.fn.Return<DurableObjectNamespace<ConversationObjectRpc>, CloudflareBindingError> {
|
|
80
|
+
if (!Predicate.isObjectKeyword(env)) {
|
|
81
|
+
return yield* CloudflareBindingError.make({
|
|
82
|
+
binding,
|
|
83
|
+
message: "The Worker environment is not an object; no bindings are available.",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const candidate = yield* Effect.try({
|
|
87
|
+
try: () => {
|
|
88
|
+
const value: unknown = Reflect.get(env, binding);
|
|
89
|
+
if (!Predicate.isObjectKeyword(value)) return undefined;
|
|
90
|
+
const idFromName: unknown = Reflect.get(value, "idFromName");
|
|
91
|
+
const get: unknown = Reflect.get(value, "get");
|
|
92
|
+
return typeof idFromName === "function" && typeof get === "function" ? value : undefined;
|
|
93
|
+
},
|
|
94
|
+
catch: () =>
|
|
103
95
|
CloudflareBindingError.make({
|
|
104
96
|
binding,
|
|
105
|
-
message:
|
|
106
|
-
`env.${binding} is not a DurableObjectNamespace binding; declare the Conversation ` +
|
|
107
|
-
"Object class under this binding in the Worker configuration.",
|
|
97
|
+
message: `env.${binding} could not be inspected as a DurableObjectNamespace binding.`,
|
|
108
98
|
}),
|
|
109
|
-
);
|
|
110
99
|
});
|
|
100
|
+
if (candidate !== undefined) {
|
|
101
|
+
// The structural probe above is the entire runtime contract this package relies on;
|
|
102
|
+
// the assertion records that `idFromName`/`get` name a DurableObjectNamespace.
|
|
103
|
+
return candidate as unknown as DurableObjectNamespace<ConversationObjectRpc>;
|
|
104
|
+
}
|
|
105
|
+
return yield* CloudflareBindingError.make({
|
|
106
|
+
binding,
|
|
107
|
+
message:
|
|
108
|
+
`env.${binding} is not a DurableObjectNamespace binding; declare the Conversation ` +
|
|
109
|
+
"Object class under this binding in the Worker configuration.",
|
|
110
|
+
});
|
|
111
|
+
});
|
|
111
112
|
|
|
112
113
|
/** `ConversationObjectNamespace` built from the untyped Worker `env` (fails typed). */
|
|
113
114
|
export const conversationNamespaceLayer = (
|
package/src/boundary.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Predicate } from "effect";
|
|
2
|
+
|
|
3
|
+
const MAX_FOREIGN_DIAGNOSTIC_LENGTH = 8_192;
|
|
4
|
+
|
|
5
|
+
const boundForeignDiagnostic = (message: string): string =>
|
|
6
|
+
message.slice(0, MAX_FOREIGN_DIAGNOSTIC_LENGTH);
|
|
7
|
+
|
|
8
|
+
/** Render a foreign failure without trusting accessors or coercion hooks on the value. */
|
|
9
|
+
export const safeCauseMessage = (cause: unknown, fallback: string): string => {
|
|
10
|
+
try {
|
|
11
|
+
const message = cause instanceof Error ? cause.message : cause;
|
|
12
|
+
return boundForeignDiagnostic(typeof message === "string" ? message : String(message));
|
|
13
|
+
} catch {
|
|
14
|
+
return boundForeignDiagnostic(fallback);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Include an Error name when worker-failure classification needs it. */
|
|
19
|
+
export const safeCauseDiagnostic = (cause: unknown, fallback: string): string => {
|
|
20
|
+
try {
|
|
21
|
+
return cause instanceof Error
|
|
22
|
+
? boundForeignDiagnostic(`${cause.name}: ${cause.message}`)
|
|
23
|
+
: safeCauseMessage(cause, fallback);
|
|
24
|
+
} catch {
|
|
25
|
+
return boundForeignDiagnostic(fallback);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface CloudflareFailureSignals {
|
|
30
|
+
readonly retryable?: boolean | undefined;
|
|
31
|
+
readonly overloaded?: boolean | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Read Cloudflare RPC classifications without letting a hostile proxy defect the client. */
|
|
35
|
+
export const cloudflareFailureSignals = (cause: unknown): CloudflareFailureSignals => {
|
|
36
|
+
if (!Predicate.isObjectKeyword(cause)) return {};
|
|
37
|
+
try {
|
|
38
|
+
const retryableValue = Reflect.get(cause, "retryable");
|
|
39
|
+
const overloadedValue = Reflect.get(cause, "overloaded");
|
|
40
|
+
const resetValue = Reflect.get(cause, "durableObjectReset");
|
|
41
|
+
const retryable =
|
|
42
|
+
typeof retryableValue === "boolean" ? retryableValue : resetValue === true ? true : undefined;
|
|
43
|
+
const overloaded = typeof overloadedValue === "boolean" ? overloadedValue : undefined;
|
|
44
|
+
return {
|
|
45
|
+
...(retryable === undefined ? {} : { retryable }),
|
|
46
|
+
...(overloaded === undefined ? {} : { overloaded }),
|
|
47
|
+
};
|
|
48
|
+
} catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
};
|
package/src/client.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { Context, Crypto, Duration, Effect, Layer, Schema } from "effect";
|
|
|
34
34
|
|
|
35
35
|
import { DurableAlarmError } from "./alarm.ts";
|
|
36
36
|
import { ConversationObjectNamespace, type ConversationObjectRpc } from "./bindings.ts";
|
|
37
|
+
import { cloudflareFailureSignals, safeCauseMessage } from "./boundary.ts";
|
|
37
38
|
import { AdmissionLimitExceeded } from "./config.ts";
|
|
38
39
|
|
|
39
40
|
/**
|
|
@@ -249,123 +250,62 @@ export const decodeHostResponse = Schema.decodeUnknownEffect(HostResponse);
|
|
|
249
250
|
// ---------------------------------------------------------------------------
|
|
250
251
|
|
|
251
252
|
/** Failure surface of `CloudflareConversationClient.submit`. */
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
| ConversationClientError;
|
|
266
|
-
|
|
267
|
-
export type ClientAwaitFailure =
|
|
268
|
-
| LedgerError
|
|
269
|
-
| SettlementConflict
|
|
270
|
-
| HostProtocolError
|
|
271
|
-
| ConversationClientError;
|
|
272
|
-
|
|
273
|
-
export type ClientObserveFailure =
|
|
274
|
-
| ConversationStoreError
|
|
275
|
-
| ConversationNotMaterialized
|
|
276
|
-
| OperationDenied
|
|
277
|
-
| HostProtocolError
|
|
278
|
-
| ConversationClientError;
|
|
279
|
-
|
|
280
|
-
export type ClientProgressFailure =
|
|
281
|
-
| ConversationStoreError
|
|
282
|
-
| ConversationNotMaterialized
|
|
283
|
-
| OperationDenied
|
|
284
|
-
| HostProtocolError
|
|
285
|
-
| ConversationClientError;
|
|
286
|
-
|
|
287
|
-
export type ClientAbortFailure =
|
|
288
|
-
| LedgerError
|
|
289
|
-
| SettlementConflict
|
|
290
|
-
| JoinedToHost
|
|
291
|
-
| DurableRuntimeFailpointError
|
|
292
|
-
| DurableAlarmError
|
|
293
|
-
| HostProtocolError
|
|
294
|
-
| ConversationClientError;
|
|
295
|
-
|
|
296
|
-
export type ClientApprovalFailure =
|
|
297
|
-
| LedgerError
|
|
298
|
-
| SettlementConflict
|
|
299
|
-
| ApprovalConflict
|
|
300
|
-
| OperationDenied
|
|
301
|
-
| DurableAlarmError
|
|
302
|
-
| HostProtocolError
|
|
303
|
-
| ConversationClientError;
|
|
304
|
-
|
|
305
|
-
export type ClientUnknownFailure =
|
|
306
|
-
| LedgerError
|
|
307
|
-
| SettlementConflict
|
|
308
|
-
| UnknownResolutionConflict
|
|
309
|
-
| JoinedToHost
|
|
310
|
-
| DurableRuntimeFailpointError
|
|
311
|
-
| OperationDenied
|
|
312
|
-
| DurableAlarmError
|
|
313
|
-
| HostProtocolError
|
|
314
|
-
| ConversationClientError;
|
|
315
|
-
|
|
316
|
-
const SUBMIT_FAILURE_TAGS: ReadonlySet<string> = new Set([
|
|
317
|
-
"AgentInputError",
|
|
318
|
-
"DigestError",
|
|
319
|
-
"AdmissionConflict",
|
|
320
|
-
"LedgerError",
|
|
321
|
-
"ConversationStoreError",
|
|
322
|
-
"ConversationNotMaterialized",
|
|
323
|
-
"AppendConflict",
|
|
324
|
-
"FenceRejected",
|
|
325
|
-
"DurableRuntimeFailpointError",
|
|
326
|
-
"AdmissionLimitExceeded",
|
|
327
|
-
"DurableAlarmError",
|
|
328
|
-
"HostProtocolError",
|
|
329
|
-
]);
|
|
330
|
-
const AWAIT_FAILURE_TAGS: ReadonlySet<string> = new Set([
|
|
331
|
-
"LedgerError",
|
|
332
|
-
"SettlementConflict",
|
|
333
|
-
"HostProtocolError",
|
|
253
|
+
const ClientSubmitHostFailure = Schema.Union([
|
|
254
|
+
AgentInputError,
|
|
255
|
+
DigestError,
|
|
256
|
+
AdmissionConflict,
|
|
257
|
+
LedgerError,
|
|
258
|
+
ConversationStoreError,
|
|
259
|
+
ConversationNotMaterialized,
|
|
260
|
+
AppendConflict,
|
|
261
|
+
FenceRejected,
|
|
262
|
+
DurableRuntimeFailpointError,
|
|
263
|
+
AdmissionLimitExceeded,
|
|
264
|
+
DurableAlarmError,
|
|
265
|
+
HostProtocolError,
|
|
334
266
|
]);
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
267
|
+
const ClientAwaitHostFailure = Schema.Union([LedgerError, SettlementConflict, HostProtocolError]);
|
|
268
|
+
const ClientObserveHostFailure = Schema.Union([
|
|
269
|
+
ConversationStoreError,
|
|
270
|
+
ConversationNotMaterialized,
|
|
271
|
+
OperationDenied,
|
|
272
|
+
HostProtocolError,
|
|
340
273
|
]);
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
"HostProtocolError",
|
|
274
|
+
const ClientAbortHostFailure = Schema.Union([
|
|
275
|
+
LedgerError,
|
|
276
|
+
SettlementConflict,
|
|
277
|
+
JoinedToHost,
|
|
278
|
+
DurableRuntimeFailpointError,
|
|
279
|
+
DurableAlarmError,
|
|
280
|
+
HostProtocolError,
|
|
349
281
|
]);
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
282
|
+
const ClientApprovalHostFailure = Schema.Union([
|
|
283
|
+
LedgerError,
|
|
284
|
+
SettlementConflict,
|
|
285
|
+
ApprovalConflict,
|
|
286
|
+
OperationDenied,
|
|
287
|
+
DurableAlarmError,
|
|
288
|
+
HostProtocolError,
|
|
357
289
|
]);
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
290
|
+
const ClientUnknownHostFailure = Schema.Union([
|
|
291
|
+
LedgerError,
|
|
292
|
+
SettlementConflict,
|
|
293
|
+
UnknownResolutionConflict,
|
|
294
|
+
JoinedToHost,
|
|
295
|
+
DurableRuntimeFailpointError,
|
|
296
|
+
OperationDenied,
|
|
297
|
+
DurableAlarmError,
|
|
298
|
+
HostProtocolError,
|
|
367
299
|
]);
|
|
368
300
|
|
|
301
|
+
export type ClientSubmitFailure = typeof ClientSubmitHostFailure.Type | ConversationClientError;
|
|
302
|
+
export type ClientAwaitFailure = typeof ClientAwaitHostFailure.Type | ConversationClientError;
|
|
303
|
+
export type ClientObserveFailure = typeof ClientObserveHostFailure.Type | ConversationClientError;
|
|
304
|
+
export type ClientProgressFailure = ClientObserveFailure;
|
|
305
|
+
export type ClientAbortFailure = typeof ClientAbortHostFailure.Type | ConversationClientError;
|
|
306
|
+
export type ClientApprovalFailure = typeof ClientApprovalHostFailure.Type | ConversationClientError;
|
|
307
|
+
export type ClientUnknownFailure = typeof ClientUnknownHostFailure.Type | ConversationClientError;
|
|
308
|
+
|
|
369
309
|
const outOfContract = (
|
|
370
310
|
conversationId: string,
|
|
371
311
|
operation: string,
|
|
@@ -378,17 +318,6 @@ const outOfContract = (
|
|
|
378
318
|
),
|
|
379
319
|
});
|
|
380
320
|
|
|
381
|
-
/**
|
|
382
|
-
* Narrow one decoded `HostFailed.failure` to the operation's declared failure family; an
|
|
383
|
-
* out-of-contract tag folds into `ConversationClientError` instead of being erased or
|
|
384
|
-
* re-thrown raw (the WP2 discipline). The predicate is the single documented narrowing over
|
|
385
|
-
* the closed `HostFailure` union.
|
|
386
|
-
*/
|
|
387
|
-
const narrowFailure =
|
|
388
|
-
<Failure extends HostFailure>(tags: ReadonlySet<string>) =>
|
|
389
|
-
(failure: HostFailure): failure is Failure =>
|
|
390
|
-
tags.has(failure._tag);
|
|
391
|
-
|
|
392
321
|
/** Worker-side client over the Conversation Object namespace (DEPLOY-010). */
|
|
393
322
|
export class CloudflareConversationClient extends Context.Service<
|
|
394
323
|
CloudflareConversationClient,
|
|
@@ -453,32 +382,6 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
453
382
|
const { namespace } = yield* ConversationObjectNamespace;
|
|
454
383
|
const crypto = yield* Crypto.Crypto;
|
|
455
384
|
|
|
456
|
-
const platformSignals = (cause: unknown) => {
|
|
457
|
-
let retryable: boolean | undefined;
|
|
458
|
-
let overloaded: boolean | undefined;
|
|
459
|
-
if (typeof cause === "object" && cause !== null) {
|
|
460
|
-
if ("retryable" in cause && typeof cause.retryable === "boolean") {
|
|
461
|
-
retryable = cause.retryable;
|
|
462
|
-
}
|
|
463
|
-
if ("overloaded" in cause && typeof cause.overloaded === "boolean") {
|
|
464
|
-
overloaded = cause.overloaded;
|
|
465
|
-
}
|
|
466
|
-
// Miniflare's faithful `ctx.abort()` signal predates the public `retryable` field.
|
|
467
|
-
// Treat only its explicit reset marker as the same idempotent-retry classification.
|
|
468
|
-
if (
|
|
469
|
-
retryable === undefined &&
|
|
470
|
-
"durableObjectReset" in cause &&
|
|
471
|
-
cause.durableObjectReset === true
|
|
472
|
-
) {
|
|
473
|
-
retryable = true;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
return {
|
|
477
|
-
...(retryable === undefined ? {} : { retryable }),
|
|
478
|
-
...(overloaded === undefined ? {} : { overloaded }),
|
|
479
|
-
};
|
|
480
|
-
};
|
|
481
|
-
|
|
482
385
|
const call = (
|
|
483
386
|
conversationId: string,
|
|
484
387
|
operation: string,
|
|
@@ -490,12 +393,13 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
490
393
|
ConversationClientError.make({
|
|
491
394
|
conversationId,
|
|
492
395
|
message: boundHostDiagnostic(
|
|
493
|
-
`${operation} did not reach the Conversation Object: ${
|
|
494
|
-
cause
|
|
495
|
-
|
|
396
|
+
`${operation} did not reach the Conversation Object: ${safeCauseMessage(
|
|
397
|
+
cause,
|
|
398
|
+
"the RPC failed without a diagnostic",
|
|
399
|
+
)}`,
|
|
496
400
|
),
|
|
497
401
|
cause,
|
|
498
|
-
...
|
|
402
|
+
...cloudflareFailureSignals(cause),
|
|
499
403
|
}),
|
|
500
404
|
}).pipe(
|
|
501
405
|
Effect.flatMap((raw) =>
|
|
@@ -515,29 +419,27 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
515
419
|
}),
|
|
516
420
|
);
|
|
517
421
|
|
|
518
|
-
const expect = <
|
|
519
|
-
Result extends Exclude<HostResponse, HostFailed>,
|
|
520
|
-
Failure extends HostFailure,
|
|
521
|
-
>(
|
|
422
|
+
const expect = <ResultSchema extends Schema.Top, FailureSchema extends Schema.Top>(
|
|
522
423
|
conversationId: string,
|
|
523
424
|
operation: string,
|
|
524
|
-
|
|
525
|
-
|
|
425
|
+
resultSchema: ResultSchema,
|
|
426
|
+
failureSchema: FailureSchema,
|
|
526
427
|
) => {
|
|
527
|
-
const
|
|
428
|
+
const isExpectedResult = Schema.is(resultSchema);
|
|
429
|
+
const isExpectedFailure = Schema.is(failureSchema);
|
|
528
430
|
return (
|
|
529
431
|
response: HostResponse,
|
|
530
|
-
): Effect.Effect<
|
|
432
|
+
): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | ConversationClientError> => {
|
|
531
433
|
if (response._tag === "HostFailed") {
|
|
532
434
|
const failure = response.failure;
|
|
533
|
-
return
|
|
435
|
+
return isExpectedFailure(failure)
|
|
534
436
|
? Effect.fail(failure)
|
|
535
437
|
: Effect.fail(outOfContract(conversationId, operation, `failure ${failure._tag}`));
|
|
536
438
|
}
|
|
537
|
-
if (response
|
|
439
|
+
if (!isExpectedResult(response)) {
|
|
538
440
|
return Effect.fail(outOfContract(conversationId, operation, `result ${response._tag}`));
|
|
539
441
|
}
|
|
540
|
-
return Effect.succeed(response
|
|
442
|
+
return Effect.succeed(response);
|
|
541
443
|
};
|
|
542
444
|
};
|
|
543
445
|
|
|
@@ -565,11 +467,11 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
565
467
|
const response = yield* call(conversationId, "observePage", (stub) =>
|
|
566
468
|
stub.observePage(encoded),
|
|
567
469
|
);
|
|
568
|
-
const page = yield* expect
|
|
470
|
+
const page = yield* expect(
|
|
569
471
|
conversationId,
|
|
570
472
|
"observePage",
|
|
571
|
-
|
|
572
|
-
|
|
473
|
+
ObservedPage,
|
|
474
|
+
ClientObserveHostFailure,
|
|
573
475
|
)(response);
|
|
574
476
|
return page.records;
|
|
575
477
|
});
|
|
@@ -627,11 +529,11 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
627
529
|
const response = yield* call(options.conversationId, "submit", (stub) =>
|
|
628
530
|
stub.submitEncoded(encoded),
|
|
629
531
|
);
|
|
630
|
-
const succeeded = yield* expect
|
|
532
|
+
const succeeded = yield* expect(
|
|
631
533
|
options.conversationId,
|
|
632
534
|
"submit",
|
|
633
|
-
|
|
634
|
-
|
|
535
|
+
SubmitSucceeded,
|
|
536
|
+
ClientSubmitHostFailure,
|
|
635
537
|
)(response);
|
|
636
538
|
return succeeded.receipt;
|
|
637
539
|
}),
|
|
@@ -648,11 +550,11 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
648
550
|
const response = yield* call(receipt.conversationId, "awaitSettlement", (stub) =>
|
|
649
551
|
stub.awaitSettlementEncoded(encoded),
|
|
650
552
|
);
|
|
651
|
-
const settled = yield* expect
|
|
553
|
+
const settled = yield* expect(
|
|
652
554
|
receipt.conversationId,
|
|
653
555
|
"awaitSettlement",
|
|
654
|
-
|
|
655
|
-
|
|
556
|
+
SettlementReached,
|
|
557
|
+
ClientAwaitHostFailure,
|
|
656
558
|
)(response);
|
|
657
559
|
return settled.settlement;
|
|
658
560
|
}),
|
|
@@ -684,11 +586,11 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
684
586
|
stub.awaitProgressEncoded(encoded),
|
|
685
587
|
).pipe(
|
|
686
588
|
Effect.flatMap(
|
|
687
|
-
expect
|
|
589
|
+
expect(
|
|
688
590
|
conversationId,
|
|
689
591
|
"awaitProgress",
|
|
690
|
-
|
|
691
|
-
|
|
592
|
+
ProgressObserved,
|
|
593
|
+
ClientObserveHostFailure,
|
|
692
594
|
),
|
|
693
595
|
),
|
|
694
596
|
Effect.asVoid,
|
|
@@ -733,11 +635,11 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
733
635
|
const response = yield* call(conversationId, "abort", (stub) =>
|
|
734
636
|
stub.abortEncoded(encoded),
|
|
735
637
|
);
|
|
736
|
-
const recorded = yield* expect
|
|
638
|
+
const recorded = yield* expect(
|
|
737
639
|
conversationId,
|
|
738
640
|
"abort",
|
|
739
|
-
|
|
740
|
-
|
|
641
|
+
AbortRecorded,
|
|
642
|
+
ClientAbortHostFailure,
|
|
741
643
|
)(response);
|
|
742
644
|
return recorded.intent;
|
|
743
645
|
}),
|
|
@@ -754,11 +656,11 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
754
656
|
const response = yield* call(conversationId, "resolveApproval", (stub) =>
|
|
755
657
|
stub.resolveApprovalEncoded(encoded),
|
|
756
658
|
);
|
|
757
|
-
const recorded = yield* expect
|
|
659
|
+
const recorded = yield* expect(
|
|
758
660
|
conversationId,
|
|
759
661
|
"resolveApproval",
|
|
760
|
-
|
|
761
|
-
|
|
662
|
+
ApprovalRecorded,
|
|
663
|
+
ClientApprovalHostFailure,
|
|
762
664
|
)(response);
|
|
763
665
|
return recorded.intent;
|
|
764
666
|
}),
|
|
@@ -777,14 +679,11 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
777
679
|
const response = yield* call(conversationId, "resolveUnknown", (stub) =>
|
|
778
680
|
stub.resolveUnknownEncoded(encoded),
|
|
779
681
|
);
|
|
780
|
-
const recorded = yield* expect
|
|
781
|
-
UnknownResolutionRecorded,
|
|
782
|
-
ClientUnknownFailure & HostFailure
|
|
783
|
-
>(
|
|
682
|
+
const recorded = yield* expect(
|
|
784
683
|
conversationId,
|
|
785
684
|
"resolveUnknown",
|
|
786
|
-
|
|
787
|
-
|
|
685
|
+
UnknownResolutionRecorded,
|
|
686
|
+
ClientUnknownHostFailure,
|
|
788
687
|
)(response);
|
|
789
688
|
return recorded.intent;
|
|
790
689
|
}),
|