@effect-agent/testing 0.1.0-beta.37 → 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.
Files changed (44) hide show
  1. package/dist/certification.d.mts +102 -0
  2. package/dist/certification.mjs +561 -0
  3. package/dist/certification.mjs.map +1 -0
  4. package/dist/chaos.d.mts +124 -0
  5. package/dist/chaos.mjs +738 -0
  6. package/dist/chaos.mjs.map +1 -0
  7. package/dist/code-executor.d.mts +48 -0
  8. package/dist/code-executor.mjs +565 -0
  9. package/dist/code-executor.mjs.map +1 -0
  10. package/dist/deterministic-layers-D6gCdoPM.mjs +354 -0
  11. package/dist/deterministic-layers-D6gCdoPM.mjs.map +1 -0
  12. package/dist/docs-researcher.d.mts +254 -0
  13. package/dist/docs-researcher.mjs +479 -0
  14. package/dist/docs-researcher.mjs.map +1 -0
  15. package/dist/index.d.mts +2 -2947
  16. package/dist/index.mjs +2 -4913
  17. package/dist/scripted-model-CEwVM9JY.mjs +169 -0
  18. package/dist/scripted-model-CEwVM9JY.mjs.map +1 -0
  19. package/dist/scripted-model-dOa_e0-n.d.mts +677 -0
  20. package/dist/travel-planner.d.mts +1767 -0
  21. package/dist/travel-planner.mjs +2102 -0
  22. package/dist/travel-planner.mjs.map +1 -0
  23. package/package.json +33 -10
  24. package/src/certification.ts +71 -61
  25. package/src/chaos.ts +52 -64
  26. package/src/code-executor.ts +3 -0
  27. package/src/docs-researcher.ts +2 -0
  28. package/src/fixtures/docs-researcher/definition.ts +7 -7
  29. package/src/fixtures/docs-researcher/harness.ts +5 -5
  30. package/src/fixtures/docs-researcher/index.ts +1 -1
  31. package/src/fixtures/travel-planner/definition.ts +1 -1
  32. package/src/fixtures/travel-planner/deterministic-layers.ts +6 -4
  33. package/src/fixtures/travel-planner/phase2.ts +1 -1
  34. package/src/fixtures/travel-planner/phase3.ts +14 -18
  35. package/src/fixtures/travel-planner/phase4.ts +7 -7
  36. package/src/fixtures/travel-planner/phase5.ts +5 -5
  37. package/src/fixtures/travel-planner/phase6.ts +30 -29
  38. package/src/fixtures/travel-planner/subagents-durable.ts +6 -6
  39. package/src/fixtures/travel-planner/subagents.ts +4 -4
  40. package/src/index.ts +1 -10
  41. package/src/travel-planner.ts +2 -0
  42. package/dist/index.mjs.map +0 -1
  43. package/src/code-executor-conformance.d.ts +0 -30
  44. 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
- ConversationId,
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
- ConversationExportRequest,
23
- ConversationStore,
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
- childConversationIdFor,
42
- verifyConversationInvariants,
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/session";
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. `verifyConversationInvariants` in convergence mode over every touched Conversation (the
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
- conversationId: ConversationId,
154
+ threadId: ThreadId,
156
155
  kind: ChaosScenarioKind,
157
156
  submissionCount: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
158
- /** Verdict of `verifyConversationInvariants` in convergence mode. */
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.define("chaos-plain", {
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.define("chaos-book", {
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.define("chaos-approval", {
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.define("chaos-itinerary", {
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.define("chaos-child", {
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.define("chaos-coordinator", {
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 decodeConversationId = Schema.decodeSync(ConversationId);
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
- nextConversationId: next(decodeConversationId, "chaos-fixture-conversation"),
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 conversationId: ConversationId;
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 childConversationOf: (firstReceipt: Receipt) => ConversationId | undefined;
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 conversationId = decodeConversationId(`chaos-${plan.seed}-lane-${laneIndex}`);
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
- conversationId,
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
- conversationId,
657
+ threadId,
659
658
  ref,
660
659
  deskInPlay,
661
660
  submissionIndexes,
662
661
  submitOne,
663
662
  drives: () => [drive],
664
- childConversationOf: () => undefined,
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
- false,
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
- .processConversation(agent, conversationId)
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.processConversation(agent, conversationId).pipe(Effect.provide(toolLayer)),
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 resolver = AgentBindingResolver.fromBindings([parentResolved, childResolved]);
745
- const driveResolved = (conversation: ConversationId) =>
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
- conversationId,
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(conversationId)];
756
+ > = [driveResolved(threadId)];
766
757
  if (firstReceipt !== undefined) {
767
758
  drives.push(
768
759
  driveResolved(
769
- childConversationIdFor(
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
- childConversationOf: (firstReceipt) =>
779
- childConversationIdFor(firstReceipt.submissionId, decodeToolCallId(DELEGATE_CALL_ID)),
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* ConversationStore;
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 (conversation, principal, key) triple reattaches, never duplicates.
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 Conversation, in convergence mode,
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 verifyConversation = Effect.fn("Chaos.verifyConversation")(function* (
1114
- conversationId: ConversationId,
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(ConversationExportRequest.make({ conversationId })).pipe(
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 ${conversationId} failed: ${String(error)}`,
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* verifyConversationInvariants({
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 ${conversationId} (${kind}): ${failed}`,
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
- conversationId,
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* verifyConversation(lane.conversationId, lane.kind, lane.deskInPlay);
1172
- // Delegation lanes: verify every materialized child Conversation too.
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.childConversationOf(receipt);
1164
+ const child = lane.childThreadOf(receipt);
1177
1165
  if (child === undefined) continue;
1178
1166
  const childExport = yield* Effect.exit(
1179
- store.export(ConversationExportRequest.make({ conversationId: child })),
1167
+ store.export(ThreadExportRequest.make({ threadId: child })),
1180
1168
  );
1181
1169
  if (Exit.isSuccess(childExport) && childExport.value.records.length > 0) {
1182
- yield* verifyConversation(child, "plain", false);
1170
+ yield* verifyThread(child, "plain", false);
1183
1171
  }
1184
1172
  }
1185
1173
  }
@@ -0,0 +1,3 @@
1
+ /** CodeExecutor adapter conformance and the deterministic in-process substitute. */
2
+ export * from "./code-executor-conformance.ts";
3
+ export * from "./code-executor-substitute.ts";
@@ -0,0 +1,2 @@
1
+ /** Shared Docs Researcher definitions and MCP delegation fixtures. */
2
+ export * from "./fixtures/docs-researcher/index.ts";
@@ -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 Conversation, the parent prompts, or a redacted preview — only
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 Conversation or an unredacted fixture value. */
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 Conversation.`,
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.define("doc-summarizer", {
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 Conversation.
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.define("docs-researcher", {
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 conversation.`,
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 ConversationId } from "@effect-agent/core";
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/session";
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
- conversationId: ConversationId,
79
+ threadId: ThreadId,
80
80
  idempotencyKey: IdempotencyKey,
81
81
  ): DurableSubmitOptions => ({
82
- conversationId,
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 Conversation (SEC-008,
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.define` needs the statically typed toolkit — so the binding
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.define("travel-planner", {
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 { ConversationId, IdGenerator, RunId, TurnId } from "@effect-agent/core";
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 conversation = yield* Ref.make(0);
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
- nextConversationId: Ref.updateAndGet(conversation, (n) => n + 1).pipe(
413
- Effect.map((n) => Schema.decodeSync(ConversationId)(`conversation-${n}`)),
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.define("travel-planner-phase-2", {
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, ConversationId, RunId, SubmissionId } from "@effect-agent/core";
1
+ import { AgentId, ThreadId, RunId, SubmissionId } from "@effect-agent/core";
2
2
  import {
3
3
  BatchId,
4
4
  CanonicalBatch,
5
- ConversationCheckpoint,
6
- ConversationProjection,
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/session";
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 Conversation history but deliberately does not
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 phase3TravelPlannerConversationId = Schema.decodeSync(ConversationId)(
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: "conversation",
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 Conversation and records its input.
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: "ConversationCreated",
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: ConversationProjection,
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
- projection: ConversationProjection,
146
- ): ConversationCheckpoint =>
147
- Schema.decodeSync(ConversationCheckpoint)({
142
+ export const makePhase3TravelPlannerCheckpoint = (projection: ThreadProjection): ThreadCheckpoint =>
143
+ Schema.decodeSync(ThreadCheckpoint)({
148
144
  schemaVersion: 1,
149
- conversationId: projection.conversationId,
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(ConversationProjection)(projection),
152
+ state: Schema.encodeSync(ThreadProjection)(projection),
157
153
  createdAt: "2026-09-01T00:00:04.000Z",
158
154
  });