@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-agent/platform-cloudflare",
3
- "version": "0.1.0-beta.23",
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.23",
12
- "@effect-agent/engine": "0.1.0-beta.23",
13
- "@effect-agent/sandbox": "0.1.0-beta.23",
14
- "@effect-agent/session": "0.1.0-beta.23",
15
- "@effect-agent/storage-cloudflare": "0.1.0-beta.23",
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.22",
47
- "@effect-agent/testing": "0.1.0-beta.22",
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 instanceof Error ? cause.message : String(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.Effect<DurableObjectNamespace<ConversationObjectRpc>, CloudflareBindingError> =>
80
- Effect.suspend(() => {
81
- if (typeof env !== "object" || env === null) {
82
- return Effect.fail(
83
- CloudflareBindingError.make({
84
- binding,
85
- message: "The Worker environment is not an object; no bindings are available.",
86
- }),
87
- );
88
- }
89
- const candidate: unknown = (env as Record<string, unknown>)[binding];
90
- if (
91
- typeof candidate === "object" &&
92
- candidate !== null &&
93
- "idFromName" in candidate &&
94
- typeof candidate.idFromName === "function" &&
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 = (
@@ -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
- export type ClientSubmitFailure =
253
- | AgentInputError
254
- | DigestError
255
- | AdmissionConflict
256
- | LedgerError
257
- | ConversationStoreError
258
- | ConversationNotMaterialized
259
- | AppendConflict
260
- | FenceRejected
261
- | DurableRuntimeFailpointError
262
- | AdmissionLimitExceeded
263
- | DurableAlarmError
264
- | HostProtocolError
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 OBSERVE_FAILURE_TAGS: ReadonlySet<string> = new Set([
336
- "ConversationStoreError",
337
- "ConversationNotMaterialized",
338
- "OperationDenied",
339
- "HostProtocolError",
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 PROGRESS_FAILURE_TAGS = OBSERVE_FAILURE_TAGS;
342
- const ABORT_FAILURE_TAGS: ReadonlySet<string> = new Set([
343
- "LedgerError",
344
- "SettlementConflict",
345
- "JoinedToHost",
346
- "DurableRuntimeFailpointError",
347
- "DurableAlarmError",
348
- "HostProtocolError",
274
+ const ClientAbortHostFailure = Schema.Union([
275
+ LedgerError,
276
+ SettlementConflict,
277
+ JoinedToHost,
278
+ DurableRuntimeFailpointError,
279
+ DurableAlarmError,
280
+ HostProtocolError,
349
281
  ]);
350
- const APPROVAL_FAILURE_TAGS: ReadonlySet<string> = new Set([
351
- "LedgerError",
352
- "SettlementConflict",
353
- "ApprovalConflict",
354
- "OperationDenied",
355
- "DurableAlarmError",
356
- "HostProtocolError",
282
+ const ClientApprovalHostFailure = Schema.Union([
283
+ LedgerError,
284
+ SettlementConflict,
285
+ ApprovalConflict,
286
+ OperationDenied,
287
+ DurableAlarmError,
288
+ HostProtocolError,
357
289
  ]);
358
- const UNKNOWN_FAILURE_TAGS: ReadonlySet<string> = new Set([
359
- "LedgerError",
360
- "SettlementConflict",
361
- "UnknownResolutionConflict",
362
- "JoinedToHost",
363
- "DurableRuntimeFailpointError",
364
- "OperationDenied",
365
- "DurableAlarmError",
366
- "HostProtocolError",
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 instanceof Error ? cause.message : String(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
- ...platformSignals(cause),
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
- resultTag: Result["_tag"],
525
- tags: ReadonlySet<string>,
425
+ resultSchema: ResultSchema,
426
+ failureSchema: FailureSchema,
526
427
  ) => {
527
- const isExpected = narrowFailure<Failure>(tags);
428
+ const isExpectedResult = Schema.is(resultSchema);
429
+ const isExpectedFailure = Schema.is(failureSchema);
528
430
  return (
529
431
  response: HostResponse,
530
- ): Effect.Effect<Result, Failure | ConversationClientError> => {
432
+ ): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | ConversationClientError> => {
531
433
  if (response._tag === "HostFailed") {
532
434
  const failure = response.failure;
533
- return isExpected(failure)
435
+ return isExpectedFailure(failure)
534
436
  ? Effect.fail(failure)
535
437
  : Effect.fail(outOfContract(conversationId, operation, `failure ${failure._tag}`));
536
438
  }
537
- if (response._tag !== resultTag) {
439
+ if (!isExpectedResult(response)) {
538
440
  return Effect.fail(outOfContract(conversationId, operation, `result ${response._tag}`));
539
441
  }
540
- return Effect.succeed(response as Result);
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<ObservedPage, ClientObserveFailure & HostFailure>(
470
+ const page = yield* expect(
569
471
  conversationId,
570
472
  "observePage",
571
- "ObservedPage",
572
- OBSERVE_FAILURE_TAGS,
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<SubmitSucceeded, ClientSubmitFailure & HostFailure>(
532
+ const succeeded = yield* expect(
631
533
  options.conversationId,
632
534
  "submit",
633
- "SubmitSucceeded",
634
- SUBMIT_FAILURE_TAGS,
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<SettlementReached, ClientAwaitFailure & HostFailure>(
553
+ const settled = yield* expect(
652
554
  receipt.conversationId,
653
555
  "awaitSettlement",
654
- "SettlementReached",
655
- AWAIT_FAILURE_TAGS,
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<ProgressObserved, ClientProgressFailure & HostFailure>(
589
+ expect(
688
590
  conversationId,
689
591
  "awaitProgress",
690
- "ProgressObserved",
691
- PROGRESS_FAILURE_TAGS,
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<AbortRecorded, ClientAbortFailure & HostFailure>(
638
+ const recorded = yield* expect(
737
639
  conversationId,
738
640
  "abort",
739
- "AbortRecorded",
740
- ABORT_FAILURE_TAGS,
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<ApprovalRecorded, ClientApprovalFailure & HostFailure>(
659
+ const recorded = yield* expect(
758
660
  conversationId,
759
661
  "resolveApproval",
760
- "ApprovalRecorded",
761
- APPROVAL_FAILURE_TAGS,
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
- "UnknownResolutionRecorded",
787
- UNKNOWN_FAILURE_TAGS,
685
+ UnknownResolutionRecorded,
686
+ ClientUnknownHostFailure,
788
687
  )(response);
789
688
  return recorded.intent;
790
689
  }),