@effect-agent/platform-cloudflare 0.1.0-beta.33 → 0.1.0-beta.35
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 +67 -49
- package/dist/index.mjs +54 -27
- package/dist/index.mjs.map +1 -1
- package/dist/interactive-browser.mjs +21 -9
- package/dist/interactive-browser.mjs.map +1 -1
- package/package.json +17 -17
- package/src/bindings.ts +22 -11
- package/src/client.ts +65 -60
- package/src/conversation-object.ts +24 -16
- package/src/interactive-browser.ts +34 -14
- package/src/layers.ts +17 -2
package/src/client.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
type DurableSubmitOptions,
|
|
32
32
|
} from "@effect-agent/session";
|
|
33
33
|
import { Context, Crypto, Duration, Effect, Layer, Schema } from "effect";
|
|
34
|
+
import { RpcTracing } from "effect-cf";
|
|
34
35
|
|
|
35
36
|
import { DurableAlarmError } from "./alarm.ts";
|
|
36
37
|
import { ConversationObjectNamespace, type ConversationObjectRpc } from "./bindings.ts";
|
|
@@ -318,6 +319,17 @@ const outOfContract = (
|
|
|
318
319
|
),
|
|
319
320
|
});
|
|
320
321
|
|
|
322
|
+
const hostRpcMethods = {
|
|
323
|
+
submit: "submitEncoded",
|
|
324
|
+
awaitSettlement: "awaitSettlementEncoded",
|
|
325
|
+
awaitProgress: "awaitProgressEncoded",
|
|
326
|
+
cancelProgress: "cancelProgressEncoded",
|
|
327
|
+
observePage: "observePage",
|
|
328
|
+
abort: "abortEncoded",
|
|
329
|
+
resolveApproval: "resolveApprovalEncoded",
|
|
330
|
+
resolveUnknown: "resolveUnknownEncoded",
|
|
331
|
+
} as const satisfies Record<string, keyof ConversationObjectRpc>;
|
|
332
|
+
|
|
321
333
|
/** Worker-side client over the Conversation Object namespace (DEPLOY-010). */
|
|
322
334
|
export class CloudflareConversationClient extends Context.Service<
|
|
323
335
|
CloudflareConversationClient,
|
|
@@ -379,45 +391,54 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
379
391
|
ConversationObjectNamespace | Crypto.Crypto
|
|
380
392
|
> = Layer.effect(CloudflareConversationClient)(
|
|
381
393
|
Effect.gen(function* () {
|
|
382
|
-
const { namespace } = yield* ConversationObjectNamespace;
|
|
394
|
+
const { namespace, rpcTracing } = yield* ConversationObjectNamespace;
|
|
383
395
|
const crypto = yield* Crypto.Crypto;
|
|
384
396
|
|
|
385
|
-
const call = (
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
Effect.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
)
|
|
397
|
+
const call = Effect.fn(
|
|
398
|
+
function* (
|
|
399
|
+
conversationId: string,
|
|
400
|
+
operation: keyof typeof hostRpcMethods,
|
|
401
|
+
encoded: unknown,
|
|
402
|
+
): Effect.fn.Return<HostResponse, ConversationClientError | HostProtocolError> {
|
|
403
|
+
// Empty arguments preserve native arity. Passing `undefined` still adds an argument.
|
|
404
|
+
const traceArgs =
|
|
405
|
+
rpcTracing === undefined ? [] : yield* RpcTracing.withRpcTraceContext([]);
|
|
406
|
+
const raw = yield* Effect.tryPromise({
|
|
407
|
+
try: () => {
|
|
408
|
+
const stub = namespace.get(namespace.idFromName(conversationId));
|
|
409
|
+
return stub[hostRpcMethods[operation]](encoded, ...traceArgs);
|
|
410
|
+
},
|
|
411
|
+
catch: (cause) =>
|
|
412
|
+
ConversationClientError.make({
|
|
413
|
+
conversationId,
|
|
414
|
+
message: boundHostDiagnostic(
|
|
415
|
+
`${operation} did not reach the Conversation Object: ${safeCauseMessage(
|
|
416
|
+
cause,
|
|
417
|
+
"the RPC failed without a diagnostic",
|
|
418
|
+
)}`,
|
|
419
|
+
),
|
|
420
|
+
cause,
|
|
421
|
+
...cloudflareFailureSignals(cause),
|
|
422
|
+
}),
|
|
423
|
+
});
|
|
424
|
+
return yield* decodeHostResponse(raw).pipe(
|
|
425
|
+
Effect.mapError(
|
|
426
|
+
(error): HostProtocolError =>
|
|
427
|
+
HostProtocolError.make({
|
|
428
|
+
message: boundHostDiagnostic(
|
|
429
|
+
`The ${operation} answer could not be decoded: ${error.message}`,
|
|
430
|
+
),
|
|
431
|
+
}),
|
|
415
432
|
),
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
433
|
+
);
|
|
434
|
+
},
|
|
435
|
+
(effect, conversationId, operation) =>
|
|
436
|
+
rpcTracing === undefined
|
|
437
|
+
? Effect.withSpan(effect, "CloudflareConversationClient.call", {
|
|
438
|
+
attributes: { conversationId, operation },
|
|
439
|
+
})
|
|
440
|
+
: RpcTracing.withRpcClientSpan(effect, rpcTracing, hostRpcMethods[operation]),
|
|
441
|
+
);
|
|
421
442
|
|
|
422
443
|
const expect = <ResultSchema extends Schema.Top, FailureSchema extends Schema.Top>(
|
|
423
444
|
conversationId: string,
|
|
@@ -464,9 +485,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
464
485
|
}),
|
|
465
486
|
),
|
|
466
487
|
);
|
|
467
|
-
const response = yield* call(conversationId, "observePage",
|
|
468
|
-
stub.observePage(encoded),
|
|
469
|
-
);
|
|
488
|
+
const response = yield* call(conversationId, "observePage", encoded);
|
|
470
489
|
const page = yield* expect(
|
|
471
490
|
conversationId,
|
|
472
491
|
"observePage",
|
|
@@ -482,9 +501,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
482
501
|
): Effect.Effect<void> =>
|
|
483
502
|
encodeCancelProgressRequest(CancelProgressRequest.make({ waiterId })).pipe(
|
|
484
503
|
Effect.mapError(() => undefined),
|
|
485
|
-
Effect.flatMap((encoded) =>
|
|
486
|
-
call(conversationId, "cancelProgress", (stub) => stub.cancelProgressEncoded(encoded)),
|
|
487
|
-
),
|
|
504
|
+
Effect.flatMap((encoded) => call(conversationId, "cancelProgress", encoded)),
|
|
488
505
|
Effect.asVoid,
|
|
489
506
|
Effect.ignore,
|
|
490
507
|
);
|
|
@@ -526,9 +543,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
526
543
|
}),
|
|
527
544
|
),
|
|
528
545
|
);
|
|
529
|
-
const response = yield* call(options.conversationId, "submit",
|
|
530
|
-
stub.submitEncoded(encoded),
|
|
531
|
-
);
|
|
546
|
+
const response = yield* call(options.conversationId, "submit", encoded);
|
|
532
547
|
const succeeded = yield* expect(
|
|
533
548
|
options.conversationId,
|
|
534
549
|
"submit",
|
|
@@ -547,9 +562,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
547
562
|
}),
|
|
548
563
|
),
|
|
549
564
|
);
|
|
550
|
-
const response = yield* call(receipt.conversationId, "awaitSettlement",
|
|
551
|
-
stub.awaitSettlementEncoded(encoded),
|
|
552
|
-
);
|
|
565
|
+
const response = yield* call(receipt.conversationId, "awaitSettlement", encoded);
|
|
553
566
|
const settled = yield* expect(
|
|
554
567
|
receipt.conversationId,
|
|
555
568
|
"awaitSettlement",
|
|
@@ -582,9 +595,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
582
595
|
);
|
|
583
596
|
|
|
584
597
|
const attempt = (retry: number): Effect.Effect<void, ClientProgressFailure> =>
|
|
585
|
-
call(conversationId, "awaitProgress", (
|
|
586
|
-
stub.awaitProgressEncoded(encoded),
|
|
587
|
-
).pipe(
|
|
598
|
+
call(conversationId, "awaitProgress", encoded).pipe(
|
|
588
599
|
Effect.flatMap(
|
|
589
600
|
expect(
|
|
590
601
|
conversationId,
|
|
@@ -632,9 +643,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
632
643
|
}),
|
|
633
644
|
),
|
|
634
645
|
);
|
|
635
|
-
const response = yield* call(conversationId, "abort",
|
|
636
|
-
stub.abortEncoded(encoded),
|
|
637
|
-
);
|
|
646
|
+
const response = yield* call(conversationId, "abort", encoded);
|
|
638
647
|
const recorded = yield* expect(
|
|
639
648
|
conversationId,
|
|
640
649
|
"abort",
|
|
@@ -653,9 +662,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
653
662
|
}),
|
|
654
663
|
),
|
|
655
664
|
);
|
|
656
|
-
const response = yield* call(conversationId, "resolveApproval",
|
|
657
|
-
stub.resolveApprovalEncoded(encoded),
|
|
658
|
-
);
|
|
665
|
+
const response = yield* call(conversationId, "resolveApproval", encoded);
|
|
659
666
|
const recorded = yield* expect(
|
|
660
667
|
conversationId,
|
|
661
668
|
"resolveApproval",
|
|
@@ -676,9 +683,7 @@ export class CloudflareConversationClient extends Context.Service<
|
|
|
676
683
|
}),
|
|
677
684
|
),
|
|
678
685
|
);
|
|
679
|
-
const response = yield* call(conversationId, "resolveUnknown",
|
|
680
|
-
stub.resolveUnknownEncoded(encoded),
|
|
681
|
-
);
|
|
686
|
+
const response = yield* call(conversationId, "resolveUnknown", encoded);
|
|
682
687
|
const recorded = yield* expect(
|
|
683
688
|
conversationId,
|
|
684
689
|
"resolveUnknown",
|
|
@@ -35,7 +35,6 @@ import {
|
|
|
35
35
|
encodePortResponse,
|
|
36
36
|
type PortRequest,
|
|
37
37
|
} from "@effect-agent/storage-cloudflare";
|
|
38
|
-
import type { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
39
38
|
import { Effect, Layer, Option, Schema, Stream } from "effect";
|
|
40
39
|
import {
|
|
41
40
|
DurableObject as EffectCfDurableObject,
|
|
@@ -110,6 +109,8 @@ import { ProgressWaitRegistry } from "./progress-wait.ts";
|
|
|
110
109
|
|
|
111
110
|
/** Construction options for one deployed Conversation Object class. */
|
|
112
111
|
export interface ConversationObjectOptions extends CloudflareDurableRuntimeOptions {
|
|
112
|
+
/** Accept transient native RPC tracing through effect-cf; disabled by default. */
|
|
113
|
+
readonly rpcTracing?: boolean;
|
|
113
114
|
/**
|
|
114
115
|
* Name of the Worker `env` binding carrying THIS class's `DurableObjectNamespace` — the
|
|
115
116
|
* Object's route back to sibling Conversation Objects for the WP2 cross-Object port calls
|
|
@@ -676,6 +677,7 @@ const gateEndpoint: Effect.Effect<void, MaintenancePassFailure, EndpointServices
|
|
|
676
677
|
*/
|
|
677
678
|
const effectCfPlatformLayer = (
|
|
678
679
|
namespaceBinding: string,
|
|
680
|
+
rpcTracing = false,
|
|
679
681
|
): Layer.Layer<
|
|
680
682
|
DurableObjectContext | ConversationObjectNamespace,
|
|
681
683
|
CloudflareBindingError,
|
|
@@ -692,22 +694,27 @@ const effectCfPlatformLayer = (
|
|
|
692
694
|
Effect.gen(function* () {
|
|
693
695
|
const env = yield* WorkerEnvironment;
|
|
694
696
|
const binding = yield* conversationNamespaceFromEnv(env, namespaceBinding);
|
|
695
|
-
return ConversationObjectNamespace.of({
|
|
697
|
+
return ConversationObjectNamespace.of({
|
|
698
|
+
namespace: binding,
|
|
699
|
+
...(rpcTracing === true ? { rpcTracing: namespaceBinding } : {}),
|
|
700
|
+
});
|
|
696
701
|
}),
|
|
697
702
|
);
|
|
698
703
|
return Layer.merge(context, namespace);
|
|
699
704
|
};
|
|
700
705
|
|
|
701
|
-
/** The public
|
|
702
|
-
export interface ConversationObjectInstance extends
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
706
|
+
/** The public endpoints and effect-cf invocation hook of one Conversation Object instance. */
|
|
707
|
+
export interface ConversationObjectInstance<EventServices = never> extends InstanceType<
|
|
708
|
+
EffectCfDurableObject.DurableObjectClass<Record<never, never>, RuntimeServices | EventServices>
|
|
709
|
+
> {
|
|
710
|
+
submitEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
711
|
+
awaitSettlementEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
712
|
+
awaitProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
713
|
+
cancelProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
714
|
+
observePage(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
715
|
+
abortEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
716
|
+
resolveApprovalEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
717
|
+
resolveUnknownEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
711
718
|
explainEncoded(encoded: unknown): Promise<unknown>;
|
|
712
719
|
verifyEncoded(encoded: unknown): Promise<unknown>;
|
|
713
720
|
retryEncoded(encoded: unknown): Promise<unknown>;
|
|
@@ -718,8 +725,8 @@ export interface ConversationObjectInstance extends CloudflareDurableObject {
|
|
|
718
725
|
}
|
|
719
726
|
|
|
720
727
|
/** The constructor shape workerd instantiates for each Conversation Object. */
|
|
721
|
-
export interface ConversationObjectClass {
|
|
722
|
-
new (ctx: DurableObjectState, env: Cloudflare.Env): ConversationObjectInstance
|
|
728
|
+
export interface ConversationObjectClass<EventServices = never> {
|
|
729
|
+
new (ctx: DurableObjectState, env: Cloudflare.Env): ConversationObjectInstance<EventServices>;
|
|
723
730
|
}
|
|
724
731
|
|
|
725
732
|
/**
|
|
@@ -739,13 +746,13 @@ export const makeConversationObjectClass = <EventLayerError = never, EventServic
|
|
|
739
746
|
| EffectCfDurableObjectState.DurableObjectState
|
|
740
747
|
| WorkerEnvironment
|
|
741
748
|
>,
|
|
742
|
-
): ConversationObjectClass => {
|
|
749
|
+
): ConversationObjectClass<EventServices> => {
|
|
743
750
|
const application: Layer.Layer<
|
|
744
751
|
RuntimeServices,
|
|
745
752
|
CloudflareDurableRuntimeInitializationError | CloudflareBindingError,
|
|
746
753
|
EffectCfDurableObjectState.DurableObjectState | WorkerEnvironment
|
|
747
754
|
> = CloudflareDurableRuntime.layer(options).pipe(
|
|
748
|
-
Layer.provideMerge(effectCfPlatformLayer(options.namespaceBinding)),
|
|
755
|
+
Layer.provideMerge(effectCfPlatformLayer(options.namespaceBinding, options.rpcTracing)),
|
|
749
756
|
);
|
|
750
757
|
|
|
751
758
|
// The storage/config Layer must acquire inside Cloudflare's constructor gate. effect-cf owns
|
|
@@ -793,6 +800,7 @@ export const makeConversationObjectClass = <EventLayerError = never, EventServic
|
|
|
793
800
|
EventLayerError,
|
|
794
801
|
typeof rpc
|
|
795
802
|
>(runtime, {
|
|
803
|
+
...(options.rpcTracing === true ? { rpcTracing: { service: options.namespaceBinding } } : {}),
|
|
796
804
|
...(observability === undefined ? {} : { eventLayer: observability }),
|
|
797
805
|
// Force the gated runtime Layer when Cloudflare loads this Object incarnation. Recovery stays
|
|
798
806
|
// in each bounded pass so cross-Object initialization cannot deadlock.
|
|
@@ -22,10 +22,13 @@ import {
|
|
|
22
22
|
InteractiveBrowserPolicy,
|
|
23
23
|
InteractiveBrowserPolicyDeniedError,
|
|
24
24
|
InteractiveBrowserProtocolError,
|
|
25
|
+
InteractiveBrowserTargetUrl,
|
|
26
|
+
InteractiveBrowserUnsupportedError,
|
|
25
27
|
PageScreenshotResult,
|
|
26
28
|
SandboxImplementation,
|
|
27
29
|
type BrowserHandle,
|
|
28
30
|
type InteractiveBrowserError,
|
|
31
|
+
type InteractiveBrowserNetworkPolicy,
|
|
29
32
|
} from "@effect-agent/sandbox";
|
|
30
33
|
import {
|
|
31
34
|
Context,
|
|
@@ -170,7 +173,7 @@ type BrowserFailure = typeof InteractiveBrowserError.Type;
|
|
|
170
173
|
type BrowserOperation = InteractiveBrowserActionError["operation"];
|
|
171
174
|
|
|
172
175
|
interface InteractiveBrowserPolicySnapshot {
|
|
173
|
-
readonly
|
|
176
|
+
readonly network: Exclude<InteractiveBrowserNetworkPolicy, { readonly _tag: "PublicWeb" }>;
|
|
174
177
|
readonly maxActions: number;
|
|
175
178
|
readonly maxElapsedMillis: number;
|
|
176
179
|
readonly maxReturnedBytes: number;
|
|
@@ -443,29 +446,46 @@ const isRemoteClosure = (cause: unknown): boolean =>
|
|
|
443
446
|
causeText(cause),
|
|
444
447
|
);
|
|
445
448
|
|
|
446
|
-
const snapshotPolicy = (
|
|
449
|
+
const snapshotPolicy = Effect.fn("BrowserRunInteractive.snapshotPolicy")(function* (
|
|
447
450
|
input: InteractiveBrowserPolicy,
|
|
448
|
-
): Effect.
|
|
449
|
-
|
|
451
|
+
): Effect.fn.Return<
|
|
452
|
+
InteractiveBrowserPolicySnapshot,
|
|
453
|
+
InteractiveBrowserPolicyDeniedError | InteractiveBrowserUnsupportedError
|
|
454
|
+
> {
|
|
455
|
+
const decoded = yield* Schema.decodeUnknownEffect(InteractiveBrowserPolicy)(input).pipe(
|
|
450
456
|
Effect.mapError(() => policyError("The interactive browser policy is malformed")),
|
|
451
|
-
Effect.map((decoded) =>
|
|
452
|
-
Object.freeze({
|
|
453
|
-
allowedHosts: Object.freeze([...decoded.allowedHosts]),
|
|
454
|
-
maxActions: decoded.maxActions,
|
|
455
|
-
maxElapsedMillis: decoded.maxElapsedMillis,
|
|
456
|
-
maxReturnedBytes: decoded.maxReturnedBytes,
|
|
457
|
-
}),
|
|
458
|
-
),
|
|
459
457
|
);
|
|
458
|
+
if (decoded.network._tag === "PublicWeb") {
|
|
459
|
+
return yield* InteractiveBrowserUnsupportedError.make({
|
|
460
|
+
implementation: browserRunInteractiveImplementation,
|
|
461
|
+
feature: "policy",
|
|
462
|
+
message:
|
|
463
|
+
"Cloudflare Browser Run cannot enforce the PublicWeb network policy for all session traffic",
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
return Object.freeze({
|
|
467
|
+
network:
|
|
468
|
+
decoded.network._tag === "ExactHosts"
|
|
469
|
+
? Object.freeze({
|
|
470
|
+
_tag: decoded.network._tag,
|
|
471
|
+
allowedHosts: Object.freeze([...decoded.network.allowedHosts]),
|
|
472
|
+
})
|
|
473
|
+
: Object.freeze({ _tag: decoded.network._tag }),
|
|
474
|
+
maxActions: decoded.maxActions,
|
|
475
|
+
maxElapsedMillis: decoded.maxElapsedMillis,
|
|
476
|
+
maxReturnedBytes: decoded.maxReturnedBytes,
|
|
477
|
+
});
|
|
478
|
+
});
|
|
460
479
|
|
|
461
480
|
const hostAllowed = (policy: InteractiveBrowserPolicySnapshot, value: string): boolean => {
|
|
481
|
+
if (policy.network._tag === "Unrestricted") return true;
|
|
462
482
|
try {
|
|
463
483
|
const url = new URL(value);
|
|
464
484
|
return (
|
|
465
485
|
url.protocol === "https:" &&
|
|
466
486
|
url.username === "" &&
|
|
467
487
|
url.password === "" &&
|
|
468
|
-
policy.allowedHosts.some((host) => host === url.host)
|
|
488
|
+
policy.network.allowedHosts.some((host) => host === url.host)
|
|
469
489
|
);
|
|
470
490
|
} catch {
|
|
471
491
|
return false;
|
|
@@ -756,7 +776,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
|
|
|
756
776
|
return yield* decodeNavigationResult(page, policy);
|
|
757
777
|
}),
|
|
758
778
|
Effect.suspend(() =>
|
|
759
|
-
hostAllowed(policy, request.url)
|
|
779
|
+
Schema.is(InteractiveBrowserTargetUrl)(request.url) && hostAllowed(policy, request.url)
|
|
760
780
|
? Effect.void
|
|
761
781
|
: Effect.fail(policyError("The navigation URL is outside the browser policy")),
|
|
762
782
|
),
|
package/src/layers.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { ConversationId } from "@effect-agent/core";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
RunContextPreparation,
|
|
4
|
+
RunCostEstimator,
|
|
5
|
+
RunToolFailureObserver,
|
|
6
|
+
} from "@effect-agent/engine";
|
|
7
|
+
import {
|
|
8
|
+
CurrentToolFailureObserver,
|
|
9
|
+
RunContextPreparationPassthrough,
|
|
10
|
+
toolFailureObserverLayer,
|
|
11
|
+
} from "@effect-agent/engine";
|
|
4
12
|
import {
|
|
5
13
|
AgentBindingResolver,
|
|
6
14
|
DurableAgentRuntime,
|
|
@@ -82,6 +90,8 @@ export interface CloudflareDurableRuntimeOptions {
|
|
|
82
90
|
readonly abortPollInterval?: number | undefined;
|
|
83
91
|
/** Deployment-owned pricing authority used by durable cost budgets and settlements. */
|
|
84
92
|
readonly estimateCostMicrousd?: RunCostEstimator | undefined;
|
|
93
|
+
/** Closed trusted Tool failure reporting. Omission masks ambient observers at construction. */
|
|
94
|
+
readonly toolFailureObserver?: RunToolFailureObserver | undefined;
|
|
85
95
|
/** Milliseconds; default 25. */
|
|
86
96
|
readonly observationPollInterval?: number | undefined;
|
|
87
97
|
/** Bytes; default just under the 2 MB platform value limit. */
|
|
@@ -382,6 +392,10 @@ export class CloudflareDurableRuntime {
|
|
|
382
392
|
options.operationAuthorizer === undefined
|
|
383
393
|
? Layer.empty
|
|
384
394
|
: operationAuthorizerLayer(options.operationAuthorizer);
|
|
395
|
+
const observerLayer =
|
|
396
|
+
options.toolFailureObserver === undefined
|
|
397
|
+
? Layer.succeed(CurrentToolFailureObserver)(undefined)
|
|
398
|
+
: toolFailureObserverLayer(options.toolFailureObserver);
|
|
385
399
|
const bindingResolverLayer = Layer.effect(AgentBindingResolver)(
|
|
386
400
|
Effect.map(
|
|
387
401
|
resolveBindings(options.bindings, { ctx, env, conversationId, producerId }),
|
|
@@ -413,6 +427,7 @@ export class CloudflareDurableRuntime {
|
|
|
413
427
|
runtimeFailpointLayer,
|
|
414
428
|
reconcilerLayer,
|
|
415
429
|
authorizerLayer,
|
|
430
|
+
observerLayer,
|
|
416
431
|
runContextLayer,
|
|
417
432
|
BrowserCrypto.layer,
|
|
418
433
|
),
|