effect-agent 0.1.0-beta.96 → 0.1.0-beta.98
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-BGwGUIT-.mjs → DurableAgentRuntime-gF4YiEj2.mjs} +485 -262
- package/dist/DurableAgentRuntime-gF4YiEj2.mjs.map +1 -0
- package/dist/{DurableFailpoint-DQJet828.d.mts → DurableFailpoint-mx3-nsxU.d.mts} +3 -3
- package/dist/{EventSource-tCO_dAff.d.mts → EventSource-BZ73J0s5.d.mts} +3 -3
- package/dist/{PreparedInputAdmission-CFSddiT8.d.mts → PreparedInputAdmission-BkeI-Xvr.d.mts} +4 -4
- package/dist/{Records-CMByd1eD.d.mts → Records-BIiRLqlz.d.mts} +93 -101
- package/dist/{Records-BEz5IFUu.mjs → Records-DR1xtbXY.mjs} +36 -7
- package/dist/Records-DR1xtbXY.mjs.map +1 -0
- package/dist/{RunJournal-B_fxe57c.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-BxCfnYr0.d.mts → Schedule-BNFlluAl.d.mts} +2 -2
- package/dist/{Scheduling-BW6BD_TP.mjs → Scheduling-HFH0pST1.mjs} +2 -2
- package/dist/{Scheduling-BW6BD_TP.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-CrTIQKZR.d.mts → Subscription-BCQ3L9Zp.d.mts} +3 -3
- package/dist/{SubscriptionInput-BVOeFF1L.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-BTa5vmQF.mjs → agent-registration-cXpB8cBU.mjs} +40 -95
- 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 -7
- 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 -4
- package/src/durable/DurableAgentRuntime.ts +936 -462
- package/src/durable/DurableFailpoint.ts +2 -0
- package/src/durable/Records.ts +31 -4
- 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 +77 -188
- package/src/durable/internal/messaging-host.ts +1 -6
- package/src/durable/internal/worker-host.ts +88 -116
- 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-BGwGUIT-.mjs.map +0 -1
- package/dist/Records-BEz5IFUu.mjs.map +0 -1
- package/dist/RunJournal-B_fxe57c.mjs.map +0 -1
- package/dist/agent-registration-BTa5vmQF.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";
|
|
@@ -272,7 +273,7 @@ const makeMessagingRuntime = Effect.fn("MessagingHost.make")(function* (options)
|
|
|
272
273
|
const log = yield* read(threadId);
|
|
273
274
|
const created = log.records[0]?.record.payload;
|
|
274
275
|
if (created?._tag !== "ThreadCreated") return yield* failure$1("context", "not-found");
|
|
275
|
-
if (deps.bindings.filter((binding) => binding.agentId === created.agentId
|
|
276
|
+
if (deps.bindings.filter((binding) => binding.agentId === created.agentId).length !== 1) return yield* failure$1("context", "binding-mismatch");
|
|
276
277
|
return {
|
|
277
278
|
log,
|
|
278
279
|
address: {
|
|
@@ -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,11 +677,15 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
676
677
|
...report.destination === void 0 ? {} : { destinationDelegationId: report.destination.delegationId }
|
|
677
678
|
};
|
|
678
679
|
});
|
|
680
|
+
const currentBinding = (agentId) => {
|
|
681
|
+
const matches = deps.bindings.filter((entry) => entry.agentId === agentId);
|
|
682
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
683
|
+
};
|
|
679
684
|
const sourceAuthority = Effect.fn("WorkerHost.sourceAuthority")(function* (threadId, submissionId) {
|
|
680
685
|
const current = yield* read(threadId, "start");
|
|
681
686
|
const created = current.records[0]?.record.payload;
|
|
682
687
|
if (created?._tag !== "ThreadCreated") return yield* failure("start", "not-found");
|
|
683
|
-
const binding =
|
|
688
|
+
const binding = currentBinding(created.agentId);
|
|
684
689
|
const worker = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
|
|
685
690
|
const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
|
|
686
691
|
const nested = worker?._tag === "WorkerOriginRecorded" || attached?._tag === "SubagentLineageRecorded";
|
|
@@ -693,9 +698,11 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
693
698
|
if (worker?._tag === "WorkerOriginRecorded" && (submission.value.workerAdmission === void 0 || !sameOrigin(submission.value.workerAdmission.origin, worker.origin))) return yield* failure("start", "denied");
|
|
694
699
|
if (attached?._tag === "SubagentLineageRecorded" && (submission.value.parentLinkage?.parentSubmissionId !== attached.parentSubmissionId || submission.value.parentLinkage.parentToolCallId !== attached.parentLink.parentToolCallId)) return yield* failure("start", "denied");
|
|
695
700
|
}
|
|
701
|
+
const ownerBinding = ownerSubmission === void 0 ? void 0 : currentBinding(ownerSubmission.agentId);
|
|
702
|
+
const selectedBinding = ownerBinding ?? binding;
|
|
696
703
|
if (worker?._tag === "WorkerOriginRecorded") return {
|
|
697
704
|
current,
|
|
698
|
-
binding,
|
|
705
|
+
binding: selectedBinding,
|
|
699
706
|
submission: ownerSubmission,
|
|
700
707
|
policyOverride: Option.none(),
|
|
701
708
|
policy: worker.origin.policy,
|
|
@@ -708,7 +715,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
708
715
|
if (attached.policy === void 0 || attached.budget === void 0 || attached.grant === void 0) return yield* failure("start", "denied");
|
|
709
716
|
return {
|
|
710
717
|
current,
|
|
711
|
-
binding,
|
|
718
|
+
binding: selectedBinding,
|
|
712
719
|
submission: ownerSubmission,
|
|
713
720
|
policyOverride: Option.none(),
|
|
714
721
|
policy: attached.policy,
|
|
@@ -718,8 +725,6 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
718
725
|
depth: attached.parentLink.depth
|
|
719
726
|
};
|
|
720
727
|
}
|
|
721
|
-
const ownerBinding = ownerSubmission === void 0 ? void 0 : deps.bindings.find((entry) => entry.agentId === ownerSubmission.agentId && bindingSupports(entry, ownerSubmission.agentDigests));
|
|
722
|
-
const selectedBinding = ownerBinding ?? binding;
|
|
723
728
|
const changedRootAgent = ownerSubmission !== void 0 && ownerSubmission.agentId !== created.agentId;
|
|
724
729
|
if (selectedBinding === void 0 || changedRootAgent && ownerBinding === void 0) return yield* failure("start", "declaration-unavailable");
|
|
725
730
|
const selected = yield* deps.policyResolver.resolveSource({
|
|
@@ -729,7 +734,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
729
734
|
...ownerSubmission === void 0 ? {} : { submission: ownerSubmission }
|
|
730
735
|
});
|
|
731
736
|
if (Option.isSome(selected) && ownerSubmission !== void 0 && ownerBinding === void 0) return yield* failure("start", "declaration-unavailable");
|
|
732
|
-
const effectiveBinding = Option.isSome(selected) || changedRootAgent ? selectedBinding : binding;
|
|
737
|
+
const effectiveBinding = Option.isSome(selected) || changedRootAgent || binding === void 0 ? selectedBinding : binding;
|
|
733
738
|
if (effectiveBinding === void 0) return yield* failure("start", "declaration-unavailable");
|
|
734
739
|
const policy = Option.isNone(selected) ? effectiveBinding.definition.policy : yield* decode(AgentPolicy, selected.value, "start");
|
|
735
740
|
return {
|
|
@@ -802,7 +807,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
802
807
|
}
|
|
803
808
|
return yield* failure("start", "storage");
|
|
804
809
|
});
|
|
805
|
-
const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal) {
|
|
810
|
+
const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal, retainedFrameworkInput) {
|
|
806
811
|
const origin = admission.origin;
|
|
807
812
|
const id = `worker-input:${admission.messageId}`;
|
|
808
813
|
for (let attempt = 0; attempt < 16; attempt++) {
|
|
@@ -823,9 +828,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
823
828
|
if (prior === void 0 && admission.messageId !== origin.firstMessageId) return yield* failure("start", "not-found");
|
|
824
829
|
const caps = origin.budget.caps;
|
|
825
830
|
const sourceBinding = source.binding;
|
|
826
|
-
const targetBinding =
|
|
831
|
+
const targetBinding = currentBinding(origin.worker.targetAgentId);
|
|
827
832
|
if (sourceBinding === void 0 || targetBinding === void 0) return yield* failure("start", "declaration-unavailable");
|
|
828
|
-
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")));
|
|
829
834
|
const targetRequest = {
|
|
830
835
|
definition: targetBinding.definition,
|
|
831
836
|
definitions: targetBinding.digests,
|
|
@@ -922,7 +927,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
922
927
|
});
|
|
923
928
|
yield* authorizeBudget(admission.origin, options.principal);
|
|
924
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");
|
|
925
|
-
|
|
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);
|
|
926
933
|
return admission;
|
|
927
934
|
});
|
|
928
935
|
const prepareUpdate = Effect.fn("WorkerHost.prepareUpdate")(function* (update, submission, messageId) {
|
|
@@ -930,23 +937,22 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
930
937
|
const intent = origin?.reporting;
|
|
931
938
|
if (origin === void 0 || intent?.mode !== "standard") return void 0;
|
|
932
939
|
if (update.threadId !== submission.threadId || update.agentId !== origin.worker.targetAgentId) return yield* failure("followUp", "worker-mismatch");
|
|
933
|
-
const sourceBinding = deps.bindings.find((entry) => entry.agentId === origin.source.agentId && bindingSupports(entry, intent.sourceDigests));
|
|
934
|
-
const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests));
|
|
935
|
-
const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
|
|
936
|
-
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");
|
|
937
940
|
const sourceHistory = yield* read(origin.source.threadId, "followUp");
|
|
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
|
|
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);
|
|
947
|
+
const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
|
|
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");
|
|
942
949
|
const recorded = source.current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
|
|
943
950
|
const receivingOrigin = recorded?._tag === "WorkerOriginRecorded" ? recorded.origin : void 0;
|
|
944
951
|
if (source.depth !== 0 && receivingOrigin === void 0) return yield* failure("followUp", "denied");
|
|
945
952
|
const now = yield* Clock.currentTimeMillis;
|
|
946
953
|
const deadlineAtMillis = Math.min(origin.expiresAtMillis, receivingOrigin?.expiresAtMillis ?? Infinity);
|
|
947
954
|
if (now >= deadlineAtMillis) return yield* failure("followUp", "denied");
|
|
948
|
-
const
|
|
949
|
-
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;
|
|
950
956
|
let workerAdmission;
|
|
951
957
|
let principal = submission.principal;
|
|
952
958
|
if (receivingOrigin !== void 0) {
|
|
@@ -1029,11 +1035,6 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1029
1035
|
messageId,
|
|
1030
1036
|
reason
|
|
1031
1037
|
});
|
|
1032
|
-
const sourceBinding = deps.bindings.find((entry) => entry.agentId === origin.source.agentId && bindingSupports(entry, intent.sourceDigests));
|
|
1033
|
-
const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests));
|
|
1034
|
-
const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
|
|
1035
|
-
const descriptor = reports[0];
|
|
1036
|
-
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");
|
|
1037
1038
|
const selected = yield* deps.ledger.lookup(SubmissionLookupById.make({ submissionId: host.submissionId })).pipe(Effect.mapError(storageFailure("inspect")));
|
|
1038
1039
|
if (Option.isNone(selected) || selected.value.threadId !== submission.threadId || selected.value.receiptId !== host.receiptId || selected.value.workerAdmission === void 0 || !sameOrigin(selected.value.workerAdmission.origin, origin)) return yield* failure("inspect", "corrupt");
|
|
1039
1040
|
const hostSubmission = selected.value;
|
|
@@ -1042,7 +1043,12 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1042
1043
|
const firstInput = requests(sourceHistory.records).find((row) => row.admission.messageId === origin.firstMessageId);
|
|
1043
1044
|
if (firstInput === void 0 || !sameOrigin(firstInput.admission.origin, origin)) return yield* failure("inspect", "corrupt");
|
|
1044
1045
|
const source = yield* sourceAuthority(origin.source.threadId, firstInput.admission.sourceSubmissionId);
|
|
1045
|
-
if (source.binding === void 0
|
|
1046
|
+
if (source.binding === void 0) return refused("declaration-unavailable");
|
|
1047
|
+
const sourceBinding = currentBinding(origin.source.agentId);
|
|
1048
|
+
const targetBinding = currentBinding(origin.worker.targetAgentId);
|
|
1049
|
+
const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
|
|
1050
|
+
const descriptor = reports[0];
|
|
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");
|
|
1046
1052
|
const sourceOriginRecord = source.current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
|
|
1047
1053
|
const sourceOrigin = sourceOriginRecord?._tag === "WorkerOriginRecorded" ? sourceOriginRecord.origin : void 0;
|
|
1048
1054
|
if (source.depth !== 0 && sourceOrigin === void 0) return refused("destination");
|
|
@@ -1087,7 +1093,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1087
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");
|
|
1088
1094
|
} else if (projection.value.message !== void 0) return refused("input");
|
|
1089
1095
|
const reportInput = descriptor.mode === "standard" ? source.submission?.inputPayload : projection.value.encodedInput;
|
|
1090
|
-
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);
|
|
1091
1097
|
if (Option.isNone(validated)) return refused("input");
|
|
1092
1098
|
const input = validated.value;
|
|
1093
1099
|
const inputDigest = yield* withCrypto(digestJson(input)).pipe(Effect.mapError(storageFailure("inspect")));
|
|
@@ -1236,12 +1242,12 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1236
1242
|
};
|
|
1237
1243
|
});
|
|
1238
1244
|
const findOrigin = Effect.fn("WorkerHost.findOrigin")(function* (worker, target, operation) {
|
|
1239
|
-
|
|
1245
|
+
yield* binding(target, operation);
|
|
1240
1246
|
if (worker.targetAgentId !== target.id) return yield* failure(operation, "worker-mismatch");
|
|
1241
1247
|
const source = yield* read(context.source.threadId, operation);
|
|
1242
1248
|
const origin = requests(source.records).find((row) => row.admission.origin.worker.threadId === worker.threadId)?.admission.origin;
|
|
1243
1249
|
if (origin === void 0) return yield* failure(operation, "not-found");
|
|
1244
|
-
if (origin.worker.delegationId !== worker.delegationId
|
|
1250
|
+
if (origin.worker.delegationId !== worker.delegationId) return yield* failure(operation, "worker-mismatch");
|
|
1245
1251
|
return origin;
|
|
1246
1252
|
});
|
|
1247
1253
|
const receiptInput = Effect.fn("WorkerHost.receiptInput")(function* (request, operation) {
|
|
@@ -1418,7 +1424,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1418
1424
|
threadId: Schema.decodeSync(ThreadId)(messageId)
|
|
1419
1425
|
},
|
|
1420
1426
|
source: existing?.envelope.workerAdmission?.origin.source ?? context.source,
|
|
1421
|
-
targetDigests: resolved.digests,
|
|
1427
|
+
targetDigests: previousOrigin?.targetDigests ?? resolved.digests,
|
|
1422
1428
|
policy: request.policy,
|
|
1423
1429
|
budget: request.budget,
|
|
1424
1430
|
...request.budgetScope === void 0 ? {} : { budgetScope: request.budgetScope },
|
|
@@ -1570,12 +1576,12 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
|
|
|
1570
1576
|
const current = yield* read(sourceThreadId, "context");
|
|
1571
1577
|
const created = current.records[0]?.record.payload;
|
|
1572
1578
|
if (created?._tag !== "ThreadCreated") return yield* failure("context", "not-found");
|
|
1573
|
-
const
|
|
1579
|
+
const selected = request.sourceSubmissionId === void 0 ? void 0 : yield* sourceAuthority(sourceThreadId, request.sourceSubmissionId);
|
|
1580
|
+
const resolved = selected === void 0 ? currentBinding(created.agentId) : selected.binding;
|
|
1574
1581
|
if (resolved === void 0) return yield* failure("context", "declaration-unavailable");
|
|
1575
1582
|
const origin = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
|
|
1576
1583
|
const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
|
|
1577
1584
|
const retained = origin?._tag === "WorkerOriginRecorded" ? origin.origin : attached?._tag === "SubagentLineageRecorded" ? attached : void 0;
|
|
1578
|
-
const selected = request.sourceSubmissionId === void 0 ? void 0 : yield* sourceAuthority(sourceThreadId, request.sourceSubmissionId);
|
|
1579
1585
|
return facet({
|
|
1580
1586
|
source: {
|
|
1581
1587
|
_tag: "programmatic",
|
|
@@ -1729,7 +1735,7 @@ var RecoveryReport = class extends Schema.Class("@effect-agent/thread/RecoveryRe
|
|
|
1729
1735
|
decision: RecoveryDecision,
|
|
1730
1736
|
/**
|
|
1731
1737
|
* `repaired` = executed; `deferred` = a claiming worker must finish it; `none` = settled;
|
|
1732
|
-
* `unknown` =
|
|
1738
|
+
* `unknown` = this Submission is parked on an Unknown Outcome awaiting the authorized
|
|
1733
1739
|
* DUR-017 resolution path — the settlement obligation stays visible while no worker permit
|
|
1734
1740
|
* is consumed (durability §16).
|
|
1735
1741
|
*/
|
|
@@ -1866,18 +1872,6 @@ const declaredToolCalls = Effect.fn("DurableAgentRuntime.declaredToolCalls")((me
|
|
|
1866
1872
|
})));
|
|
1867
1873
|
/** Application calls alone drive durable preparation, settlement, and batch resume. */
|
|
1868
1874
|
const declaredApplicationCalls = Effect.fn("DurableAgentRuntime.declaredApplicationCalls")((messages) => declaredToolCalls(messages).pipe(Effect.map(({ application }) => application)));
|
|
1869
|
-
/** Rebuild the exact JSON text that the engine decoded from one terminal no-Tool response. */
|
|
1870
|
-
const terminalAssistantText = Effect.fn("DurableAgentRuntime.terminalAssistantText")((messages) => decodePrompt(messages).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
1871
|
-
message: "ModelResponseRecorded messages are not Schema-encoded Prompt messages",
|
|
1872
|
-
cause
|
|
1873
|
-
})), Effect.map((prompt) => {
|
|
1874
|
-
let text = "";
|
|
1875
|
-
for (const message of prompt.content) {
|
|
1876
|
-
if (message.role !== "assistant") continue;
|
|
1877
|
-
for (const part of message.content) if (part.type === "text") text += part.text;
|
|
1878
|
-
}
|
|
1879
|
-
return text;
|
|
1880
|
-
})));
|
|
1881
1875
|
const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
1882
1876
|
const registeredBindings = [...bindings];
|
|
1883
1877
|
const ledger = yield* SubmissionLedger;
|
|
@@ -1899,6 +1893,34 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
1899
1893
|
const operationAuthorizer = yield* OperationAuthorizer;
|
|
1900
1894
|
const toolFailureObserver = yield* CurrentToolFailureObserver;
|
|
1901
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 === definition ? 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
|
+
};
|
|
1902
1924
|
const hit = (location) => failpoint.hit(location);
|
|
1903
1925
|
const makeEnvelope = (recordId, payload) => Effect.map(nowUtc, (createdAt) => RecordEnvelope.make({
|
|
1904
1926
|
recordId,
|
|
@@ -1979,7 +2001,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
1979
2001
|
return yield* Stream.runCollect(view.canonical.pipe(Stream.filter(controlRecords(submissionIds))));
|
|
1980
2002
|
});
|
|
1981
2003
|
/** A checkpoint is useful only for its explicitly retained submissions and exact definitions. */
|
|
1982
|
-
const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds) {
|
|
2004
|
+
const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds, journalOwner) {
|
|
1983
2005
|
const full = { canonical: canonicalRange(threadId, throughSequence) };
|
|
1984
2006
|
if (store.recoveryCheckpoints === void 0) return full;
|
|
1985
2007
|
const loaded = yield* store.recoveryCheckpoints.load(LoadCheckpointRequest.make({
|
|
@@ -1992,7 +2014,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
1992
2014
|
const decoded = yield* Schema.decodeUnknownEffect(RecoveryCheckpointContents)(checkpoint.state).pipe(Effect.option);
|
|
1993
2015
|
if (Option.isNone(decoded)) return full;
|
|
1994
2016
|
const { state, digest } = decoded.value;
|
|
1995
|
-
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;
|
|
1996
2018
|
const encoded = yield* Schema.encodeEffect(RecoveryCheckpointState)(state).pipe(Effect.mapError((cause) => ThreadStoreError.make({
|
|
1997
2019
|
operation: "decode recovery checkpoint",
|
|
1998
2020
|
message: "Invalid recovery state",
|
|
@@ -2119,6 +2141,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2119
2141
|
const preparedTurns = /* @__PURE__ */ new Set();
|
|
2120
2142
|
const settledIds = /* @__PURE__ */ new Set();
|
|
2121
2143
|
const resolvedIds = /* @__PURE__ */ new Set();
|
|
2144
|
+
const unknownIds = /* @__PURE__ */ new Set();
|
|
2122
2145
|
const requested = [];
|
|
2123
2146
|
const decidedIds = /* @__PURE__ */ new Set();
|
|
2124
2147
|
let lastResponse;
|
|
@@ -2159,8 +2182,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2159
2182
|
case "ToolCallSettled":
|
|
2160
2183
|
if (payload.runId === runId) settledIds.add(payload.toolCallId);
|
|
2161
2184
|
break;
|
|
2185
|
+
case "ToolCallUnknown":
|
|
2186
|
+
if (payload.runId === runId) unknownIds.add(payload.toolCallId);
|
|
2187
|
+
break;
|
|
2162
2188
|
case "ToolCallResolved":
|
|
2163
|
-
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);
|
|
2164
2190
|
break;
|
|
2165
2191
|
case "ToolApprovalRequested":
|
|
2166
2192
|
if (payload.runId !== runId) break;
|
|
@@ -2241,8 +2267,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2241
2267
|
}));
|
|
2242
2268
|
}
|
|
2243
2269
|
const isPreparedWorker = (call) => preparedKinds.get(toolCallPreparedRecordId(runId, call.turn, call.toolCallId)) === "orchestration";
|
|
2244
|
-
const openWorkerCalls = allOpenCalls.filter(isPreparedWorker);
|
|
2245
|
-
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));
|
|
2246
2272
|
let declaredPendingBatch;
|
|
2247
2273
|
if (lastResponse !== void 0 && !preparedTurns.has(lastResponse.turn)) {
|
|
2248
2274
|
const declared = yield* declaredApplicationCalls(lastResponse.messages);
|
|
@@ -2267,6 +2293,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2267
2293
|
...hostSettlementOutcome === void 0 ? {} : { hostSettlementOutcome }
|
|
2268
2294
|
});
|
|
2269
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
|
+
});
|
|
2270
2315
|
/**
|
|
2271
2316
|
* The declared-but-unsettled Tool batch of the Run's LAST committed Turn (§2.4). Keyed on
|
|
2272
2317
|
* SETTLED coverage deliberately: a call closed by a never-started/safe-retry `ToolCallResolved`
|
|
@@ -2285,12 +2330,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2285
2330
|
turn: payload.turn,
|
|
2286
2331
|
messages: payload.messages,
|
|
2287
2332
|
toolParameterRejections: payload.toolParameterRejections,
|
|
2333
|
+
toolOperations: payload.toolOperations,
|
|
2288
2334
|
...payload.toolExposure === void 0 ? {} : { toolExposure: payload.toolExposure }
|
|
2289
2335
|
};
|
|
2290
2336
|
}
|
|
2291
2337
|
continue;
|
|
2292
2338
|
}
|
|
2293
2339
|
if (payload._tag === "ToolCallSettled" && payload.runId === runId) settledByCallId.set(payload.toolCallId, {
|
|
2340
|
+
recordId: envelope.record.recordId,
|
|
2341
|
+
toolName: payload.toolName,
|
|
2294
2342
|
result: payload.result,
|
|
2295
2343
|
isFailure: payload.isFailure,
|
|
2296
2344
|
...payload.budgetRejected === true ? { budgetRejected: true } : {},
|
|
@@ -2301,6 +2349,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2301
2349
|
const declared = yield* declaredToolCalls(lastResponse.messages);
|
|
2302
2350
|
if (declared.application.length === 0) return void 0;
|
|
2303
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
|
+
}
|
|
2304
2364
|
for (const part of declared.providerResults) {
|
|
2305
2365
|
const result = yield* decodePersisted(part.result).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
2306
2366
|
message: `Invalid canonical provider result ${part.id}`,
|
|
@@ -2315,10 +2375,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2315
2375
|
for (const call of calls) {
|
|
2316
2376
|
const recorded = settledByCallId.get(call.id);
|
|
2317
2377
|
if (call.providerExecuted === true && recorded === void 0) return yield* RunJournalError.make({ message: `Pending Turn lacks canonical provider result for ${call.id}` });
|
|
2318
|
-
if (recorded !== void 0)
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
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
|
+
}
|
|
2322
2386
|
}
|
|
2323
2387
|
const completionCall = declared.application[0];
|
|
2324
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);
|
|
@@ -2332,6 +2396,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2332
2396
|
responseRecordId: modelResponseRecordId(runId, lastResponse.turn),
|
|
2333
2397
|
messages: lastResponse.messages,
|
|
2334
2398
|
toolParameterRejections: lastResponse.toolParameterRejections,
|
|
2399
|
+
toolOperations: lastResponse.toolOperations,
|
|
2335
2400
|
...lastResponse.toolExposure === void 0 ? {} : { toolExposure: lastResponse.toolExposure }
|
|
2336
2401
|
};
|
|
2337
2402
|
});
|
|
@@ -2392,9 +2457,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2392
2457
|
};
|
|
2393
2458
|
});
|
|
2394
2459
|
/**
|
|
2395
|
-
*
|
|
2396
|
-
*
|
|
2397
|
-
*
|
|
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.
|
|
2398
2463
|
*/
|
|
2399
2464
|
const attemptContextAtTail = Effect.fn("DurableAgentRuntime.attemptContextAtTail")(function* (threadId) {
|
|
2400
2465
|
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
|
|
@@ -2538,16 +2603,28 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2538
2603
|
* `Uncertain` collects for Unknown marking, and a reconciler FAILURE is no proof at all (the
|
|
2539
2604
|
* call stays open and blocked without being marked). Nothing here ever executes a handler.
|
|
2540
2605
|
*/
|
|
2541
|
-
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) {
|
|
2542
2607
|
const submissionId = submission.submissionId;
|
|
2543
2608
|
const runId = runIdForSubmission(submissionId);
|
|
2609
|
+
yield* pendingToolBatchFor(records, runId);
|
|
2610
|
+
const operations = operationsFor(records, runId);
|
|
2544
2611
|
const preparedByCallId = /* @__PURE__ */ new Map();
|
|
2545
|
-
for (const
|
|
2546
|
-
const payload =
|
|
2547
|
-
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
|
+
}
|
|
2548
2618
|
}
|
|
2549
|
-
const
|
|
2550
|
-
|
|
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]));
|
|
2551
2628
|
const review = {
|
|
2552
2629
|
uncertain: [],
|
|
2553
2630
|
unproven: [],
|
|
@@ -2556,7 +2633,39 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2556
2633
|
};
|
|
2557
2634
|
let recovered = 0;
|
|
2558
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);
|
|
2559
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
|
+
});
|
|
2560
2669
|
if (intent !== void 0) switch (intent.resolution._tag) {
|
|
2561
2670
|
case "AbortSubmission": continue;
|
|
2562
2671
|
case "CompletedWithResult":
|
|
@@ -2566,33 +2675,29 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2566
2675
|
isFailure: intent.resolution.isFailure
|
|
2567
2676
|
},
|
|
2568
2677
|
resolution: intent.resolution.isFailure ? "failed-with-error" : "completed-with-result",
|
|
2569
|
-
author
|
|
2570
|
-
reason
|
|
2678
|
+
author,
|
|
2679
|
+
reason
|
|
2571
2680
|
});
|
|
2572
2681
|
recovered += 1;
|
|
2573
2682
|
continue;
|
|
2574
2683
|
case "NeverHappened":
|
|
2575
|
-
yield*
|
|
2576
|
-
resolution: "never-started",
|
|
2577
|
-
author: intent.author,
|
|
2578
|
-
reason: intent.reason
|
|
2579
|
-
});
|
|
2580
|
-
review.retryable.push(call);
|
|
2684
|
+
yield* neverStarted;
|
|
2581
2685
|
continue;
|
|
2582
2686
|
case "SafeToRetry":
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
reason: intent.reason
|
|
2587
|
-
});
|
|
2588
|
-
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);
|
|
2589
2690
|
continue;
|
|
2590
2691
|
}
|
|
2591
|
-
|
|
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") {
|
|
2592
2698
|
review.retryable.push(call);
|
|
2593
2699
|
continue;
|
|
2594
2700
|
}
|
|
2595
|
-
const prepared = preparedByCallId.get(call.toolCallId);
|
|
2596
2701
|
if (prepared === void 0) {
|
|
2597
2702
|
review.unproven.push(call);
|
|
2598
2703
|
continue;
|
|
@@ -2605,7 +2710,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2605
2710
|
toolCallId: prepared.toolCallId,
|
|
2606
2711
|
toolName: prepared.toolName,
|
|
2607
2712
|
parameters: prepared.parameters,
|
|
2608
|
-
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 }
|
|
2609
2717
|
})).pipe(Effect.map(Option.some), Effect.catchTag("ToolReconcilerError", () => Effect.succeed(Option.none())));
|
|
2610
2718
|
if (Option.isNone(reconciled)) {
|
|
2611
2719
|
review.unproven.push(call);
|
|
@@ -2620,14 +2728,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
2620
2728
|
isFailure: decision.isFailure
|
|
2621
2729
|
},
|
|
2622
2730
|
resolution: decision.isFailure ? "failed-with-error" : "completed-with-result",
|
|
2623
|
-
author
|
|
2624
|
-
reason
|
|
2731
|
+
author,
|
|
2732
|
+
reason
|
|
2625
2733
|
});
|
|
2626
2734
|
recovered += 1;
|
|
2627
2735
|
continue;
|
|
2628
2736
|
case "NeverStarted":
|
|
2737
|
+
yield* neverStarted;
|
|
2738
|
+
continue;
|
|
2629
2739
|
case "SafeToRetry":
|
|
2630
|
-
review.
|
|
2740
|
+
if (current === void 0) review.unproven.push(call);
|
|
2741
|
+
else if (supported) review.retryable.push(call);
|
|
2742
|
+
else review.uncertain.push(call);
|
|
2631
2743
|
continue;
|
|
2632
2744
|
case "Uncertain":
|
|
2633
2745
|
review.uncertain.push(call);
|
|
@@ -3168,11 +3280,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3168
3280
|
* Join a settled child after parent abort or expiry without running application handlers.
|
|
3169
3281
|
* The canonical child outcome stays intact; the parent result records why it cannot continue.
|
|
3170
3282
|
*/
|
|
3171
|
-
const
|
|
3283
|
+
const joinSettledChildWithoutHandler = Effect.fn("DurableAgentRuntime.joinSettledChildWithoutHandler")(function* (ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, reason) {
|
|
3172
3284
|
const runId = runIdForSubmission(parent.submissionId);
|
|
3173
3285
|
const request = subagent.requested.get(toolCallId);
|
|
3174
3286
|
if (request === void 0) return yield* LedgerError.make({
|
|
3175
|
-
operation: "
|
|
3287
|
+
operation: "joinSettledChildWithoutHandler",
|
|
3176
3288
|
message: `Tool Call ${toolCallId} has an attached child but no canonical SubagentRequested record`
|
|
3177
3289
|
});
|
|
3178
3290
|
const joinedRecordId = subagentJoinedRecordId(runId, toolCallId);
|
|
@@ -3182,16 +3294,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3182
3294
|
else {
|
|
3183
3295
|
const verification = yield* verifySettledChild(parent, request, childSubmissionId);
|
|
3184
3296
|
if (verification._tag === "mismatch") return yield* LedgerError.make({
|
|
3185
|
-
operation: "
|
|
3297
|
+
operation: "joinSettledChildWithoutHandler",
|
|
3186
3298
|
message: `Child Settlement verification failed for Tool Call ${toolCallId}: ${verification.message}`
|
|
3187
3299
|
});
|
|
3188
3300
|
const verified = verification.value;
|
|
3189
|
-
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
|
+
} : {
|
|
3190
3307
|
errorTag: reason === "aborted" ? "SubagentParentAborted" : "SubagentParentDurationExceeded",
|
|
3191
3308
|
message: boundedText(`The parent Submission ${reason === "aborted" ? "aborted" : "exceeded its duration"}; attached child ${childSubmissionId} settled ${verified.outcome}`)
|
|
3192
3309
|
}).pipe(Effect.orDie);
|
|
3193
3310
|
finalAccounting = yield* decodePersisted({
|
|
3194
|
-
basis: reason
|
|
3311
|
+
basis: `${reason}-conservative`,
|
|
3195
3312
|
allocation: reservation.allocation
|
|
3196
3313
|
}).pipe(Effect.orDie);
|
|
3197
3314
|
const childRunId = runIdForSubmission(childSubmissionId);
|
|
@@ -3262,25 +3379,51 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3262
3379
|
return open;
|
|
3263
3380
|
});
|
|
3264
3381
|
/** Release unused reservations or repair and join existing children, never admit new work. */
|
|
3265
|
-
const
|
|
3382
|
+
const reconcileRetainedChildren = Effect.fn("DurableAgentRuntime.reconcileRetainedChildren")(function* (ctx, parent, ownershipToken, unavailableCalls) {
|
|
3266
3383
|
const snapshot = yield* ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId: parent.submissionId }));
|
|
3267
3384
|
const records = yield* readControl(parent.threadId, [parent.submissionId]);
|
|
3268
3385
|
const runId = runIdForSubmission(parent.submissionId);
|
|
3269
3386
|
const pending = yield* pendingToolBatchFor(records, runId);
|
|
3270
3387
|
const knownIds = knownRecordIdsOf(records);
|
|
3271
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
|
+
}
|
|
3272
3400
|
for (const reservation of snapshot.childReservations) {
|
|
3273
3401
|
const toolCallId = reservation.parentToolCallId;
|
|
3274
|
-
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
|
+
}
|
|
3275
3417
|
let started = subagent.started.get(toolCallId);
|
|
3276
3418
|
const request = subagent.requested.get(toolCallId);
|
|
3277
3419
|
if (started === void 0 && request === void 0 && reservation.childSubmissionId === void 0) {
|
|
3278
3420
|
yield* releaseOrphanReservation(reservation.reservationId);
|
|
3421
|
+
notExecuted.add(toolCallId);
|
|
3279
3422
|
continue;
|
|
3280
3423
|
}
|
|
3281
3424
|
const call = pending?.calls.find((candidate) => candidate.id === toolCallId);
|
|
3282
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({
|
|
3283
|
-
operation: "
|
|
3426
|
+
operation: "reconcileRetainedChildren",
|
|
3284
3427
|
message: `Tool Call ${toolCallId} has inconsistent canonical child request evidence`
|
|
3285
3428
|
});
|
|
3286
3429
|
if (started === void 0) {
|
|
@@ -3289,17 +3432,26 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3289
3432
|
principal: decodePrincipalSync(request.childPrincipal),
|
|
3290
3433
|
idempotencyKey: decodeIdempotencyKeySync(request.childIdempotencyKey)
|
|
3291
3434
|
}));
|
|
3292
|
-
if (resolution._tag === "Indeterminate")
|
|
3435
|
+
if (resolution._tag === "Indeterminate") {
|
|
3436
|
+
indeterminate.add(toolCallId);
|
|
3437
|
+
continue;
|
|
3438
|
+
}
|
|
3293
3439
|
if (resolution._tag === "NotAdmitted") {
|
|
3294
3440
|
if (reservation.childSubmissionId !== void 0) return yield* LedgerError.make({
|
|
3295
|
-
operation: "
|
|
3441
|
+
operation: "reconcileRetainedChildren",
|
|
3296
3442
|
message: `Tool Call ${toolCallId} has an attachment but no admitted child`
|
|
3297
3443
|
});
|
|
3298
|
-
|
|
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);
|
|
3299
3453
|
continue;
|
|
3300
3454
|
}
|
|
3301
|
-
const admission = yield* establishChildFromRequest(parent, request, false);
|
|
3302
|
-
if (admission._tag === "indeterminate") continue;
|
|
3303
3455
|
started = SubagentStarted.make({
|
|
3304
3456
|
runId,
|
|
3305
3457
|
toolCallId,
|
|
@@ -3321,7 +3473,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3321
3473
|
}
|
|
3322
3474
|
const child = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: started.childSubmissionId }));
|
|
3323
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({
|
|
3324
|
-
operation: "
|
|
3476
|
+
operation: "reconcileRetainedChildren",
|
|
3325
3477
|
message: `Tool Call ${call.id} has inconsistent canonical child attachment evidence`
|
|
3326
3478
|
});
|
|
3327
3479
|
yield* ledger.attachChildToReservation(AttachChildToReservationRequest.make({
|
|
@@ -3329,9 +3481,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3329
3481
|
ownershipToken,
|
|
3330
3482
|
childSubmissionId: started.childSubmissionId
|
|
3331
3483
|
})).pipe(Effect.catchTag("ChildReservationConflict", conflictToLedgerError("attachChildToReservation")));
|
|
3332
|
-
if (child.value.state !== "settled")
|
|
3333
|
-
|
|
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");
|
|
3334
3493
|
}
|
|
3494
|
+
return {
|
|
3495
|
+
notExecuted,
|
|
3496
|
+
waiting,
|
|
3497
|
+
indeterminate
|
|
3498
|
+
};
|
|
3335
3499
|
});
|
|
3336
3500
|
/**
|
|
3337
3501
|
* Request-abort-and-join over every attached child of an aborting parent (spec §13.1,
|
|
@@ -3385,7 +3549,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3385
3549
|
message: `Attached child ${childSubmissionId} is unknown to the ledger`
|
|
3386
3550
|
});
|
|
3387
3551
|
if (child.value.state === "settled") {
|
|
3388
|
-
yield*
|
|
3552
|
+
yield* joinSettledChildWithoutHandler(ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, "aborted");
|
|
3389
3553
|
continue;
|
|
3390
3554
|
}
|
|
3391
3555
|
yield* ledger.requestAbort(AbortCommand.make({
|
|
@@ -3613,14 +3777,20 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3613
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" });
|
|
3614
3778
|
const pending = yield* pendingToolBatchFor(records, runId, agent.definition.completionFromTools?.map((declaration) => declaration.tool));
|
|
3615
3779
|
const resumeProjection = pending === void 0 ? journal : yield* projectRunJournalStream(withoutPendingBatch(canonical, pending, runId), runId, void 0, journalSeed);
|
|
3616
|
-
const
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
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;
|
|
3624
3794
|
const knownIds = knownRecordIdsOf(records);
|
|
3625
3795
|
const stepOutputs = /* @__PURE__ */ new Map();
|
|
3626
3796
|
for (const envelope of records) {
|
|
@@ -3740,51 +3910,12 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3740
3910
|
const response = [...records].reverse().find((envelope) => envelope.record.payload._tag === "ModelResponseRecorded" && envelope.record.payload.runId === runId)?.record.payload;
|
|
3741
3911
|
if (response?._tag !== "ModelResponseRecorded") return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without a response` });
|
|
3742
3912
|
const calls = (yield* declaredToolCalls(response.messages)).application;
|
|
3743
|
-
let expectedOutput;
|
|
3744
3913
|
if (calls.length > 0) {
|
|
3745
|
-
const completion = agent.definition.completion;
|
|
3746
3914
|
const call = calls[0];
|
|
3747
|
-
const
|
|
3748
|
-
|
|
3749
|
-
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` });
|
|
3750
|
-
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({
|
|
3751
|
-
message: `Run ${runId} completion Tool output failed recovery projection`,
|
|
3752
|
-
cause
|
|
3753
|
-
})));
|
|
3754
|
-
if (Option.isNone(projected)) return yield* RunJournalError.make({ message: `Run ${runId} completion Tool declined its recorded terminal output` });
|
|
3755
|
-
expectedOutput = projected.value;
|
|
3756
|
-
} else {
|
|
3757
|
-
const responseText = yield* terminalAssistantText(response.messages);
|
|
3758
|
-
expectedOutput = yield* decodeFinalOutput(agent, responseText).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3759
|
-
message: `Run ${runId} final response output failed recovery decoding`,
|
|
3760
|
-
cause
|
|
3761
|
-
})));
|
|
3762
|
-
}
|
|
3763
|
-
const boundedExpectedOutput = yield* decodePersisted(expectedOutput.encoded).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3764
|
-
message: `Run ${runId} reconstructed output exceeds canonical persistence bounds`,
|
|
3765
|
-
cause
|
|
3766
|
-
})));
|
|
3767
|
-
const [expectedOutputDigest, recordedOutputDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedOutput)), withCrypto(digestJson(recordedCompletion.output))]).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3768
|
-
message: `Run ${runId} completion output comparison failed`,
|
|
3769
|
-
cause
|
|
3770
|
-
})));
|
|
3771
|
-
if (expectedOutputDigest !== recordedOutputDigest) return yield* RunJournalError.make({ message: `Run ${runId} terminal completion output disagrees with its canonical response or Tool result` });
|
|
3772
|
-
const expectedRunDisposition = recordedCompletion.finishReason === "budget-exhausted" ? void 0 : yield* encodeRunDisposition(agent, expectedOutput.decoded).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3773
|
-
message: `Run ${runId} run disposition failed recovery projection`,
|
|
3774
|
-
cause
|
|
3775
|
-
})));
|
|
3776
|
-
if (expectedRunDisposition === void 0 !== (recordedCompletion.runDisposition === void 0)) return yield* RunJournalError.make({ message: `Run ${runId} terminal run disposition disagrees with its reconstructed output` });
|
|
3777
|
-
if (expectedRunDisposition !== void 0 && recordedCompletion.runDisposition !== void 0) {
|
|
3778
|
-
const boundedExpectedRunDisposition = yield* decodePersisted(expectedRunDisposition).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3779
|
-
message: `Run ${runId} reconstructed run disposition exceeds canonical persistence bounds`,
|
|
3780
|
-
cause
|
|
3781
|
-
})));
|
|
3782
|
-
const [expectedRunDispositionDigest, recordedRunDispositionDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedRunDisposition)), withCrypto(digestJson(recordedCompletion.runDisposition))]).pipe(Effect.mapError((cause) => RunJournalError.make({
|
|
3783
|
-
message: `Run ${runId} run disposition comparison failed`,
|
|
3784
|
-
cause
|
|
3785
|
-
})));
|
|
3786
|
-
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` });
|
|
3787
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" });
|
|
3788
3919
|
return {
|
|
3789
3920
|
_tag: "completed",
|
|
3790
3921
|
result: recordedCompletion.output,
|
|
@@ -3822,7 +3953,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3822
3953
|
cause
|
|
3823
3954
|
})));
|
|
3824
3955
|
const firstAssistant = pendingMessages.content.findIndex((message) => message.role === "assistant");
|
|
3825
|
-
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"));
|
|
3826
3957
|
if (firstAssistant >= 0) resumeResponseMessages = Prompt.fromMessages(pendingMessages.content.slice(firstAssistant));
|
|
3827
3958
|
}
|
|
3828
3959
|
let currentToolTurn = pending === void 0 ? void 0 : {
|
|
@@ -3859,7 +3990,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3859
3990
|
...state,
|
|
3860
3991
|
history
|
|
3861
3992
|
});
|
|
3862
|
-
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)]) }))) };
|
|
3863
4011
|
const externalContext = runContextPreparation.hook;
|
|
3864
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({
|
|
3865
4013
|
...request,
|
|
@@ -3904,6 +4052,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3904
4052
|
const createdAt = yield* nowUtc;
|
|
3905
4053
|
const batch = yield* withCrypto(turnResponseBatch({
|
|
3906
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
|
+
})),
|
|
3907
4062
|
toolParameterRejections: commit.toolParameterRejections,
|
|
3908
4063
|
runId,
|
|
3909
4064
|
turn: canonicalTurn,
|
|
@@ -3942,7 +4097,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
3942
4097
|
toolName: call.toolName,
|
|
3943
4098
|
parameters,
|
|
3944
4099
|
parametersDigest,
|
|
3945
|
-
executionKind: call.executionKind
|
|
4100
|
+
executionKind: call.executionKind,
|
|
4101
|
+
executionClass: call.executionClass,
|
|
4102
|
+
replay: currentContracts[call.toolName]
|
|
3946
4103
|
})));
|
|
3947
4104
|
}
|
|
3948
4105
|
const head = preparedRecords[0];
|
|
@@ -4010,14 +4167,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4010
4167
|
}
|
|
4011
4168
|
const coverable = sourceBoundaries.filter((boundary) => commit.kind !== "summarize" || ownerFirstSequence === void 0 || boundary.sequence < ownerFirstSequence);
|
|
4012
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);
|
|
4013
4172
|
let matchingPrefix = 0;
|
|
4014
4173
|
let requiredThrough = 0;
|
|
4015
|
-
const comparisonLimit = Math.min(
|
|
4174
|
+
const comparisonLimit = Math.min(comparisonView.messages.length, commit.through);
|
|
4016
4175
|
for (let index = 0; index < commit.through; index += 1) {
|
|
4017
4176
|
const message = commit.source.content[index];
|
|
4018
4177
|
if (message !== void 0 && (commit.kind === "clear-tool-results" ? message.role === "tool" : message.role !== "system")) requiredThrough = index + 1;
|
|
4019
4178
|
if (index >= comparisonLimit || index !== matchingPrefix || message === void 0) continue;
|
|
4020
|
-
const canonicalMessage =
|
|
4179
|
+
const canonicalMessage = comparisonView.messages[index];
|
|
4021
4180
|
if (canonicalMessage === void 0) continue;
|
|
4022
4181
|
const encode = (entry) => Schema.encodeEffect(Prompt.Message)(entry).pipe(Effect.mapError((cause) => CompactionError.make({
|
|
4023
4182
|
message: "Could not encode the canonical compaction prefix",
|
|
@@ -4033,7 +4192,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4033
4192
|
if (candidate === void 0) continue;
|
|
4034
4193
|
const following = coverable[index + 1];
|
|
4035
4194
|
if (following !== void 0 && following.tag !== "ModelResponseRecorded") continue;
|
|
4036
|
-
const length = candidate.promptLength;
|
|
4195
|
+
const length = comparisonView.prefixLength(candidate.promptLength);
|
|
4037
4196
|
if (length === 0 || length > commit.through) continue;
|
|
4038
4197
|
if (length < requiredThrough || length > matchingPrefix) continue;
|
|
4039
4198
|
lastCovered = candidate;
|
|
@@ -4576,6 +4735,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4576
4735
|
history: pending === void 0 ? journal.historyBefore : resumeProjection.historyBefore,
|
|
4577
4736
|
onHistory,
|
|
4578
4737
|
input,
|
|
4738
|
+
...journal.committedTurns > 0 || Schema.is(FrameworkMessage)(submission.messageAdmission) ? { retainedInput: submission.inputPayload } : {},
|
|
4579
4739
|
...Schema.is(FrameworkMessage)(submission.messageAdmission) ? { frameworkMessage: submission.messageAdmission } : {},
|
|
4580
4740
|
approval,
|
|
4581
4741
|
toolAuthorization,
|
|
@@ -4591,12 +4751,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4591
4751
|
...submission.workerAdmission === void 0 ? {} : { toolCallAllowance: submission.workerAdmission.origin.toolCallAllowance },
|
|
4592
4752
|
...submission.parentLinkage !== void 0 && childLineage?._tag === "SubagentLineageRecorded" ? { toolCallAllowance: childLineage.toolCallAllowance } : {},
|
|
4593
4753
|
...pending === void 0 ? {} : { resume: {
|
|
4754
|
+
...settledCompletion === void 0 ? {} : { settledCompletion },
|
|
4594
4755
|
turn: pending.turn,
|
|
4595
4756
|
turnId: pending.turnId,
|
|
4596
4757
|
calls: pending.calls,
|
|
4597
4758
|
toolParameterRejections: pending.toolParameterRejections,
|
|
4598
4759
|
settled: pending.settled,
|
|
4599
|
-
|
|
4760
|
+
toolExposure: pending.toolExposure ?? Snapshot.make({ exposedToolNames: [...new Set(pending.calls.map((call) => call.name))] }),
|
|
4600
4761
|
...resumeLeadingMessages === void 0 ? {} : { leadingMessages: resumeLeadingMessages },
|
|
4601
4762
|
...resumeResponseMessages === void 0 ? {} : { responseMessages: resumeResponseMessages }
|
|
4602
4763
|
} },
|
|
@@ -4618,7 +4779,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4618
4779
|
},
|
|
4619
4780
|
...journal.contextWindowId === void 0 ? {} : { initialContextWindowId: journal.contextWindowId },
|
|
4620
4781
|
...journal.protectedContext === void 0 ? {} : { protectedContext: journal.protectedContext },
|
|
4621
|
-
...
|
|
4782
|
+
...pendingContextToolCallId === void 0 ? {} : { pendingContextToolCallId }
|
|
4622
4783
|
};
|
|
4623
4784
|
const commitPendingTurn = Effect.gen(function* () {
|
|
4624
4785
|
const state = yield* Ref.get(stateRef);
|
|
@@ -4664,7 +4825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4664
4825
|
const completion = agent.definition.completion;
|
|
4665
4826
|
const completionPart = resultParts[0];
|
|
4666
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;
|
|
4667
|
-
const batch = yield* turnResultsBatch({
|
|
4828
|
+
const batch = yield* withCrypto(turnResultsBatch({
|
|
4668
4829
|
toolSelections: stagedToolSelections,
|
|
4669
4830
|
budgetRejectedCalls,
|
|
4670
4831
|
runId,
|
|
@@ -4675,7 +4836,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4675
4836
|
deploymentId: config.deploymentId,
|
|
4676
4837
|
createdAt,
|
|
4677
4838
|
...runCompletion === void 0 ? {} : { runCompletion }
|
|
4678
|
-
});
|
|
4839
|
+
}));
|
|
4679
4840
|
let pendingBatch = batch;
|
|
4680
4841
|
if (settledCompletionPart !== void 0) {
|
|
4681
4842
|
const completionRecord = batch.records.find((record) => record.payload._tag === "RunCompleted");
|
|
@@ -4946,7 +5107,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
4946
5107
|
const ctx = yield* attemptContextFor(threadId, claim.producerEpoch);
|
|
4947
5108
|
let controlThrough = (yield* store.inspectTail(ThreadTailRequest.make({ threadId }))).tailSequence;
|
|
4948
5109
|
const initialThrough = controlThrough;
|
|
4949
|
-
const initialView = yield* recoveryView(threadId, initialThrough, [submissionId]);
|
|
5110
|
+
const initialView = yield* recoveryView(threadId, initialThrough, [submissionId], runIdForSubmission(submissionId));
|
|
4950
5111
|
let journalMetadata = initialView.seed === void 0 ? makeJournalMetadata(runIdForSubmission(submissionId)) : void 0;
|
|
4951
5112
|
const retainControl = controlRecords([submissionId]);
|
|
4952
5113
|
const collectControl = (record) => {
|
|
@@ -5005,7 +5166,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5005
5166
|
};
|
|
5006
5167
|
let expiredChildObligation = false;
|
|
5007
5168
|
if ((yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(runTiming.deadline)) {
|
|
5008
|
-
yield*
|
|
5169
|
+
yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
|
|
5009
5170
|
expiredChildObligation = yield* completeJoinedReleases(submission);
|
|
5010
5171
|
}
|
|
5011
5172
|
const ordinaryCallsResolved = Effect.gen(function* () {
|
|
@@ -5014,10 +5175,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5014
5175
|
const reconciliationEvidence = yield* evidenceFor(reconciliationRecords, submissionId, true, reconciliationSnapshot.hostSubmissionId);
|
|
5015
5176
|
if (reconciliationEvidence.openToolCalls.length === 0) return true;
|
|
5016
5177
|
for (const envelope of reconciliationRecords) knownIds.add(envelope.record.recordId);
|
|
5017
|
-
const
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
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)
|
|
5021
5181
|
});
|
|
5022
5182
|
if (review.uncertain.length > 0 || review.unproven.length > 0) {
|
|
5023
5183
|
if (review.uncertain.length > 0) yield* markCallsUnknown(ctx, submissionId, knownIds, review.uncertain, "An ordinary Tool call may have executed without a canonical outcome");
|
|
@@ -5031,6 +5191,74 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5031
5191
|
return true;
|
|
5032
5192
|
});
|
|
5033
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
|
+
}
|
|
5034
5262
|
const lineage = {
|
|
5035
5263
|
attemptId: claim.attemptId,
|
|
5036
5264
|
supersededEpoch,
|
|
@@ -5099,7 +5327,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5099
5327
|
}));
|
|
5100
5328
|
yield* hit("run:after-duration-append");
|
|
5101
5329
|
}
|
|
5102
|
-
yield*
|
|
5330
|
+
yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
|
|
5103
5331
|
}
|
|
5104
5332
|
if (yield* completeJoinedReleases(submission)) {
|
|
5105
5333
|
if (outcome._tag === "failed") {
|
|
@@ -5168,24 +5396,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5168
5396
|
const resolution = yield* resolve(submission).pipe(Effect.map((binding) => ({
|
|
5169
5397
|
_tag: "resolved",
|
|
5170
5398
|
binding
|
|
5171
|
-
})), Effect.catchTags({
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
}),
|
|
5176
|
-
BindingDigestMismatch: (failure) => Effect.succeed({
|
|
5177
|
-
_tag: "refused",
|
|
5178
|
-
failure
|
|
5179
|
-
})
|
|
5180
|
-
}));
|
|
5399
|
+
})), Effect.catchTags({ BindingUnavailable: (failure) => Effect.succeed({
|
|
5400
|
+
_tag: "refused",
|
|
5401
|
+
failure
|
|
5402
|
+
}) }));
|
|
5181
5403
|
if (resolution._tag === "refused") return yield* resolution.failure;
|
|
5182
5404
|
return yield* resolution.binding.attempt((agent, attemptThreadId, attemptClaim) => runAttempt(agent, attemptThreadId, attemptClaim, tokenRef, options?.yieldAfter), threadId, claim);
|
|
5183
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)))));
|
|
5184
5407
|
const drainThread = (resolve, threadId) => Effect.gen(function* () {
|
|
5185
5408
|
const settlements = [];
|
|
5186
5409
|
while (true) {
|
|
5410
|
+
const before = yield* eligibleThreadHead(threadId);
|
|
5187
5411
|
const settlement = yield* processThreadHead(resolve, threadId);
|
|
5188
|
-
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
|
+
}
|
|
5189
5417
|
settlements.push(settlement.value);
|
|
5190
5418
|
}
|
|
5191
5419
|
});
|
|
@@ -5196,25 +5424,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5196
5424
|
message: `This worker is bound to Agent ${binding.agentId}; the claimed head belongs to Agent ${submission.agentId} (SUB-023)`
|
|
5197
5425
|
})), threadId);
|
|
5198
5426
|
});
|
|
5199
|
-
const
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
const
|
|
5204
|
-
if (candidate === void 0 || candidates.length !== 1) return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests);
|
|
5205
|
-
if (bindingSupports(candidate, submission.agentDigests)) return candidate;
|
|
5206
|
-
const records = yield* Effect.gen(function* () {
|
|
5207
|
-
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId: submission.threadId }));
|
|
5208
|
-
return yield* Stream.runCollect(canonicalRange(submission.threadId, tail.tailSequence).pipe(Stream.filter(controlRecords([submission.submissionId]))));
|
|
5209
|
-
}).pipe(Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed([])));
|
|
5210
|
-
const completionTools = candidate === void 0 ? [] : [...candidate.definition.completion === void 0 ? [] : [candidate.definition.completion.tool], ...candidate.definition.completionFromTools?.map((entry) => entry.tool) ?? []];
|
|
5211
|
-
const pending = yield* pendingToolBatchFor(records, runIdForSubmission(submission.submissionId), completionTools);
|
|
5212
|
-
return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests, pending?.calls.filter((call) => call.providerExecuted !== true || completionTools.includes(call.name)).map((call) => call.name) ?? []);
|
|
5213
|
-
});
|
|
5214
|
-
const processThreadResolvedImpl = (threadId) => drainThread(resolveRetainedBinding, threadId);
|
|
5215
|
-
const processThreadHeadImpl = (threadId, options) => processThreadHead(resolveRetainedBinding, threadId, options);
|
|
5216
|
-
const claimFor = Effect.fn("DurableAgentRuntime.claimFor")(function* (submission) {
|
|
5217
|
-
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);
|
|
5218
5432
|
if (Option.isNone(head) || head.value.submissionId !== submission.submissionId) return Option.none();
|
|
5219
5433
|
const claimed = yield* acquireClaim(submission.threadId);
|
|
5220
5434
|
if (Option.isNone(claimed)) return Option.none();
|
|
@@ -5225,35 +5439,40 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5225
5439
|
})).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
|
|
5226
5440
|
return Option.none();
|
|
5227
5441
|
}
|
|
5442
|
+
yield* annotateRepair(submission.threadId, decision, claimed.value.claim.producerEpoch);
|
|
5228
5443
|
return Option.some(claimed.value.claim);
|
|
5229
5444
|
});
|
|
5230
5445
|
/**
|
|
5231
|
-
* DUR-013 audit
|
|
5232
|
-
*
|
|
5233
|
-
*
|
|
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.
|
|
5234
5449
|
*/
|
|
5235
|
-
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;
|
|
5236
5457
|
const encodedDecision = yield* Schema.encodeEffect(RecoveryDecision)(decision).pipe(Effect.orDie);
|
|
5237
5458
|
const details = yield* Schema.decodeEffect(PersistedJson)(encodedDecision).pipe(Effect.orDie);
|
|
5238
|
-
const envelope = yield* makeEnvelope(recoveryRepairRecordId(submissionId, decision._tag), RepairAnnotated.make({
|
|
5459
|
+
const envelope = yield* makeEnvelope(recoveryRepairRecordId(decision.submissionId, decision._tag), RepairAnnotated.make({
|
|
5239
5460
|
reason: `recovery:${decision._tag}`,
|
|
5240
5461
|
details
|
|
5241
5462
|
}));
|
|
5242
|
-
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
|
|
5243
5463
|
yield* store.append(FencedAppendRequest.make({
|
|
5244
5464
|
threadId,
|
|
5245
5465
|
batch: CanonicalBatch.make({
|
|
5246
|
-
batchId: recoveryRepairBatchId(submissionId, decision._tag),
|
|
5466
|
+
batchId: recoveryRepairBatchId(decision.submissionId, decision._tag),
|
|
5247
5467
|
producerId: config.producerId,
|
|
5248
5468
|
records: [envelope]
|
|
5249
5469
|
}),
|
|
5250
5470
|
expectedTailSequence: tail.tailSequence,
|
|
5251
5471
|
expectedTailDigest: tail.tailDigest,
|
|
5252
|
-
producerEpoch
|
|
5472
|
+
producerEpoch
|
|
5253
5473
|
}));
|
|
5254
|
-
yield* wake.notify(threadId);
|
|
5255
5474
|
}).pipe(Effect.ignore);
|
|
5256
|
-
const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records,
|
|
5475
|
+
const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records, decision) {
|
|
5257
5476
|
const intent = snapshot.abortIntent;
|
|
5258
5477
|
if (intent === void 0) return "deferred";
|
|
5259
5478
|
const submission = snapshot.submission;
|
|
@@ -5280,7 +5499,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5280
5499
|
}
|
|
5281
5500
|
}
|
|
5282
5501
|
}
|
|
5283
|
-
const claimed = yield* claimFor(submission);
|
|
5502
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5284
5503
|
if (Option.isNone(claimed)) {
|
|
5285
5504
|
if (submission.state === "ready" && snapshot.ownership === void 0) return yield* Effect.gen(function* () {
|
|
5286
5505
|
yield* materializeAtLeast(submission.threadId, ZERO_EPOCH);
|
|
@@ -5308,15 +5527,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5308
5527
|
return "repaired";
|
|
5309
5528
|
});
|
|
5310
5529
|
/**
|
|
5311
|
-
* Execute the reconcile-then-mark flow for a `MarkUnknown` decision (plan §2.2).
|
|
5312
|
-
*
|
|
5313
|
-
*
|
|
5314
|
-
*
|
|
5315
|
-
*
|
|
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.
|
|
5316
5535
|
*/
|
|
5317
5536
|
const markUnknownForRecovery = Effect.fn("DurableAgentRuntime.markUnknownForRecovery")(function* (snapshot, evidence, records, decision) {
|
|
5318
5537
|
const submission = snapshot.submission;
|
|
5319
|
-
const claimed = yield* claimFor(submission);
|
|
5538
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5320
5539
|
if (Option.isNone(claimed)) return "deferred";
|
|
5321
5540
|
const claim = claimed.value;
|
|
5322
5541
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5326,7 +5545,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5326
5545
|
const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
|
|
5327
5546
|
const knownIds = knownRecordIdsOf(records);
|
|
5328
5547
|
const markableIds = new Set(decision.openToolCallIds);
|
|
5329
|
-
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));
|
|
5330
5549
|
let disposition;
|
|
5331
5550
|
if (review.uncertain.length > 0) {
|
|
5332
5551
|
yield* markCallsUnknown(ctx, submission.submissionId, knownIds, review.uncertain, decision.reason);
|
|
@@ -5339,17 +5558,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5339
5558
|
})).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
|
|
5340
5559
|
return disposition;
|
|
5341
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
|
+
});
|
|
5342
5573
|
/**
|
|
5343
|
-
*
|
|
5344
|
-
*
|
|
5345
|
-
* exist); the wake itself is the ledger's idempotent re-check on intent replay (WP2), so a
|
|
5346
|
-
* 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.
|
|
5347
5576
|
*/
|
|
5348
|
-
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";
|
|
5349
5579
|
const submission = snapshot.submission;
|
|
5350
|
-
const ctx = yield* attemptContextAtTail(submission.threadId);
|
|
5351
|
-
const knownIds = knownRecordIdsOf(records);
|
|
5352
|
-
if (!(yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls, knownIds, () => void 0).pipe(Effect.as(true), Effect.catchTag("FenceRejected", () => Effect.succeed(false))))) return "deferred";
|
|
5353
5580
|
for (const intent of snapshot.unknownResolutions) yield* ledger.recordUnknownResolution(UnknownResolutionCommand.make({
|
|
5354
5581
|
submissionId: intent.submissionId,
|
|
5355
5582
|
toolCallId: intent.toolCallId,
|
|
@@ -5374,14 +5601,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5374
5601
|
* permit, and stays `deferred`. Decisions that raced ahead of the repair leave the lane to a
|
|
5375
5602
|
* worker's batch resume (`deferred`).
|
|
5376
5603
|
*/
|
|
5377
|
-
const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence) {
|
|
5604
|
+
const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence, decision) {
|
|
5378
5605
|
const submission = snapshot.submission;
|
|
5379
5606
|
if (submission.state === "suspended") return "deferred";
|
|
5380
5607
|
const decided = new Set(snapshot.approvalDecisions.map((decision) => decision.toolCallId));
|
|
5381
5608
|
const undecided = evidence.approvalsPending.filter((pending) => !decided.has(pending.toolCallId));
|
|
5382
5609
|
const first = undecided[0];
|
|
5383
5610
|
if (first === void 0) return "deferred";
|
|
5384
|
-
const claimed = yield* claimFor(submission);
|
|
5611
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5385
5612
|
if (Option.isNone(claimed)) return "deferred";
|
|
5386
5613
|
const claim = claimed.value;
|
|
5387
5614
|
const outcome = yield* ledger.suspend(SuspendRequest.make({
|
|
@@ -5416,7 +5643,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5416
5643
|
const runId = runIdForSubmission(submission.submissionId);
|
|
5417
5644
|
const start = yield* canonicalRunStartFromRecords(records, runId);
|
|
5418
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"))) {
|
|
5419
|
-
const claimed = yield* claimFor(submission);
|
|
5646
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5420
5647
|
if (Option.isNone(claimed)) return "deferred";
|
|
5421
5648
|
const claim = claimed.value;
|
|
5422
5649
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5424,7 +5651,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5424
5651
|
producerEpoch: claim.producerEpoch
|
|
5425
5652
|
}));
|
|
5426
5653
|
const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
|
|
5427
|
-
yield*
|
|
5654
|
+
yield* reconcileRetainedChildren(ctx, submission, claim.ownershipToken);
|
|
5428
5655
|
const open = yield* completeJoinedReleases(submission);
|
|
5429
5656
|
yield* ledger.releaseOwnership(ReleaseOwnershipRequest.make({
|
|
5430
5657
|
submissionId: submission.submissionId,
|
|
@@ -5439,7 +5666,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5439
5666
|
case "MarkUnknown": return yield* markUnknownForRecovery(snapshot, evidence, records, decision);
|
|
5440
5667
|
case "ApplyUnknownResolutions": return yield* applyUnknownResolutionsForRecovery(snapshot, evidence, records);
|
|
5441
5668
|
case "AwaitUnknownResolution": return "unknown";
|
|
5442
|
-
case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence);
|
|
5669
|
+
case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence, decision);
|
|
5443
5670
|
case "ResumeSuspended": return yield* resumeSuspendedForRecovery(snapshot);
|
|
5444
5671
|
case "RevertJoining":
|
|
5445
5672
|
yield* ledger.revertJoining(RevertJoiningRequest.make({ submissionId: submission.submissionId }));
|
|
@@ -5460,7 +5687,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5460
5687
|
}
|
|
5461
5688
|
const host = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: hostSubmissionId }));
|
|
5462
5689
|
if (Option.isNone(host)) return "deferred";
|
|
5463
|
-
const claimed = yield* claimFor(host.value);
|
|
5690
|
+
const claimed = yield* claimFor(host.value, decision);
|
|
5464
5691
|
if (Option.isNone(claimed)) return "deferred";
|
|
5465
5692
|
const claim = claimed.value;
|
|
5466
5693
|
yield* ledger.markJoined(MarkJoinedRequest.make({
|
|
@@ -5499,7 +5726,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5499
5726
|
return "repaired";
|
|
5500
5727
|
case "ApplyInput":
|
|
5501
5728
|
case "RepairInputMarker": {
|
|
5502
|
-
const claimed = yield* claimFor(submission);
|
|
5729
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5503
5730
|
if (Option.isNone(claimed)) return "deferred";
|
|
5504
5731
|
const claim = claimed.value;
|
|
5505
5732
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5521,7 +5748,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5521
5748
|
case "AppendReservedSettlement": {
|
|
5522
5749
|
const reservation = snapshot.reservation;
|
|
5523
5750
|
if (reservation === void 0) return "deferred";
|
|
5524
|
-
const claimed = yield* claimFor(submission);
|
|
5751
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5525
5752
|
if (Option.isNone(claimed)) {
|
|
5526
5753
|
if (reservation.outcome === "aborted" && snapshot.abortIntent !== void 0 && snapshot.ownership === void 0) {
|
|
5527
5754
|
const ctx = yield* attemptContextAtTail(submission.threadId);
|
|
@@ -5558,7 +5785,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5558
5785
|
if (requestedPayload === void 0) return "deferred";
|
|
5559
5786
|
const admission = yield* establishChildFromRequest(submission, requestedPayload);
|
|
5560
5787
|
if (admission._tag === "indeterminate") return "deferred";
|
|
5561
|
-
const claimed = yield* claimFor(submission);
|
|
5788
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5562
5789
|
if (Option.isNone(claimed)) return "deferred";
|
|
5563
5790
|
const claim = claimed.value;
|
|
5564
5791
|
yield* store.materialize(ThreadMaterialization.make({
|
|
@@ -5598,7 +5825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5598
5825
|
return "repaired";
|
|
5599
5826
|
}
|
|
5600
5827
|
case "EnsureWaitingForChild": {
|
|
5601
|
-
const claimed = yield* claimFor(submission);
|
|
5828
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5602
5829
|
if (Option.isNone(claimed)) return "deferred";
|
|
5603
5830
|
const claim = claimed.value;
|
|
5604
5831
|
const suspension = yield* ledger.suspend(SuspendRequest.make({
|
|
@@ -5653,7 +5880,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5653
5880
|
if (Option.isSome(childRow)) yield* wake.notify(childRow.value.threadId);
|
|
5654
5881
|
}
|
|
5655
5882
|
if (submission.state !== "suspended") {
|
|
5656
|
-
const claimed = yield* claimFor(submission);
|
|
5883
|
+
const claimed = yield* claimFor(submission, decision);
|
|
5657
5884
|
if (Option.isNone(claimed)) return "deferred";
|
|
5658
5885
|
const claim = claimed.value;
|
|
5659
5886
|
const suspension = yield* ledger.suspend(SuspendRequest.make({
|
|
@@ -5697,7 +5924,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5697
5924
|
const evidence = yield* evidenceFor(history.records, submission.submissionId, history.materialized, snapshot.hostSubmissionId);
|
|
5698
5925
|
const decision = classifyRecovery(snapshot, evidence);
|
|
5699
5926
|
const disposition = decision._tag === "ApplyInput" && snapshot.submission.state === "ready" ? "deferred" : yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, history.records, history));
|
|
5700
|
-
if (disposition === "repaired") yield* annotateRepair(submission.threadId, submission.submissionId, decision);
|
|
5701
5927
|
return RecoveryReport.make({
|
|
5702
5928
|
submissionId: submission.submissionId,
|
|
5703
5929
|
threadId: submission.threadId,
|
|
@@ -5754,10 +5980,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5754
5980
|
reason: cause.reason === "storage" || cause.reason === "unavailable" ? "unavailable" : "refused",
|
|
5755
5981
|
code: `message-${cause.reason}`
|
|
5756
5982
|
})));
|
|
5757
|
-
if (options.definitions.replay !== void 0 && workerAdmission === void 0 && messageAdmission === void 0 && !registeredBindings.some((binding) => binding.agentId === agent.definition.id &&
|
|
5758
|
-
...retained.digests,
|
|
5759
|
-
replay: retained.replay
|
|
5760
|
-
}), 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))) {
|
|
5761
5984
|
const retained = yield* ledger.lookup(SubmissionLookupByKey.make({
|
|
5762
5985
|
threadId: options.threadId,
|
|
5763
5986
|
principal: options.principal,
|
|
@@ -5976,7 +6199,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
5976
6199
|
const nowMillis = yield* Clock.currentTimeMillis;
|
|
5977
6200
|
const explainedAt = yield* nowUtc;
|
|
5978
6201
|
const runId = runIdForSubmission(submission.submissionId);
|
|
5979
|
-
const resolvedIds = new Set(
|
|
6202
|
+
const resolvedIds = new Set(read.records.flatMap(({ record }) => record.payload._tag === "ToolCallSettled" && record.payload.runId === runId ? [record.payload.toolCallId] : []));
|
|
5980
6203
|
const unknownCalls = [];
|
|
5981
6204
|
for (const envelope of read.records) {
|
|
5982
6205
|
const payload = envelope.record.payload;
|
|
@@ -6009,6 +6232,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6009
6232
|
inputRecorded: evidence.inputRecorded,
|
|
6010
6233
|
abortRecorded: evidence.abortRecorded,
|
|
6011
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] : []),
|
|
6012
6236
|
openDelegationCalls: evidence.openDelegationCalls,
|
|
6013
6237
|
approvalsPending: evidence.approvalsPending,
|
|
6014
6238
|
unknownCalls,
|
|
@@ -6023,7 +6247,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6023
6247
|
}),
|
|
6024
6248
|
decision,
|
|
6025
6249
|
decisionMeaning: recoveryDecisionMeaning(decision._tag),
|
|
6026
|
-
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),
|
|
6027
6254
|
explainedAt
|
|
6028
6255
|
});
|
|
6029
6256
|
});
|
|
@@ -6101,9 +6328,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6101
6328
|
});
|
|
6102
6329
|
/**
|
|
6103
6330
|
* Safe re-drive of exactly one Submission's recovery decision (plan §3): classify, execute
|
|
6104
|
-
* the ONE repair the classifier names,
|
|
6105
|
-
*
|
|
6106
|
-
* 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
|
|
6107
6334
|
* on Unknown Outcomes (`resolveUnknown`, DUR-017), and lanes awaiting approval decisions
|
|
6108
6335
|
* (`resolveApproval`). The mandatory `author`/`reason` audit fields (SEC-011) annotate the
|
|
6109
6336
|
* structured operator log.
|
|
@@ -6128,7 +6355,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6128
6355
|
submissionId: command.submissionId,
|
|
6129
6356
|
refusal: "await-unknown-resolution",
|
|
6130
6357
|
decisionTag: decision._tag,
|
|
6131
|
-
message: "
|
|
6358
|
+
message: "This Submission is parked on Unknown Outcomes; resolve them through the authorized resolveUnknown path (DUR-017) instead of retrying."
|
|
6132
6359
|
});
|
|
6133
6360
|
if (decision._tag === "AwaitApprovalDecision") return yield* RetryRefused.make({
|
|
6134
6361
|
submissionId: command.submissionId,
|
|
@@ -6144,7 +6371,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6144
6371
|
decision: decision._tag
|
|
6145
6372
|
}));
|
|
6146
6373
|
const disposition = yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, read.records, read));
|
|
6147
|
-
if (disposition === "repaired") yield* annotateRepair(submission.threadId, command.submissionId, decision);
|
|
6148
6374
|
yield* wake.notify(submission.threadId);
|
|
6149
6375
|
return RecoveryReport.make({
|
|
6150
6376
|
submissionId: command.submissionId,
|
|
@@ -6181,8 +6407,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6181
6407
|
case "unknown": {
|
|
6182
6408
|
blockedOn = "unknown";
|
|
6183
6409
|
const records = (yield* readAllTolerant(submission.threadId, [submission.submissionId])).records;
|
|
6184
|
-
const resolvedIds = new Set(snapshot.unknownResolutions.map((intent) => intent.toolCallId));
|
|
6185
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] : []));
|
|
6186
6412
|
let earliest;
|
|
6187
6413
|
for (const envelope of records) {
|
|
6188
6414
|
const payload = envelope.record.payload;
|
|
@@ -6334,19 +6560,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
|
|
|
6334
6560
|
* the next model request, reattaches through the prompt-coverage rule after a crash, and the
|
|
6335
6561
|
* joined Submissions settle with the host outcome (DUR-002/DUR-016).
|
|
6336
6562
|
* - `runWorker(agent)` — scan-seeded, wake-driven worker loop over every lane (WP4's host driver),
|
|
6337
|
-
*
|
|
6338
|
-
*
|
|
6339
|
-
* 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.
|
|
6340
6565
|
* - `processThreadResolved(threadId)` / `runResolvedWorker` — the
|
|
6341
|
-
*
|
|
6342
|
-
*
|
|
6343
|
-
*
|
|
6344
|
-
* `ChildCompatibilityFailure` (no application code, the child never executes), and an
|
|
6345
|
-
* 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.
|
|
6346
6569
|
* - `runRecovery()` — classify every nonterminal Submission with the pure `classifyRecovery` and
|
|
6347
|
-
* execute
|
|
6570
|
+
* execute repair decisions, annotating owned repair attempts with their granted epochs
|
|
6348
6571
|
* (DUR-013). Untouched ready input and model-resuming work are reported `deferred` for a
|
|
6349
|
-
* worker claim;
|
|
6572
|
+
* worker claim; Submissions parked on
|
|
6350
6573
|
* Unknown Outcomes are reported `unknown`. The S2 binding-free Subagent executors (admission
|
|
6351
6574
|
* completion, start-link repair, waiting restoration, wake replay, canonical join accounting,
|
|
6352
6575
|
* abort propagation, orphan reservation release) run here; the settlement join itself is
|
|
@@ -6381,4 +6604,4 @@ var DurableAgentRuntime = class DurableAgentRuntime extends Context.Service()("@
|
|
|
6381
6604
|
//#endregion
|
|
6382
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 };
|
|
6383
6606
|
|
|
6384
|
-
//# sourceMappingURL=DurableAgentRuntime-
|
|
6607
|
+
//# sourceMappingURL=DurableAgentRuntime-gF4YiEj2.mjs.map
|