@effect-agent/platform-cloudflare 0.1.0-beta.33 → 0.1.0-beta.34
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 +72 -54
- package/dist/index.mjs +54 -27
- package/dist/index.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/layers.ts +17 -2
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.34",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"types": "./dist/index.d.mts",
|
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@cloudflare/puppeteer": "1.1.0",
|
|
28
|
-
"@effect-agent/core": "0.1.0-beta.
|
|
29
|
-
"@effect-agent/engine": "0.1.0-beta.
|
|
30
|
-
"@effect-agent/sandbox": "0.1.0-beta.
|
|
31
|
-
"@effect-agent/session": "0.1.0-beta.
|
|
32
|
-
"@effect-agent/storage-cloudflare": "0.1.0-beta.
|
|
33
|
-
"@effect/platform-browser": "4.0.0-rc.
|
|
34
|
-
"@effect/sql-sqlite-do": "4.0.0-rc.
|
|
35
|
-
"effect": "4.0.0-rc.
|
|
28
|
+
"@effect-agent/core": "0.1.0-beta.34",
|
|
29
|
+
"@effect-agent/engine": "0.1.0-beta.34",
|
|
30
|
+
"@effect-agent/sandbox": "0.1.0-beta.34",
|
|
31
|
+
"@effect-agent/session": "0.1.0-beta.34",
|
|
32
|
+
"@effect-agent/storage-cloudflare": "0.1.0-beta.34",
|
|
33
|
+
"@effect/platform-browser": "4.0.0-rc.111",
|
|
34
|
+
"@effect/sql-sqlite-do": "4.0.0-rc.111",
|
|
35
|
+
"effect": "4.0.0-rc.111"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"effect-cf": "^0.
|
|
38
|
+
"effect-cf": "^0.34.0"
|
|
39
39
|
},
|
|
40
40
|
"description": "Cloudflare Layer assembly for Effect Agent: Durable Objects and Browser Run adapters.",
|
|
41
41
|
"license": "MIT",
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "vp pack",
|
|
57
|
-
"check": "tsc --noEmit -p tsconfig.json"
|
|
58
|
-
"test": "vitest run"
|
|
57
|
+
"check": "tsc --noEmit -p tsconfig.json"
|
|
59
58
|
},
|
|
60
59
|
"devDependencies": {
|
|
61
60
|
"@cloudflare/vitest-pool-workers": "0.21.3",
|
|
62
|
-
"@cloudflare/workers-types": "5.
|
|
63
|
-
"@effect-agent/capabilities": "0.1.0-beta.
|
|
64
|
-
"@effect-agent/testing": "0.1.0-beta.
|
|
65
|
-
"@effect/
|
|
66
|
-
"effect
|
|
61
|
+
"@cloudflare/workers-types": "5.20260825.1",
|
|
62
|
+
"@effect-agent/capabilities": "0.1.0-beta.33",
|
|
63
|
+
"@effect-agent/testing": "0.1.0-beta.33",
|
|
64
|
+
"@effect/sql-d1": "4.0.0-rc.111",
|
|
65
|
+
"@effect/vitest": "4.0.0-rc.111",
|
|
66
|
+
"effect-cf": "0.34.0",
|
|
67
67
|
"esbuild": "0.28.1",
|
|
68
68
|
"miniflare": "5.20260811.1-alpha",
|
|
69
69
|
"typescript": "7.0.2",
|
package/src/bindings.ts
CHANGED
|
@@ -24,25 +24,27 @@ export class CloudflareBindingError extends Schema.TaggedError<CloudflareBinding
|
|
|
24
24
|
* and the cross-Object transport call it through `DurableObjectNamespace` stubs. Every
|
|
25
25
|
* `encoded` value is a Schema-encoded envelope (`client.ts` wire schemas for host entry
|
|
26
26
|
* points, `@effect-agent/storage-cloudflare` port envelopes for `portCall`), so the RPC
|
|
27
|
-
* boundary carries only structured-cloneable JSON.
|
|
27
|
+
* boundary carries only structured-cloneable JSON. The optional trailing trace context is
|
|
28
|
+
* transient native RPC metadata, stripped by an opted-in effect-cf receiver before decoding
|
|
29
|
+
* the host envelope. It never enters durable state.
|
|
28
30
|
*/
|
|
29
31
|
export interface ConversationObjectRpc extends Rpc.DurableObjectBranded {
|
|
30
32
|
/** Admission-limits gate + `DurableAgentRuntime.submit`; answers a `SubmitResponse`. */
|
|
31
|
-
submitEncoded(encoded: unknown): Promise<unknown>;
|
|
33
|
+
submitEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
32
34
|
/** Wake-hinted, poll-guaranteed settlement wait; answers an `AwaitSettlementResponse`. */
|
|
33
|
-
awaitSettlementEncoded(encoded: unknown): Promise<unknown>;
|
|
35
|
+
awaitSettlementEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
34
36
|
/** Event-driven durable progress wait; answers a `ProgressObserved` host response. */
|
|
35
|
-
awaitProgressEncoded(encoded: unknown): Promise<unknown>;
|
|
37
|
+
awaitProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
36
38
|
/** Best-effort cancellation for one in-flight progress wait. */
|
|
37
|
-
cancelProgressEncoded(encoded: unknown): Promise<unknown>;
|
|
39
|
+
cancelProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
38
40
|
/** One bounded page of canonical records; answers an `ObservePageResponse`. */
|
|
39
|
-
observePage(encoded: unknown): Promise<unknown>;
|
|
41
|
+
observePage(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
40
42
|
/** Durable abort intent; answers an `AbortResponse`. */
|
|
41
|
-
abortEncoded(encoded: unknown): Promise<unknown>;
|
|
43
|
+
abortEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
42
44
|
/** Durable approval decision (plan §2.6); answers a `ResolveApprovalResponse`. */
|
|
43
|
-
resolveApprovalEncoded(encoded: unknown): Promise<unknown>;
|
|
45
|
+
resolveApprovalEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
44
46
|
/** Authorized DUR-017 Unknown-Outcome resolution; answers a `ResolveUnknownResponse`. */
|
|
45
|
-
resolveUnknownEncoded(encoded: unknown): Promise<unknown>;
|
|
47
|
+
resolveUnknownEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;
|
|
46
48
|
/** Owner-side cross-Object port endpoint (WP2 envelopes, executed on LOCAL facets). */
|
|
47
49
|
portCall(encoded: unknown): Promise<unknown>;
|
|
48
50
|
/** Droppable liveness hint from another Object: arms an immediate alarm. */
|
|
@@ -58,6 +60,8 @@ export class ConversationObjectNamespace extends Context.Service<
|
|
|
58
60
|
ConversationObjectNamespace,
|
|
59
61
|
{
|
|
60
62
|
readonly namespace: DurableObjectNamespace<ConversationObjectRpc>;
|
|
63
|
+
/** Stable binding name for opted-in native RPC tracing; absent by default. */
|
|
64
|
+
readonly rpcTracing?: string;
|
|
61
65
|
}
|
|
62
66
|
>()("@effect-agent/platform-cloudflare/ConversationObjectNamespace") {
|
|
63
67
|
static layer(
|
|
@@ -110,13 +114,20 @@ export const conversationNamespaceFromEnv = Effect.fn("conversationNamespaceFrom
|
|
|
110
114
|
});
|
|
111
115
|
});
|
|
112
116
|
|
|
113
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Build the namespace from Worker `env` (fails typed). Enable `rpcTracing` only when the
|
|
119
|
+
* receiver also opts into the effect-cf native RPC trace-context contract.
|
|
120
|
+
*/
|
|
114
121
|
export const conversationNamespaceLayer = (
|
|
115
122
|
env: unknown,
|
|
116
123
|
binding: string,
|
|
124
|
+
options: { readonly rpcTracing?: boolean } = {},
|
|
117
125
|
): Layer.Layer<ConversationObjectNamespace, CloudflareBindingError> =>
|
|
118
126
|
Layer.effect(ConversationObjectNamespace)(
|
|
119
|
-
Effect.map(conversationNamespaceFromEnv(env, binding), (namespace) => ({
|
|
127
|
+
Effect.map(conversationNamespaceFromEnv(env, binding), (namespace) => ({
|
|
128
|
+
namespace,
|
|
129
|
+
...(options.rpcTracing === true ? { rpcTracing: binding } : {}),
|
|
130
|
+
})),
|
|
120
131
|
);
|
|
121
132
|
|
|
122
133
|
/**
|
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.
|
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
|
),
|