effect-agent 0.1.0-beta.97 → 0.1.0-beta.99
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/{AgentRuntime-B64362Id.d.mts → AgentRuntime-BMzrJos6.d.mts} +2 -2
- package/dist/{AgentRuntime-Cdvqw7l_.mjs → AgentRuntime-BWOnNc0d.mjs} +78 -42
- package/dist/AgentRuntime-BWOnNc0d.mjs.map +1 -0
- package/dist/{DurableAgentRuntime-DbUvZHDZ.mjs → DurableAgentRuntime-BE_OjQjz.mjs} +471 -252
- package/dist/DurableAgentRuntime-BE_OjQjz.mjs.map +1 -0
- package/dist/{DurableFailpoint-DQJet828.d.mts → DurableFailpoint-mx3-nsxU.d.mts} +3 -3
- package/dist/{EventSource-D6PAAEyK.d.mts → EventSource-BZ73J0s5.d.mts} +3 -3
- package/dist/{PreparedInputAdmission-omMbwd--.d.mts → PreparedInputAdmission-BkeI-Xvr.d.mts} +4 -4
- package/dist/{Records-CV49DgQK.d.mts → Records-BIiRLqlz.d.mts} +92 -128
- package/dist/{Records-BAutSSnJ.mjs → Records-DR1xtbXY.mjs} +35 -8
- package/dist/Records-DR1xtbXY.mjs.map +1 -0
- package/dist/{RunJournal-DRRRZmG7.mjs → RunJournal-DNs3PM4K.mjs} +80 -23
- package/dist/RunJournal-DNs3PM4K.mjs.map +1 -0
- package/dist/{RunOptions-Clj8cLsp.d.mts → RunOptions-Bruk54SE.d.mts} +13 -4
- package/dist/{Schedule-DePbGfgd.d.mts → Schedule-BNFlluAl.d.mts} +2 -2
- package/dist/{Scheduling-C3QpYn0W.mjs → Scheduling-HFH0pST1.mjs} +2 -2
- package/dist/{Scheduling-C3QpYn0W.mjs.map → Scheduling-HFH0pST1.mjs.map} +1 -1
- package/dist/{Subagent-Cbfzylz1.mjs → Subagent-C2s8PFrd.mjs} +2 -2
- package/dist/{Subagent-Cbfzylz1.mjs.map → Subagent-C2s8PFrd.mjs.map} +1 -1
- package/dist/{Subagent-Bm1dRecN.d.mts → Subagent-m7ywTCe4.d.mts} +3 -3
- package/dist/{Subscription-BR_8Cxh3.d.mts → Subscription-BCQ3L9Zp.d.mts} +3 -3
- package/dist/{SubscriptionInput-B95sQbuk.d.mts → SubscriptionInput-Cqu4_RW3.d.mts} +3 -3
- package/dist/{ToolReconciler-DQVaJfpE.d.mts → ToolReconciler-BmVRAHKs.d.mts} +8 -4
- package/dist/agent-registration-cXpB8cBU.mjs +199 -0
- package/dist/agent-registration-cXpB8cBU.mjs.map +1 -0
- package/dist/capabilities/Commands.d.mts +1 -1
- package/dist/capabilities/RunHooks.d.mts +1 -1
- package/dist/capabilities/Subagent.d.mts +1 -1
- package/dist/capabilities/Subagent.mjs +1 -1
- package/dist/durable/ActivityStore.d.mts +1 -1
- package/dist/durable/ActivityStore.mjs +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/Admin.mjs +12 -10
- package/dist/durable/Admin.mjs.map +1 -1
- package/dist/durable/AgentRegistration.d.mts +2 -2
- package/dist/durable/AgentRegistration.mjs +4 -8
- package/dist/durable/AgentRegistration.mjs.map +1 -1
- package/dist/durable/Certification.d.mts +2 -2
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/CommittedActivity.mjs +1 -1
- package/dist/durable/Digest.d.mts +1 -1
- package/dist/durable/Digest.mjs +1 -1
- package/dist/durable/DurableAgentRuntime.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.mjs +1 -1
- package/dist/durable/DurableFailpoint.d.mts +1 -1
- package/dist/durable/DurableFailpoint.mjs +2 -0
- package/dist/durable/DurableFailpoint.mjs.map +1 -1
- package/dist/durable/DurableFailpointTestControl.d.mts +1 -1
- package/dist/durable/EventSource.d.mts +1 -1
- package/dist/durable/EventSource.mjs +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/GitHubWorkflowSource.mjs +1 -1
- package/dist/durable/MessageDelivery.d.mts +3 -3
- package/dist/durable/MessageDelivery.mjs +1 -1
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +39 -39
- package/dist/durable/MessageDeliveryStoreConformance.mjs +1 -1
- package/dist/durable/PersistentHistory.d.mts +1 -1
- package/dist/durable/PersistentHistory.mjs +2 -2
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/Records.d.mts +2 -2
- package/dist/durable/Records.mjs +2 -2
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/Recovery.mjs +9 -9
- package/dist/durable/Recovery.mjs.map +1 -1
- package/dist/durable/RunJournal.d.mts +2 -2
- package/dist/durable/RunJournal.mjs +2 -2
- package/dist/durable/Schedule.d.mts +1 -1
- package/dist/durable/Schedule.mjs +1 -1
- package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
- package/dist/durable/ScheduleStoreConformance.mjs +1 -1
- package/dist/durable/ScheduleTransition.d.mts +1 -1
- package/dist/durable/ScheduleTransition.mjs +1 -1
- package/dist/durable/Scheduling.d.mts +2 -2
- package/dist/durable/Scheduling.mjs +1 -1
- package/dist/durable/SqlStorageV2Upgrade.d.mts +2 -2
- package/dist/durable/SqlStorageV2Upgrade.mjs +1 -1
- package/dist/durable/SqlSubscriptionStore.d.mts +2 -2
- package/dist/durable/SqlSubscriptionStore.mjs +1 -1
- package/dist/durable/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedger.mjs +14 -13
- package/dist/durable/SubmissionLedger.mjs.map +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.mjs +9 -2
- package/dist/durable/SubmissionLedgerConformance.mjs.map +1 -1
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/Subscription.mjs +1 -1
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionInput.mjs +2 -2
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
- package/dist/durable/SubscriptionTools.d.mts +3 -3
- package/dist/durable/SubscriptionTransition.d.mts +1 -1
- package/dist/durable/SubscriptionTransition.mjs +1 -1
- package/dist/durable/Subscriptions.d.mts +5 -5
- package/dist/durable/Subscriptions.mjs +1 -1
- package/dist/durable/ThreadContextHistory.d.mts +1 -1
- package/dist/durable/ThreadContextHistory.mjs +1 -1
- package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
- package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
- package/dist/durable/ThreadInvariants.d.mts +4 -2
- package/dist/durable/ThreadInvariants.mjs +49 -3
- package/dist/durable/ThreadInvariants.mjs.map +1 -1
- package/dist/durable/ThreadProjection.d.mts +1 -1
- package/dist/durable/ThreadProjection.mjs +1 -1
- package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
- package/dist/durable/ThreadStore.d.mts +1 -1
- package/dist/durable/ThreadStore.mjs +1 -1
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/ThreadStoreConformance.mjs +1 -1
- package/dist/durable/ToolReconciler.d.mts +1 -1
- package/dist/durable/ToolReconciler.mjs +9 -5
- package/dist/durable/ToolReconciler.mjs.map +1 -1
- package/dist/durable/WorkerHost.d.mts +1 -1
- package/dist/engine/AgentRuntime.d.mts +1 -1
- package/dist/engine/AgentRuntime.mjs +1 -1
- package/dist/engine/RunOptions.d.mts +1 -1
- package/dist/engine/RunOptions.mjs.map +1 -1
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +6 -6
- package/package.json +1 -1
- package/src/durable/Admin.ts +20 -11
- package/src/durable/AgentRegistration.ts +1 -5
- package/src/durable/DurableAgentRuntime.ts +947 -464
- package/src/durable/DurableFailpoint.ts +2 -0
- package/src/durable/Records.ts +30 -5
- package/src/durable/Recovery.ts +8 -8
- package/src/durable/RunJournal.ts +128 -51
- package/src/durable/SubmissionLedger.ts +13 -12
- package/src/durable/SubmissionLedgerConformance.ts +13 -2
- package/src/durable/ThreadInvariants.ts +70 -5
- package/src/durable/ToolReconciler.ts +7 -3
- package/src/durable/internal/agent-registration.ts +65 -414
- package/src/durable/internal/messaging-host.ts +0 -2
- package/src/durable/internal/worker-host.ts +34 -96
- package/src/engine/RunOptions.ts +13 -3
- package/src/engine/internal/agent-runtime.ts +160 -106
- package/dist/AgentRuntime-Cdvqw7l_.mjs.map +0 -1
- package/dist/DurableAgentRuntime-DbUvZHDZ.mjs.map +0 -1
- package/dist/Records-BAutSSnJ.mjs.map +0 -1
- package/dist/RunJournal-DRRRZmG7.mjs.map +0 -1
- package/dist/agent-registration-CXGDwvJH.mjs +0 -379
- package/dist/agent-registration-CXGDwvJH.mjs.map +0 -1
|
@@ -32,6 +32,8 @@ export const DurableRuntimeFailpointLocation = Schema.Literals([
|
|
|
32
32
|
"checkpoint:after-save",
|
|
33
33
|
"tools:after-prepared-append",
|
|
34
34
|
"tools:before-prepared-append",
|
|
35
|
+
"tools:before-unavailable-append",
|
|
36
|
+
"tools:after-unavailable-append",
|
|
35
37
|
"policy:before-reservation-append",
|
|
36
38
|
"policy:after-reservation-append",
|
|
37
39
|
"step:after-step-append",
|
package/src/durable/Records.ts
CHANGED
|
@@ -200,17 +200,17 @@ export class DefinitionDigestInput extends Schema.Class<DefinitionDigestInput>(
|
|
|
200
200
|
tools: PersistedJson,
|
|
201
201
|
}) {}
|
|
202
202
|
|
|
203
|
-
/**
|
|
203
|
+
/** Admission-time operation fingerprints. Agent fingerprints remain immutable evidence only. */
|
|
204
204
|
export class ReplayContract extends Schema.Class<ReplayContract>(
|
|
205
205
|
"@effect-agent/thread/ReplayContract",
|
|
206
206
|
)({
|
|
207
207
|
agent: Digest,
|
|
208
|
-
/**
|
|
208
|
+
/** Retained for reading earlier admissions; never selects executable code. */
|
|
209
209
|
agentBehavior: Schema.optionalKey(Digest),
|
|
210
210
|
tools: Schema.Record(Schema.String, Digest),
|
|
211
211
|
}) {}
|
|
212
212
|
|
|
213
|
-
/** Digests identify admission exactly;
|
|
213
|
+
/** Digests identify admission exactly; only an unfinished operation needs replay compatibility. */
|
|
214
214
|
export class DefinitionDigests extends Schema.Class<DefinitionDigests>(
|
|
215
215
|
"@effect-agent/thread/DefinitionDigests",
|
|
216
216
|
)({
|
|
@@ -292,6 +292,24 @@ export class ToolCallSettled extends Schema.TaggedClass<ToolCallSettled>(
|
|
|
292
292
|
budgetRejected: Schema.optionalKey(Schema.Literal(true)),
|
|
293
293
|
}) {}
|
|
294
294
|
|
|
295
|
+
/** Compact execution evidence committed with a declaration, before any handler can start. */
|
|
296
|
+
export class ToolOperation extends Schema.Class<ToolOperation>(
|
|
297
|
+
"@effect-agent/thread/ToolOperation",
|
|
298
|
+
)({
|
|
299
|
+
toolCallId: ToolCallId,
|
|
300
|
+
toolName: BoundedName,
|
|
301
|
+
executionClass: Schema.Literals(["readonly", "idempotent", "uncertain"]),
|
|
302
|
+
executionKind: ToolExecutionKind,
|
|
303
|
+
replay: Digest,
|
|
304
|
+
}) {}
|
|
305
|
+
|
|
306
|
+
/** A retired call's runtime result. Unavailable readonly work may already have been observed. */
|
|
307
|
+
export class ToolUnavailable extends Schema.TaggedClass<ToolUnavailable>()("ToolUnavailable", {
|
|
308
|
+
toolName: BoundedName,
|
|
309
|
+
execution: Schema.Literals(["not-executed", "unavailable"]),
|
|
310
|
+
message: BoundedText,
|
|
311
|
+
}) {}
|
|
312
|
+
|
|
295
313
|
/**
|
|
296
314
|
* One committed model Turn. `messages` carries the Schema-encoded Effect AI Prompt messages this
|
|
297
315
|
* Turn appended (assistant response plus any tool-call declarations), committed atomically at the
|
|
@@ -299,6 +317,8 @@ export class ToolCallSettled extends Schema.TaggedClass<ToolCallSettled>(
|
|
|
299
317
|
* `messagesDigest` pins the exact encoded content.
|
|
300
318
|
*/
|
|
301
319
|
const ModelResponseRecordedFields = Schema.Struct({
|
|
320
|
+
/** Original operation identity/semantics, independent of later Agent and toolbox changes. */
|
|
321
|
+
toolOperations: Schema.optionalKey(Schema.Array(ToolOperation)),
|
|
302
322
|
/** Explicit pre-execution failures, committed with the original arguments before any approval. */
|
|
303
323
|
toolParameterRejections: Schema.optionalKey(Schema.Array(ToolParameterRejection)),
|
|
304
324
|
toolExposure: Schema.optionalKey(Snapshot),
|
|
@@ -309,8 +329,9 @@ const ModelResponseRecordedFields = Schema.Struct({
|
|
|
309
329
|
messagesDigest: Digest,
|
|
310
330
|
/**
|
|
311
331
|
* Number of leading messages that belong only to this Run's evaluated instructions and wake
|
|
312
|
-
* input. They remain canonical
|
|
313
|
-
*
|
|
332
|
+
* input. They remain canonical. Continuations replace the instruction messages with current
|
|
333
|
+
* instructions while retaining user input; later Runs also retain that original user intent.
|
|
334
|
+
* Records without this field retain their full history.
|
|
314
335
|
*/
|
|
315
336
|
runScopedPrefixLength: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThan(0))),
|
|
316
337
|
/**
|
|
@@ -366,6 +387,8 @@ export class ToolCallPrepared extends Schema.TaggedClass<ToolCallPrepared>(
|
|
|
366
387
|
parametersDigest: Digest,
|
|
367
388
|
/** Absent legacy evidence grants no delegation replay authority. */
|
|
368
389
|
executionKind: Schema.optionalKey(ToolExecutionKind),
|
|
390
|
+
executionClass: Schema.optionalKey(ToolOperation.fields.executionClass),
|
|
391
|
+
replay: Schema.optionalKey(Digest),
|
|
369
392
|
}) {}
|
|
370
393
|
|
|
371
394
|
/** Monotonic reservations charged before programmatic execution or grace finalization. */
|
|
@@ -520,6 +543,8 @@ export class RunFailed extends Schema.TaggedClass<RunFailed>("@effect-agent/thre
|
|
|
520
543
|
const RunCompletedFields = Schema.Struct({
|
|
521
544
|
runId: RunId,
|
|
522
545
|
output: PersistedJson,
|
|
546
|
+
/** Integrity of validated terminal values, independent of future application codecs. */
|
|
547
|
+
resultDigest: Schema.optionalKey(Digest),
|
|
523
548
|
/** Application disposition captured with an ordinary completion. */
|
|
524
549
|
runDisposition: Schema.optionalKey(PersistedJson),
|
|
525
550
|
/** Honest soft-landing marker, present exactly when `exhausted` is present. */
|
package/src/durable/Recovery.ts
CHANGED
|
@@ -22,8 +22,8 @@ export class OpenToolCallEvidence extends Schema.Class<OpenToolCallEvidence>(
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* A committed tool-declaring response with ZERO prepared and ZERO settled records for its Turn:
|
|
25
|
-
* the
|
|
26
|
-
*
|
|
25
|
+
* the durability §15 batch-resume window. Mutation preparation has not committed; readonly
|
|
26
|
+
* handlers may already have run. The worker checks each unfinished operation before execution.
|
|
27
27
|
*/
|
|
28
28
|
export class DeclaredPendingBatchEvidence extends Schema.Class<DeclaredPendingBatchEvidence>(
|
|
29
29
|
"@effect-agent/thread/DeclaredPendingBatchEvidence",
|
|
@@ -217,7 +217,7 @@ export class MarkUnknown extends Schema.TaggedClass<MarkUnknown>(
|
|
|
217
217
|
}) {}
|
|
218
218
|
|
|
219
219
|
/** A committed tool-declaring response has zero prepared and zero settled records: a worker
|
|
220
|
-
* resumes the declared batch
|
|
220
|
+
* checks unfinished contracts and resumes the declared batch without model re-invocation. S2 also
|
|
221
221
|
* routes an open delegation call WITHOUT establishment evidence here (spec §13 row 1): the
|
|
222
222
|
* declared batch re-executes and the idempotent establishment converges on one child. */
|
|
223
223
|
export class ResumePendingToolBatch extends Schema.TaggedClass<ResumePendingToolBatch>(
|
|
@@ -239,14 +239,14 @@ export class ResumeSuspended extends Schema.TaggedClass<ResumeSuspended>(
|
|
|
239
239
|
"@effect-agent/thread/ResumeSuspended",
|
|
240
240
|
)("ResumeSuspended", { submissionId: SubmissionId }) {}
|
|
241
241
|
|
|
242
|
-
/** Unknown Outcomes lack covering resolutions:
|
|
242
|
+
/** Unknown Outcomes lack covering resolutions: this Submission stays parked awaiting the authorized
|
|
243
243
|
* DUR-017 resolution path; the obligation stays visible, nothing replays. */
|
|
244
244
|
export class AwaitUnknownResolution extends Schema.TaggedClass<AwaitUnknownResolution>(
|
|
245
245
|
"@effect-agent/thread/AwaitUnknownResolution",
|
|
246
246
|
)("AwaitUnknownResolution", { submissionId: SubmissionId }) {}
|
|
247
247
|
|
|
248
|
-
/** Durable resolution intents cover every open call:
|
|
249
|
-
*
|
|
248
|
+
/** Durable resolution intents cover every open call: wake the original Submission when its
|
|
249
|
+
* required retry contract is supported. Canonical outcomes are applied by the next claim. */
|
|
250
250
|
export class ApplyUnknownResolutions extends Schema.TaggedClass<ApplyUnknownResolutions>(
|
|
251
251
|
"@effect-agent/thread/ApplyUnknownResolutions",
|
|
252
252
|
)("ApplyUnknownResolutions", { submissionId: SubmissionId }) {}
|
|
@@ -843,8 +843,8 @@ const classifyDelegationAbort = (
|
|
|
843
843
|
* ApplyJoinAccounting → CompleteChildAdmission → RepairSubagentStartLink →
|
|
844
844
|
* AwaitChildAdmissionResolution → ResumePendingToolBatch (idempotent handler re-entry) →
|
|
845
845
|
* EnsureWaitingForChild → ResumeWaitingParent → ReleaseOrphanChildReservation.
|
|
846
|
-
* 10. declared-but-unprepared tool batch → ResumePendingToolBatch —
|
|
847
|
-
*
|
|
846
|
+
* 10. declared-but-unprepared tool batch → ResumePendingToolBatch — the worker checks original
|
|
847
|
+
* operation contracts; missing preparation proves no mutation dispatch, not readonly nonexecution (§15).
|
|
848
848
|
* 11. `admitted` → a parent-linked Submission whose Thread lacks the canonical lineage
|
|
849
849
|
* record defers (AwaitParentEstablishment: the parent's idempotent establishment
|
|
850
850
|
* completes it); otherwise
|
|
@@ -372,7 +372,7 @@ export interface RunJournalProjection {
|
|
|
372
372
|
readonly policyUsage: RunPolicyUsage;
|
|
373
373
|
/** Canonical projection for the requested Run; may end at its resumable Tool declaration. */
|
|
374
374
|
readonly prompt: Prompt.Prompt;
|
|
375
|
-
/**
|
|
375
|
+
/** Prior-Run history with model-only unknown results closing incomplete application calls. */
|
|
376
376
|
readonly historyBefore: Prompt.Prompt;
|
|
377
377
|
/** Number of canonical Turns already committed for the projected Run. */
|
|
378
378
|
readonly committedTurns: number;
|
|
@@ -471,13 +471,6 @@ const declaredApplicationToolCallIds = (prompt: Prompt.Prompt): ReadonlyArray<st
|
|
|
471
471
|
return ids;
|
|
472
472
|
};
|
|
473
473
|
|
|
474
|
-
const withoutApplicationToolCallMessages = (prompt: Prompt.Prompt): ReadonlyArray<Prompt.Message> =>
|
|
475
|
-
prompt.content.filter(
|
|
476
|
-
(message) =>
|
|
477
|
-
message.role !== "assistant" ||
|
|
478
|
-
!message.content.some((part) => part.type === "tool-call" && !part.providerExecuted),
|
|
479
|
-
);
|
|
480
|
-
|
|
481
474
|
/**
|
|
482
475
|
* Phase 5 audit tags that are prompt-transparent: they carry durability evidence (preparation,
|
|
483
476
|
* unknown marking, resolution, Step results, approvals, interruption) but contribute nothing to
|
|
@@ -517,9 +510,8 @@ const PROMPT_TRANSPARENT_TAGS: ReadonlySet<string> = new Set([
|
|
|
517
510
|
|
|
518
511
|
/**
|
|
519
512
|
* Pure projection: rebuild one Run's resume state from canonical records (DUR-015). Canonical
|
|
520
|
-
* order is authoritative; the fold projects each
|
|
521
|
-
* owning Run's resumable incomplete Tool Turn
|
|
522
|
-
* Runs. It flushes each contiguous group of valid `ToolCallSettled` records into one Tool message,
|
|
513
|
+
* order is authoritative; the fold projects each `ModelResponseRecorded` Turn and the
|
|
514
|
+
* owning Run's resumable incomplete Tool Turn. It flushes each contiguous group of valid `ToolCallSettled` records into one Tool message,
|
|
523
515
|
* exactly mirroring the per-Turn commit shape produced by `turnCanonicalBatch` (no-tool Turns) and
|
|
524
516
|
* by the
|
|
525
517
|
* `turnResponseBatch`/`turnResultsBatch` split (tool-declaring Turns). The Phase 5 audit tags
|
|
@@ -528,8 +520,10 @@ const PROMPT_TRANSPARENT_TAGS: ReadonlySet<string> = new Set([
|
|
|
528
520
|
*
|
|
529
521
|
* An incomplete application Tool turn remains visible while projecting its owning Run so active
|
|
530
522
|
* recovery can resume the declared batch. It is not a valid model-visible Turn boundary for a
|
|
531
|
-
* later Run
|
|
532
|
-
* are
|
|
523
|
+
* later Run until model-only unknown results close its missing calls. Those explanatory results
|
|
524
|
+
* are never canonical settlements or evidence for compaction, recovery or accounting. Real
|
|
525
|
+
* results replace them at the original declaration, including results appended after another Run.
|
|
526
|
+
* Prior user intent and assistant text remain visible; prior system instructions do not.
|
|
533
527
|
*/
|
|
534
528
|
/** @internal Lightweight canonical boundaries collected without retaining record payloads. */
|
|
535
529
|
export interface JournalBoundary {
|
|
@@ -921,6 +915,18 @@ export const projectRunJournalStream = Effect.fn("RunJournal.projectRunJournalSt
|
|
|
921
915
|
}
|
|
922
916
|
});
|
|
923
917
|
|
|
918
|
+
// Slots refer to the retained Prompt itself, not an additional history payload index.
|
|
919
|
+
// Only still-unseen results retain a slot; late results fill the original declaration in place.
|
|
920
|
+
const historicalResults = new Map<
|
|
921
|
+
string,
|
|
922
|
+
{
|
|
923
|
+
readonly allIndex: number;
|
|
924
|
+
readonly beforeIndex: number;
|
|
925
|
+
readonly parts: Array<Prompt.ToolResultPart>;
|
|
926
|
+
readonly partIndex: number;
|
|
927
|
+
}
|
|
928
|
+
>();
|
|
929
|
+
|
|
924
930
|
let pendingToolOrder = new Map<string, number>();
|
|
925
931
|
|
|
926
932
|
const flushTools = Effect.fn("RunJournal.flushTools")(function* (
|
|
@@ -1009,12 +1015,32 @@ export const projectRunJournalStream = Effect.fn("RunJournal.projectRunJournalSt
|
|
|
1009
1015
|
}
|
|
1010
1016
|
emitSummary();
|
|
1011
1017
|
if (payload._tag === "ToolCallSettled") {
|
|
1012
|
-
if (payload.runId !== ownerRunId
|
|
1018
|
+
if (payload.runId !== ownerRunId) {
|
|
1019
|
+
const slot = historicalResults.get(envelope.record.recordId);
|
|
1020
|
+
|
|
1021
|
+
if (slot === undefined)
|
|
1022
|
+
return yield* journalError("Historical Tool result has no matching declaration");
|
|
1023
|
+
const declared = slot.parts[slot.partIndex];
|
|
1024
|
+
|
|
1025
|
+
if (declared?.id !== payload.toolCallId || declared.name !== payload.toolName)
|
|
1026
|
+
return yield* journalError("Historical Tool result differs from its declaration");
|
|
1027
|
+
slot.parts[slot.partIndex] = Prompt.makePart("tool-result", {
|
|
1028
|
+
id: payload.toolCallId,
|
|
1029
|
+
name: payload.toolName,
|
|
1030
|
+
result: envelope.sequence <= clearBound ? CLEARED_TOOL_RESULT : payload.result,
|
|
1031
|
+
isFailure: payload.isFailure,
|
|
1032
|
+
providerExecuted: false,
|
|
1033
|
+
});
|
|
1034
|
+
const message = Prompt.makeMessage("tool", { content: [...slot.parts] });
|
|
1035
|
+
|
|
1036
|
+
state.all[slot.allIndex] = message;
|
|
1037
|
+
state.before[slot.beforeIndex] = message;
|
|
1038
|
+
historicalResults.delete(envelope.record.recordId);
|
|
1013
1039
|
onBoundary?.({
|
|
1014
1040
|
sequence: envelope.sequence,
|
|
1015
1041
|
tag: payload._tag,
|
|
1016
|
-
promptLength: state.all.length
|
|
1017
|
-
incomplete: true,
|
|
1042
|
+
promptLength: state.all.length,
|
|
1043
|
+
...(incompleteToolCalls.has(envelope.record.recordId) ? { incomplete: true } : {}),
|
|
1018
1044
|
...(isTerminalPriorRun(payload.runId, ownerRunId, Number.POSITIVE_INFINITY)
|
|
1019
1045
|
? { terminalPriorRun: true }
|
|
1020
1046
|
: {}),
|
|
@@ -1066,20 +1092,59 @@ export const projectRunJournalStream = Effect.fn("RunJournal.projectRunJournalSt
|
|
|
1066
1092
|
|
|
1067
1093
|
yield* accountResponse(envelope, payload, messages);
|
|
1068
1094
|
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
: messages;
|
|
1073
|
-
|
|
1074
|
-
const visibleMessages =
|
|
1075
|
-
!forRun && incompleteToolTurns.has(envelope.record.recordId)
|
|
1076
|
-
? withoutApplicationToolCallMessages(modelVisible)
|
|
1077
|
-
: modelVisible.content;
|
|
1095
|
+
const visibleMessages = forRun
|
|
1096
|
+
? messages.content
|
|
1097
|
+
: messages.content.filter((message) => message.role !== "system");
|
|
1078
1098
|
|
|
1079
1099
|
for (const message of visibleMessages) {
|
|
1080
1100
|
state.all.push(message);
|
|
1081
1101
|
if (!forRun) state.before.push(message);
|
|
1082
1102
|
}
|
|
1103
|
+
if (!forRun) {
|
|
1104
|
+
const calls = messages.content.flatMap((message) =>
|
|
1105
|
+
message.role === "assistant"
|
|
1106
|
+
? message.content.filter(
|
|
1107
|
+
(part): part is Prompt.ToolCallPart =>
|
|
1108
|
+
part.type === "tool-call" && !part.providerExecuted,
|
|
1109
|
+
)
|
|
1110
|
+
: [],
|
|
1111
|
+
);
|
|
1112
|
+
|
|
1113
|
+
if (calls.length > 0) {
|
|
1114
|
+
const parts = calls.map((call) =>
|
|
1115
|
+
Prompt.makePart("tool-result", {
|
|
1116
|
+
id: call.id,
|
|
1117
|
+
name: call.name,
|
|
1118
|
+
result: {
|
|
1119
|
+
_tag: "ToolOutcomeUnknown",
|
|
1120
|
+
message:
|
|
1121
|
+
"This earlier operation has no recorded outcome. It may have executed. Do not assume success or retry it; its original operation remains unresolved.",
|
|
1122
|
+
},
|
|
1123
|
+
isFailure: true,
|
|
1124
|
+
providerExecuted: false,
|
|
1125
|
+
}),
|
|
1126
|
+
);
|
|
1127
|
+
|
|
1128
|
+
const allIndex = state.all.length;
|
|
1129
|
+
const beforeIndex = state.before.length;
|
|
1130
|
+
const message = Prompt.makeMessage("tool", { content: parts });
|
|
1131
|
+
|
|
1132
|
+
state.all.push(message);
|
|
1133
|
+
state.before.push(message);
|
|
1134
|
+
for (const [partIndex, call] of calls.entries()) {
|
|
1135
|
+
const callId = yield* Schema.decodeEffect(ToolCallId)(call.id).pipe(
|
|
1136
|
+
Effect.mapError((cause) => journalError("Invalid historical Tool Call ID", cause)),
|
|
1137
|
+
);
|
|
1138
|
+
|
|
1139
|
+
historicalResults.set(toolCallSettledRecordId(payload.runId, payload.turn, callId), {
|
|
1140
|
+
allIndex,
|
|
1141
|
+
beforeIndex,
|
|
1142
|
+
parts,
|
|
1143
|
+
partIndex,
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1083
1148
|
state = {
|
|
1084
1149
|
...state,
|
|
1085
1150
|
committedTurns: forRun
|
|
@@ -1158,6 +1223,7 @@ const validStagedUsage = (label: string, value: number): Effect.Effect<number, R
|
|
|
1158
1223
|
);
|
|
1159
1224
|
|
|
1160
1225
|
export interface TurnCommitInput {
|
|
1226
|
+
readonly toolOperations?: ModelResponseRecorded["toolOperations"] | undefined;
|
|
1161
1227
|
readonly toolParameterRejections?: ReadonlyArray<ToolParameterRejection> | undefined;
|
|
1162
1228
|
readonly toolExposure?: Snapshot | undefined;
|
|
1163
1229
|
readonly toolSelections?: ReadonlyMap<string, Selection> | undefined;
|
|
@@ -1300,6 +1366,7 @@ const modelResponseRecord = Effect.fn("RunJournal.modelResponseRecord")(function
|
|
|
1300
1366
|
createdAt: input.createdAt,
|
|
1301
1367
|
deploymentId: input.deploymentId,
|
|
1302
1368
|
payload: ModelResponseRecorded.make({
|
|
1369
|
+
...(input.toolOperations === undefined ? {} : { toolOperations: input.toolOperations }),
|
|
1303
1370
|
...(input.toolParameterRejections === undefined || input.toolParameterRejections.length === 0
|
|
1304
1371
|
? {}
|
|
1305
1372
|
: { toolParameterRejections: input.toolParameterRejections }),
|
|
@@ -1379,29 +1446,39 @@ const toolSettledRecords = Effect.fn("RunJournal.toolSettledRecords")(function*
|
|
|
1379
1446
|
return toolRecords;
|
|
1380
1447
|
});
|
|
1381
1448
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1449
|
+
/** Integrity of the original validated terminal values; never a current-code projection. */
|
|
1450
|
+
export const runCompletionDigest = (
|
|
1451
|
+
completion: Pick<
|
|
1452
|
+
RunCompleted,
|
|
1453
|
+
"runId" | "output" | "runDisposition" | "finishReason" | "exhausted"
|
|
1454
|
+
>,
|
|
1455
|
+
) =>
|
|
1456
|
+
digestJson({
|
|
1457
|
+
runId: completion.runId,
|
|
1458
|
+
output: completion.output,
|
|
1459
|
+
runDisposition: completion.runDisposition ?? null,
|
|
1460
|
+
finishReason: completion.finishReason ?? null,
|
|
1461
|
+
exhausted: completion.exhausted ?? null,
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
const runCompletionRecord = Effect.fn("RunJournal.runCompletionRecord")(function* (
|
|
1465
|
+
input: TurnCommitInput,
|
|
1466
|
+
) {
|
|
1467
|
+
if (input.runCompletion === undefined) return undefined;
|
|
1468
|
+
const completion = { runId: input.runId, ...input.runCompletion };
|
|
1469
|
+
|
|
1470
|
+
return RecordEnvelope.make({
|
|
1471
|
+
recordId: runCompletedRecordId(input.runId),
|
|
1472
|
+
family: "thread",
|
|
1473
|
+
schemaVersion: 1,
|
|
1474
|
+
createdAt: input.createdAt,
|
|
1475
|
+
deploymentId: input.deploymentId,
|
|
1476
|
+
payload: RunCompleted.make({
|
|
1477
|
+
...completion,
|
|
1478
|
+
resultDigest: yield* runCompletionDigest(completion),
|
|
1479
|
+
}),
|
|
1480
|
+
});
|
|
1481
|
+
});
|
|
1405
1482
|
|
|
1406
1483
|
/**
|
|
1407
1484
|
* Pure per-Turn canonical batch builder (TurnCompleted seam fold, D6/D8): one
|
|
@@ -1420,7 +1497,7 @@ export const turnCanonicalBatch = Effect.fn("RunJournal.turnCanonicalBatch")(fun
|
|
|
1420
1497
|
const { promptMessages, toolParts } = splitTurnMessages(input.appended);
|
|
1421
1498
|
const modelResponse = yield* modelResponseRecord(input, promptMessages);
|
|
1422
1499
|
const toolRecords = yield* toolSettledRecords(input, toolParts);
|
|
1423
|
-
const completionRecord = runCompletionRecord(input);
|
|
1500
|
+
const completionRecord = yield* runCompletionRecord(input);
|
|
1424
1501
|
|
|
1425
1502
|
return CanonicalBatch.make({
|
|
1426
1503
|
batchId: turnBatchId(input.runId, input.turn),
|
|
@@ -1465,7 +1542,7 @@ export const turnResponseBatch = Effect.fn("RunJournal.turnResponseBatch")(funct
|
|
|
1465
1542
|
*/
|
|
1466
1543
|
export const turnResultsBatch = Effect.fn("RunJournal.turnResultsBatch")(function* (
|
|
1467
1544
|
input: TurnCommitInput,
|
|
1468
|
-
): Effect.fn.Return<CanonicalBatch, RunJournalError> {
|
|
1545
|
+
): Effect.fn.Return<CanonicalBatch, RunJournalError | DigestError, Crypto.Crypto> {
|
|
1469
1546
|
yield* requireCanonicalTurn(input.turn);
|
|
1470
1547
|
const { toolParts } = splitTurnMessages(input.appended);
|
|
1471
1548
|
const toolRecords = yield* toolSettledRecords(input, toolParts);
|
|
@@ -1477,7 +1554,7 @@ export const turnResultsBatch = Effect.fn("RunJournal.turnResultsBatch")(functio
|
|
|
1477
1554
|
if (input.runCompletion !== undefined && toolRecords.length !== 1) {
|
|
1478
1555
|
return yield* journalError("A terminal Tool completion requires exactly one settled result");
|
|
1479
1556
|
}
|
|
1480
|
-
const completionRecord = runCompletionRecord(input);
|
|
1557
|
+
const completionRecord = yield* runCompletionRecord(input);
|
|
1481
1558
|
|
|
1482
1559
|
return CanonicalBatch.make({
|
|
1483
1560
|
batchId: turnResultsBatchId(input.runId, input.turn),
|
|
@@ -61,12 +61,12 @@ export type OwnershipToken = typeof OwnershipToken.Type;
|
|
|
61
61
|
* host Run's outcome.
|
|
62
62
|
* - `suspended` — durably waiting for explicit approval decisions; the ownership period has
|
|
63
63
|
* ended and the lane consumes no worker permit while the obligation stays owed.
|
|
64
|
-
* - `unknown` — at least one
|
|
65
|
-
*
|
|
66
|
-
* authorizes cleanup
|
|
64
|
+
* - `unknown` — at least one Tool Call has a durable Unknown Outcome; this Submission is
|
|
65
|
+
* parked until an authorized resolution covers every open call or a durable abort intent
|
|
66
|
+
* authorizes cleanup without Tool replay. Later eligible input may run (DUR-012/DUR-017).
|
|
67
67
|
*
|
|
68
|
-
* `claim` never grants a `joining`, `joined`, or `suspended` head.
|
|
69
|
-
*
|
|
68
|
+
* `claim` never grants or bypasses a `joining`, `joined`, or `suspended` head. It skips unknown
|
|
69
|
+
* work without an abort intent. Any live ownership in the Thread prevents another claim.
|
|
70
70
|
*/
|
|
71
71
|
export const SubmissionState = Schema.Literals([
|
|
72
72
|
"admitted",
|
|
@@ -676,7 +676,7 @@ export class ApprovalDecisionIntent extends Schema.Class<ApprovalDecisionIntent>
|
|
|
676
676
|
|
|
677
677
|
/**
|
|
678
678
|
* Durable Unknown marking for one Submission's open ordinary Tool Calls (DUR-009): state →
|
|
679
|
-
* `unknown`,
|
|
679
|
+
* `unknown`, parking that Submission without changing ownership or the Thread epoch. Idempotent.
|
|
680
680
|
*/
|
|
681
681
|
export class MarkUnknownRequest extends Schema.Class<MarkUnknownRequest>(
|
|
682
682
|
"@effect-agent/thread/MarkUnknownRequest",
|
|
@@ -954,10 +954,10 @@ export type SubmissionLedgerFailure =
|
|
|
954
954
|
* Marking an already-ready (or later-state) Submission is a no-op.
|
|
955
955
|
* - `lookup` — read one Submission by identity or scoped idempotency key; strongly consistent
|
|
956
956
|
* with prior ledger writes.
|
|
957
|
-
* - `claim` —
|
|
958
|
-
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
957
|
+
* - `claim` — claims the lowest unsettled `queueSequence` after skipping only unknown rows
|
|
958
|
+
* without abort intent. Approval, joining and child-wait barriers remain ordered. Returns
|
|
959
|
+
* `Option.none` when no eligible work exists or ANY lease in the Thread is live, including
|
|
960
|
+
* a lease belonging to the same producer. A successful claim atomically bumps the Thread's producer
|
|
961
961
|
* epoch — fencing every stale Attempt out of canonical appends (DUR-006) — mints a fresh
|
|
962
962
|
* `attemptId` and `ownershipToken`, and starts the ownership lease (D5: adapters default to
|
|
963
963
|
* `DEFAULT_OWNERSHIP_LEASE_DURATION`, configurable). An expired lease makes the head
|
|
@@ -1040,8 +1040,9 @@ export type SubmissionLedgerFailure =
|
|
|
1040
1040
|
* with `ChildReservationConflict`. "A crash before release leaves budget unavailable until
|
|
1041
1041
|
* repair, never available twice" (spec §12).
|
|
1042
1042
|
* - `markUnknown` — idempotent, ownership-free (canonical evidence authorizes it): state →
|
|
1043
|
-
* `unknown`, the
|
|
1044
|
-
*
|
|
1043
|
+
* `unknown`, parking the Submission while its accepted settlement obligation stays visible.
|
|
1044
|
+
* Existing ownership must release or expire before another claim (DUR-009/DUR-017).
|
|
1045
|
+
* Fails with `SettlementConflict` once settled.
|
|
1045
1046
|
* - `recordUnknownResolution` — durable, idempotent per `(submissionId, toolCallId)`; a
|
|
1046
1047
|
* divergent re-resolution fails with `UnknownResolutionConflict`. Transitions
|
|
1047
1048
|
* `unknown → input-applied` once no open call remains, waking the lane. Fails with
|
|
@@ -2877,9 +2877,20 @@ const unknownAbortClaim = conformanceCase(
|
|
|
2877
2877
|
}
|
|
2878
2878
|
yield* advancePastLease(original.leaseExpiresAt);
|
|
2879
2879
|
if (!abortFirst) {
|
|
2880
|
+
const later = yield* expectSome(
|
|
2881
|
+
"runnable follower behind unknown work",
|
|
2882
|
+
yield* claimLane(threadId, PRODUCER_B),
|
|
2883
|
+
);
|
|
2884
|
+
|
|
2880
2885
|
yield* ensure(
|
|
2881
|
-
|
|
2882
|
-
"Unknown work
|
|
2886
|
+
later.submissionId === follower.submissionId,
|
|
2887
|
+
"Unknown work must preserve the queue order of runnable followers",
|
|
2888
|
+
);
|
|
2889
|
+
yield* ledger.releaseOwnership(
|
|
2890
|
+
ReleaseOwnershipRequest.make({
|
|
2891
|
+
submissionId: later.submissionId,
|
|
2892
|
+
ownershipToken: later.ownershipToken,
|
|
2893
|
+
}),
|
|
2883
2894
|
);
|
|
2884
2895
|
}
|
|
2885
2896
|
const intent = yield* ledger.requestAbort(command);
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type ProducerId,
|
|
11
11
|
type RecordEnvelope,
|
|
12
12
|
} from "./Records.ts";
|
|
13
|
+
import { runIdForSubmission } from "./RunJournal.ts";
|
|
13
14
|
import {
|
|
14
15
|
submissionInputRecordId,
|
|
15
16
|
submissionSettlementRecordId,
|
|
@@ -110,7 +111,9 @@ const batchRunsOf = (records: ReadonlyArray<CanonicalRecordEnvelope>): Array<Bat
|
|
|
110
111
|
* 4. `digest-chain` — the tail digest recomputes from `EMPTY_TAIL_DIGEST` through every batch
|
|
111
112
|
* (requires the per-batch producer directory; skipped honestly otherwise).
|
|
112
113
|
* 5. `fifo-input-order` / `fifo-settlement-order` — canonical input and settlement records
|
|
113
|
-
* follow the admitted queue order (DUR-004)
|
|
114
|
+
* follow the admitted queue order (DUR-004), except a later input may settle before a Run
|
|
115
|
+
* with canonically unresolved unknown calls when that input became active. Resolving those
|
|
116
|
+
* calls later does not revoke the later Run's ownership. Aborted settlements of never-run work (no
|
|
114
117
|
* canonical `input:{sid}` record) are exempt from the settlement comparison: P7 §7(c)
|
|
115
118
|
* settles them immediately without waiting for the head, and DUR-004 bounds execution
|
|
116
119
|
* order, which never-run work has none of.
|
|
@@ -279,14 +282,65 @@ export const verifyThreadInvariants = Effect.fn("Thread.verifyThreadInvariants")
|
|
|
279
282
|
// 5b. fifo-settlement-order — P7 §7(c) exemption: an ABORTED settlement for never-run work
|
|
280
283
|
// (no canonical `input:{sid}` record) settles immediately by design, without waiting to
|
|
281
284
|
// head the lane, so it is excluded from the FIFO comparison. DUR-004 bounds EXECUTION
|
|
282
|
-
// order; the exempted rows provably never executed.
|
|
285
|
+
// order; the exempted rows provably never executed. Unknown work can also be bypassed,
|
|
286
|
+
// but only the inputs that became active while it was parked may settle ahead of it.
|
|
283
287
|
{
|
|
284
288
|
const recordIdSet = new Set<string>(recordIds);
|
|
285
289
|
const abortedNeverRun = new Set<string>();
|
|
286
290
|
|
|
291
|
+
const settlementByRun = new Map<string, string>(
|
|
292
|
+
ordered.map((row) => [
|
|
293
|
+
runIdForSubmission(row.submissionId),
|
|
294
|
+
submissionSettlementRecordId(row.submissionId),
|
|
295
|
+
]),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const inputSettlements = new Map(
|
|
299
|
+
ordered.map((row) => [
|
|
300
|
+
submissionInputRecordId(row.submissionId),
|
|
301
|
+
submissionSettlementRecordId(row.submissionId),
|
|
302
|
+
]),
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const unknownCalls = new Map<string, Set<string>>();
|
|
306
|
+
const abortsOrTerminals = new Set<string>();
|
|
307
|
+
const bypassedSettlements = new Map<string, Set<string>>();
|
|
308
|
+
|
|
287
309
|
for (const envelope of records) {
|
|
288
310
|
const payload = envelope.record.payload;
|
|
289
311
|
|
|
312
|
+
if (payload._tag === "ToolCallUnknown" && !abortsOrTerminals.has(payload.runId)) {
|
|
313
|
+
const calls = unknownCalls.get(payload.runId) ?? new Set<string>();
|
|
314
|
+
|
|
315
|
+
calls.add(payload.toolCallId);
|
|
316
|
+
unknownCalls.set(payload.runId, calls);
|
|
317
|
+
} else if (payload._tag === "ToolCallResolved" || payload._tag === "ToolCallSettled") {
|
|
318
|
+
const calls = unknownCalls.get(payload.runId);
|
|
319
|
+
|
|
320
|
+
calls?.delete(payload.toolCallId);
|
|
321
|
+
if (calls?.size === 0) unknownCalls.delete(payload.runId);
|
|
322
|
+
} else if (payload._tag === "AbortRequested" || payload._tag === "SubmissionSettled") {
|
|
323
|
+
const runId = runIdForSubmission(payload.submissionId);
|
|
324
|
+
|
|
325
|
+
abortsOrTerminals.add(runId);
|
|
326
|
+
unknownCalls.delete(runId);
|
|
327
|
+
} else if (payload._tag === "RunCompleted" || payload._tag === "RunFailed") {
|
|
328
|
+
abortsOrTerminals.add(payload.runId);
|
|
329
|
+
unknownCalls.delete(payload.runId);
|
|
330
|
+
} else if (payload._tag === "UserInputRecorded") {
|
|
331
|
+
const settlementId = inputSettlements.get(envelope.record.recordId);
|
|
332
|
+
|
|
333
|
+
if (settlementId !== undefined) {
|
|
334
|
+
const bypassed = new Set<string>();
|
|
335
|
+
|
|
336
|
+
for (const runId of unknownCalls.keys()) {
|
|
337
|
+
const earlier = settlementByRun.get(runId);
|
|
338
|
+
|
|
339
|
+
if (earlier !== undefined) bypassed.add(earlier);
|
|
340
|
+
}
|
|
341
|
+
bypassedSettlements.set(settlementId, bypassed);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
290
344
|
if (
|
|
291
345
|
payload._tag === "SubmissionSettled" &&
|
|
292
346
|
payload.outcome === "aborted" &&
|
|
@@ -309,9 +363,20 @@ export const verifyThreadInvariants = Effect.fn("Thread.verifyThreadInvariants")
|
|
|
309
363
|
presentSettlements.has(expected),
|
|
310
364
|
);
|
|
311
365
|
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
366
|
+
const remaining = new Set(expectedPresent);
|
|
367
|
+
let matches = settlementOrder.length === expectedPresent.length;
|
|
368
|
+
|
|
369
|
+
for (const recordId of settlementOrder) {
|
|
370
|
+
for (const earlier of remaining) {
|
|
371
|
+
if (earlier === recordId) break;
|
|
372
|
+
if (!bypassedSettlements.get(recordId)?.has(earlier)) {
|
|
373
|
+
matches = false;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (!matches) break;
|
|
378
|
+
remaining.delete(recordId);
|
|
379
|
+
}
|
|
315
380
|
|
|
316
381
|
checks.push(
|
|
317
382
|
matches
|
|
@@ -20,9 +20,12 @@ export class PreparedToolCallEvidence extends Schema.Class<PreparedToolCallEvide
|
|
|
20
20
|
toolName: ToolCallPrepared.fields.toolName,
|
|
21
21
|
parameters: PersistedJson,
|
|
22
22
|
parametersDigest: Digest,
|
|
23
|
+
executionKind: ToolCallPrepared.fields.executionKind,
|
|
24
|
+
executionClass: ToolCallPrepared.fields.executionClass,
|
|
25
|
+
replay: ToolCallPrepared.fields.replay,
|
|
23
26
|
}) {}
|
|
24
27
|
|
|
25
|
-
/** Proof
|
|
28
|
+
/** Proof of nonexecution: resume the original operation or report it unavailable. */
|
|
26
29
|
export class ReconciliationNeverStarted extends Schema.TaggedClass<ReconciliationNeverStarted>(
|
|
27
30
|
"@effect-agent/thread/ReconciliationNeverStarted",
|
|
28
31
|
)("NeverStarted", {}) {}
|
|
@@ -38,7 +41,7 @@ export class ReconciliationCompleted extends Schema.TaggedClass<ReconciliationCo
|
|
|
38
41
|
isFailure: Schema.Boolean,
|
|
39
42
|
}) {}
|
|
40
43
|
|
|
41
|
-
/**
|
|
44
|
+
/** Only the original operation, identity and parameters may repeat under supported semantics. */
|
|
42
45
|
export class ReconciliationSafeToRetry extends Schema.TaggedClass<ReconciliationSafeToRetry>(
|
|
43
46
|
"@effect-agent/thread/ReconciliationSafeToRetry",
|
|
44
47
|
)("SafeToRetry", {}) {}
|
|
@@ -54,7 +57,8 @@ export class ReconciliationUncertain extends Schema.TaggedClass<ReconciliationUn
|
|
|
54
57
|
* What a reconciliation policy can prove about one prepared-but-unsettled ordinary Tool Call
|
|
55
58
|
* (durability §10): execution never started, execution completed with a recoverable result,
|
|
56
59
|
* execution is safe to repeat, or nothing — in which case the Run enters Unknown. The engine
|
|
57
|
-
* never
|
|
60
|
+
* never invents an external result. Proven nonexecution can close a retired operation with
|
|
61
|
+
* an explicit not-executed result; uncertainty stays parked.
|
|
58
62
|
*/
|
|
59
63
|
export const ReconciliationDecision = Schema.Union([
|
|
60
64
|
ReconciliationNeverStarted,
|