effect-agent 0.1.0-beta.159 → 0.1.0-beta.160

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 (62) hide show
  1. package/dist/{Admin-DWDYlvvZ.d.mts → Admin-Dirux8PR.d.mts} +9 -2
  2. package/dist/{DurableAgentRuntime-C7A5guvq.mjs → DurableAgentRuntime-pr7cCaZw.mjs} +216 -55
  3. package/dist/DurableAgentRuntime-pr7cCaZw.mjs.map +1 -0
  4. package/dist/{EventSource-BfNZBG4A.d.mts → EventSource-DOXB0RJR.d.mts} +2 -2
  5. package/dist/{PreparedInputAdmission-VStpn8u2.d.mts → PreparedInputAdmission-B7r401xJ.d.mts} +3 -3
  6. package/dist/{RunJournal-ixFRCyW3.mjs → RunJournal-CA2bsvHn.mjs} +8 -5
  7. package/dist/RunJournal-CA2bsvHn.mjs.map +1 -0
  8. package/dist/{Subscription-Bx1ZRRVS.d.mts → Subscription-B3ScCBaN.d.mts} +2 -2
  9. package/dist/{SubscriptionInput-B5k5vb1S.d.mts → SubscriptionInput-BP5WVsX_.d.mts} +2 -2
  10. package/dist/{WorkerAdmission-N9hZV85Q.mjs → WorkerAdmission-Cmcxd6nS.mjs} +2 -2
  11. package/dist/{WorkerAdmission-N9hZV85Q.mjs.map → WorkerAdmission-Cmcxd6nS.mjs.map} +1 -1
  12. package/dist/durable/Admin.d.mts +1 -1
  13. package/dist/durable/AgentRegistration.d.mts +1 -1
  14. package/dist/durable/Certification.d.mts +1 -1
  15. package/dist/durable/CommittedActivity.d.mts +1 -1
  16. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  17. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  18. package/dist/durable/EventSource.d.mts +1 -1
  19. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  20. package/dist/durable/LifecyclePublication.d.mts +1 -1
  21. package/dist/durable/MessageDelivery.d.mts +3 -3
  22. package/dist/durable/MessageDeliveryStoreConformance.d.mts +1 -1
  23. package/dist/durable/PersistentHistory.d.mts +1 -1
  24. package/dist/durable/PersistentHistory.mjs +1 -1
  25. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  26. package/dist/durable/Recovery.d.mts +1 -1
  27. package/dist/durable/RunJournal.d.mts +1 -1
  28. package/dist/durable/RunJournal.mjs +1 -1
  29. package/dist/durable/Scheduling.d.mts +1 -1
  30. package/dist/durable/SqlMemoryStore.d.mts +3 -0
  31. package/dist/durable/SqlMemoryStore.mjs +3 -2
  32. package/dist/durable/SqlMemoryStore.mjs.map +1 -1
  33. package/dist/durable/SubmissionLedger.d.mts +1 -1
  34. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  35. package/dist/durable/SubmissionLedgerConformance.mjs +1 -1
  36. package/dist/durable/SubmissionStatus.d.mts +1 -1
  37. package/dist/durable/Subscription.d.mts +1 -1
  38. package/dist/durable/SubscriptionInput.d.mts +1 -1
  39. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  40. package/dist/durable/SubscriptionTools.d.mts +2 -2
  41. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  42. package/dist/durable/Subscriptions.d.mts +5 -5
  43. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  44. package/dist/durable/ThreadInvariants.d.mts +1 -1
  45. package/dist/durable/ThreadInvariants.mjs +1 -1
  46. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  47. package/dist/durable/ThreadStore.d.mts +1 -1
  48. package/dist/durable/ThreadStore.mjs +1 -1
  49. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  50. package/dist/durable/ThreadStoreConformance.mjs +1 -1
  51. package/dist/durable/WorkerAdmission.d.mts +1 -1
  52. package/dist/durable/WorkerAdmission.mjs +1 -1
  53. package/dist/durable/WorkerHost.d.mts +1 -1
  54. package/dist/index.d.mts +5 -5
  55. package/dist/index.mjs +3 -3
  56. package/package.json +1 -1
  57. package/src/durable/DurableAgentRuntime.ts +371 -84
  58. package/src/durable/RunJournal.ts +16 -4
  59. package/src/durable/SqlMemoryStore.ts +7 -3
  60. package/src/durable/internal/journal-checkpoint.ts +57 -3
  61. package/dist/DurableAgentRuntime-C7A5guvq.mjs.map +0 -1
  62. package/dist/RunJournal-ixFRCyW3.mjs.map +0 -1
@@ -15,7 +15,10 @@ import {
15
15
  contextWindowMessage,
16
16
  } from "../engine/Compaction.ts";
17
17
  import { digestJson, type DigestError } from "./Digest.ts";
18
- import { type JournalCheckpointSeed } from "./internal/journal-checkpoint.ts";
18
+ import {
19
+ type JournalCheckpointSeed,
20
+ type ThreadContextCheckpoint,
21
+ } from "./internal/journal-checkpoint.ts";
19
22
  import {
20
23
  makeJournalMetadata,
21
24
  toolExecutionKey,
@@ -610,13 +613,19 @@ export const projectRunJournalStream = Effect.fn("RunJournal.projectRunJournalSt
610
613
  onBoundary?: (boundary: JournalBoundary) => void,
611
614
  seed?: JournalCheckpointSeed,
612
615
  preparedMetadata?: JournalMetadata,
616
+ priorContext?: ThreadContextCheckpoint,
613
617
  ): Effect.fn.Return<RunJournalProjection, RunJournalError | E, R> {
614
618
  if (seed !== undefined && seed.runId !== ownerRunId)
615
619
  return yield* journalError("Recovery checkpoint belongs to another Run");
620
+ if (seed !== undefined && priorContext !== undefined)
621
+ return yield* journalError("Run recovery and prior Thread context cannot seed the same replay");
622
+
623
+ const historical =
624
+ priorContext === undefined ? [] : (yield* decodePromptMessages(priorContext.prompt)).content;
616
625
 
617
626
  let state: FoldState = {
618
- all: [],
619
- before: [],
627
+ all: [...historical],
628
+ before: [...historical],
620
629
  pendingTools: [],
621
630
  pendingToolsForRun: false,
622
631
  committedTurns: seed?.committedTurns ?? 0,
@@ -679,6 +688,9 @@ export const projectRunJournalStream = Effect.fn("RunJournal.projectRunJournalSt
679
688
  isInRunView(sequence, payload, ownerRunId),
680
689
  );
681
690
 
691
+ if (priorContext !== undefined && compactions.length > 0)
692
+ return yield* journalError("New compaction requires the complete canonical context mapping");
693
+
682
694
  const recordsForRun = records.pipe(
683
695
  Stream.filter(({ sequence, record: { payload } }) =>
684
696
  isInRunView(sequence, payload, ownerRunId),
@@ -827,7 +839,7 @@ export const projectRunJournalStream = Effect.fn("RunJournal.projectRunJournalSt
827
839
  ];
828
840
  };
829
841
 
830
- let latestWindowId: string | undefined = seed?.contextWindowId;
842
+ let latestWindowId: string | undefined = seed?.contextWindowId ?? priorContext?.contextWindowId;
831
843
  let latestWindowSequence = seed?.throughSequence ?? -1;
832
844
  let rolloverCoveredThrough = 0;
833
845
 
@@ -63,6 +63,9 @@ export class SqlMemoryBatchWriter extends Context.Service<
63
63
  * releasing readers. Hosts with independent database writers must leave this unset.
64
64
  */
65
65
  export interface SqlStorageOwner {
66
+ /** Cache identity for transaction decorators; omission uses the owner itself.
67
+ * Shared identities must name the same database, reader gate and invalidator set. */
68
+ readonly identity?: object;
66
69
  readonly read: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
67
70
  readonly transaction: <A, E, R>(
68
71
  effect: Effect.Effect<A, E, R>,
@@ -172,17 +175,18 @@ class MemoryViews {
172
175
  }
173
176
  }
174
177
 
175
- const ownedViews = new WeakMap<SqlStorageOwner, MemoryViews>();
178
+ const ownedViews = new WeakMap<object, MemoryViews>();
176
179
 
177
180
  const memoryViews = Effect.fn("SqliteMemoryStore.memoryViews")(function* () {
178
181
  const owner = yield* SqlStorageOwner;
179
182
 
180
183
  if (owner === undefined) return undefined;
181
- let views = ownedViews.get(owner);
184
+ const identity = owner.identity ?? owner;
185
+ let views = ownedViews.get(identity);
182
186
 
183
187
  if (views === undefined) {
184
188
  views = new MemoryViews();
185
- ownedViews.set(owner, views);
189
+ ownedViews.set(identity, views);
186
190
  owner.invalidators.add(views.clear);
187
191
  }
188
192
 
@@ -37,15 +37,25 @@ export class JournalCheckpointSeed extends Schema.Class<JournalCheckpointSeed>(
37
37
  compaction: CanonicalRecordEnvelope,
38
38
  }) {}
39
39
 
40
+ /** Canonical historical context for a later Run, without the previous Run's authority or usage. */
41
+ export class ThreadContextCheckpoint extends Schema.Class<ThreadContextCheckpoint>(
42
+ "@effect-agent/thread/internal/ThreadContextCheckpoint",
43
+ )({
44
+ throughSequence: CanonicalSequence,
45
+ prompt: PersistedJson,
46
+ contextWindowId: Schema.optionalKey(Schema.String),
47
+ }) {}
48
+
40
49
  /** A bounded sparse projection, never a canonical log or a submission-ownership record. */
41
50
  export class RecoveryCheckpointState extends Schema.Class<RecoveryCheckpointState>(
42
51
  "@effect-agent/thread/internal/RecoveryCheckpointState",
43
52
  )({
44
- schemaVersion: Schema.Literal(1),
53
+ schemaVersion: Schema.Literal(2),
45
54
  policyAccountingVersion: Schema.Literal(1),
46
55
  submissionId: SubmissionId,
47
56
  submissionIds: Schema.Array(SubmissionId).check(Schema.isMaxLength(4_096)),
48
- seed: JournalCheckpointSeed,
57
+ seed: Schema.optionalKey(JournalCheckpointSeed),
58
+ context: Schema.optionalKey(ThreadContextCheckpoint),
49
59
  records: Schema.Array(CanonicalRecordEnvelope).check(Schema.isMaxLength(4_096)),
50
60
  }) {}
51
61
 
@@ -56,7 +66,51 @@ export class RecoveryCheckpointContents extends Schema.Class<RecoveryCheckpointC
56
66
  digest: Digest,
57
67
  }) {}
58
68
 
59
- export const RECOVERY_ENGINE_VERSION = "effect-agent/recovery@2";
69
+ export const RECOVERY_ENGINE_VERSION = "effect-agent/recovery@3";
70
+
71
+ /**
72
+ * Prove that indexed original-input absence also means absent prefix Run/control evidence.
73
+ * Only cache consumers with colon-free Submission IDs use this certificate. For those IDs,
74
+ * every possible control marker is exactly :run:<submission-id>:. Parsing here can only decline
75
+ * caching; it never supplies an execution identity. Separator-bearing identities use full replay.
76
+ * The temporary anchor set is discarded after certification, not persisted with the context.
77
+ */
78
+ export const makeThreadContextCertificate = () => {
79
+ const anchored = new Set<string>();
80
+ let valid = true;
81
+
82
+ return {
83
+ add: ({ record }: CanonicalRecordEnvelope): void => {
84
+ if (!valid) return;
85
+ const payload = record.payload;
86
+
87
+ if ("runId" in payload && payload.runId !== undefined) {
88
+ const originalInput = payload._tag === "UserInputRecorded" && payload.kind === "user";
89
+
90
+ if (originalInput ? anchored.has(payload.runId) : !anchored.has(payload.runId)) {
91
+ valid = false;
92
+
93
+ return;
94
+ }
95
+ if (originalInput) anchored.add(payload.runId);
96
+ }
97
+ for (
98
+ let start = record.recordId.indexOf(":run:");
99
+ start !== -1;
100
+ start = record.recordId.indexOf(":run:", start + 1)
101
+ ) {
102
+ const end = record.recordId.indexOf(":", start + 5);
103
+
104
+ if (end !== -1 && !anchored.has(record.recordId.slice(start + 1, end))) {
105
+ valid = false;
106
+
107
+ return;
108
+ }
109
+ }
110
+ },
111
+ isValid: (): boolean => valid,
112
+ };
113
+ };
60
114
 
61
115
  /**
62
116
  * Late evidence can invalidate an old compaction. Such histories use full canonical replay;