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
|
@@ -7,24 +7,25 @@ import { Update, UpdateError } from "./core/AgentUpdates.mjs";
|
|
|
7
7
|
import { AgentApprovalDenied, AgentApprovalPending, AgentInputError, AgentToolAuthorizationDenied, PolicyLimit } from "./core/AgentError.mjs";
|
|
8
8
|
import { IdGenerator as IdGenerator$1 } from "./core/IdGenerator.mjs";
|
|
9
9
|
import { DelegationDepth, SubagentBudgetReservation, SubagentDelegationCaps, SubagentGrant, SubagentParentLink, SubagentReservationAmounts, getToolExecutionKind } from "./core/SubagentContract.mjs";
|
|
10
|
-
import { o as Selection } from "./ToolExposure-C93U1sPu.mjs";
|
|
10
|
+
import { o as Selection, s as Snapshot } from "./ToolExposure-C93U1sPu.mjs";
|
|
11
11
|
import { ChildRunUsage, InputTokenUsage, ModelUsageGroup, OutputTokenUsage, RunUsageReport, RunUsageSummary, emptyRunTotals, runTotalsFromSummary, sumRunTotals, summarizeModelUsage, unknownRunTotals } from "./core/Usage.mjs";
|
|
12
12
|
import { FrameworkMessage, WorkerCompletion, WorkerError, WorkerHistoryEntry, WorkerRef, WorkerUpdate } from "./core/Worker.mjs";
|
|
13
13
|
import { a as PeerMessageCapacity, d as MessageAdmission, f as MessageRef, h as PeerName, i as PeerDeliveryLifetime, m as MessagingError, o as PeerRoutes, p as MessageStatus, r as PeerAuthorizer, t as MessagingHost } from "./MessagingHost-CWi_PP3g.mjs";
|
|
14
14
|
import { SubagentHost } from "./engine/SubagentHost.mjs";
|
|
15
15
|
import { ThreadHistory } from "./engine/ThreadHistory.mjs";
|
|
16
16
|
import { n as RunToolVisibility } from "./ToolExposure-9uF_Yo0T.mjs";
|
|
17
|
-
import {
|
|
17
|
+
import { f as renderInputPrompt, n as AgentChildPending, y as streamWithUsageAccountingUnknown } from "./AgentRuntime-BWOnNc0d.mjs";
|
|
18
|
+
import { ContextRolloverTool } from "./engine/ContextWindow.mjs";
|
|
18
19
|
import { getToolExecutionClass } from "./engine/DurableStep.mjs";
|
|
19
20
|
import { CompactionError, ContextCompactor } from "./engine/ContextCompactor.mjs";
|
|
20
21
|
import { RunPolicyUsage } from "./core/RunPolicyUsage.mjs";
|
|
21
22
|
import { AgentUpdateAcceptance, CurrentToolFailureObserver, ModelUsageAccounting, RunContextPreparation, RunContextPreparationPassthrough, RunToolAuthorization, RunToolScheduling } from "./engine/RunOptions.mjs";
|
|
22
|
-
import { $ as ToolCallSettled, B as SubagentJoined, E as ProducerEpoch, G as SubmissionSettledRecord, H as SubagentRequested, I as RunPolicyUsageReserved, J as ToolApprovalDecided, K as SubtreeBudgetReserved, L as RunStartedRecord, O as RecordEnvelope, P as RunDurationExhausted, Q as ToolCallResolved, R as SettlementFailureDiagnostic, T as PersistedJson, U as SubagentStarted, V as SubagentLineageRecorded, W as SubmissionSettled, X as ToolCallPrepared, Y as ToolApprovalRequested, at as
|
|
23
|
+
import { $ as ToolCallSettled, B as SubagentJoined, E as ProducerEpoch, G as SubmissionSettledRecord, H as SubagentRequested, I as RunPolicyUsageReserved, J as ToolApprovalDecided, K as SubtreeBudgetReserved, L as RunStartedRecord, O as RecordEnvelope, P as RunDurationExhausted, Q as ToolCallResolved, R as SettlementFailureDiagnostic, T as PersistedJson, U as SubagentStarted, V as SubagentLineageRecorded, W as SubmissionSettled, X as ToolCallPrepared, Y as ToolApprovalRequested, at as WorkerAdmission, c as CanonicalRecordEnvelope, ct as WorkerOrigin, d as CompactionCreated, dt as WorkerReportRefused, et as ToolCallUnknown, ft as WorkerReportingIntent, h as Digest, i as BatchId, it as UserInputRecorded, j as RepairAnnotated, k as RecordId, lt as WorkerOriginRecorded, n as AgentUpdateEmitted, nt as ToolStepSettled, o as CanonicalBatch, ot as WorkerInputCompleted, p as DefinitionDigests, q as ThreadCreated, rt as ToolUnavailable, st as WorkerInputRequested, t as AbortRequested, tt as ToolOperation, u as CanonicalSequence, ut as WorkerReportPrepared, w as PeerMessagePrepared, x as ModelResponseInterrupted } from "./Records-DR1xtbXY.mjs";
|
|
23
24
|
import { AbortCommand, AbortIntentRequest, AdmissionPolicyError, AdmissionRequest, ApprovalDecisionCommand, ApprovalPendingSuspension, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, ChildBudgetReservationRequest, ChildReservationId, ChildSettledNotification, ClaimJoiningRequest, ClaimRequest, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipToken, ParentLinkage, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReleaseOwnershipRequest, RenewOwnershipRequest, RevertJoiningRequest, Settlement, SettlementFinalization, SettlementReservation, SubmissionLedger, SubmissionLookupById, SubmissionLookupByKey, SuspendRequest, UnknownResolutionCommand, WaitingChild, WaitingForChildSuspension, submissionAbortBatchId, submissionAbortRecordId, submissionInputBatchId, submissionInputRecordId, submissionSettlementBatchId, submissionSettlementId, submissionSettlementRecordId } from "./durable/SubmissionLedger.mjs";
|
|
24
25
|
import { DeclaredPendingBatchEvidence, OpenDelegationCallEvidence, OpenToolCallEvidence, PendingApprovalEvidence, RecoveryDecision, RecoveryEvidence, classifyRecovery } from "./durable/Recovery.mjs";
|
|
25
26
|
import { ExplainedEvidence, ExplainedSubmission, ExplainedUnknownCall, IntegrityCheck, IntegrityReport, ObligationEntry, ObligationReport, RecoveryExplanation, RetryRefused, obligationSeverityOf, predictRecoveryDisposition, recoveryDecisionMeaning } from "./durable/Admin.mjs";
|
|
26
27
|
import { digestJson } from "./durable/Digest.mjs";
|
|
27
|
-
import { a as
|
|
28
|
+
import { a as definitionDigestsEqual, c as resolveWorkerBinding, i as compileRegistrations, l as toolReplayContracts, o as makeLegacyWorkerBinding, s as resolveDefinitionBinding, t as BindingUnavailable } from "./agent-registration-cXpB8cBU.mjs";
|
|
28
29
|
import { FencedAppendRequest, LoadCheckpointRequest, SaveRecoveryCheckpointRequest, ThreadCheckpoint, ThreadExportRequest, ThreadMaterialization, ThreadObservation, ThreadRead, ThreadStore, ThreadStoreError, ThreadTailRequest } from "./durable/ThreadStore.mjs";
|
|
29
30
|
import { DurableRuntimeFailpoint } from "./durable/DurableFailpoint.mjs";
|
|
30
31
|
import { ScheduledInputRefused, ScheduledInputRetryable } from "./durable/Schedule.mjs";
|
|
@@ -32,7 +33,7 @@ import { PreparedInputAdmission } from "./durable/PreparedInputAdmission.mjs";
|
|
|
32
33
|
import { PreparedInput } from "./durable/Subscription.mjs";
|
|
33
34
|
import { MessageDeliveryDriver, MessageDeliveryFailpoint, MessageDeliveryRecord, MessageDeliveryStore, messageDeliveryCapacity, messageDeliveryUsesCapacity, prepareMessageDelivery } from "./durable/MessageDelivery.mjs";
|
|
34
35
|
import { n as safeUnknownString, t as inspectForeignDiagnostic } from "./foreign-diagnostic-DBQ1XHKh.mjs";
|
|
35
|
-
import { A as
|
|
36
|
+
import { A as toolApprovalDecisionRecordId, B as turnApprovalsBatchId, C as subagentJoinedRecordId, D as subagentRequestedRecordId, E as subagentRequestedBatchId, F as toolCallResultBatchId, G as turnResponseBatch, H as turnCanonicalBatch, I as toolCallSettledRecordId, L as toolCallUnknownRecordId, M as toolCallPreparedRecordId, N as toolCallResolutionBatchId, O as subagentStartedBatchId, P as toolCallResolvedRecordId, R as toolStepSettledBatchId, S as subagentJoinBatchId, T as subagentLineageRecordId, U as turnIdForRun, W as turnPreparedBatchId, Y as makeJournalMetadata, _ as runDurationBatchId, a as childThreadIdFor, b as runStartedBatchId, c as markUnknownBatchId, d as modelResponseRecordId, g as runCompletionDigest, h as runCompletedRecordId, i as childIdempotencyKeyFor, j as toolApprovalRequestRecordId, k as subagentStartedRecordId, l as modelResponseInterruptedBatchId, o as compactionBatchId, p as projectRunJournalStream, q as turnResultsBatch, r as approvalDecisionBatchId, s as compactionRecordId, t as RunJournalError, u as modelResponseInterruptedRecordId, v as runDurationRecordId, w as subagentLineageBatchId, x as runStartedRecordId, y as runIdForSubmission, z as toolStepSettledRecordId } from "./RunJournal-DNs3PM4K.mjs";
|
|
36
37
|
import { WorkerBudgetAuthorizer, WorkerConcurrencyLimit, WorkerConcurrencyResolver, WorkerHostAuthorizer, WorkerHostConfig, WorkerPolicyResolver } from "./durable/WorkerHost.mjs";
|
|
37
38
|
import { OperationAuthorizationRequest, OperationAuthorizer, OperationDenied } from "./durable/OperationAuthorizer.mjs";
|
|
38
39
|
import { PendingSubmission, SettledSubmission } from "./durable/SubmissionStatus.mjs";
|
|
@@ -375,7 +376,7 @@ const makeMessagingRuntime = Effect.fn("MessagingHost.make")(function* (options)
|
|
|
375
376
|
if (prior !== void 0) {
|
|
376
377
|
saved = yield* decodeProof(prior);
|
|
377
378
|
const metadata = saved.envelope.messageAdmission;
|
|
378
|
-
if (!Schema.is(MessageAdmission)(metadata) || metadata.peerName !== request.name || saved.envelope.agentId !== target.agentId || !
|
|
379
|
+
if (!Schema.is(MessageAdmission)(metadata) || metadata.peerName !== request.name || saved.envelope.agentId !== target.agentId || !sameJson$1(saved.envelope.input, input) || (inReplyTo === void 0 || metadata.inReplyTo === void 0 ? inReplyTo !== metadata.inReplyTo : !sameRef(inReplyTo, metadata.inReplyTo))) return yield* failure$1(operation, "conflict");
|
|
379
380
|
yield* authorize(operation, "send", request, {
|
|
380
381
|
threadId: saved.envelope.threadId,
|
|
381
382
|
agentId: target.agentId
|
|
@@ -676,15 +677,15 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
676
677
|
...report.destination === void 0 ? {} : { destinationDelegationId: report.destination.delegationId }
|
|
677
678
|
};
|
|
678
679
|
});
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
return
|
|
680
|
+
const currentBinding = (agentId) => {
|
|
681
|
+
const matches = deps.bindings.filter((entry) => entry.agentId === agentId);
|
|
682
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
682
683
|
};
|
|
683
684
|
const sourceAuthority = Effect.fn("WorkerHost.sourceAuthority")(function* (threadId, submissionId) {
|
|
684
685
|
const current = yield* read(threadId, "start");
|
|
685
686
|
const created = current.records[0]?.record.payload;
|
|
686
687
|
if (created?._tag !== "ThreadCreated") return yield* failure("start", "not-found");
|
|
687
|
-
const binding =
|
|
688
|
+
const binding = currentBinding(created.agentId);
|
|
688
689
|
const worker = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
|
|
689
690
|
const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
|
|
690
691
|
const nested = worker?._tag === "WorkerOriginRecorded" || attached?._tag === "SubagentLineageRecorded";
|
|
@@ -697,7 +698,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
697
698
|
if (worker?._tag === "WorkerOriginRecorded" && (submission.value.workerAdmission === void 0 || !sameOrigin(submission.value.workerAdmission.origin, worker.origin))) return yield* failure("start", "denied");
|
|
698
699
|
if (attached?._tag === "SubagentLineageRecorded" && (submission.value.parentLinkage?.parentSubmissionId !== attached.parentSubmissionId || submission.value.parentLinkage.parentToolCallId !== attached.parentLink.parentToolCallId)) return yield* failure("start", "denied");
|
|
699
700
|
}
|
|
700
|
-
const ownerBinding = ownerSubmission === void 0 ? void 0 :
|
|
701
|
+
const ownerBinding = ownerSubmission === void 0 ? void 0 : currentBinding(ownerSubmission.agentId);
|
|
701
702
|
const selectedBinding = ownerBinding ?? binding;
|
|
702
703
|
if (worker?._tag === "WorkerOriginRecorded") return {
|
|
703
704
|
current,
|
|
@@ -806,7 +807,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
806
807
|
}
|
|
807
808
|
return yield* failure("start", "storage");
|
|
808
809
|
});
|
|
809
|
-
const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal) {
|
|
810
|
+
const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal, retainedFrameworkInput) {
|
|
810
811
|
const origin = admission.origin;
|
|
811
812
|
const id = `worker-input:${admission.messageId}`;
|
|
812
813
|
for (let attempt = 0; attempt < 16; attempt++) {
|
|
@@ -827,9 +828,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
827
828
|
if (prior === void 0 && admission.messageId !== origin.firstMessageId) return yield* failure("start", "not-found");
|
|
828
829
|
const caps = origin.budget.caps;
|
|
829
830
|
const sourceBinding = source.binding;
|
|
830
|
-
const targetBinding =
|
|
831
|
+
const targetBinding = currentBinding(origin.worker.targetAgentId);
|
|
831
832
|
if (sourceBinding === void 0 || targetBinding === void 0) return yield* failure("start", "declaration-unavailable");
|
|
832
|
-
yield* Schema.decodeEffect(Schema.toEncoded(targetBinding.definition.input))(input).pipe(Effect.mapError(() => failure("start", "corrupt")));
|
|
833
|
+
if (!retainedFrameworkInput) yield* Schema.decodeEffect(Schema.toEncoded(targetBinding.definition.input))(input).pipe(Effect.mapError(() => failure("start", "corrupt")));
|
|
833
834
|
const targetRequest = {
|
|
834
835
|
definition: targetBinding.definition,
|
|
835
836
|
definitions: targetBinding.digests,
|
|
@@ -926,7 +927,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
926
927
|
});
|
|
927
928
|
yield* authorizeBudget(admission.origin, options.principal);
|
|
928
929
|
if (admission.origin.worker.threadId !== options.threadId || admission.origin.worker.targetAgentId !== agentId || !definitionDigestsEqual(admission.origin.targetDigests, options.definitions) || options.idempotencyKey !== admission.messageId || admission.deliveryPrincipal !== void 0 && admission.deliveryPrincipal !== options.principal) return yield* failure("start", "worker-mismatch");
|
|
929
|
-
|
|
930
|
+
const retainedFrameworkInput = Schema.is(FrameworkMessage)(options.messageAdmission);
|
|
931
|
+
if (retainedFrameworkInput && admission.reportKind !== "update") yield* validateCompletion(options.messageAdmission, options, agentId, inputDigest);
|
|
932
|
+
yield* reservation(admission, inputDigest, input, options.principal, retainedFrameworkInput);
|
|
930
933
|
return admission;
|
|
931
934
|
});
|
|
932
935
|
const prepareUpdate = Effect.fn("WorkerHost.prepareUpdate")(function* (update, submission, messageId) {
|
|
@@ -938,9 +941,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
938
941
|
const first = requests(sourceHistory.records).find((row) => row.admission.messageId === origin.firstMessageId);
|
|
939
942
|
if (first === void 0 || !sameOrigin(first.admission.origin, origin)) return yield* failure("followUp", "corrupt");
|
|
940
943
|
const source = yield* sourceAuthority(origin.source.threadId, first.admission.sourceSubmissionId);
|
|
941
|
-
if (source.binding === void 0 || source.submission === void 0
|
|
942
|
-
const sourceBinding =
|
|
943
|
-
const targetBinding =
|
|
944
|
+
if (source.binding === void 0 || source.submission === void 0) return yield* failure("followUp", "declaration-unavailable");
|
|
945
|
+
const sourceBinding = currentBinding(origin.source.agentId);
|
|
946
|
+
const targetBinding = currentBinding(origin.worker.targetAgentId);
|
|
944
947
|
const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
|
|
945
948
|
if (sourceBinding === void 0 || targetBinding === void 0 || reports.length !== 1 || reports[0]?.mode !== "standard" || reports[0].target !== targetBinding.definition || !definitionDigestsEqual(submission.agentDigests, origin.targetDigests)) return yield* failure("followUp", "declaration-unavailable");
|
|
946
949
|
const recorded = source.current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
|
|
@@ -949,8 +952,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
949
952
|
const now = yield* Clock.currentTimeMillis;
|
|
950
953
|
const deadlineAtMillis = Math.min(origin.expiresAtMillis, receivingOrigin?.expiresAtMillis ?? Infinity);
|
|
951
954
|
if (now >= deadlineAtMillis) return yield* failure("followUp", "denied");
|
|
952
|
-
const
|
|
953
|
-
const input = yield* Schema.decodeEffect(Schema.toEncoded(sourceBinding.definition.input))(source.submission.inputPayload).pipe(Effect.flatMap(() => Schema.decodeEffect(PersistedJson)(sourceSubmission.inputPayload)), Effect.mapError(() => failure("followUp", "corrupt")));
|
|
955
|
+
const input = source.submission.inputPayload;
|
|
954
956
|
let workerAdmission;
|
|
955
957
|
let principal = submission.principal;
|
|
956
958
|
if (receivingOrigin !== void 0) {
|
|
@@ -1041,9 +1043,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1041
1043
|
const firstInput = requests(sourceHistory.records).find((row) => row.admission.messageId === origin.firstMessageId);
|
|
1042
1044
|
if (firstInput === void 0 || !sameOrigin(firstInput.admission.origin, origin)) return yield* failure("inspect", "corrupt");
|
|
1043
1045
|
const source = yield* sourceAuthority(origin.source.threadId, firstInput.admission.sourceSubmissionId);
|
|
1044
|
-
if (source.binding === void 0
|
|
1045
|
-
const sourceBinding =
|
|
1046
|
-
const targetBinding =
|
|
1046
|
+
if (source.binding === void 0) return refused("declaration-unavailable");
|
|
1047
|
+
const sourceBinding = currentBinding(origin.source.agentId);
|
|
1048
|
+
const targetBinding = currentBinding(origin.worker.targetAgentId);
|
|
1047
1049
|
const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
|
|
1048
1050
|
const descriptor = reports[0];
|
|
1049
1051
|
if (sourceBinding === void 0 || targetBinding === void 0 || descriptor === void 0 || reports.length !== 1 || !Object.is(descriptor.target, targetBinding.definition) || descriptor.mode !== "standard" && !Object.is(descriptor.input, sourceBinding.definition.input) || descriptor.mode !== intent.mode || descriptor.destination?.delegationId !== intent.destinationDelegationId || descriptor.destination !== void 0 && !Object.is(descriptor.destination.target, sourceBinding.definition)) return refused("declaration-unavailable");
|
|
@@ -1091,7 +1093,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1091
1093
|
if (!Schema.toEquivalence(WorkerRef)(message.report.worker, origin.worker) || !Schema.toEquivalence(Receipt)(message.report.receipt, report.observation.receipt) || message.report.runId !== runId || message.report.settlementId !== host.settlementId || message.report.outcome !== host.outcome || message.budgetExhausted !== report.observation.budgetExhausted) return refused("input");
|
|
1092
1094
|
} else if (projection.value.message !== void 0) return refused("input");
|
|
1093
1095
|
const reportInput = descriptor.mode === "standard" ? source.submission?.inputPayload : projection.value.encodedInput;
|
|
1094
|
-
const validated = yield* Schema.decodeEffect(Schema.toEncoded(sourceBinding.definition.input))(reportInput).pipe(Effect.flatMap(() => Schema.decodeUnknownEffect(PersistedJson)(reportInput))
|
|
1096
|
+
const validated = yield* (descriptor.mode === "standard" ? Schema.decodeUnknownEffect(PersistedJson)(reportInput) : Schema.decodeEffect(Schema.toEncoded(sourceBinding.definition.input))(reportInput).pipe(Effect.flatMap(() => Schema.decodeUnknownEffect(PersistedJson)(reportInput)))).pipe(Effect.option);
|
|
1095
1097
|
if (Option.isNone(validated)) return refused("input");
|
|
1096
1098
|
const input = validated.value;
|
|
1097
1099
|
const inputDigest = yield* withCrypto(digestJson(input)).pipe(Effect.mapError(storageFailure("inspect")));
|
|
@@ -1422,7 +1424,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1422
1424
|
threadId: Schema.decodeSync(ThreadId)(messageId)
|
|
1423
1425
|
},
|
|
1424
1426
|
source: existing?.envelope.workerAdmission?.origin.source ?? context.source,
|
|
1425
|
-
targetDigests: resolved.digests,
|
|
1427
|
+
targetDigests: previousOrigin?.targetDigests ?? resolved.digests,
|
|
1426
1428
|
policy: request.policy,
|
|
1427
1429
|
budget: request.budget,
|
|
1428
1430
|
...request.budgetScope === void 0 ? {} : { budgetScope: request.budgetScope },
|
|
@@ -1575,7 +1577,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1575
1577
|
const created = current.records[0]?.record.payload;
|
|
1576
1578
|
if (created?._tag !== "ThreadCreated") return yield* failure("context", "not-found");
|
|
1577
1579
|
const selected = request.sourceSubmissionId === void 0 ? void 0 : yield* sourceAuthority(sourceThreadId, request.sourceSubmissionId);
|
|
1578
|
-
const resolved = selected === void 0 ?
|
|
1580
|
+
const resolved = selected === void 0 ? currentBinding(created.agentId) : selected.binding;
|
|
1579
1581
|
if (resolved === void 0) return yield* failure("context", "declaration-unavailable");
|
|
1580
1582
|
const origin = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
|
|
1581
1583
|
const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
|
|
@@ -1733,7 +1735,7 @@ var RecoveryReport = class extends Schema.Class("@effect-agent/thread/RecoveryRe
|
|
|
1733
1735
|
decision: RecoveryDecision,
|
|
1734
1736
|
/**
|
|
1735
1737
|
* `repaired` = executed; `deferred` = a claiming worker must finish it; `none` = settled;
|
|
1736
|
-
* `unknown` =
|
|
1738
|
+
* `unknown` = this Submission is parked on an Unknown Outcome awaiting the authorized
|
|
1737
1739
|
* DUR-017 resolution path — the settlement obligation stays visible while no worker permit
|
|
1738
1740
|
* is consumed (durability §16).
|
|
1739
1741
|
*/
|
|
@@ -1870,18 +1872,6 @@ const declaredToolCalls = Effect.fn("DurableAgentRuntime.declaredToolCalls")((me
|
|
|
1870
1872
|
})));
|
|
1871
1873
|
/** Application calls alone drive durable preparation, settlement, and batch resume. */
|
|
1872
1874
|
const declaredApplicationCalls = Effect.fn("DurableAgentRuntime.declaredApplicationCalls")((messages) => declaredToolCalls(messages).pipe(Effect.map(({ application }) => application)));
|
|
1873
|
-
/** Rebuild the exact JSON text that the engine decoded from one terminal no-Tool response. */
|
|
1874
|
-
const terminalAssistantText = Effect.fn("DurableAgentRuntime.terminalAssistantText")((messages) => decodePrompt(messages).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
1875
|
-
message: "ModelResponseRecorded messages are not Schema-encoded Prompt messages",
|
|
1876
|
-
cause
|
|
1877
|
-
})), Effect.map((prompt) => {
|
|
1878
|
-
let text = "";
|
|
1879
|
-
for (const message of prompt.content) {
|
|
1880
|
-
if (message.role !== "assistant") continue;
|
|
1881
|
-
for (const part of message.content) if (part.type === "text") text += part.text;
|
|
1882
|
-
}
|
|
1883
|
-
return text;
|
|
1884
|
-
})));
|
|
1885
1875
|
const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
1886
1876
|
const registeredBindings = [...bindings];
|
|
1887
1877
|
const ledger = yield* SubmissionLedger;
|
|
@@ -1903,6 +1893,34 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
1903
1893
|
const operationAuthorizer = yield* OperationAuthorizer;
|
|
1904
1894
|
const toolFailureObserver = yield* CurrentToolFailureObserver;
|
|
1905
1895
|
const withCrypto = (effect) => Effect.provideService(effect, Crypto.Crypto, crypto);
|
|
1896
|
+
const contractsFor = (definition) => {
|
|
1897
|
+
const registered = registeredBindings.filter((binding) => binding.agentId === definition.id);
|
|
1898
|
+
const binding = registered.length === 1 ? registered[0] : void 0;
|
|
1899
|
+
const current = binding?.definition.toolkit === definition.toolkit && binding.definition.completion === definition.completion && binding.definition.completionFromTools === definition.completionFromTools ? binding : void 0;
|
|
1900
|
+
const contracts = current?.digests.replay?.tools;
|
|
1901
|
+
return contracts === void 0 ? withCrypto(toolReplayContracts(definition, void 0, current?.digests.tools ?? config.deploymentId)) : Effect.succeed(contracts);
|
|
1902
|
+
};
|
|
1903
|
+
const currentOperationsFor = Effect.fnUntraced(function* (agentId) {
|
|
1904
|
+
const registered = registeredBindings.filter((binding) => binding.agentId === agentId);
|
|
1905
|
+
const binding = registered.length === 1 ? registered[0] : void 0;
|
|
1906
|
+
return binding === void 0 ? void 0 : {
|
|
1907
|
+
definition: binding.definition,
|
|
1908
|
+
contracts: yield* contractsFor(binding.definition)
|
|
1909
|
+
};
|
|
1910
|
+
});
|
|
1911
|
+
const operationsFor = (records, runId) => {
|
|
1912
|
+
const operations = /* @__PURE__ */ new Map();
|
|
1913
|
+
for (const { record: { payload } } of records) {
|
|
1914
|
+
if (payload._tag !== "ModelResponseRecorded" || payload.runId !== runId) continue;
|
|
1915
|
+
for (const operation of payload.toolOperations ?? []) operations.set(operation.toolCallId, operation);
|
|
1916
|
+
}
|
|
1917
|
+
return operations;
|
|
1918
|
+
};
|
|
1919
|
+
const supportsOperation = (submission, call, operation, prepared, current) => {
|
|
1920
|
+
const tool = current?.definition.toolkit.tools[call.toolName];
|
|
1921
|
+
const replay = operation?.replay ?? prepared?.replay ?? submission.agentDigests.replay?.tools[call.toolName];
|
|
1922
|
+
return tool !== void 0 && replay !== void 0 && current?.contracts[call.toolName] === replay && (operation === void 0 || operation.toolName === call.toolName && operation.executionClass === getToolExecutionClass(tool) && operation.executionKind === getToolExecutionKind(tool.annotations)) && (prepared === void 0 || prepared.toolName === call.toolName && (prepared.executionClass === void 0 || prepared.executionClass === getToolExecutionClass(tool)) && (prepared.executionKind ?? "ordinary") === getToolExecutionKind(tool.annotations));
|
|
1923
|
+
};
|
|
1906
1924
|
const hit = (location) => failpoint.hit(location);
|
|
1907
1925
|
const makeEnvelope = (recordId, payload) => Effect.map(nowUtc, (createdAt) => RecordEnvelope.make({
|
|
1908
1926
|
recordId,
|
|
@@ -1983,7 +2001,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
1983
2001
|
return yield* Stream.runCollect(view.canonical.pipe(Stream.filter(controlRecords(submissionIds))));
|
|
1984
2002
|
});
|
|
1985
2003
|
/** A checkpoint is useful only for its explicitly retained submissions and exact definitions. */
|
|
1986
|
-
const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds) {
|
|
2004
|
+
const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds, journalOwner) {
|
|
1987
2005
|
const full = { canonical: canonicalRange(threadId, throughSequence) };
|
|
1988
2006
|
if (store.recoveryCheckpoints === void 0) return full;
|
|
1989
2007
|
const loaded = yield* store.recoveryCheckpoints.load(LoadCheckpointRequest.make({
|
|
@@ -1996,7 +2014,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
1996
2014
|
const decoded = yield* Schema.decodeUnknownEffect(RecoveryCheckpointContents)(checkpoint.state).pipe(Effect.option);
|
|
1997
2015
|
if (Option.isNone(decoded)) return full;
|
|
1998
2016
|
const { state, digest } = decoded.value;
|
|
1999
|
-
if (!submissionIds.every((id) => state.submissionIds.includes(id)) || state.seed.runId !== runIdForSubmission(state.submissionId)) return full;
|
|
2017
|
+
if (!submissionIds.every((id) => state.submissionIds.includes(id)) || state.seed.runId !== runIdForSubmission(state.submissionId) || journalOwner !== void 0 && state.seed.runId !== journalOwner) return full;
|
|
2000
2018
|
const encoded = yield* Schema.encodeEffect(RecoveryCheckpointState)(state).pipe(Effect.mapError((cause) => ThreadStoreError.make({
|
|
2001
2019
|
operation: "decode recovery checkpoint",
|
|
2002
2020
|
message: "Invalid recovery state",
|
|
@@ -2123,6 +2141,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2123
2141
|
const preparedTurns = /* @__PURE__ */ new Set();
|
|
2124
2142
|
const settledIds = /* @__PURE__ */ new Set();
|
|
2125
2143
|
const resolvedIds = /* @__PURE__ */ new Set();
|
|
2144
|
+
const unknownIds = /* @__PURE__ */ new Set();
|
|
2126
2145
|
const requested = [];
|
|
2127
2146
|
const decidedIds = /* @__PURE__ */ new Set();
|
|
2128
2147
|
let lastResponse;
|
|
@@ -2163,8 +2182,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2163
2182
|
case "ToolCallSettled":
|
|
2164
2183
|
if (payload.runId === runId) settledIds.add(payload.toolCallId);
|
|
2165
2184
|
break;
|
|
2185
|
+
case "ToolCallUnknown":
|
|
2186
|
+
if (payload.runId === runId) unknownIds.add(payload.toolCallId);
|
|
2187
|
+
break;
|
|
2166
2188
|
case "ToolCallResolved":
|
|
2167
|
-
if (payload.runId === runId) resolvedIds.add(payload.toolCallId);
|
|
2189
|
+
if (payload.runId === runId && (payload.resolution === "completed-with-result" || payload.resolution === "failed-with-error")) resolvedIds.add(payload.toolCallId);
|
|
2168
2190
|
break;
|
|
2169
2191
|
case "ToolApprovalRequested":
|
|
2170
2192
|
if (payload.runId !== runId) break;
|
|
@@ -2245,8 +2267,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2245
2267
|
}));
|
|
2246
2268
|
}
|
|
2247
2269
|
const isPreparedWorker = (call) => preparedKinds.get(toolCallPreparedRecordId(runId, call.turn, call.toolCallId)) === "orchestration";
|
|
2248
|
-
const openWorkerCalls = allOpenCalls.filter(isPreparedWorker);
|
|
2249
|
-
const openToolCalls = allOpenCalls.filter((call) => !isPreparedDelegation(call) && !isPreparedWorker(call));
|
|
2270
|
+
const openWorkerCalls = allOpenCalls.filter((call) => isPreparedWorker(call) && !unknownIds.has(call.toolCallId));
|
|
2271
|
+
const openToolCalls = allOpenCalls.filter((call) => unknownIds.has(call.toolCallId) || !isPreparedDelegation(call) && !isPreparedWorker(call));
|
|
2250
2272
|
let declaredPendingBatch;
|
|
2251
2273
|
if (lastResponse !== void 0 && !preparedTurns.has(lastResponse.turn)) {
|
|
2252
2274
|
const declared = yield* declaredApplicationCalls(lastResponse.messages);
|
|
@@ -2271,6 +2293,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2271
2293
|
...hostSettlementOutcome === void 0 ? {} : { hostSettlementOutcome }
|
|
2272
2294
|
});
|
|
2273
2295
|
});
|
|
2296
|
+
/** Verify immutable preparation against the original declaration before any recovery side effect. */
|
|
2297
|
+
const validatePreparedCall = Effect.fnUntraced(function* (prepared, declared, operations) {
|
|
2298
|
+
const call = declared.application.find((call) => call.id === prepared.toolCallId);
|
|
2299
|
+
if (call === void 0 || prepared.toolName !== call.name) return yield* RunJournalError.make({ message: `Prepared Tool ${prepared.toolCallId} differs from its original declaration` });
|
|
2300
|
+
const parameters = yield* decodePersisted(call.params).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
2301
|
+
message: "Invalid canonical Tool parameters",
|
|
2302
|
+
cause
|
|
2303
|
+
})));
|
|
2304
|
+
const declarationDigest = yield* withCrypto(digestJson(parameters)).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
2305
|
+
message: "Cannot verify declared Tool parameters",
|
|
2306
|
+
cause
|
|
2307
|
+
})));
|
|
2308
|
+
const preparedDigest = yield* withCrypto(digestJson(prepared.parameters)).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
2309
|
+
message: "Cannot verify prepared Tool parameters",
|
|
2310
|
+
cause
|
|
2311
|
+
})));
|
|
2312
|
+
const operation = operations?.find((entry) => entry.toolCallId === prepared.toolCallId);
|
|
2313
|
+
if (prepared.parametersDigest !== declarationDigest || preparedDigest !== declarationDigest || operation !== void 0 && (prepared.executionClass !== void 0 && prepared.executionClass !== operation.executionClass || (prepared.executionKind ?? "ordinary") !== operation.executionKind || prepared.replay !== void 0 && prepared.replay !== operation.replay)) return yield* RunJournalError.make({ message: `Prepared Tool ${prepared.toolCallId} differs from its original operation` });
|
|
2314
|
+
});
|
|
2274
2315
|
/**
|
|
2275
2316
|
* The declared-but-unsettled Tool batch of the Run's LAST committed Turn (§2.4). Keyed on
|
|
2276
2317
|
* SETTLED coverage deliberately: a call closed by a never-started/safe-retry `ToolCallResolved`
|
|
@@ -2289,12 +2330,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2289
2330
|
turn: payload.turn,
|
|
2290
2331
|
messages: payload.messages,
|
|
2291
2332
|
toolParameterRejections: payload.toolParameterRejections,
|
|
2333
|
+
toolOperations: payload.toolOperations,
|
|
2292
2334
|
...payload.toolExposure === void 0 ? {} : { toolExposure: payload.toolExposure }
|
|
2293
2335
|
};
|
|
2294
2336
|
}
|
|
2295
2337
|
continue;
|
|
2296
2338
|
}
|
|
2297
2339
|
if (payload._tag === "ToolCallSettled" && payload.runId === runId) settledByCallId.set(payload.toolCallId, {
|
|
2340
|
+
recordId: envelope.record.recordId,
|
|
2341
|
+
toolName: payload.toolName,
|
|
2298
2342
|
result: payload.result,
|
|
2299
2343
|
isFailure: payload.isFailure,
|
|
2300
2344
|
...payload.budgetRejected === true ? { budgetRejected: true } : {},
|
|
@@ -2305,6 +2349,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2305
2349
|
const declared = yield* declaredToolCalls(lastResponse.messages);
|
|
2306
2350
|
if (declared.application.length === 0) return void 0;
|
|
2307
2351
|
const calls = declared.all;
|
|
2352
|
+
if (lastResponse.toolOperations !== void 0) {
|
|
2353
|
+
const identities = /* @__PURE__ */ new Set();
|
|
2354
|
+
for (const operation of lastResponse.toolOperations) {
|
|
2355
|
+
if (identities.has(operation.toolCallId) || !declared.application.some((call) => call.id === operation.toolCallId && call.name === operation.toolName)) return yield* RunJournalError.make({ message: "Operation evidence differs from the declared Tool batch" });
|
|
2356
|
+
identities.add(operation.toolCallId);
|
|
2357
|
+
}
|
|
2358
|
+
if (identities.size !== declared.application.length) return yield* RunJournalError.make({ message: "Declared Tool batch has incomplete operation evidence" });
|
|
2359
|
+
}
|
|
2360
|
+
for (const { record: { payload } } of records) {
|
|
2361
|
+
if (payload._tag !== "ToolCallPrepared" || payload.runId !== runId || payload.turn !== lastResponse.turn) continue;
|
|
2362
|
+
yield* validatePreparedCall(payload, declared, lastResponse.toolOperations);
|
|
2363
|
+
}
|
|
2308
2364
|
for (const part of declared.providerResults) {
|
|
2309
2365
|
const result = yield* decodePersisted(part.result).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
2310
2366
|
message: `Invalid canonical provider result ${part.id}`,
|
|
@@ -2319,10 +2375,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2319
2375
|
for (const call of calls) {
|
|
2320
2376
|
const recorded = settledByCallId.get(call.id);
|
|
2321
2377
|
if (call.providerExecuted === true && recorded === void 0) return yield* RunJournalError.make({ message: `Pending Turn lacks canonical provider result for ${call.id}` });
|
|
2322
|
-
if (recorded !== void 0)
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2378
|
+
if (recorded !== void 0) {
|
|
2379
|
+
const { recordId, toolName, ...result } = recorded;
|
|
2380
|
+
if (call.providerExecuted !== true && (toolName !== call.name || recordId !== toolCallSettledRecordId(runId, lastResponse.turn, Schema.decodeSync(ToolCallId)(call.id)))) return yield* RunJournalError.make({ message: `Settled Tool ${call.id} differs from its original declaration` });
|
|
2381
|
+
settled.push({
|
|
2382
|
+
id: call.id,
|
|
2383
|
+
...result
|
|
2384
|
+
});
|
|
2385
|
+
}
|
|
2326
2386
|
}
|
|
2327
2387
|
const completionCall = declared.application[0];
|
|
2328
2388
|
const projectSettledCompletion = declared.application.length === 1 && completionCall !== void 0 && completionTools.includes(completionCall.name) && settledByCallId.get(completionCall.id)?.isFailure === false && !records.some(({ record }) => record.payload._tag === "RunCompleted" && record.payload.runId === runId);
|
|
@@ -2336,6 +2396,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2336
2396
|
responseRecordId: modelResponseRecordId(runId, lastResponse.turn),
|
|
2337
2397
|
messages: lastResponse.messages,
|
|
2338
2398
|
toolParameterRejections: lastResponse.toolParameterRejections,
|
|
2399
|
+
toolOperations: lastResponse.toolOperations,
|
|
2339
2400
|
...lastResponse.toolExposure === void 0 ? {} : { toolExposure: lastResponse.toolExposure }
|
|
2340
2401
|
};
|
|
2341
2402
|
});
|
|
@@ -2396,9 +2457,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2396
2457
|
};
|
|
2397
2458
|
});
|
|
2398
2459
|
/**
|
|
2399
|
-
*
|
|
2400
|
-
*
|
|
2401
|
-
*
|
|
2460
|
+
* Existing ownership-free settlement protocol for queued aborts and joined outcomes.
|
|
2461
|
+
* Its canonical settlement reservation authorizes the append; it never authorizes tool
|
|
2462
|
+
* execution, unknown-resolution appends, or repair audits beside a live writer.
|
|
2402
2463
|
*/
|
|
2403
2464
|
const attemptContextAtTail = Effect.fn("DurableAgentRuntime.attemptContextAtTail")(function* (threadId) {
|
|
2404
2465
|
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
|
|
@@ -2542,16 +2603,28 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2542
2603
|
* `Uncertain` collects for Unknown marking, and a reconciler FAILURE is no proof at all (the
|
|
2543
2604
|
* call stays open and blocked without being marked). Nothing here ever executes a handler.
|
|
2544
2605
|
*/
|
|
2545
|
-
const reconcileOpenCalls = Effect.fn("DurableAgentRuntime.reconcileOpenCalls")(function* (ctx, submission, snapshot, records, openCalls, knownIds,
|
|
2606
|
+
const reconcileOpenCalls = Effect.fn("DurableAgentRuntime.reconcileOpenCalls")(function* (ctx, submission, snapshot, records, openCalls, knownIds, current) {
|
|
2546
2607
|
const submissionId = submission.submissionId;
|
|
2547
2608
|
const runId = runIdForSubmission(submissionId);
|
|
2609
|
+
yield* pendingToolBatchFor(records, runId);
|
|
2610
|
+
const operations = operationsFor(records, runId);
|
|
2548
2611
|
const preparedByCallId = /* @__PURE__ */ new Map();
|
|
2549
|
-
for (const
|
|
2550
|
-
const payload =
|
|
2551
|
-
if (payload._tag === "ToolCallPrepared" && payload.runId === runId)
|
|
2612
|
+
for (const { record } of records) {
|
|
2613
|
+
const payload = record.payload;
|
|
2614
|
+
if (payload._tag === "ToolCallPrepared" && payload.runId === runId && openCalls.some((call) => call.toolCallId === payload.toolCallId && call.turn === payload.turn)) {
|
|
2615
|
+
if (preparedByCallId.has(payload.toolCallId) || record.recordId !== toolCallPreparedRecordId(runId, payload.turn, payload.toolCallId)) return yield* RunJournalError.make({ message: `Prepared Tool ${payload.toolCallId} has inconsistent canonical identity` });
|
|
2616
|
+
preparedByCallId.set(payload.toolCallId, payload);
|
|
2617
|
+
}
|
|
2552
2618
|
}
|
|
2553
|
-
const
|
|
2554
|
-
|
|
2619
|
+
for (const call of openCalls) {
|
|
2620
|
+
const prepared = preparedByCallId.get(call.toolCallId);
|
|
2621
|
+
const responses = records.filter(({ record }) => record.payload._tag === "ModelResponseRecorded" && record.payload.runId === runId && record.payload.turn === call.turn);
|
|
2622
|
+
const response = responses[0]?.record;
|
|
2623
|
+
if (prepared === void 0 || prepared.turn !== call.turn || prepared.toolName !== call.toolName || responses.length !== 1 || response?.payload._tag !== "ModelResponseRecorded" || response.recordId !== modelResponseRecordId(runId, call.turn) || response.payload.turnId !== turnIdForRun(runId, call.turn)) return yield* RunJournalError.make({ message: `Prepared Tool ${call.toolCallId} has no unique original response` });
|
|
2624
|
+
if ((yield* withCrypto(digestJson(response.payload.messages))) !== response.payload.messagesDigest) return yield* RunJournalError.make({ message: `Original response for Tool ${call.toolCallId} has an invalid digest` });
|
|
2625
|
+
yield* validatePreparedCall(prepared, yield* declaredToolCalls(response.payload.messages), response.payload.toolOperations);
|
|
2626
|
+
}
|
|
2627
|
+
const intents = new Map(snapshot.unknownResolutions.map((intent) => [intent.toolCallId, intent]));
|
|
2555
2628
|
const review = {
|
|
2556
2629
|
uncertain: [],
|
|
2557
2630
|
unproven: [],
|
|
@@ -2560,7 +2633,39 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2560
2633
|
};
|
|
2561
2634
|
let recovered = 0;
|
|
2562
2635
|
for (const call of openCalls) {
|
|
2636
|
+
const prepared = preparedByCallId.get(call.toolCallId);
|
|
2637
|
+
const operation = operations.get(call.toolCallId);
|
|
2638
|
+
const supported = supportsOperation(submission, call, operation, prepared, current);
|
|
2563
2639
|
const intent = intents.get(call.toolCallId);
|
|
2640
|
+
const author = intent?.author ?? RECONCILER_AUTHOR;
|
|
2641
|
+
const reason = intent?.reason ?? "A registered reconciliation policy recovered the external outcome";
|
|
2642
|
+
const neverStarted = Effect.gen(function* () {
|
|
2643
|
+
if (current === void 0) {
|
|
2644
|
+
review.unproven.push(call);
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
if (supported) {
|
|
2648
|
+
review.retryable.push(call);
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
const unavailable = yield* Schema.encodeEffect(ToolUnavailable)(ToolUnavailable.make({
|
|
2652
|
+
toolName: call.toolName,
|
|
2653
|
+
execution: "not-executed",
|
|
2654
|
+
message: "This operation never started and its original implementation is unavailable. Continue with the current tools."
|
|
2655
|
+
})).pipe(Effect.flatMap(decodePersisted), Effect.orDie);
|
|
2656
|
+
yield* hit("tools:before-unavailable-append");
|
|
2657
|
+
yield* appendClosedCall(ctx, submissionId, knownIds, call, {
|
|
2658
|
+
result: {
|
|
2659
|
+
value: unavailable,
|
|
2660
|
+
isFailure: true
|
|
2661
|
+
},
|
|
2662
|
+
resolution: "never-started",
|
|
2663
|
+
author,
|
|
2664
|
+
reason
|
|
2665
|
+
});
|
|
2666
|
+
yield* hit("tools:after-unavailable-append");
|
|
2667
|
+
recovered += 1;
|
|
2668
|
+
});
|
|
2564
2669
|
if (intent !== void 0) switch (intent.resolution._tag) {
|
|
2565
2670
|
case "AbortSubmission": continue;
|
|
2566
2671
|
case "CompletedWithResult":
|
|
@@ -2570,33 +2675,29 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2570
2675
|
isFailure: intent.resolution.isFailure
|
|
2571
2676
|
},
|
|
2572
2677
|
resolution: intent.resolution.isFailure ? "failed-with-error" : "completed-with-result",
|
|
2573
|
-
author
|
|
2574
|
-
reason
|
|
2678
|
+
author,
|
|
2679
|
+
reason
|
|
2575
2680
|
});
|
|
2576
2681
|
recovered += 1;
|
|
2577
2682
|
continue;
|
|
2578
2683
|
case "NeverHappened":
|
|
2579
|
-
yield*
|
|
2580
|
-
resolution: "never-started",
|
|
2581
|
-
author: intent.author,
|
|
2582
|
-
reason: intent.reason
|
|
2583
|
-
});
|
|
2584
|
-
review.retryable.push(call);
|
|
2684
|
+
yield* neverStarted;
|
|
2585
2685
|
continue;
|
|
2586
2686
|
case "SafeToRetry":
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
reason: intent.reason
|
|
2591
|
-
});
|
|
2592
|
-
review.retryable.push(call);
|
|
2687
|
+
if (current === void 0) review.unproven.push(call);
|
|
2688
|
+
else if (supported) review.retryable.push(call);
|
|
2689
|
+
else review.uncertain.push(call);
|
|
2593
2690
|
continue;
|
|
2594
2691
|
}
|
|
2595
|
-
|
|
2692
|
+
const tool = current?.definition.toolkit.tools[call.toolName];
|
|
2693
|
+
if (current === void 0 && (operation?.executionClass ?? prepared?.executionClass) === "idempotent") {
|
|
2694
|
+
review.unproven.push(call);
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2697
|
+
if (supported && tool !== void 0 && getToolExecutionClass(tool) === "idempotent") {
|
|
2596
2698
|
review.retryable.push(call);
|
|
2597
2699
|
continue;
|
|
2598
2700
|
}
|
|
2599
|
-
const prepared = preparedByCallId.get(call.toolCallId);
|
|
2600
2701
|
if (prepared === void 0) {
|
|
2601
2702
|
review.unproven.push(call);
|
|
2602
2703
|
continue;
|
|
@@ -2609,7 +2710,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2609
2710
|
toolCallId: prepared.toolCallId,
|
|
2610
2711
|
toolName: prepared.toolName,
|
|
2611
2712
|
parameters: prepared.parameters,
|
|
2612
|
-
parametersDigest: prepared.parametersDigest
|
|
2713
|
+
parametersDigest: prepared.parametersDigest,
|
|
2714
|
+
...prepared.executionKind === void 0 ? {} : { executionKind: prepared.executionKind },
|
|
2715
|
+
...prepared.executionClass === void 0 ? {} : { executionClass: prepared.executionClass },
|
|
2716
|
+
...prepared.replay === void 0 ? {} : { replay: prepared.replay }
|
|
2613
2717
|
})).pipe(Effect.map(Option.some), Effect.catchTag("ToolReconcilerError", () => Effect.succeed(Option.none())));
|
|
2614
2718
|
if (Option.isNone(reconciled)) {
|
|
2615
2719
|
review.unproven.push(call);
|
|
@@ -2624,14 +2728,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2624
2728
|
isFailure: decision.isFailure
|
|
2625
2729
|
},
|
|
2626
2730
|
resolution: decision.isFailure ? "failed-with-error" : "completed-with-result",
|
|
2627
|
-
author
|
|
2628
|
-
reason
|
|
2731
|
+
author,
|
|
2732
|
+
reason
|
|
2629
2733
|
});
|
|
2630
2734
|
recovered += 1;
|
|
2631
2735
|
continue;
|
|
2632
2736
|
case "NeverStarted":
|
|
2737
|
+
yield* neverStarted;
|
|
2738
|
+
continue;
|
|
2633
2739
|
case "SafeToRetry":
|
|
2634
|
-
review.
|
|
2740
|
+
if (current === void 0) review.unproven.push(call);
|
|
2741
|
+
else if (supported) review.retryable.push(call);
|
|
2742
|
+
else review.uncertain.push(call);
|
|
2635
2743
|
continue;
|
|
2636
2744
|
case "Uncertain":
|
|
2637
2745
|
review.uncertain.push(call);
|
|
@@ -3172,11 +3280,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3172
3280
|
* Join a settled child after parent abort or expiry without running application handlers.
|
|
3173
3281
|
* The canonical child outcome stays intact; the parent result records why it cannot continue.
|
|
3174
3282
|
*/
|
|
3175
|
-
const
|
|
3283
|
+
const joinSettledChildWithoutHandler = Effect.fn("DurableAgentRuntime.joinSettledChildWithoutHandler")(function* (ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, reason) {
|
|
3176
3284
|
const runId = runIdForSubmission(parent.submissionId);
|
|
3177
3285
|
const request = subagent.requested.get(toolCallId);
|
|
3178
3286
|
if (request === void 0) return yield* LedgerError.make({
|
|
3179
|
-
operation: "
|
|
3287
|
+
operation: "joinSettledChildWithoutHandler",
|
|
3180
3288
|
message: `Tool Call ${toolCallId} has an attached child but no canonical SubagentRequested record`
|
|
3181
3289
|
});
|
|
3182
3290
|
const joinedRecordId = subagentJoinedRecordId(runId, toolCallId);
|
|
@@ -3186,16 +3294,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3186
3294
|
else {
|
|
3187
3295
|
const verification = yield* verifySettledChild(parent, request, childSubmissionId);
|
|
3188
3296
|
if (verification._tag === "mismatch") return yield* LedgerError.make({
|
|
3189
|
-
operation: "
|
|
3297
|
+
operation: "joinSettledChildWithoutHandler",
|
|
3190
3298
|
message: `Child Settlement verification failed for Tool Call ${toolCallId}: ${verification.message}`
|
|
3191
3299
|
});
|
|
3192
3300
|
const verified = verification.value;
|
|
3193
|
-
const boundedResult = yield* decodePersisted({
|
|
3301
|
+
const boundedResult = yield* decodePersisted(reason === "unavailable" ? {
|
|
3302
|
+
_tag: "ToolUnavailable",
|
|
3303
|
+
toolName: subagent.preparedNames.get(toolCallId) ?? request.delegationId,
|
|
3304
|
+
execution: "unavailable",
|
|
3305
|
+
message: `The original delegation implementation is unavailable. Its original child settled ${verified.outcome}; no replacement child was started.`
|
|
3306
|
+
} : {
|
|
3194
3307
|
errorTag: reason === "aborted" ? "SubagentParentAborted" : "SubagentParentDurationExceeded",
|
|
3195
3308
|
message: boundedText(`The parent Submission ${reason === "aborted" ? "aborted" : "exceeded its duration"}; attached child ${childSubmissionId} settled ${verified.outcome}`)
|
|
3196
3309
|
}).pipe(Effect.orDie);
|
|
3197
3310
|
finalAccounting = yield* decodePersisted({
|
|
3198
|
-
basis: reason
|
|
3311
|
+
basis: `${reason}-conservative`,
|
|
3199
3312
|
allocation: reservation.allocation
|
|
3200
3313
|
}).pipe(Effect.orDie);
|
|
3201
3314
|
const childRunId = runIdForSubmission(childSubmissionId);
|
|
@@ -3266,25 +3379,51 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3266
3379
|
return open;
|
|
3267
3380
|
});
|
|
3268
3381
|
/** Release unused reservations or repair and join existing children, never admit new work. */
|
|
3269
|
-
const
|
|
3382
|
+
const reconcileRetainedChildren = Effect.fn("DurableAgentRuntime.reconcileRetainedChildren")(function* (ctx, parent, ownershipToken, unavailableCalls) {
|
|
3270
3383
|
const snapshot = yield* ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId: parent.submissionId }));
|
|
3271
3384
|
const records = yield* readControl(parent.threadId, [parent.submissionId]);
|
|
3272
3385
|
const runId = runIdForSubmission(parent.submissionId);
|
|
3273
3386
|
const pending = yield* pendingToolBatchFor(records, runId);
|
|
3274
3387
|
const knownIds = knownRecordIdsOf(records);
|
|
3275
3388
|
const subagent = subagentRecordsOf(records, runId);
|
|
3389
|
+
const notExecuted = /* @__PURE__ */ new Set();
|
|
3390
|
+
const waiting = [];
|
|
3391
|
+
const indeterminate = /* @__PURE__ */ new Set();
|
|
3392
|
+
for (const toolCallId of unavailableCalls ?? []) {
|
|
3393
|
+
if (snapshot.childReservations.some((reservation) => reservation.parentToolCallId === toolCallId)) continue;
|
|
3394
|
+
if (subagent.requested.has(toolCallId) || subagent.started.has(toolCallId)) return yield* LedgerError.make({
|
|
3395
|
+
operation: "reconcileRetainedChildren",
|
|
3396
|
+
message: "Child request has no original reservation"
|
|
3397
|
+
});
|
|
3398
|
+
notExecuted.add(toolCallId);
|
|
3399
|
+
}
|
|
3276
3400
|
for (const reservation of snapshot.childReservations) {
|
|
3277
3401
|
const toolCallId = reservation.parentToolCallId;
|
|
3278
|
-
if (
|
|
3402
|
+
if (unavailableCalls !== void 0 && !unavailableCalls.has(toolCallId)) continue;
|
|
3403
|
+
const joined = subagent.joined.get(toolCallId);
|
|
3404
|
+
if (joined !== void 0) {
|
|
3405
|
+
yield* applyReservationRelease(reservation.reservationId, joined.finalAccounting);
|
|
3406
|
+
continue;
|
|
3407
|
+
}
|
|
3408
|
+
if (reservation.status !== "reserved") {
|
|
3409
|
+
if (reservation.accounting !== void 0) yield* applyReservationRelease(reservation.reservationId, reservation.accounting);
|
|
3410
|
+
if (reservation.childSubmissionId !== void 0 || subagent.started.has(toolCallId)) return yield* LedgerError.make({
|
|
3411
|
+
operation: "reconcileRetainedChildren",
|
|
3412
|
+
message: "Unjoined child reservation was already released"
|
|
3413
|
+
});
|
|
3414
|
+
notExecuted.add(toolCallId);
|
|
3415
|
+
continue;
|
|
3416
|
+
}
|
|
3279
3417
|
let started = subagent.started.get(toolCallId);
|
|
3280
3418
|
const request = subagent.requested.get(toolCallId);
|
|
3281
3419
|
if (started === void 0 && request === void 0 && reservation.childSubmissionId === void 0) {
|
|
3282
3420
|
yield* releaseOrphanReservation(reservation.reservationId);
|
|
3421
|
+
notExecuted.add(toolCallId);
|
|
3283
3422
|
continue;
|
|
3284
3423
|
}
|
|
3285
3424
|
const call = pending?.calls.find((candidate) => candidate.id === toolCallId);
|
|
3286
3425
|
if (pending === void 0 || call === void 0 || request === void 0 || subagent.preparedNames.get(toolCallId) !== call.name || request.delegationId !== call.name || reservation.parentSubmissionId !== parent.submissionId || request.reservationId !== reservation.reservationId || request.reservationDigest !== reservation.allocationDigest || request.turn !== pending.turn || request.turnId !== pending.turnId) return yield* LedgerError.make({
|
|
3287
|
-
operation: "
|
|
3426
|
+
operation: "reconcileRetainedChildren",
|
|
3288
3427
|
message: `Tool Call ${toolCallId} has inconsistent canonical child request evidence`
|
|
3289
3428
|
});
|
|
3290
3429
|
if (started === void 0) {
|
|
@@ -3293,17 +3432,26 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3293
3432
|
principal: decodePrincipalSync(request.childPrincipal),
|
|
3294
3433
|
idempotencyKey: decodeIdempotencyKeySync(request.childIdempotencyKey)
|
|
3295
3434
|
}));
|
|
3296
|
-
if (resolution._tag === "Indeterminate")
|
|
3435
|
+
if (resolution._tag === "Indeterminate") {
|
|
3436
|
+
indeterminate.add(toolCallId);
|
|
3437
|
+
continue;
|
|
3438
|
+
}
|
|
3297
3439
|
if (resolution._tag === "NotAdmitted") {
|
|
3298
3440
|
if (reservation.childSubmissionId !== void 0) return yield* LedgerError.make({
|
|
3299
|
-
operation: "
|
|
3441
|
+
operation: "reconcileRetainedChildren",
|
|
3300
3442
|
message: `Tool Call ${toolCallId} has an attachment but no admitted child`
|
|
3301
3443
|
});
|
|
3302
|
-
|
|
3444
|
+
if (unavailableCalls === void 0) {
|
|
3445
|
+
yield* releaseOrphanReservation(reservation.reservationId);
|
|
3446
|
+
notExecuted.add(toolCallId);
|
|
3447
|
+
continue;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
const admission = yield* establishChildFromRequest(parent, request, unavailableCalls !== void 0);
|
|
3451
|
+
if (admission._tag === "indeterminate") {
|
|
3452
|
+
indeterminate.add(toolCallId);
|
|
3303
3453
|
continue;
|
|
3304
3454
|
}
|
|
3305
|
-
const admission = yield* establishChildFromRequest(parent, request, false);
|
|
3306
|
-
if (admission._tag === "indeterminate") continue;
|
|
3307
3455
|
started = SubagentStarted.make({
|
|
3308
3456
|
runId,
|
|
3309
3457
|
toolCallId,
|
|
@@ -3325,7 +3473,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3325
3473
|
}
|
|
3326
3474
|
const child = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: started.childSubmissionId }));
|
|
3327
3475
|
if (Option.isNone(child) || reservation.childSubmissionId !== void 0 && reservation.childSubmissionId !== started.childSubmissionId || request.childThreadId !== started.childThreadId || started.childThreadId !== child.value.threadId || started.childReceiptId !== child.value.receiptId || started.childRunId !== runIdForSubmission(started.childSubmissionId)) return yield* LedgerError.make({
|
|
3328
|
-
operation: "
|
|
3476
|
+
operation: "reconcileRetainedChildren",
|
|
3329
3477
|
message: `Tool Call ${call.id} has inconsistent canonical child attachment evidence`
|
|
3330
3478
|
});
|
|
3331
3479
|
yield* ledger.attachChildToReservation(AttachChildToReservationRequest.make({
|
|
@@ -3333,9 +3481,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3333
3481
|
ownershipToken,
|
|
3334
3482
|
childSubmissionId: started.childSubmissionId
|
|
3335
3483
|
})).pipe(Effect.catchTag("ChildReservationConflict", conflictToLedgerError("attachChildToReservation")));
|
|
3336
|
-
if (child.value.state !== "settled")
|
|
3337
|
-
|
|
3484
|
+
if (child.value.state !== "settled") {
|
|
3485
|
+
waiting.push(WaitingChild.make({
|
|
3486
|
+
toolCallId,
|
|
3487
|
+
childSubmissionId: started.childSubmissionId
|
|
3488
|
+
}));
|
|
3489
|
+
yield* wake.notify(child.value.threadId);
|
|
3490
|
+
continue;
|
|
3491
|
+
}
|
|
3492
|
+
yield* joinSettledChildWithoutHandler(ctx, parent, knownIds, subagent, reservation, started.toolCallId, started.childSubmissionId, unavailableCalls === void 0 ? "duration" : "unavailable");
|
|
3338
3493
|
}
|
|
3494
|
+
return {
|
|
3495
|
+
notExecuted,
|
|
3496
|
+
waiting,
|
|
3497
|
+
indeterminate
|
|
3498
|
+
};
|
|
3339
3499
|
});
|
|
3340
3500
|
/**
|
|
3341
3501
|
* Request-abort-and-join over every attached child of an aborting parent (spec §13.1,
|
|
@@ -3389,7 +3549,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3389
3549
|
message: `Attached child ${childSubmissionId} is unknown to the ledger`
|
|
3390
3550
|
});
|
|
3391
3551
|
if (child.value.state === "settled") {
|
|
3392
|
-
yield*
|
|
3552
|
+
yield* joinSettledChildWithoutHandler(ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, "aborted");
|
|
3393
3553
|
continue;
|
|
3394
3554
|
}
|
|
3395
3555
|
yield* ledger.requestAbort(AbortCommand.make({
|
|
@@ -3617,14 +3777,20 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3617
3777
|
if (submission.parentLinkage !== void 0 && (childLineage?._tag !== "SubagentLineageRecorded" || childLineage.parentSubmissionId !== submission.parentLinkage.parentSubmissionId || childLineage.parentLink.parentToolCallId !== submission.parentLinkage.parentToolCallId || !definitionDigestsEqual(childLineage.childDefinitionDigests, submission.agentDigests) || childLineage.childInputDigest !== submission.inputDigest)) return yield* RunJournalError.make({ message: "The child Run has no matching canonical lineage and execution bounds" });
|
|
3618
3778
|
const pending = yield* pendingToolBatchFor(records, runId, agent.definition.completionFromTools?.map((declaration) => declaration.tool));
|
|
3619
3779
|
const resumeProjection = pending === void 0 ? journal : yield* projectRunJournalStream(withoutPendingBatch(canonical, pending, runId), runId, void 0, journalSeed);
|
|
3620
|
-
const
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3780
|
+
const currentContracts = yield* contractsFor(agent.definition);
|
|
3781
|
+
const rolloverOperation = journal.pendingContextToolCallId === void 0 ? void 0 : operationsFor(records, runId).get(journal.pendingContextToolCallId);
|
|
3782
|
+
const rolloverTool = rolloverOperation === void 0 ? void 0 : agent.definition.toolkit.tools[rolloverOperation.toolName];
|
|
3783
|
+
const pendingContextToolCallId = rolloverOperation !== void 0 && rolloverTool !== void 0 && Context.get(rolloverTool.annotations, ContextRolloverTool) && currentContracts[rolloverOperation.toolName] === rolloverOperation.replay ? journal.pendingContextToolCallId : void 0;
|
|
3784
|
+
const applicationCalls = pending?.calls.filter((call) => !call.providerExecuted);
|
|
3785
|
+
const completionCandidate = applicationCalls?.length === 1 && agent.definition.completionFromTools?.some((declaration) => declaration.tool === applicationCalls[0]?.name) && pending?.settled.some((result) => result.id === applicationCalls[0]?.id && !result.isFailure) ? applicationCalls[0] : void 0;
|
|
3786
|
+
const settledCompletion = pending !== void 0 && completionCandidate !== void 0 && supportsOperation(submission, OpenToolCallEvidence.make({
|
|
3787
|
+
toolCallId: Schema.decodeSync(ToolCallId)(completionCandidate.id),
|
|
3788
|
+
toolName: completionCandidate.name,
|
|
3789
|
+
turn: pending.turn
|
|
3790
|
+
}), pending.toolOperations?.find((operation) => operation.toolCallId === completionCandidate.id), void 0, {
|
|
3791
|
+
definition: agent.definition,
|
|
3792
|
+
contracts: currentContracts
|
|
3793
|
+
}) ? Schema.decodeSync(ToolCallId)(completionCandidate.id) : void 0;
|
|
3628
3794
|
const knownIds = knownRecordIdsOf(records);
|
|
3629
3795
|
const stepOutputs = /* @__PURE__ */ new Map();
|
|
3630
3796
|
for (const envelope of records) {
|
|
@@ -3744,51 +3910,12 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3744
3910
|
const response = [...records].reverse().find((envelope) => envelope.record.payload._tag === "ModelResponseRecorded" && envelope.record.payload.runId === runId)?.record.payload;
|
|
3745
3911
|
if (response?._tag !== "ModelResponseRecorded") return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without a response` });
|
|
3746
3912
|
const calls = (yield* declaredToolCalls(response.messages)).application;
|
|
3747
|
-
let expectedOutput;
|
|
3748
3913
|
if (calls.length > 0) {
|
|
3749
|
-
const completion = agent.definition.completion;
|
|
3750
3914
|
const call = calls[0];
|
|
3751
|
-
const
|
|
3752
|
-
|
|
3753
|
-
if (calls.length !== 1 || call === void 0 || call.name !== completion?.tool && actionCompletion === void 0 || settled?._tag !== "ToolCallSettled" || settled.toolName !== call.name || settled.isFailure) return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without one successful declared completion Tool result` });
|
|
3754
|
-
const projected = yield* (actionCompletion !== void 0 ? projectCompletionFromToolOutput(agent, actionCompletion, call.params, settled.result) : completion !== void 0 ? Effect.map(projectCompletionOutput(agent, completion, call.params, settled.result), Option.some) : Effect.succeed(Option.none())).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3755
|
-
message: `Run ${runId} completion Tool output failed recovery projection`,
|
|
3756
|
-
cause
|
|
3757
|
-
})));
|
|
3758
|
-
if (Option.isNone(projected)) return yield* RunJournalError.make({ message: `Run ${runId} completion Tool declined its recorded terminal output` });
|
|
3759
|
-
expectedOutput = projected.value;
|
|
3760
|
-
} else {
|
|
3761
|
-
const responseText = yield* terminalAssistantText(response.messages);
|
|
3762
|
-
expectedOutput = yield* decodeFinalOutput(agent, responseText).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3763
|
-
message: `Run ${runId} final response output failed recovery decoding`,
|
|
3764
|
-
cause
|
|
3765
|
-
})));
|
|
3766
|
-
}
|
|
3767
|
-
const boundedExpectedOutput = yield* decodePersisted(expectedOutput.encoded).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3768
|
-
message: `Run ${runId} reconstructed output exceeds canonical persistence bounds`,
|
|
3769
|
-
cause
|
|
3770
|
-
})));
|
|
3771
|
-
const [expectedOutputDigest, recordedOutputDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedOutput)), withCrypto(digestJson(recordedCompletion.output))]).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3772
|
-
message: `Run ${runId} completion output comparison failed`,
|
|
3773
|
-
cause
|
|
3774
|
-
})));
|
|
3775
|
-
if (expectedOutputDigest !== recordedOutputDigest) return yield* RunJournalError.make({ message: `Run ${runId} terminal completion output disagrees with its canonical response or Tool result` });
|
|
3776
|
-
const expectedRunDisposition = recordedCompletion.finishReason === "budget-exhausted" ? void 0 : yield* encodeRunDisposition(agent, expectedOutput.decoded).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3777
|
-
message: `Run ${runId} run disposition failed recovery projection`,
|
|
3778
|
-
cause
|
|
3779
|
-
})));
|
|
3780
|
-
if (expectedRunDisposition === void 0 !== (recordedCompletion.runDisposition === void 0)) return yield* RunJournalError.make({ message: `Run ${runId} terminal run disposition disagrees with its reconstructed output` });
|
|
3781
|
-
if (expectedRunDisposition !== void 0 && recordedCompletion.runDisposition !== void 0) {
|
|
3782
|
-
const boundedExpectedRunDisposition = yield* decodePersisted(expectedRunDisposition).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3783
|
-
message: `Run ${runId} reconstructed run disposition exceeds canonical persistence bounds`,
|
|
3784
|
-
cause
|
|
3785
|
-
})));
|
|
3786
|
-
const [expectedRunDispositionDigest, recordedRunDispositionDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedRunDisposition)), withCrypto(digestJson(recordedCompletion.runDisposition))]).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3787
|
-
message: `Run ${runId} run disposition comparison failed`,
|
|
3788
|
-
cause
|
|
3789
|
-
})));
|
|
3790
|
-
if (expectedRunDispositionDigest !== recordedRunDispositionDigest) return yield* RunJournalError.make({ message: `Run ${runId} terminal run disposition disagrees with its reconstructed output` });
|
|
3915
|
+
const settled = call === void 0 ? void 0 : records.find(({ record }) => record.payload._tag === "ToolCallSettled" && record.payload.runId === runId && record.payload.toolCallId === call.id)?.record.payload;
|
|
3916
|
+
if (calls.length !== 1 || call === void 0 || settled?._tag !== "ToolCallSettled" || settled.toolName !== call.name || settled.isFailure) return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without one successful declared Tool result` });
|
|
3791
3917
|
}
|
|
3918
|
+
if (recordedCompletion.resultDigest !== void 0 && recordedCompletion.resultDigest !== (yield* withCrypto(runCompletionDigest(recordedCompletion)))) return yield* RunJournalError.make({ message: "RunCompleted terminal values failed their integrity check" });
|
|
3792
3919
|
return {
|
|
3793
3920
|
_tag: "completed",
|
|
3794
3921
|
result: recordedCompletion.output,
|
|
@@ -3826,7 +3953,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3826
3953
|
cause
|
|
3827
3954
|
})));
|
|
3828
3955
|
const firstAssistant = pendingMessages.content.findIndex((message) => message.role === "assistant");
|
|
3829
|
-
if (firstAssistant > 0) resumeLeadingMessages = Prompt.fromMessages(pendingMessages.content.slice(0, firstAssistant));
|
|
3956
|
+
if (firstAssistant > 0) resumeLeadingMessages = Prompt.fromMessages(pendingMessages.content.slice(0, firstAssistant).filter((message) => message.role !== "system"));
|
|
3830
3957
|
if (firstAssistant >= 0) resumeResponseMessages = Prompt.fromMessages(pendingMessages.content.slice(firstAssistant));
|
|
3831
3958
|
}
|
|
3832
3959
|
let currentToolTurn = pending === void 0 ? void 0 : {
|
|
@@ -3863,7 +3990,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3863
3990
|
...state,
|
|
3864
3991
|
history
|
|
3865
3992
|
});
|
|
3866
|
-
const
|
|
3993
|
+
const instructionView = (messages, instructions, insertWhenAbsent) => {
|
|
3994
|
+
const projected = [];
|
|
3995
|
+
const lengths = [0];
|
|
3996
|
+
let inserted = false;
|
|
3997
|
+
for (const message of messages) {
|
|
3998
|
+
if (message.role === "system") {
|
|
3999
|
+
if (!inserted) projected.push(...instructions);
|
|
4000
|
+
inserted = true;
|
|
4001
|
+
} else projected.push(message);
|
|
4002
|
+
lengths.push(projected.length);
|
|
4003
|
+
}
|
|
4004
|
+
if (!inserted && insertWhenAbsent) projected.push(...instructions);
|
|
4005
|
+
return {
|
|
4006
|
+
messages: projected,
|
|
4007
|
+
prefixLength: (length) => lengths[length] ?? projected.length
|
|
4008
|
+
};
|
|
4009
|
+
};
|
|
4010
|
+
const resumeContext = { prepare: ({ source }) => Ref.get(stateRef).pipe(Effect.map((state) => ({ prompt: Prompt.fromMessages([...instructionView(resumeProjection.prompt.content, source.content.slice(0, state.baseLen).filter((message) => message.role === "system"), true).messages, ...source.content.slice(state.baseLen ?? source.content.length)]) }))) };
|
|
3867
4011
|
const externalContext = runContextPreparation.hook;
|
|
3868
4012
|
const preparedContext = externalContext === void 0 ? journal.committedTurns === 0 ? void 0 : resumeContext : { prepare: (request) => (journal.committedTurns === 0 ? Effect.succeed({ prompt: request.source }) : resumeContext.prepare(request)).pipe(Effect.flatMap(({ prompt }) => externalContext.prepare({
|
|
3869
4013
|
...request,
|
|
@@ -3908,6 +4052,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3908
4052
|
const createdAt = yield* nowUtc;
|
|
3909
4053
|
const batch = yield* withCrypto(turnResponseBatch({
|
|
3910
4054
|
toolExposure: commit.toolExposure,
|
|
4055
|
+
toolOperations: commit.calls.map((call) => ToolOperation.make({
|
|
4056
|
+
toolCallId: call.toolCallId,
|
|
4057
|
+
toolName: call.toolName,
|
|
4058
|
+
executionClass: call.executionClass,
|
|
4059
|
+
executionKind: call.executionKind,
|
|
4060
|
+
replay: currentContracts[call.toolName]
|
|
4061
|
+
})),
|
|
3911
4062
|
toolParameterRejections: commit.toolParameterRejections,
|
|
3912
4063
|
runId,
|
|
3913
4064
|
turn: canonicalTurn,
|
|
@@ -3946,7 +4097,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3946
4097
|
toolName: call.toolName,
|
|
3947
4098
|
parameters,
|
|
3948
4099
|
parametersDigest,
|
|
3949
|
-
executionKind: call.executionKind
|
|
4100
|
+
executionKind: call.executionKind,
|
|
4101
|
+
executionClass: call.executionClass,
|
|
4102
|
+
replay: currentContracts[call.toolName]
|
|
3950
4103
|
})));
|
|
3951
4104
|
}
|
|
3952
4105
|
const head = preparedRecords[0];
|
|
@@ -4014,14 +4167,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4014
4167
|
}
|
|
4015
4168
|
const coverable = sourceBoundaries.filter((boundary) => commit.kind !== "summarize" || ownerFirstSequence === void 0 || boundary.sequence < ownerFirstSequence);
|
|
4016
4169
|
if (coverable.length === 0) return yield* CompactionError.make({ message: commit.kind === "rollover" ? "Durable rollover requires complete canonical records" : "Durable compaction requires eligible canonical records" });
|
|
4170
|
+
const state = yield* Ref.get(stateRef);
|
|
4171
|
+
const comparisonView = instructionView(sourceJournal.prompt.content, state.history?.content.slice(0, state.baseLen).filter((message) => message.role === "system") ?? [], false);
|
|
4017
4172
|
let matchingPrefix = 0;
|
|
4018
4173
|
let requiredThrough = 0;
|
|
4019
|
-
const comparisonLimit = Math.min(
|
|
4174
|
+
const comparisonLimit = Math.min(comparisonView.messages.length, commit.through);
|
|
4020
4175
|
for (let index = 0; index < commit.through; index += 1) {
|
|
4021
4176
|
const message = commit.source.content[index];
|
|
4022
4177
|
if (message !== void 0 && (commit.kind === "clear-tool-results" ? message.role === "tool" : message.role !== "system")) requiredThrough = index + 1;
|
|
4023
4178
|
if (index >= comparisonLimit || index !== matchingPrefix || message === void 0) continue;
|
|
4024
|
-
const canonicalMessage =
|
|
4179
|
+
const canonicalMessage = comparisonView.messages[index];
|
|
4025
4180
|
if (canonicalMessage === void 0) continue;
|
|
4026
4181
|
const encode = (entry) => Schema.encodeEffect(Prompt.Message)(entry).pipe(Effect.mapError((cause) => CompactionError.make({
|
|
4027
4182
|
message: "Could not encode the canonical compaction prefix",
|
|
@@ -4037,7 +4192,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4037
4192
|
if (candidate === void 0) continue;
|
|
4038
4193
|
const following = coverable[index + 1];
|
|
4039
4194
|
if (following !== void 0 && following.tag !== "ModelResponseRecorded") continue;
|
|
4040
|
-
const length = candidate.promptLength;
|
|
4195
|
+
const length = comparisonView.prefixLength(candidate.promptLength);
|
|
4041
4196
|
if (length === 0 || length > commit.through) continue;
|
|
4042
4197
|
if (length < requiredThrough || length > matchingPrefix) continue;
|
|
4043
4198
|
lastCovered = candidate;
|
|
@@ -4580,6 +4735,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4580
4735
|
history: pending === void 0 ? journal.historyBefore : resumeProjection.historyBefore,
|
|
4581
4736
|
onHistory,
|
|
4582
4737
|
input,
|
|
4738
|
+
...journal.committedTurns > 0 || Schema.is(FrameworkMessage)(submission.messageAdmission) ? { retainedInput: submission.inputPayload } : {},
|
|
4583
4739
|
...Schema.is(FrameworkMessage)(submission.messageAdmission) ? { frameworkMessage: submission.messageAdmission } : {},
|
|
4584
4740
|
approval,
|
|
4585
4741
|
toolAuthorization,
|
|
@@ -4595,12 +4751,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4595
4751
|
...submission.workerAdmission === void 0 ? {} : { toolCallAllowance: submission.workerAdmission.origin.toolCallAllowance },
|
|
4596
4752
|
...submission.parentLinkage !== void 0 && childLineage?._tag === "SubagentLineageRecorded" ? { toolCallAllowance: childLineage.toolCallAllowance } : {},
|
|
4597
4753
|
...pending === void 0 ? {} : { resume: {
|
|
4754
|
+
...settledCompletion === void 0 ? {} : { settledCompletion },
|
|
4598
4755
|
turn: pending.turn,
|
|
4599
4756
|
turnId: pending.turnId,
|
|
4600
4757
|
calls: pending.calls,
|
|
4601
4758
|
toolParameterRejections: pending.toolParameterRejections,
|
|
4602
4759
|
settled: pending.settled,
|
|
4603
|
-
|
|
4760
|
+
toolExposure: pending.toolExposure ?? Snapshot.make({ exposedToolNames: [...new Set(pending.calls.map((call) => call.name))] }),
|
|
4604
4761
|
...resumeLeadingMessages === void 0 ? {} : { leadingMessages: resumeLeadingMessages },
|
|
4605
4762
|
...resumeResponseMessages === void 0 ? {} : { responseMessages: resumeResponseMessages }
|
|
4606
4763
|
} },
|
|
@@ -4622,7 +4779,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4622
4779
|
},
|
|
4623
4780
|
...journal.contextWindowId === void 0 ? {} : { initialContextWindowId: journal.contextWindowId },
|
|
4624
4781
|
...journal.protectedContext === void 0 ? {} : { protectedContext: journal.protectedContext },
|
|
4625
|
-
...
|
|
4782
|
+
...pendingContextToolCallId === void 0 ? {} : { pendingContextToolCallId }
|
|
4626
4783
|
};
|
|
4627
4784
|
const commitPendingTurn = Effect.gen(function* () {
|
|
4628
4785
|
const state = yield* Ref.get(stateRef);
|
|
@@ -4668,7 +4825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4668
4825
|
const completion = agent.definition.completion;
|
|
4669
4826
|
const completionPart = resultParts[0];
|
|
4670
4827
|
const runCompletion = resultParts.length === 1 && (completionPart?.name === completion?.tool || agent.definition.completionFromTools?.some((declaration) => declaration.tool === completionPart?.name)) && completionPart.isFailure !== true && completedRun !== void 0 ? completedRun : void 0;
|
|
4671
|
-
const batch = yield* turnResultsBatch({
|
|
4828
|
+
const batch = yield* withCrypto(turnResultsBatch({
|
|
4672
4829
|
toolSelections: stagedToolSelections,
|
|
4673
4830
|
budgetRejectedCalls,
|
|
4674
4831
|
runId,
|
|
@@ -4679,7 +4836,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4679
4836
|
deploymentId: config.deploymentId,
|
|
4680
4837
|
createdAt,
|
|
4681
4838
|
...runCompletion === void 0 ? {} : { runCompletion }
|
|
4682
|
-
});
|
|
4839
|
+
}));
|
|
4683
4840
|
let pendingBatch = batch;
|
|
4684
4841
|
if (settledCompletionPart !== void 0) {
|
|
4685
4842
|
const completionRecord = batch.records.find((record) => record.payload._tag === "RunCompleted");
|
|
@@ -4950,7 +5107,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4950
5107
|
const ctx = yield* attemptContextFor(threadId, claim.producerEpoch);
|
|
4951
5108
|
let controlThrough = (yield* store.inspectTail(ThreadTailRequest.make({ threadId }))).tailSequence;
|
|
4952
5109
|
const initialThrough = controlThrough;
|
|
4953
|
-
const initialView = yield* recoveryView(threadId, initialThrough, [submissionId]);
|
|
5110
|
+
const initialView = yield* recoveryView(threadId, initialThrough, [submissionId], runIdForSubmission(submissionId));
|
|
4954
5111
|
let journalMetadata = initialView.seed === void 0 ? makeJournalMetadata(runIdForSubmission(submissionId)) : void 0;
|
|
4955
5112
|
const retainControl = controlRecords([submissionId]);
|
|
4956
5113
|
const collectControl = (record) => {
|
|
@@ -5009,7 +5166,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5009
5166
|
};
|
|
5010
5167
|
let expiredChildObligation = false;
|
|
5011
5168
|
if ((yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(runTiming.deadline)) {
|
|
5012
|
-
yield*
|
|
5169
|
+
yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
|
|
5013
5170
|
expiredChildObligation = yield* completeJoinedReleases(submission);
|
|
5014
5171
|
}
|
|
5015
5172
|
const ordinaryCallsResolved = Effect.gen(function* () {
|
|
@@ -5018,10 +5175,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5018
5175
|
const reconciliationEvidence = yield* evidenceFor(reconciliationRecords, submissionId, true, reconciliationSnapshot.hostSubmissionId);
|
|
5019
5176
|
if (reconciliationEvidence.openToolCalls.length === 0) return true;
|
|
5020
5177
|
for (const envelope of reconciliationRecords) knownIds.add(envelope.record.recordId);
|
|
5021
|
-
const
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
return tool === void 0 ? void 0 : getToolExecutionClass(tool);
|
|
5178
|
+
const review = yield* reconcileOpenCalls(ctx, submission, reconciliationSnapshot, reconciliationRecords, reconciliationEvidence.openToolCalls, knownIds, {
|
|
5179
|
+
definition: agent.definition,
|
|
5180
|
+
contracts: yield* contractsFor(agent.definition)
|
|
5025
5181
|
});
|
|
5026
5182
|
if (review.uncertain.length > 0 || review.unproven.length > 0) {
|
|
5027
5183
|
if (review.uncertain.length > 0) yield* markCallsUnknown(ctx, submissionId, knownIds, review.uncertain, "An ordinary Tool call may have executed without a canonical outcome");
|
|
@@ -5035,6 +5191,74 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5035
5191
|
return true;
|
|
5036
5192
|
});
|
|
5037
5193
|
if (!expiredChildObligation && !(yield* ordinaryCallsResolved)) return Option.none();
|
|
5194
|
+
const continuationRecords = yield* refreshControl();
|
|
5195
|
+
const continuation = yield* pendingToolBatchFor(continuationRecords, runIdForSubmission(submissionId));
|
|
5196
|
+
if (continuation !== void 0) {
|
|
5197
|
+
const current = {
|
|
5198
|
+
definition: agent.definition,
|
|
5199
|
+
contracts: yield* contractsFor(agent.definition)
|
|
5200
|
+
};
|
|
5201
|
+
const operations = operationsFor(continuationRecords, runIdForSubmission(submissionId));
|
|
5202
|
+
const settled = new Set(continuation.settled.map((call) => call.id));
|
|
5203
|
+
const rejected = new Set(continuation.toolParameterRejections?.map((call) => call.toolCallId));
|
|
5204
|
+
const unsupported = [];
|
|
5205
|
+
const unavailableDelegations = /* @__PURE__ */ new Set();
|
|
5206
|
+
for (const call of continuation.calls) {
|
|
5207
|
+
const original = continuationRecords.map(({ record }) => record.payload).find((payload) => payload._tag === "ToolCallPrepared" && payload.runId === runIdForSubmission(submissionId) && payload.toolCallId === call.id);
|
|
5208
|
+
if (original?._tag === "ToolCallPrepared" && original.executionKind === "delegation" && !settled.has(call.id) && !supportsOperation(submission, OpenToolCallEvidence.make(original), operations.get(call.id), original, current)) unavailableDelegations.add(original.toolCallId);
|
|
5209
|
+
}
|
|
5210
|
+
const children = unavailableDelegations.size === 0 ? void 0 : yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef), unavailableDelegations);
|
|
5211
|
+
for (const envelope of yield* refreshControl()) knownIds.add(envelope.record.recordId);
|
|
5212
|
+
for (const call of continuation.calls) {
|
|
5213
|
+
if (call.providerExecuted || settled.has(call.id) || rejected.has(Schema.decodeSync(ToolCallId)(call.id))) continue;
|
|
5214
|
+
const evidence = OpenToolCallEvidence.make({
|
|
5215
|
+
toolCallId: Schema.decodeSync(ToolCallId)(call.id),
|
|
5216
|
+
toolName: call.name,
|
|
5217
|
+
turn: continuation.turn
|
|
5218
|
+
});
|
|
5219
|
+
const operation = operations.get(call.id);
|
|
5220
|
+
const prepared = continuationRecords.map(({ record }) => record.payload).find((payload) => payload._tag === "ToolCallPrepared" && payload.runId === runIdForSubmission(submissionId) && payload.turn === continuation.turn && payload.toolCallId === call.id);
|
|
5221
|
+
const preparation = prepared?._tag === "ToolCallPrepared" ? prepared : void 0;
|
|
5222
|
+
if (supportsOperation(submission, evidence, operation, preparation, current)) continue;
|
|
5223
|
+
if (preparation !== void 0 && !children?.notExecuted.has(evidence.toolCallId)) {
|
|
5224
|
+
if (!unavailableDelegations.has(evidence.toolCallId)) unsupported.push(evidence);
|
|
5225
|
+
continue;
|
|
5226
|
+
}
|
|
5227
|
+
const execution = children?.notExecuted.has(evidence.toolCallId) || operation !== void 0 && (operation.executionClass !== "readonly" || operation.executionKind !== "ordinary") ? "not-executed" : "unavailable";
|
|
5228
|
+
const result = yield* Schema.encodeEffect(ToolUnavailable)(ToolUnavailable.make({
|
|
5229
|
+
toolName: call.name,
|
|
5230
|
+
execution,
|
|
5231
|
+
message: execution === "not-executed" ? "This operation was not executed and its original implementation is unavailable. Continue with the current tools." : "The original operation is unavailable. No mutating handler was dispatched; readonly work may have run without a recorded result."
|
|
5232
|
+
})).pipe(Effect.flatMap(decodePersisted), Effect.orDie);
|
|
5233
|
+
yield* hit("tools:before-unavailable-append");
|
|
5234
|
+
yield* appendClosedCall(ctx, submissionId, knownIds, evidence, {
|
|
5235
|
+
result: {
|
|
5236
|
+
value: result,
|
|
5237
|
+
isFailure: true
|
|
5238
|
+
},
|
|
5239
|
+
resolution: execution === "not-executed" ? "never-started" : "failed-with-error",
|
|
5240
|
+
author: "runtime",
|
|
5241
|
+
reason: "The declared operation cannot execute under its original contract"
|
|
5242
|
+
});
|
|
5243
|
+
yield* hit("tools:after-unavailable-append");
|
|
5244
|
+
}
|
|
5245
|
+
const firstWaiting = children?.waiting[0];
|
|
5246
|
+
if (children !== void 0 && firstWaiting !== void 0) {
|
|
5247
|
+
const disposition = yield* ledger.suspend(SuspendRequest.make({
|
|
5248
|
+
submissionId,
|
|
5249
|
+
ownershipToken: yield* Ref.get(tokenRef),
|
|
5250
|
+
reason: WaitingForChildSuspension.make({ children: [firstWaiting, ...children.waiting.slice(1)] })
|
|
5251
|
+
}));
|
|
5252
|
+
yield* hit("subagent:after-suspend");
|
|
5253
|
+
if (disposition === "resume-immediately") yield* wake.notify(threadId);
|
|
5254
|
+
return Option.none();
|
|
5255
|
+
}
|
|
5256
|
+
if ((children?.indeterminate.size ?? 0) > 0) return Option.none();
|
|
5257
|
+
if (unsupported.length > 0) {
|
|
5258
|
+
yield* markCallsUnknown(ctx, submissionId, knownIds, unsupported, "The unfinished operation has no supported recovery implementation; its original identity and evidence remain unresolved");
|
|
5259
|
+
return Option.none();
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5038
5262
|
const lineage = {
|
|
5039
5263
|
attemptId: claim.attemptId,
|
|
5040
5264
|
supersededEpoch,
|
|
@@ -5103,7 +5327,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5103
5327
|
}));
|
|
5104
5328
|
yield* hit("run:after-duration-append");
|
|
5105
5329
|
}
|
|
5106
|
-
yield*
|
|
5330
|
+
yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
|
|
5107
5331
|
}
|
|
5108
5332
|
if (yield* completeJoinedReleases(submission)) {
|
|
5109
5333
|
if (outcome._tag === "failed") {
|
|
@@ -5172,24 +5396,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5172
5396
|
const resolution = yield* resolve(submission).pipe(Effect.map((binding) => ({
|
|
5173
5397
|
_tag: "resolved",
|
|
5174
5398
|
binding
|
|
5175
|
-
})), Effect.catchTags({
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
}),
|
|
5180
|
-
BindingDigestMismatch: (failure) => Effect.succeed({
|
|
5181
|
-
_tag: "refused",
|
|
5182
|
-
failure
|
|
5183
|
-
})
|
|
5184
|
-
}));
|
|
5399
|
+
})), Effect.catchTags({ BindingUnavailable: (failure) => Effect.succeed({
|
|
5400
|
+
_tag: "refused",
|
|
5401
|
+
failure
|
|
5402
|
+
}) }));
|
|
5185
5403
|
if (resolution._tag === "refused") return yield* resolution.failure;
|
|
5186
5404
|
return yield* resolution.binding.attempt((agent, attemptThreadId, attemptClaim) => runAttempt(agent, attemptThreadId, attemptClaim, tokenRef, options?.yieldAfter), threadId, claim);
|
|
5187
5405
|
})).pipe(withThreadHeadSpan);
|
|
5406
|
+
const eligibleThreadHead = (threadId) => Stream.runHead(ledger.scanNonterminal.pipe(Stream.filter((entry) => entry.threadId === threadId), Stream.filterEffect((entry) => entry.state !== "unknown" ? Effect.succeed(true) : ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId: entry.submissionId })).pipe(Effect.map((snapshot) => snapshot.abortIntent !== void 0)))));
|
|
5188
5407
|
const drainThread = (resolve, threadId) => Effect.gen(function* () {
|
|
5189
5408
|
const settlements = [];
|
|
5190
5409
|
while (true) {
|
|
5410
|
+
const before = yield* eligibleThreadHead(threadId);
|
|
5191
5411
|
const settlement = yield* processThreadHead(resolve, threadId);
|
|
5192
|
-
if (Option.isNone(settlement))
|
|
5412
|
+
if (Option.isNone(settlement)) {
|
|
5413
|
+
const after = yield* eligibleThreadHead(threadId);
|
|
5414
|
+
if (Option.isSome(before) && Option.isSome(after) && before.value.submissionId !== after.value.submissionId) continue;
|
|
5415
|
+
return settlements;
|
|
5416
|
+
}
|
|
5193
5417
|
settlements.push(settlement.value);
|
|
5194
5418
|
}
|
|
5195
5419
|
});
|
|
@@ -5200,25 +5424,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5200
5424
|
message: `This worker is bound to Agent ${binding.agentId}; the claimed head belongs to Agent ${submission.agentId} (SUB-023)`
|
|
5201
5425
|
})), threadId);
|
|
5202
5426
|
});
|
|
5203
|
-
const
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
const
|
|
5208
|
-
if (candidate === void 0 || candidates.length !== 1) return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests);
|
|
5209
|
-
if (bindingSupports(candidate, submission.agentDigests, void 0, submission.inputPayload)) return candidate;
|
|
5210
|
-
const records = yield* Effect.gen(function* () {
|
|
5211
|
-
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId: submission.threadId }));
|
|
5212
|
-
return yield* Stream.runCollect(canonicalRange(submission.threadId, tail.tailSequence).pipe(Stream.filter(controlRecords([submission.submissionId]))));
|
|
5213
|
-
}).pipe(Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed([])));
|
|
5214
|
-
const completionTools = candidate === void 0 ? [] : [...candidate.definition.completion === void 0 ? [] : [candidate.definition.completion.tool], ...candidate.definition.completionFromTools?.map((entry) => entry.tool) ?? []];
|
|
5215
|
-
const pending = yield* pendingToolBatchFor(records, runIdForSubmission(submission.submissionId), completionTools);
|
|
5216
|
-
return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests, pending?.calls.filter((call) => call.providerExecuted !== true || completionTools.includes(call.name)).map((call) => call.name) ?? [], submission.inputPayload);
|
|
5217
|
-
});
|
|
5218
|
-
const processThreadResolvedImpl = (threadId) => drainThread(resolveRetainedBinding, threadId);
|
|
5219
|
-
const processThreadHeadImpl = (threadId, options) => processThreadHead(resolveRetainedBinding, threadId, options);
|
|
5220
|
-
const claimFor = Effect.fn("DurableAgentRuntime.claimFor")(function* (submission) {
|
|
5221
|
-
const head = yield* Stream.runHead(ledger.scanNonterminal.pipe(Stream.filter((entry) => entry.threadId === submission.threadId)));
|
|
5427
|
+
const resolveCurrentBinding = (submission) => resolveWorkerBinding(registeredBindings, submission.agentId);
|
|
5428
|
+
const processThreadResolvedImpl = (threadId) => drainThread(resolveCurrentBinding, threadId);
|
|
5429
|
+
const processThreadHeadImpl = (threadId, options) => processThreadHead(resolveCurrentBinding, threadId, options);
|
|
5430
|
+
const claimFor = Effect.fn("DurableAgentRuntime.claimFor")(function* (submission, decision) {
|
|
5431
|
+
const head = yield* eligibleThreadHead(submission.threadId);
|
|
5222
5432
|
if (Option.isNone(head) || head.value.submissionId !== submission.submissionId) return Option.none();
|
|
5223
5433
|
const claimed = yield* acquireClaim(submission.threadId);
|
|
5224
5434
|
if (Option.isNone(claimed)) return Option.none();
|
|
@@ -5229,35 +5439,40 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5229
5439
|
})).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
|
|
5230
5440
|
return Option.none();
|
|
5231
5441
|
}
|
|
5442
|
+
yield* annotateRepair(submission.threadId, decision, claimed.value.claim.producerEpoch);
|
|
5232
5443
|
return Option.some(claimed.value.claim);
|
|
5233
5444
|
});
|
|
5234
5445
|
/**
|
|
5235
|
-
* DUR-013 audit
|
|
5236
|
-
*
|
|
5237
|
-
*
|
|
5446
|
+
* Best-effort DUR-013 audit of an owned repair attempt, before it can release its claim.
|
|
5447
|
+
* The fixed granted epoch never borrows a later writer's authority. State-only repairs
|
|
5448
|
+
* omit canonical annotations; their decision and operator author/reason remain in telemetry.
|
|
5238
5449
|
*/
|
|
5239
|
-
const annotateRepair = (threadId,
|
|
5450
|
+
const annotateRepair = (threadId, decision, producerEpoch) => Effect.gen(function* () {
|
|
5451
|
+
yield* store.materialize(ThreadMaterialization.make({
|
|
5452
|
+
threadId,
|
|
5453
|
+
producerEpoch
|
|
5454
|
+
}));
|
|
5455
|
+
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
|
|
5456
|
+
if (tail.tailSequence === ZERO_SEQUENCE) return;
|
|
5240
5457
|
const encodedDecision = yield* Schema.encodeEffect(RecoveryDecision)(decision).pipe(Effect.orDie);
|
|
5241
5458
|
const details = yield* Schema.decodeEffect(PersistedJson)(encodedDecision).pipe(Effect.orDie);
|
|
5242
|
-
const envelope = yield* makeEnvelope(recoveryRepairRecordId(submissionId, decision._tag), RepairAnnotated.make({
|
|
5459
|
+
const envelope = yield* makeEnvelope(recoveryRepairRecordId(decision.submissionId, decision._tag), RepairAnnotated.make({
|
|
5243
5460
|
reason: `recovery:${decision._tag}`,
|
|
5244
5461
|
details
|
|
5245
5462
|
}));
|
|
5246
|
-
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
|
|
5247
5463
|
yield* store.append(FencedAppendRequest.make({
|
|
5248
5464
|
threadId,
|
|
5249
5465
|
batch: CanonicalBatch.make({
|
|
5250
|
-
batchId: recoveryRepairBatchId(submissionId, decision._tag),
|
|
5466
|
+
batchId: recoveryRepairBatchId(decision.submissionId, decision._tag),
|
|
5251
5467
|
producerId: config.producerId,
|
|
5252
5468
|
records: [envelope]
|
|
5253
5469
|
}),
|
|
5254
5470
|
expectedTailSequence: tail.tailSequence,
|
|
5255
5471
|
expectedTailDigest: tail.tailDigest,
|
|
5256
|
-
producerEpoch
|
|
5472
|
+
producerEpoch
|
|
5257
5473
|
}));
|
|
5258
|
-
yield* wake.notify(threadId);
|
|
5259
5474
|
}).pipe(Effect.ignore);
|
|
5260
|
-
const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records,
|
|
5475
|
+
const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records, decision) {
|
|
5261
5476
|
const intent = snapshot.abortIntent;
|
|
5262
5477
|
if (intent === void 0) return "deferred";
|
|
5263
5478
|
const submission = snapshot.submission;
|
|
@@ -5284,7 +5499,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5284
5499
|
}
|
|
5285
5500
|
}
|
|
5286
5501
|
}
|
|
5287
|
-
const claimed = yield* claimFor(submission);
|
|
5502
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5288
5503
|
if (Option.isNone(claimed)) {
|
|
5289
5504
|
if (submission.state === "ready" && snapshot.ownership === void 0) return yield* Effect.gen(function* () {
|
|
5290
5505
|
yield* materializeAtLeast(submission.threadId, ZERO_EPOCH);
|
|
@@ -5312,15 +5527,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5312
5527
|
return "repaired";
|
|
5313
5528
|
});
|
|
5314
5529
|
/**
|
|
5315
|
-
* Execute the reconcile-then-mark flow for a `MarkUnknown` decision (plan §2.2).
|
|
5316
|
-
*
|
|
5317
|
-
*
|
|
5318
|
-
*
|
|
5319
|
-
*
|
|
5530
|
+
* Execute the reconcile-then-mark flow for a `MarkUnknown` decision (plan §2.2). Current
|
|
5531
|
+
* registration metadata checks retry support when available. Low-level drivers supply their
|
|
5532
|
+
* Agent at claim time, so retry proofs defer that check without permitting execution. Calls
|
|
5533
|
+
* without a durable intent, original idempotent declaration, or reconciler proof stay uncertain.
|
|
5534
|
+
* Closed calls report `repaired`; deferred checks report `deferred`; uncertain calls become Unknown.
|
|
5320
5535
|
*/
|
|
5321
5536
|
const markUnknownForRecovery = Effect.fn("DurableAgentRuntime.markUnknownForRecovery")(function* (snapshot, evidence, records, decision) {
|
|
5322
5537
|
const submission = snapshot.submission;
|
|
5323
|
-
const claimed = yield* claimFor(submission);
|
|
5538
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5324
5539
|
if (Option.isNone(claimed)) return "deferred";
|
|
5325
5540
|
const claim = claimed.value;
|
|
5326
5541
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5330,7 +5545,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5330
5545
|
const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
|
|
5331
5546
|
const knownIds = knownRecordIdsOf(records);
|
|
5332
5547
|
const markableIds = new Set(decision.openToolCallIds);
|
|
5333
|
-
const review = yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls.filter((call) => markableIds.has(call.toolCallId)), knownIds, ()
|
|
5548
|
+
const review = yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls.filter((call) => markableIds.has(call.toolCallId)), knownIds, yield* currentOperationsFor(submission.agentId));
|
|
5334
5549
|
let disposition;
|
|
5335
5550
|
if (review.uncertain.length > 0) {
|
|
5336
5551
|
yield* markCallsUnknown(ctx, submission.submissionId, knownIds, review.uncertain, decision.reason);
|
|
@@ -5343,17 +5558,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5343
5558
|
})).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
|
|
5344
5559
|
return disposition;
|
|
5345
5560
|
});
|
|
5561
|
+
/** A retry intent permits only the saved operation under its supported current contract. */
|
|
5562
|
+
const hasUnsupportedRetry = Effect.fnUntraced(function* (snapshot, records) {
|
|
5563
|
+
if (!snapshot.unknownResolutions.some((intent) => intent.resolution._tag === "SafeToRetry")) return false;
|
|
5564
|
+
const submission = snapshot.submission;
|
|
5565
|
+
const current = yield* currentOperationsFor(submission.agentId);
|
|
5566
|
+
const operations = operationsFor(records, runIdForSubmission(submission.submissionId));
|
|
5567
|
+
for (const intent of snapshot.unknownResolutions) if (intent.resolution._tag === "SafeToRetry") {
|
|
5568
|
+
const prepared = records.map(({ record }) => record.payload).find((payload) => payload._tag === "ToolCallPrepared" && payload.runId === runIdForSubmission(submission.submissionId) && payload.toolCallId === intent.toolCallId);
|
|
5569
|
+
if (prepared?._tag !== "ToolCallPrepared" || current !== void 0 && !supportsOperation(submission, OpenToolCallEvidence.make(prepared), operations.get(intent.toolCallId), prepared, current)) return true;
|
|
5570
|
+
}
|
|
5571
|
+
return false;
|
|
5572
|
+
});
|
|
5346
5573
|
/**
|
|
5347
|
-
*
|
|
5348
|
-
*
|
|
5349
|
-
* exist); the wake itself is the ledger's idempotent re-check on intent replay (WP2), so a
|
|
5350
|
-
* crash anywhere in this pass converges on the next one.
|
|
5574
|
+
* Replay covering resolution intents as state-only wakes. Canonical outcomes wait for an
|
|
5575
|
+
* actual claim, so resolving an older request cannot append using a later writer's epoch.
|
|
5351
5576
|
*/
|
|
5352
|
-
const applyUnknownResolutionsForRecovery = Effect.fn("DurableAgentRuntime.applyUnknownResolutionsForRecovery")(function* (snapshot,
|
|
5577
|
+
const applyUnknownResolutionsForRecovery = Effect.fn("DurableAgentRuntime.applyUnknownResolutionsForRecovery")(function* (snapshot, _evidence, records) {
|
|
5578
|
+
if (yield* hasUnsupportedRetry(snapshot, records)) return "unknown";
|
|
5353
5579
|
const submission = snapshot.submission;
|
|
5354
|
-
const ctx = yield* attemptContextAtTail(submission.threadId);
|
|
5355
|
-
const knownIds = knownRecordIdsOf(records);
|
|
5356
|
-
if (!(yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls, knownIds, () => void 0).pipe(Effect.as(true), Effect.catchTag("FenceRejected", () => Effect.succeed(false))))) return "deferred";
|
|
5357
5580
|
for (const intent of snapshot.unknownResolutions) yield* ledger.recordUnknownResolution(UnknownResolutionCommand.make({
|
|
5358
5581
|
submissionId: intent.submissionId,
|
|
5359
5582
|
toolCallId: intent.toolCallId,
|
|
@@ -5378,14 +5601,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5378
5601
|
* permit, and stays `deferred`. Decisions that raced ahead of the repair leave the lane to a
|
|
5379
5602
|
* worker's batch resume (`deferred`).
|
|
5380
5603
|
*/
|
|
5381
|
-
const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence) {
|
|
5604
|
+
const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence, decision) {
|
|
5382
5605
|
const submission = snapshot.submission;
|
|
5383
5606
|
if (submission.state === "suspended") return "deferred";
|
|
5384
5607
|
const decided = new Set(snapshot.approvalDecisions.map((decision) => decision.toolCallId));
|
|
5385
5608
|
const undecided = evidence.approvalsPending.filter((pending) => !decided.has(pending.toolCallId));
|
|
5386
5609
|
const first = undecided[0];
|
|
5387
5610
|
if (first === void 0) return "deferred";
|
|
5388
|
-
const claimed = yield* claimFor(submission);
|
|
5611
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5389
5612
|
if (Option.isNone(claimed)) return "deferred";
|
|
5390
5613
|
const claim = claimed.value;
|
|
5391
5614
|
const outcome = yield* ledger.suspend(SuspendRequest.make({
|
|
@@ -5420,7 +5643,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5420
5643
|
const runId = runIdForSubmission(submission.submissionId);
|
|
5421
5644
|
const start = yield* canonicalRunStartFromRecords(records, runId);
|
|
5422
5645
|
if (start?.payload._tag === "RunStarted" && start.payload.runId === runId && (records.some(({ record }) => record.payload._tag === "RunDurationExhausted" && record.payload.runId === runId) || submission.workerAdmission !== void 0 && (yield* Clock.currentTimeMillis) >= submission.workerAdmission.origin.expiresAtMillis) && (decision._tag === "CompleteChildAdmission" || decision._tag === "RepairSubagentStartLink" || decision._tag === "AwaitChildAdmissionResolution" || decision._tag === "MarkUnknown" && snapshot.childReservations.some((reservation) => reservation.status !== "released"))) {
|
|
5423
|
-
const claimed = yield* claimFor(submission);
|
|
5646
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5424
5647
|
if (Option.isNone(claimed)) return "deferred";
|
|
5425
5648
|
const claim = claimed.value;
|
|
5426
5649
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5428,7 +5651,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5428
5651
|
producerEpoch: claim.producerEpoch
|
|
5429
5652
|
}));
|
|
5430
5653
|
const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
|
|
5431
|
-
yield*
|
|
5654
|
+
yield* reconcileRetainedChildren(ctx, submission, claim.ownershipToken);
|
|
5432
5655
|
const open = yield* completeJoinedReleases(submission);
|
|
5433
5656
|
yield* ledger.releaseOwnership(ReleaseOwnershipRequest.make({
|
|
5434
5657
|
submissionId: submission.submissionId,
|
|
@@ -5443,7 +5666,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5443
5666
|
case "MarkUnknown": return yield* markUnknownForRecovery(snapshot, evidence, records, decision);
|
|
5444
5667
|
case "ApplyUnknownResolutions": return yield* applyUnknownResolutionsForRecovery(snapshot, evidence, records);
|
|
5445
5668
|
case "AwaitUnknownResolution": return "unknown";
|
|
5446
|
-
case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence);
|
|
5669
|
+
case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence, decision);
|
|
5447
5670
|
case "ResumeSuspended": return yield* resumeSuspendedForRecovery(snapshot);
|
|
5448
5671
|
case "RevertJoining":
|
|
5449
5672
|
yield* ledger.revertJoining(RevertJoiningRequest.make({ submissionId: submission.submissionId }));
|
|
@@ -5464,7 +5687,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5464
5687
|
}
|
|
5465
5688
|
const host = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: hostSubmissionId }));
|
|
5466
5689
|
if (Option.isNone(host)) return "deferred";
|
|
5467
|
-
const claimed = yield* claimFor(host.value);
|
|
5690
|
+
const claimed = yield* claimFor(host.value, decision);
|
|
5468
5691
|
if (Option.isNone(claimed)) return "deferred";
|
|
5469
5692
|
const claim = claimed.value;
|
|
5470
5693
|
yield* ledger.markJoined(MarkJoinedRequest.make({
|
|
@@ -5503,7 +5726,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5503
5726
|
return "repaired";
|
|
5504
5727
|
case "ApplyInput":
|
|
5505
5728
|
case "RepairInputMarker": {
|
|
5506
|
-
const claimed = yield* claimFor(submission);
|
|
5729
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5507
5730
|
if (Option.isNone(claimed)) return "deferred";
|
|
5508
5731
|
const claim = claimed.value;
|
|
5509
5732
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5525,7 +5748,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5525
5748
|
case "AppendReservedSettlement": {
|
|
5526
5749
|
const reservation = snapshot.reservation;
|
|
5527
5750
|
if (reservation === void 0) return "deferred";
|
|
5528
|
-
const claimed = yield* claimFor(submission);
|
|
5751
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5529
5752
|
if (Option.isNone(claimed)) {
|
|
5530
5753
|
if (reservation.outcome === "aborted" && snapshot.abortIntent !== void 0 && snapshot.ownership === void 0) {
|
|
5531
5754
|
const ctx = yield* attemptContextAtTail(submission.threadId);
|
|
@@ -5562,7 +5785,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5562
5785
|
if (requestedPayload === void 0) return "deferred";
|
|
5563
5786
|
const admission = yield* establishChildFromRequest(submission, requestedPayload);
|
|
5564
5787
|
if (admission._tag === "indeterminate") return "deferred";
|
|
5565
|
-
const claimed = yield* claimFor(submission);
|
|
5788
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5566
5789
|
if (Option.isNone(claimed)) return "deferred";
|
|
5567
5790
|
const claim = claimed.value;
|
|
5568
5791
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5602,7 +5825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5602
5825
|
return "repaired";
|
|
5603
5826
|
}
|
|
5604
5827
|
case "EnsureWaitingForChild": {
|
|
5605
|
-
const claimed = yield* claimFor(submission);
|
|
5828
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5606
5829
|
if (Option.isNone(claimed)) return "deferred";
|
|
5607
5830
|
const claim = claimed.value;
|
|
5608
5831
|
const suspension = yield* ledger.suspend(SuspendRequest.make({
|
|
@@ -5657,7 +5880,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5657
5880
|
if (Option.isSome(childRow)) yield* wake.notify(childRow.value.threadId);
|
|
5658
5881
|
}
|
|
5659
5882
|
if (submission.state !== "suspended") {
|
|
5660
|
-
const claimed = yield* claimFor(submission);
|
|
5883
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5661
5884
|
if (Option.isNone(claimed)) return "deferred";
|
|
5662
5885
|
const claim = claimed.value;
|
|
5663
5886
|
const suspension = yield* ledger.suspend(SuspendRequest.make({
|
|
@@ -5701,7 +5924,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5701
5924
|
const evidence = yield* evidenceFor(history.records, submission.submissionId, history.materialized, snapshot.hostSubmissionId);
|
|
5702
5925
|
const decision = classifyRecovery(snapshot, evidence);
|
|
5703
5926
|
const disposition = decision._tag === "ApplyInput" && snapshot.submission.state === "ready" ? "deferred" : yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, history.records, history));
|
|
5704
|
-
if (disposition === "repaired") yield* annotateRepair(submission.threadId, submission.submissionId, decision);
|
|
5705
5927
|
return RecoveryReport.make({
|
|
5706
5928
|
submissionId: submission.submissionId,
|
|
5707
5929
|
threadId: submission.threadId,
|
|
@@ -5758,10 +5980,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5758
5980
|
reason: cause.reason === "storage" || cause.reason === "unavailable" ? "unavailable" : "refused",
|
|
5759
5981
|
code: `message-${cause.reason}`
|
|
5760
5982
|
})));
|
|
5761
|
-
if (options.definitions.replay !== void 0 && workerAdmission === void 0 && messageAdmission === void 0 && !registeredBindings.some((binding) => binding.agentId === agent.definition.id &&
|
|
5762
|
-
...retained.digests,
|
|
5763
|
-
replay: retained.replay
|
|
5764
|
-
}), options.definitions))))) {
|
|
5983
|
+
if (options.definitions.replay !== void 0 && workerAdmission === void 0 && messageAdmission === void 0 && !registeredBindings.some((binding) => binding.agentId === agent.definition.id && definitionDigestsEqual(binding.digests, options.definitions))) {
|
|
5765
5984
|
const retained = yield* ledger.lookup(SubmissionLookupByKey.make({
|
|
5766
5985
|
threadId: options.threadId,
|
|
5767
5986
|
principal: options.principal,
|
|
@@ -5980,7 +6199,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5980
6199
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
5981
6200
|
const explainedAt = yield* nowUtc;
|
|
5982
6201
|
const runId = runIdForSubmission(submission.submissionId);
|
|
5983
|
-
const resolvedIds = new Set(
|
|
6202
|
+
const resolvedIds = new Set(read.records.flatMap(({ record }) => record.payload._tag === "ToolCallSettled" && record.payload.runId === runId ? [record.payload.toolCallId] : []));
|
|
5984
6203
|
const unknownCalls = [];
|
|
5985
6204
|
for (const envelope of read.records) {
|
|
5986
6205
|
const payload = envelope.record.payload;
|
|
@@ -6013,6 +6232,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6013
6232
|
inputRecorded: evidence.inputRecorded,
|
|
6014
6233
|
abortRecorded: evidence.abortRecorded,
|
|
6015
6234
|
openToolCalls: evidence.openToolCalls,
|
|
6235
|
+
pendingOperations: read.records.flatMap(({ record }) => record.payload._tag === "ToolCallPrepared" && record.payload.runId === runId && !resolvedIds.has(record.payload.toolCallId) ? [record.payload] : []),
|
|
6016
6236
|
openDelegationCalls: evidence.openDelegationCalls,
|
|
6017
6237
|
approvalsPending: evidence.approvalsPending,
|
|
6018
6238
|
unknownCalls,
|
|
@@ -6027,7 +6247,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6027
6247
|
}),
|
|
6028
6248
|
decision,
|
|
6029
6249
|
decisionMeaning: recoveryDecisionMeaning(decision._tag),
|
|
6030
|
-
disposition:
|
|
6250
|
+
disposition: decision._tag === "ApplyUnknownResolutions" && (yield* hasUnsupportedRetry(snapshot, read.records).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
6251
|
+
message: "Cannot verify the pending retry contract",
|
|
6252
|
+
cause
|
|
6253
|
+
})))) ? "unknown" : predictRecoveryDisposition(decision, snapshot),
|
|
6031
6254
|
explainedAt
|
|
6032
6255
|
});
|
|
6033
6256
|
});
|
|
@@ -6105,9 +6328,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6105
6328
|
});
|
|
6106
6329
|
/**
|
|
6107
6330
|
* Safe re-drive of exactly one Submission's recovery decision (plan §3): classify, execute
|
|
6108
|
-
* the ONE repair the classifier names,
|
|
6109
|
-
*
|
|
6110
|
-
* protect the paths that own their own operations: settled work (`NoAction`),
|
|
6331
|
+
* the ONE repair the classifier names, annotate owned repair attempts with their actual
|
|
6332
|
+
* claim epoch (DUR-013), and wake the lane. Typed refusals
|
|
6333
|
+
* protect the paths that own their own operations: settled work (`NoAction`), requests parked
|
|
6111
6334
|
* on Unknown Outcomes (`resolveUnknown`, DUR-017), and lanes awaiting approval decisions
|
|
6112
6335
|
* (`resolveApproval`). The mandatory `author`/`reason` audit fields (SEC-011) annotate the
|
|
6113
6336
|
* structured operator log.
|
|
@@ -6132,7 +6355,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6132
6355
|
submissionId: command.submissionId,
|
|
6133
6356
|
refusal: "await-unknown-resolution",
|
|
6134
6357
|
decisionTag: decision._tag,
|
|
6135
|
-
message: "
|
|
6358
|
+
message: "This Submission is parked on Unknown Outcomes; resolve them through the authorized resolveUnknown path (DUR-017) instead of retrying."
|
|
6136
6359
|
});
|
|
6137
6360
|
if (decision._tag === "AwaitApprovalDecision") return yield* RetryRefused.make({
|
|
6138
6361
|
submissionId: command.submissionId,
|
|
@@ -6148,7 +6371,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6148
6371
|
decision: decision._tag
|
|
6149
6372
|
}));
|
|
6150
6373
|
const disposition = yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, read.records, read));
|
|
6151
|
-
if (disposition === "repaired") yield* annotateRepair(submission.threadId, command.submissionId, decision);
|
|
6152
6374
|
yield* wake.notify(submission.threadId);
|
|
6153
6375
|
return RecoveryReport.make({
|
|
6154
6376
|
submissionId: command.submissionId,
|
|
@@ -6185,8 +6407,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6185
6407
|
case "unknown": {
|
|
6186
6408
|
blockedOn = "unknown";
|
|
6187
6409
|
const records = (yield* readAllTolerant(submission.threadId, [submission.submissionId])).records;
|
|
6188
|
-
const resolvedIds = new Set(snapshot.unknownResolutions.map((intent) => intent.toolCallId));
|
|
6189
6410
|
const runId = runIdForSubmission(submission.submissionId);
|
|
6411
|
+
const resolvedIds = new Set(records.flatMap(({ record }) => record.payload._tag === "ToolCallSettled" && record.payload.runId === runId ? [record.payload.toolCallId] : []));
|
|
6190
6412
|
let earliest;
|
|
6191
6413
|
for (const envelope of records) {
|
|
6192
6414
|
const payload = envelope.record.payload;
|
|
@@ -6338,19 +6560,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6338
6560
|
* the next model request, reattaches through the prompt-coverage rule after a crash, and the
|
|
6339
6561
|
* joined Submissions settle with the host outcome (DUR-002/DUR-016).
|
|
6340
6562
|
* - `runWorker(agent)` — scan-seeded, wake-driven worker loop over every lane (WP4's host driver),
|
|
6341
|
-
*
|
|
6342
|
-
*
|
|
6343
|
-
* parent-linked head refused this way settles with the framework `ChildCompatibilityFailure`.
|
|
6563
|
+
* over a singleton current Binding: a claimed head belonging to a different Agent never
|
|
6564
|
+
* runs against this binding. Missing bindings leave accepted roots and children owed.
|
|
6344
6565
|
* - `processThreadResolved(threadId)` / `runResolvedWorker` — the
|
|
6345
|
-
*
|
|
6346
|
-
*
|
|
6347
|
-
*
|
|
6348
|
-
* `ChildCompatibilityFailure` (no application code, the child never executes), and an
|
|
6349
|
-
* unresolvable root surfaces the typed refusal after releasing the claim.
|
|
6566
|
+
* equivalents over registrations owned by the runtime Layer: each stable `agentId` selects
|
|
6567
|
+
* one current Binding. Unfinished operation contracts gate handler execution independently
|
|
6568
|
+
* of immutable admission evidence; missing bindings release the claim with a typed refusal.
|
|
6350
6569
|
* - `runRecovery()` — classify every nonterminal Submission with the pure `classifyRecovery` and
|
|
6351
|
-
* execute
|
|
6570
|
+
* execute repair decisions, annotating owned repair attempts with their granted epochs
|
|
6352
6571
|
* (DUR-013). Untouched ready input and model-resuming work are reported `deferred` for a
|
|
6353
|
-
* worker claim;
|
|
6572
|
+
* worker claim; Submissions parked on
|
|
6354
6573
|
* Unknown Outcomes are reported `unknown`. The S2 binding-free Subagent executors (admission
|
|
6355
6574
|
* completion, start-link repair, waiting restoration, wake replay, canonical join accounting,
|
|
6356
6575
|
* abort propagation, orphan reservation release) run here; the settlement join itself is
|
|
@@ -6385,4 +6604,4 @@ var DurableAgentRuntime = class DurableAgentRuntime extends Context.Service()("@
|
|
|
6385
6604
|
//#endregion
|
|
6386
6605
|
export { RecoveryReport as a, recoveryRepairRecordId as c, DurableRuntimeConfig as i, threadCreatedBatchId as l, DurableAgentRuntime_exports as n, childReservationIdFor as o, DurableApprovalResolver as r, recoveryRepairBatchId as s, DurableAgentRuntime as t, threadCreatedRecordId as u };
|
|
6387
6606
|
|
|
6388
|
-
//# sourceMappingURL=DurableAgentRuntime-
|
|
6607
|
+
//# sourceMappingURL=DurableAgentRuntime-BE_OjQjz.mjs.map
|