@effect-agent/testing 0.1.0-beta.38 → 0.1.0-beta.39
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/certification.d.mts +102 -0
- package/dist/certification.mjs +561 -0
- package/dist/certification.mjs.map +1 -0
- package/dist/chaos.d.mts +124 -0
- package/dist/chaos.mjs +738 -0
- package/dist/chaos.mjs.map +1 -0
- package/dist/code-executor.d.mts +48 -0
- package/dist/code-executor.mjs +565 -0
- package/dist/code-executor.mjs.map +1 -0
- package/dist/deterministic-layers-D6gCdoPM.mjs +354 -0
- package/dist/deterministic-layers-D6gCdoPM.mjs.map +1 -0
- package/dist/docs-researcher.d.mts +254 -0
- package/dist/docs-researcher.mjs +479 -0
- package/dist/docs-researcher.mjs.map +1 -0
- package/dist/index.d.mts +2 -2947
- package/dist/index.mjs +2 -4913
- package/dist/scripted-model-CEwVM9JY.mjs +169 -0
- package/dist/scripted-model-CEwVM9JY.mjs.map +1 -0
- package/dist/scripted-model-dOa_e0-n.d.mts +677 -0
- package/dist/travel-planner.d.mts +1767 -0
- package/dist/travel-planner.mjs +2102 -0
- package/dist/travel-planner.mjs.map +1 -0
- package/package.json +33 -10
- package/src/certification.ts +71 -61
- package/src/chaos.ts +52 -64
- package/src/code-executor.ts +3 -0
- package/src/docs-researcher.ts +2 -0
- package/src/fixtures/docs-researcher/definition.ts +7 -7
- package/src/fixtures/docs-researcher/harness.ts +5 -5
- package/src/fixtures/docs-researcher/index.ts +1 -1
- package/src/fixtures/travel-planner/definition.ts +1 -1
- package/src/fixtures/travel-planner/deterministic-layers.ts +6 -4
- package/src/fixtures/travel-planner/phase2.ts +1 -1
- package/src/fixtures/travel-planner/phase3.ts +14 -18
- package/src/fixtures/travel-planner/phase4.ts +7 -7
- package/src/fixtures/travel-planner/phase5.ts +5 -5
- package/src/fixtures/travel-planner/phase6.ts +30 -29
- package/src/fixtures/travel-planner/subagents-durable.ts +6 -6
- package/src/fixtures/travel-planner/subagents.ts +4 -4
- package/src/index.ts +1 -10
- package/src/travel-planner.ts +2 -0
- package/dist/index.mjs.map +0 -1
- package/src/code-executor-conformance.d.ts +0 -30
- package/src/fixtures/warehouse/index.ts +0 -412
package/src/chaos.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
Agent,
|
|
9
9
|
AgentPolicy,
|
|
10
|
-
|
|
10
|
+
ThreadId,
|
|
11
11
|
IdGenerator,
|
|
12
12
|
RunId,
|
|
13
13
|
ToolCallId,
|
|
@@ -17,17 +17,15 @@ import {
|
|
|
17
17
|
import { DurableStep, DurableStepError, ToolExecutionClass } from "@effect-agent/engine";
|
|
18
18
|
import {
|
|
19
19
|
AbortCommand,
|
|
20
|
-
AgentBindingResolver,
|
|
21
20
|
ApprovalDecisionCommand,
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
ThreadExportRequest,
|
|
22
|
+
ThreadStore,
|
|
24
23
|
DefinitionDigests,
|
|
25
24
|
Digest,
|
|
26
25
|
DurableAgentRuntime,
|
|
27
26
|
DurableRuntimeConfig,
|
|
28
27
|
DurableRuntimeFailpointError,
|
|
29
28
|
DurableRuntimeFailpointLocation,
|
|
30
|
-
DurableRuntimeFailpointTestControl,
|
|
31
29
|
DurableWorkerBinding,
|
|
32
30
|
IdempotencyKey,
|
|
33
31
|
ObligationThresholds,
|
|
@@ -38,8 +36,8 @@ import {
|
|
|
38
36
|
SubmissionLedger,
|
|
39
37
|
SubmissionLookupById,
|
|
40
38
|
UnknownResolutionCommand,
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
childThreadIdFor,
|
|
40
|
+
verifyThreadInvariants,
|
|
43
41
|
type CanonicalRecordEnvelope,
|
|
44
42
|
type BatchId,
|
|
45
43
|
type DurableBindingFailure,
|
|
@@ -51,7 +49,8 @@ import {
|
|
|
51
49
|
type Settlement,
|
|
52
50
|
type SubmissionSnapshot,
|
|
53
51
|
type UnknownResolution,
|
|
54
|
-
} from "@effect-agent/
|
|
52
|
+
} from "@effect-agent/thread";
|
|
53
|
+
import { DurableRuntimeFailpointTestControl } from "@effect-agent/thread/testing";
|
|
55
54
|
import { Cause, Effect, Exit, Layer, Option, Ref, Schema, Stream } from "effect";
|
|
56
55
|
import { FastCheck } from "effect/testing";
|
|
57
56
|
import {
|
|
@@ -69,7 +68,7 @@ import {
|
|
|
69
68
|
* deterministic runner that drives the durable coordinator over whatever adapter pair the test
|
|
70
69
|
* provides. Every plan ends in the SAME claims the crash matrices make:
|
|
71
70
|
*
|
|
72
|
-
* 1. `
|
|
71
|
+
* 1. `verifyThreadInvariants` in convergence mode over every touched Thread (the
|
|
73
72
|
* shared WP1 checker — one set of claims for admin verify, certification, chaos, and soak);
|
|
74
73
|
* 2. `scanObligations` returning ZERO entries (everything settled; nothing invisibly stuck);
|
|
75
74
|
* 3. supplier non-fabrication wherever the deterministic desk was in play (durability §10: no
|
|
@@ -152,10 +151,10 @@ export class ChaosPlan extends Schema.Class<ChaosPlan>("@effect-agent/testing/Ch
|
|
|
152
151
|
export class ChaosLaneReport extends Schema.Class<ChaosLaneReport>(
|
|
153
152
|
"@effect-agent/testing/ChaosLaneReport",
|
|
154
153
|
)({
|
|
155
|
-
|
|
154
|
+
threadId: ThreadId,
|
|
156
155
|
kind: ChaosScenarioKind,
|
|
157
156
|
submissionCount: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
158
|
-
/** Verdict of `
|
|
157
|
+
/** Verdict of `verifyThreadInvariants` in convergence mode. */
|
|
159
158
|
verified: Schema.Boolean,
|
|
160
159
|
}) {}
|
|
161
160
|
|
|
@@ -371,7 +370,7 @@ const policy = AgentPolicy.make({
|
|
|
371
370
|
const PlainInput = Schema.Struct({ question: Schema.String });
|
|
372
371
|
const PlainOutput = Schema.Struct({ answer: Schema.String });
|
|
373
372
|
|
|
374
|
-
const plainDefinition = Agent.
|
|
373
|
+
const plainDefinition = Agent.make("chaos-plain", {
|
|
375
374
|
input: PlainInput,
|
|
376
375
|
output: PlainOutput,
|
|
377
376
|
instructions: "Answer as JSON.",
|
|
@@ -385,7 +384,7 @@ const BookUncertain = Tool.make("book", {
|
|
|
385
384
|
success: Schema.Struct({ confirmation: Schema.String }),
|
|
386
385
|
});
|
|
387
386
|
const bookTools = Toolkit.make(BookUncertain);
|
|
388
|
-
const bookDefinition = Agent.
|
|
387
|
+
const bookDefinition = Agent.make("chaos-book", {
|
|
389
388
|
input: PlainInput,
|
|
390
389
|
output: PlainOutput,
|
|
391
390
|
instructions: "Book it.",
|
|
@@ -399,7 +398,7 @@ const BookApproval = Tool.make("book", {
|
|
|
399
398
|
needsApproval: true,
|
|
400
399
|
});
|
|
401
400
|
const approvalTools = Toolkit.make(BookApproval);
|
|
402
|
-
const approvalDefinition = Agent.
|
|
401
|
+
const approvalDefinition = Agent.make("chaos-approval", {
|
|
403
402
|
input: PlainInput,
|
|
404
403
|
output: PlainOutput,
|
|
405
404
|
instructions: "Book after approval.",
|
|
@@ -414,7 +413,7 @@ const Itinerary = Tool.make("itinerary", {
|
|
|
414
413
|
dependencies: [DurableStep],
|
|
415
414
|
}).annotate(ToolExecutionClass, "uncertain");
|
|
416
415
|
const itineraryTools = Toolkit.make(Itinerary);
|
|
417
|
-
const itineraryDefinition = Agent.
|
|
416
|
+
const itineraryDefinition = Agent.make("chaos-itinerary", {
|
|
418
417
|
input: PlainInput,
|
|
419
418
|
output: PlainOutput,
|
|
420
419
|
instructions: "Reserve the itinerary.",
|
|
@@ -422,7 +421,7 @@ const itineraryDefinition = Agent.define("chaos-itinerary", {
|
|
|
422
421
|
policy,
|
|
423
422
|
});
|
|
424
423
|
|
|
425
|
-
const childDefinition = Agent.
|
|
424
|
+
const childDefinition = Agent.make("chaos-child", {
|
|
426
425
|
input: PlainInput,
|
|
427
426
|
output: PlainOutput,
|
|
428
427
|
instructions: "Answer as JSON.",
|
|
@@ -457,7 +456,7 @@ const chaosDelegation = Subagent.define("delegate_chaos", {
|
|
|
457
456
|
}),
|
|
458
457
|
});
|
|
459
458
|
|
|
460
|
-
const coordinatorDefinition = Agent.
|
|
459
|
+
const coordinatorDefinition = Agent.make("chaos-coordinator", {
|
|
461
460
|
input: Schema.Struct({ mission: Schema.String }),
|
|
462
461
|
output: Schema.Struct({ report: Schema.String }),
|
|
463
462
|
instructions: "Delegate, then report as JSON.",
|
|
@@ -487,7 +486,7 @@ const childLaneDigests = (lane: number): DefinitionDigests => {
|
|
|
487
486
|
};
|
|
488
487
|
|
|
489
488
|
const CHAOS_PRINCIPAL = Schema.decodeSync(Principal)("principal-chaos");
|
|
490
|
-
const
|
|
489
|
+
const decodeThreadId = Schema.decodeSync(ThreadId);
|
|
491
490
|
const decodeIdempotencyKey = Schema.decodeSync(IdempotencyKey);
|
|
492
491
|
const decodeToolCallId = Schema.decodeSync(ToolCallId);
|
|
493
492
|
const decodeRunId = Schema.decodeSync(RunId);
|
|
@@ -503,7 +502,7 @@ const chaosIdentifiers = Layer.effect(
|
|
|
503
502
|
Effect.map((value) => decode(`${prefix}-${value}`)),
|
|
504
503
|
);
|
|
505
504
|
return {
|
|
506
|
-
|
|
505
|
+
nextThreadId: next(decodeThreadId, "chaos-fixture-thread"),
|
|
507
506
|
nextRunId: next(decodeRunId, "chaos-fixture-run"),
|
|
508
507
|
nextTurnId: next(decodeTurnId, "chaos-fixture-turn"),
|
|
509
508
|
};
|
|
@@ -573,7 +572,7 @@ const tolerateTyped = <A, E, R>(
|
|
|
573
572
|
interface LaneFixture {
|
|
574
573
|
readonly index: number;
|
|
575
574
|
readonly kind: ChaosScenarioKind;
|
|
576
|
-
readonly
|
|
575
|
+
readonly threadId: ThreadId;
|
|
577
576
|
readonly ref: string;
|
|
578
577
|
readonly deskInPlay: boolean;
|
|
579
578
|
readonly submissionIndexes: ReadonlyArray<number>;
|
|
@@ -583,7 +582,7 @@ interface LaneFixture {
|
|
|
583
582
|
) => ReadonlyArray<
|
|
584
583
|
Effect.Effect<ReadonlyArray<Settlement>, DurableWorkerFailure | DurableBindingFailure>
|
|
585
584
|
>;
|
|
586
|
-
readonly
|
|
585
|
+
readonly childThreadOf: (firstReceipt: Receipt) => ThreadId | undefined;
|
|
587
586
|
}
|
|
588
587
|
|
|
589
588
|
interface SubmissionState {
|
|
@@ -627,14 +626,14 @@ const makeLaneFixture = Effect.fn("Chaos.makeLaneFixture")(function* (
|
|
|
627
626
|
desk: ChaosDesk,
|
|
628
627
|
) {
|
|
629
628
|
const runtime = yield* DurableAgentRuntime;
|
|
630
|
-
const
|
|
629
|
+
const threadId = decodeThreadId(`chaos-${plan.seed}-lane-${laneIndex}`);
|
|
631
630
|
const ref = `ref-l${laneIndex}`;
|
|
632
631
|
const script = scriptFor(kind, ref);
|
|
633
632
|
const model = promptScriptedModel(`chaos-${kind}-${laneIndex}`, script);
|
|
634
633
|
const digests = laneDigests(laneIndex);
|
|
635
634
|
|
|
636
635
|
const submitOptionsFor = (flatIndex: number) => ({
|
|
637
|
-
|
|
636
|
+
threadId,
|
|
638
637
|
principal: CHAOS_PRINCIPAL,
|
|
639
638
|
idempotencyKey: decodeIdempotencyKey(`chaos-${plan.seed}-s${flatIndex}`),
|
|
640
639
|
definitions: digests,
|
|
@@ -655,33 +654,28 @@ const makeLaneFixture = Effect.fn("Chaos.makeLaneFixture")(function* (
|
|
|
655
654
|
): LaneFixture => ({
|
|
656
655
|
index: laneIndex,
|
|
657
656
|
kind,
|
|
658
|
-
|
|
657
|
+
threadId,
|
|
659
658
|
ref,
|
|
660
659
|
deskInPlay,
|
|
661
660
|
submissionIndexes,
|
|
662
661
|
submitOne,
|
|
663
662
|
drives: () => [drive],
|
|
664
|
-
|
|
663
|
+
childThreadOf: () => undefined,
|
|
665
664
|
});
|
|
666
665
|
|
|
667
666
|
switch (kind) {
|
|
668
667
|
case "plain":
|
|
669
668
|
case "join": {
|
|
670
669
|
const agent = Agent.withModel(plainDefinition, model);
|
|
671
|
-
return plainLaneFixture(
|
|
672
|
-
|
|
673
|
-
runtime.processConversation(agent, conversationId),
|
|
674
|
-
(flatIndex) =>
|
|
675
|
-
runtime.submit(agent, { question: `chaos ${flatIndex}` }, submitOptionsFor(flatIndex)),
|
|
670
|
+
return plainLaneFixture(false, runtime.processThread(agent, threadId), (flatIndex) =>
|
|
671
|
+
runtime.submit(agent, { question: `chaos ${flatIndex}` }, submitOptionsFor(flatIndex)),
|
|
676
672
|
);
|
|
677
673
|
}
|
|
678
674
|
case "uncertain-tool": {
|
|
679
675
|
const agent = Agent.withModel(bookDefinition, model);
|
|
680
676
|
return plainLaneFixture(
|
|
681
677
|
true,
|
|
682
|
-
runtime
|
|
683
|
-
.processConversation(agent, conversationId)
|
|
684
|
-
.pipe(Effect.provide(bookToolLayerFor(bookTools))),
|
|
678
|
+
runtime.processThread(agent, threadId).pipe(Effect.provide(bookToolLayerFor(bookTools))),
|
|
685
679
|
(flatIndex) =>
|
|
686
680
|
runtime.submit(agent, { question: `chaos ${flatIndex}` }, submitOptionsFor(flatIndex)),
|
|
687
681
|
);
|
|
@@ -691,7 +685,7 @@ const makeLaneFixture = Effect.fn("Chaos.makeLaneFixture")(function* (
|
|
|
691
685
|
return plainLaneFixture(
|
|
692
686
|
true,
|
|
693
687
|
runtime
|
|
694
|
-
.
|
|
688
|
+
.processThread(agent, threadId)
|
|
695
689
|
.pipe(Effect.provide(bookToolLayerFor(approvalTools))),
|
|
696
690
|
(flatIndex) =>
|
|
697
691
|
runtime.submit(agent, { question: `chaos ${flatIndex}` }, submitOptionsFor(flatIndex)),
|
|
@@ -718,7 +712,7 @@ const makeLaneFixture = Effect.fn("Chaos.makeLaneFixture")(function* (
|
|
|
718
712
|
});
|
|
719
713
|
return plainLaneFixture(
|
|
720
714
|
true,
|
|
721
|
-
runtime.
|
|
715
|
+
runtime.processThread(agent, threadId).pipe(Effect.provide(toolLayer)),
|
|
722
716
|
(flatIndex) =>
|
|
723
717
|
runtime.submit(agent, { question: `chaos ${flatIndex}` }, submitOptionsFor(flatIndex)),
|
|
724
718
|
);
|
|
@@ -741,15 +735,12 @@ const makeLaneFixture = Effect.fn("Chaos.makeLaneFixture")(function* (
|
|
|
741
735
|
childBinding,
|
|
742
736
|
childLaneDigests(laneIndex),
|
|
743
737
|
);
|
|
744
|
-
const
|
|
745
|
-
const driveResolved = (
|
|
746
|
-
runtime
|
|
747
|
-
.processConversationResolved(conversation)
|
|
748
|
-
.pipe(Effect.provideService(AgentBindingResolver, resolver));
|
|
738
|
+
const bindings = [parentResolved, childResolved];
|
|
739
|
+
const driveResolved = (thread: ThreadId) => runtime.processThreadResolved(thread, bindings);
|
|
749
740
|
const fixture: LaneFixture = {
|
|
750
741
|
index: laneIndex,
|
|
751
742
|
kind,
|
|
752
|
-
|
|
743
|
+
threadId,
|
|
753
744
|
ref,
|
|
754
745
|
deskInPlay: false,
|
|
755
746
|
submissionIndexes,
|
|
@@ -762,21 +753,18 @@ const makeLaneFixture = Effect.fn("Chaos.makeLaneFixture")(function* (
|
|
|
762
753
|
drives: (firstReceipt) => {
|
|
763
754
|
const drives: Array<
|
|
764
755
|
Effect.Effect<ReadonlyArray<Settlement>, DurableWorkerFailure | DurableBindingFailure>
|
|
765
|
-
> = [driveResolved(
|
|
756
|
+
> = [driveResolved(threadId)];
|
|
766
757
|
if (firstReceipt !== undefined) {
|
|
767
758
|
drives.push(
|
|
768
759
|
driveResolved(
|
|
769
|
-
|
|
770
|
-
firstReceipt.submissionId,
|
|
771
|
-
decodeToolCallId(DELEGATE_CALL_ID),
|
|
772
|
-
),
|
|
760
|
+
childThreadIdFor(firstReceipt.submissionId, decodeToolCallId(DELEGATE_CALL_ID)),
|
|
773
761
|
),
|
|
774
762
|
);
|
|
775
763
|
}
|
|
776
764
|
return drives;
|
|
777
765
|
},
|
|
778
|
-
|
|
779
|
-
|
|
766
|
+
childThreadOf: (firstReceipt) =>
|
|
767
|
+
childThreadIdFor(firstReceipt.submissionId, decodeToolCallId(DELEGATE_CALL_ID)),
|
|
780
768
|
};
|
|
781
769
|
return fixture;
|
|
782
770
|
}
|
|
@@ -902,7 +890,7 @@ const submissionIdsNamedBy = (
|
|
|
902
890
|
payload._tag === "SubmissionSettled" ||
|
|
903
891
|
payload._tag === "AbortRequested"
|
|
904
892
|
) {
|
|
905
|
-
named.add(payload.submissionId);
|
|
893
|
+
if (payload.submissionId !== undefined) named.add(payload.submissionId);
|
|
906
894
|
}
|
|
907
895
|
}
|
|
908
896
|
return named;
|
|
@@ -968,7 +956,7 @@ export const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (
|
|
|
968
956
|
) {
|
|
969
957
|
const runtime = yield* DurableAgentRuntime;
|
|
970
958
|
const ledger = yield* SubmissionLedger;
|
|
971
|
-
const store = yield*
|
|
959
|
+
const store = yield* ThreadStore;
|
|
972
960
|
const config = yield* DurableRuntimeConfig;
|
|
973
961
|
const failpoints = yield* DurableRuntimeFailpointTestControl;
|
|
974
962
|
const random = mulberry32(plan.seed);
|
|
@@ -1036,7 +1024,7 @@ export const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (
|
|
|
1036
1024
|
}
|
|
1037
1025
|
|
|
1038
1026
|
// Admission chaos: pending submissions retry under the active arm until a Receipt lands;
|
|
1039
|
-
// the identical (
|
|
1027
|
+
// the identical (thread, principal, key) triple reattaches, never duplicates.
|
|
1040
1028
|
for (const state of states) {
|
|
1041
1029
|
if (state.receipt !== undefined) continue;
|
|
1042
1030
|
const receipt = yield* tolerateTyped(state.lane.submitOne(state.flatIndex));
|
|
@@ -1106,20 +1094,20 @@ export const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (
|
|
|
1106
1094
|
});
|
|
1107
1095
|
}
|
|
1108
1096
|
|
|
1109
|
-
// Final claims: the shared invariant checker per touched
|
|
1097
|
+
// Final claims: the shared invariant checker per touched Thread, in convergence mode,
|
|
1110
1098
|
// with the full digest chain (single known producer), plus the desk non-fabrication sweep.
|
|
1111
1099
|
const produced = yield* desk.produced;
|
|
1112
1100
|
const laneReports: Array<ChaosLaneReport> = [];
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1101
|
+
const verifyThread = Effect.fn("Chaos.verifyThread")(function* (
|
|
1102
|
+
threadId: ThreadId,
|
|
1115
1103
|
kind: ChaosScenarioKind,
|
|
1116
1104
|
deskInPlay: boolean,
|
|
1117
1105
|
) {
|
|
1118
|
-
const exported = yield* store.export(
|
|
1106
|
+
const exported = yield* store.export(ThreadExportRequest.make({ threadId })).pipe(
|
|
1119
1107
|
Effect.mapError((error) =>
|
|
1120
1108
|
ChaosConvergenceFailure.make({
|
|
1121
1109
|
seed: plan.seed,
|
|
1122
|
-
message: `export of ${
|
|
1110
|
+
message: `export of ${threadId} failed: ${String(error)}`,
|
|
1123
1111
|
}),
|
|
1124
1112
|
),
|
|
1125
1113
|
);
|
|
@@ -1138,7 +1126,7 @@ export const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (
|
|
|
1138
1126
|
const batchProducers = new Map<BatchId, ProducerId>(
|
|
1139
1127
|
exported.records.map((envelope) => [envelope.batchId, config.producerId]),
|
|
1140
1128
|
);
|
|
1141
|
-
const report = yield*
|
|
1129
|
+
const report = yield* verifyThreadInvariants({
|
|
1142
1130
|
export: exported,
|
|
1143
1131
|
submissions: rows,
|
|
1144
1132
|
batchProducers,
|
|
@@ -1151,7 +1139,7 @@ export const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (
|
|
|
1151
1139
|
.join("; ");
|
|
1152
1140
|
return yield* ChaosConvergenceFailure.make({
|
|
1153
1141
|
seed: plan.seed,
|
|
1154
|
-
message: `invariants failed for ${
|
|
1142
|
+
message: `invariants failed for ${threadId} (${kind}): ${failed}`,
|
|
1155
1143
|
});
|
|
1156
1144
|
}
|
|
1157
1145
|
if (deskInPlay) {
|
|
@@ -1159,7 +1147,7 @@ export const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (
|
|
|
1159
1147
|
}
|
|
1160
1148
|
laneReports.push(
|
|
1161
1149
|
ChaosLaneReport.make({
|
|
1162
|
-
|
|
1150
|
+
threadId,
|
|
1163
1151
|
kind,
|
|
1164
1152
|
submissionCount: rows.length,
|
|
1165
1153
|
verified: report.ok,
|
|
@@ -1168,18 +1156,18 @@ export const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (
|
|
|
1168
1156
|
});
|
|
1169
1157
|
|
|
1170
1158
|
for (const lane of lanes) {
|
|
1171
|
-
yield*
|
|
1172
|
-
// Delegation lanes: verify every materialized child
|
|
1159
|
+
yield* verifyThread(lane.threadId, lane.kind, lane.deskInPlay);
|
|
1160
|
+
// Delegation lanes: verify every materialized child Thread too.
|
|
1173
1161
|
for (const flatIndex of lane.submissionIndexes) {
|
|
1174
1162
|
const receipt = states[flatIndex]?.receipt;
|
|
1175
1163
|
if (receipt === undefined) continue;
|
|
1176
|
-
const child = lane.
|
|
1164
|
+
const child = lane.childThreadOf(receipt);
|
|
1177
1165
|
if (child === undefined) continue;
|
|
1178
1166
|
const childExport = yield* Effect.exit(
|
|
1179
|
-
store.export(
|
|
1167
|
+
store.export(ThreadExportRequest.make({ threadId: child })),
|
|
1180
1168
|
);
|
|
1181
1169
|
if (Exit.isSuccess(childExport) && childExport.value.records.length > 0) {
|
|
1182
|
-
yield*
|
|
1170
|
+
yield* verifyThread(child, "plain", false);
|
|
1183
1171
|
}
|
|
1184
1172
|
}
|
|
1185
1173
|
}
|
|
@@ -73,11 +73,11 @@ export const docContentToolkitLayer = DocContentToolkit.toLayer({
|
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
74
|
// Deterministic corpus. Every body deliberately embeds BOTH a secret marker
|
|
75
75
|
// and a distinctive body phrase: the tests assert that neither ever reaches
|
|
76
|
-
// the parent
|
|
76
|
+
// the parent Thread, the parent prompts, or a redacted preview — only
|
|
77
77
|
// the bounded summary crosses the delegation boundary (SUB-015, SEC-008).
|
|
78
78
|
// ---------------------------------------------------------------------------
|
|
79
79
|
|
|
80
|
-
/** Never allowed outside a child
|
|
80
|
+
/** Never allowed outside a child Thread or an unredacted fixture value. */
|
|
81
81
|
export const docsDocumentBodySecret = "docs-vault-secret-771";
|
|
82
82
|
|
|
83
83
|
const decodeDocumentId = Schema.decodeSync(ResearchDocumentId);
|
|
@@ -109,7 +109,7 @@ const corpusEntries = new Map<string, CorpusEntry>(
|
|
|
109
109
|
document: ResearchDocument.make({
|
|
110
110
|
documentId: decodeDocumentId(entry.documentId),
|
|
111
111
|
title: entry.title,
|
|
112
|
-
body: `${entry.bodyPhrase}: internal working notes. ${docsDocumentBodySecret}. ${entry.summary} Raw notes stay inside the child
|
|
112
|
+
body: `${entry.bodyPhrase}: internal working notes. ${docsDocumentBodySecret}. ${entry.summary} Raw notes stay inside the child Thread.`,
|
|
113
113
|
}),
|
|
114
114
|
bodyPhrase: entry.bodyPhrase,
|
|
115
115
|
summary: entry.summary,
|
|
@@ -180,7 +180,7 @@ export const documentSummaryFor = (documentId: string): DocumentSummary =>
|
|
|
180
180
|
export const encodedDocumentSummary = (documentId: string): string =>
|
|
181
181
|
JSON.stringify(Schema.encodeSync(DocumentSummary)(documentSummaryFor(documentId)));
|
|
182
182
|
|
|
183
|
-
export const DocSummarizer = Agent.
|
|
183
|
+
export const DocSummarizer = Agent.make("doc-summarizer", {
|
|
184
184
|
input: SummaryBrief,
|
|
185
185
|
output: DocumentSummary,
|
|
186
186
|
instructions:
|
|
@@ -200,7 +200,7 @@ export const DocSummarizer = Agent.define("doc-summarizer", {
|
|
|
200
200
|
// Delegation Definition: the coordinator sees exactly one Tool with explicit
|
|
201
201
|
// projections and finite bounds. `projectResult` is the declassification
|
|
202
202
|
// boundary (SUB-015): only the bounded summary crosses; the fetched body —
|
|
203
|
-
// secret marker included — stays in the child
|
|
203
|
+
// secret marker included — stays in the child Thread.
|
|
204
204
|
// ---------------------------------------------------------------------------
|
|
205
205
|
|
|
206
206
|
export class SummaryRequest extends Schema.Class<SummaryRequest>("SummaryRequest")({
|
|
@@ -300,7 +300,7 @@ export const docsMissionConfidentialMarker = "docs-mission-dossier-42f";
|
|
|
300
300
|
|
|
301
301
|
export const DocsResearcherToolkit = Toolkit.make(delegateDocumentSummary.tool);
|
|
302
302
|
|
|
303
|
-
export const DocsResearcher = Agent.
|
|
303
|
+
export const DocsResearcher = Agent.make("docs-researcher", {
|
|
304
304
|
input: ResearchRequest,
|
|
305
305
|
output: ResearchDigest,
|
|
306
306
|
instructions: [
|
|
@@ -322,7 +322,7 @@ export const DocsResearcher = Agent.define("docs-researcher", {
|
|
|
322
322
|
|
|
323
323
|
/** The default two-document research mission. */
|
|
324
324
|
export const researchMissionRequest = ResearchRequest.make({
|
|
325
|
-
question: `Summarize the durability and subagent notes; keep ${docsMissionConfidentialMarker} inside the coordinator
|
|
325
|
+
question: `Summarize the durability and subagent notes; keep ${docsMissionConfidentialMarker} inside the coordinator thread.`,
|
|
326
326
|
documentIds: researchCorpusDocumentIds,
|
|
327
327
|
});
|
|
328
328
|
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type RedactedPreview,
|
|
7
7
|
type RedactionError,
|
|
8
8
|
} from "@effect-agent/capabilities";
|
|
9
|
-
import { Agent, type
|
|
9
|
+
import { Agent, type ThreadId } from "@effect-agent/core";
|
|
10
10
|
import {
|
|
11
11
|
DefinitionDigests,
|
|
12
12
|
DeploymentId,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
type DurableSubmitOptions,
|
|
18
18
|
type IdempotencyKey,
|
|
19
19
|
type ResolvedBinding,
|
|
20
|
-
} from "@effect-agent/
|
|
20
|
+
} from "@effect-agent/thread";
|
|
21
21
|
import type { Crypto } from "effect";
|
|
22
22
|
import { Effect, Layer, Ref, Schema, Stream } from "effect";
|
|
23
23
|
import { LanguageModel, Model, type Response } from "effect/unstable/ai";
|
|
@@ -76,10 +76,10 @@ export const docsSummarizerDigests = DefinitionDigests.make({
|
|
|
76
76
|
|
|
77
77
|
/** Durable admission options for one docs-researcher Submission on one mission lane. */
|
|
78
78
|
export const docsResearcherSubmitOptions = (
|
|
79
|
-
|
|
79
|
+
threadId: ThreadId,
|
|
80
80
|
idempotencyKey: IdempotencyKey,
|
|
81
81
|
): DurableSubmitOptions => ({
|
|
82
|
-
|
|
82
|
+
threadId,
|
|
83
83
|
principal: docsResearcherPrincipal,
|
|
84
84
|
idempotencyKey,
|
|
85
85
|
definitions: docsCoordinatorDigests,
|
|
@@ -298,7 +298,7 @@ const encodeResearchDocument = Schema.encodeEffect(ResearchDocument);
|
|
|
298
298
|
/**
|
|
299
299
|
* The audit-surface preview of one fetched document: the raw document —
|
|
300
300
|
* secret marker and all — passes through the configured structural `Redactor`
|
|
301
|
-
* before anything may quote it outside the child
|
|
301
|
+
* before anything may quote it outside the child Thread (SEC-008,
|
|
302
302
|
* CAP-013). Tests assert the preview keeps shape but no scalar content.
|
|
303
303
|
*/
|
|
304
304
|
export const redactedDocumentPreview = Effect.fn("DocsResearcher.redactedDocumentPreview")(
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// 1. MCP discovery and Agent authoring do not meet in the type system. The
|
|
9
9
|
// connector's validated Toolkit arrives as `Toolkit.Any`, while
|
|
10
|
-
// `Agent.
|
|
10
|
+
// `Agent.make` needs the statically typed toolkit — so the binding
|
|
11
11
|
// between "what discovery served" and "what the child was authored
|
|
12
12
|
// against" had to be re-proved by hand (`assertDiscoveryMatchesAuthoredToolkit`
|
|
13
13
|
// re-deriving `Tool.getJsonSchema` on both sides). A framework helper that
|
|
@@ -150,7 +150,7 @@ export const TravelPlannerToolkitLayer = TravelPlannerToolkit.toLayer({
|
|
|
150
150
|
search_activities: (query) => Effect.flatMap(ActivityCatalog, (catalog) => catalog.search(query)),
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
export const TravelPlanner = Agent.
|
|
153
|
+
export const TravelPlanner = Agent.make("travel-planner", {
|
|
154
154
|
input: TripRequest,
|
|
155
155
|
output: TravelPlan,
|
|
156
156
|
instructions: (input) =>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ThreadId, IdGenerator, RunId, TurnId } from "@effect-agent/core";
|
|
2
|
+
import { ThreadHistory } from "@effect-agent/engine";
|
|
2
3
|
import { Context, Deferred, Effect, Layer, Option, Ref, Schema } from "effect";
|
|
3
4
|
|
|
4
5
|
import {
|
|
@@ -405,12 +406,12 @@ export const TravelGuidanceLayer = Layer.succeed(
|
|
|
405
406
|
export const DeterministicIdGeneratorLayer = Layer.effect(
|
|
406
407
|
IdGenerator,
|
|
407
408
|
Effect.gen(function* () {
|
|
408
|
-
const
|
|
409
|
+
const thread = yield* Ref.make(0);
|
|
409
410
|
const run = yield* Ref.make(0);
|
|
410
411
|
const turn = yield* Ref.make(0);
|
|
411
412
|
return IdGenerator.of({
|
|
412
|
-
|
|
413
|
-
Effect.map((n) => Schema.decodeSync(
|
|
413
|
+
nextThreadId: Ref.updateAndGet(thread, (n) => n + 1).pipe(
|
|
414
|
+
Effect.map((n) => Schema.decodeSync(ThreadId)(`thread-${n}`)),
|
|
414
415
|
),
|
|
415
416
|
nextRunId: Ref.updateAndGet(run, (n) => n + 1).pipe(
|
|
416
417
|
Effect.map((n) => Schema.decodeSync(RunId)(`run-${n}`)),
|
|
@@ -422,6 +423,7 @@ export const DeterministicIdGeneratorLayer = Layer.effect(
|
|
|
422
423
|
}),
|
|
423
424
|
);
|
|
424
425
|
export const TravelPlannerRuntimeLayer = Layer.mergeAll(
|
|
426
|
+
ThreadHistory.layerTransient,
|
|
425
427
|
TravelPlannerToolkitLayer,
|
|
426
428
|
FlightCatalogLayer,
|
|
427
429
|
LodgingCatalogLayer,
|
|
@@ -75,7 +75,7 @@ export const TravelPlannerPhase2ToolkitLayer = TravelPlannerPhase2Toolkit.toLaye
|
|
|
75
75
|
Effect.flatMap(ItineraryHoldGateway, (gateway) => gateway.hold(request)),
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
export const TravelPlannerPhase2 = Agent.
|
|
78
|
+
export const TravelPlannerPhase2 = Agent.make("travel-planner-phase-2", {
|
|
79
79
|
input: TripRequest,
|
|
80
80
|
output: TravelPlan,
|
|
81
81
|
instructions: (input) =>
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { AgentId,
|
|
1
|
+
import { AgentId, ThreadId, RunId, SubmissionId } from "@effect-agent/core";
|
|
2
2
|
import {
|
|
3
3
|
BatchId,
|
|
4
4
|
CanonicalBatch,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
ThreadCheckpoint,
|
|
6
|
+
ThreadProjection,
|
|
7
7
|
DefinitionDigests,
|
|
8
8
|
DeploymentId,
|
|
9
9
|
Digest,
|
|
10
10
|
ProducerId,
|
|
11
11
|
RecordEnvelope,
|
|
12
12
|
RecordId,
|
|
13
|
-
} from "@effect-agent/
|
|
13
|
+
} from "@effect-agent/thread";
|
|
14
14
|
import { Effect, Schema } from "effect";
|
|
15
15
|
|
|
16
16
|
import { TravelPlan, TripRequest } from "./definition.ts";
|
|
17
17
|
import { expectedTravelPlan, phase1Trip } from "./scenarios.ts";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* The Phase 3 profile persists
|
|
20
|
+
* The Phase 3 profile persists Thread history but deliberately does not
|
|
21
21
|
* claim durable admission or recovery of accepted work.
|
|
22
22
|
*/
|
|
23
23
|
export class TravelPlannerPersistenceProfile extends Schema.Class<TravelPlannerPersistenceProfile>(
|
|
@@ -34,9 +34,7 @@ export const phase3TravelPlannerProfile = TravelPlannerPersistenceProfile.make({
|
|
|
34
34
|
canonicalSchemaVersion: 1,
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
export const
|
|
38
|
-
"travel-planner-p3-conversation",
|
|
39
|
-
);
|
|
37
|
+
export const phase3TravelPlannerThreadId = Schema.decodeSync(ThreadId)("travel-planner-p3-thread");
|
|
40
38
|
export const phase3TravelPlannerProducerId = Schema.decodeSync(ProducerId)(
|
|
41
39
|
"travel-planner-p3-producer",
|
|
42
40
|
);
|
|
@@ -61,7 +59,7 @@ const travelPlanOutput = Schema.encodeSync(TravelPlan)(expectedTravelPlan);
|
|
|
61
59
|
const record = (recordId: string, createdAt: string, payload: unknown) =>
|
|
62
60
|
Schema.decodeUnknownSync(RecordEnvelope)({
|
|
63
61
|
recordId: Schema.decodeSync(RecordId)(recordId),
|
|
64
|
-
family: "
|
|
62
|
+
family: "thread",
|
|
65
63
|
schemaVersion: 1,
|
|
66
64
|
createdAt,
|
|
67
65
|
deploymentId,
|
|
@@ -69,7 +67,7 @@ const record = (recordId: string, createdAt: string, payload: unknown) =>
|
|
|
69
67
|
});
|
|
70
68
|
|
|
71
69
|
/**
|
|
72
|
-
* The first atomic append establishes the
|
|
70
|
+
* The first atomic append establishes the Thread and records its input.
|
|
73
71
|
* Its encoded value is the redacted current-version persistence fixture.
|
|
74
72
|
*/
|
|
75
73
|
export const phase3TravelPlannerInitialBatch = CanonicalBatch.make({
|
|
@@ -77,7 +75,7 @@ export const phase3TravelPlannerInitialBatch = CanonicalBatch.make({
|
|
|
77
75
|
producerId: phase3TravelPlannerProducerId,
|
|
78
76
|
records: [
|
|
79
77
|
record("travel-planner-p3-created", "2026-09-01T00:00:00.000Z", {
|
|
80
|
-
_tag: "
|
|
78
|
+
_tag: "ThreadCreated",
|
|
81
79
|
agentId,
|
|
82
80
|
definitions: phase3TravelPlannerDefinitionDigests,
|
|
83
81
|
}),
|
|
@@ -125,7 +123,7 @@ export class TravelPlannerProjectionError extends Schema.TaggedError<TravelPlann
|
|
|
125
123
|
|
|
126
124
|
/** Decode the itinerary projection rebuilt from canonical model-completion records. */
|
|
127
125
|
export const travelPlanFromProjection = (
|
|
128
|
-
projection:
|
|
126
|
+
projection: ThreadProjection,
|
|
129
127
|
): Effect.Effect<TravelPlan, TravelPlannerProjectionError> => {
|
|
130
128
|
const output = projection.modelOutputs.at(-1);
|
|
131
129
|
if (output === undefined) {
|
|
@@ -141,18 +139,16 @@ export const travelPlanFromProjection = (
|
|
|
141
139
|
};
|
|
142
140
|
|
|
143
141
|
/** Build a disposable checkpoint bound to a validated canonical prefix. */
|
|
144
|
-
export const makePhase3TravelPlannerCheckpoint = (
|
|
145
|
-
|
|
146
|
-
): ConversationCheckpoint =>
|
|
147
|
-
Schema.decodeSync(ConversationCheckpoint)({
|
|
142
|
+
export const makePhase3TravelPlannerCheckpoint = (projection: ThreadProjection): ThreadCheckpoint =>
|
|
143
|
+
Schema.decodeSync(ThreadCheckpoint)({
|
|
148
144
|
schemaVersion: 1,
|
|
149
|
-
|
|
145
|
+
threadId: projection.threadId,
|
|
150
146
|
throughSequence: projection.throughSequence,
|
|
151
147
|
tailDigest: projection.tailDigest,
|
|
152
148
|
engineVersion: "phase-3-test-runtime",
|
|
153
149
|
agentDefinitionDigest: phase3TravelPlannerDefinitionDigests.agent,
|
|
154
150
|
modelDigest: phase3TravelPlannerDefinitionDigests.model,
|
|
155
151
|
toolDigest: phase3TravelPlannerDefinitionDigests.tools,
|
|
156
|
-
state: Schema.encodeSync(
|
|
152
|
+
state: Schema.encodeSync(ThreadProjection)(projection),
|
|
157
153
|
createdAt: "2026-09-01T00:00:04.000Z",
|
|
158
154
|
});
|