@yielded/agent 0.1.0-beta.173 → 0.1.0-beta.175
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/{Admin-BX9ns28E.d.mts → Admin-Qa3I1oA5.d.mts} +61 -21
- package/dist/{Agent-9aBoIi74.d.mts → Agent-B39jRnFv.d.mts} +2 -2
- package/dist/{Agent-0WvBWWbM.mjs → Agent-BiRbr73L.mjs} +7 -6
- package/dist/Agent-BiRbr73L.mjs.map +1 -0
- package/dist/{AgentRuntime-BfP14534.d.mts → AgentRuntime-CHDyRbSf.d.mts} +12 -12
- package/dist/{AgentRuntime-CQ-qcETl.mjs → AgentRuntime-DaDKMasp.mjs} +290 -273
- package/dist/AgentRuntime-DaDKMasp.mjs.map +1 -0
- package/dist/{Digest-DCuPlhNk.mjs → Digest-C3EODOee.mjs} +42 -15
- package/dist/Digest-C3EODOee.mjs.map +1 -0
- package/dist/{DurableAgentRuntime-BAmSkWYr.mjs → DurableAgentRuntime-CPlw5_7e.mjs} +1557 -1458
- package/dist/DurableAgentRuntime-CPlw5_7e.mjs.map +1 -0
- package/dist/{EventSource-BwliwQz9.d.mts → EventSource-BTu68yDW.d.mts} +2 -2
- package/dist/InMemory.d.mts +2 -2
- package/dist/{MessageDelivery-B943ebI_.d.mts → MessageDelivery-DjAZJ4Fd.d.mts} +42 -42
- package/dist/{Messaging-C_6OOg_s.d.mts → Messaging-SVu6rWV8.d.mts} +12 -6
- package/dist/{MessagingHost-B8oKM7AC.d.mts → MessagingHost-C6SOAcoG.d.mts} +2 -2
- package/dist/{MessagingHost-CRUK4JBi.mjs → MessagingHost-CZCyrnvJ.mjs} +27 -26
- package/dist/MessagingHost-CZCyrnvJ.mjs.map +1 -0
- package/dist/{Output-Dsx6k52t.d.mts → Output-CRamN1Fy.d.mts} +2 -2
- package/dist/{PreparedInputAdmission-CxSJSiTl.d.mts → PreparedInputAdmission-wgY4JMZY.d.mts} +3 -3
- package/dist/{Records-BiuJDWNe.mjs → Records-DzL4Kbtw.mjs} +2 -2
- package/dist/{Records-BiuJDWNe.mjs.map → Records-DzL4Kbtw.mjs.map} +1 -1
- package/dist/{RunContinuation-FHM7XPjq.d.mts → RunContinuation-3jB5pULC.d.mts} +13 -7
- package/dist/{RunContinuation-DHYNAQFi.mjs → RunContinuation-BiyKJK32.mjs} +173 -82
- package/dist/RunContinuation-BiyKJK32.mjs.map +1 -0
- package/dist/{RunJournal-kfKVxJj7.mjs → RunJournal-CPMKcfd0.mjs} +106 -73
- package/dist/RunJournal-CPMKcfd0.mjs.map +1 -0
- package/dist/{RunOptions-D8zxFIrZ.d.mts → RunOptions-Be77lKVZ.d.mts} +2 -2
- package/dist/{RunStorage-uqPxps09.mjs → RunStorage-CzutHJBE.mjs} +10 -6
- package/dist/RunStorage-CzutHJBE.mjs.map +1 -0
- package/dist/{Scheduling-CVyfXlBR.mjs → Scheduling-07_dsGED.mjs} +12 -12
- package/dist/Scheduling-07_dsGED.mjs.map +1 -0
- package/dist/{Subagent-vpffemql.mjs → Subagent-CexndZSp.mjs} +53 -40
- package/dist/Subagent-CexndZSp.mjs.map +1 -0
- package/dist/{Subagent-E6tvd7-k.d.mts → Subagent-xhN_aD1r.d.mts} +20 -9
- package/dist/{SubagentHost-B6w2m-SI.d.mts → SubagentHost-D1xBgn9g.d.mts} +2 -2
- package/dist/{Subscription-DONig54Q.d.mts → Subscription-DmIHlOJK.d.mts} +2 -2
- package/dist/{SubscriptionInput-mJIRxWJM.d.mts → SubscriptionInput-DgcQhvbe.d.mts} +2 -2
- package/dist/{Thread-dk-QiNml.d.mts → Thread-DzdJAY4J.d.mts} +4 -3
- package/dist/{ThreadHistory-ByxS8HU1.d.mts → ThreadHistory-KPPBJLJb.d.mts} +12 -2
- package/dist/{ThreadImport-DckUMEqJ.mjs → ThreadImport-CjlG57vH.mjs} +9 -9
- package/dist/ThreadImport-CjlG57vH.mjs.map +1 -0
- package/dist/{ThreadImport-DpHxjBrA.d.mts → ThreadImport-DP-vOk2F.d.mts} +5 -5
- package/dist/{ThreadStore-CEekRa80.mjs → ThreadStore-EuUilthA.mjs} +162 -85
- package/dist/ThreadStore-EuUilthA.mjs.map +1 -0
- package/dist/{ThreadTransfer-B4gTQLIl.mjs → ThreadTransfer-DB_zsrtJ.mjs} +4 -4
- package/dist/{ThreadTransfer-B4gTQLIl.mjs.map → ThreadTransfer-DB_zsrtJ.mjs.map} +1 -1
- package/dist/{WorkerAdmission-DFVbbVh2.mjs → WorkerAdmission-eBeXt_mu.mjs} +5 -5
- package/dist/{WorkerAdmission-DFVbbVh2.mjs.map → WorkerAdmission-eBeXt_mu.mjs.map} +1 -1
- package/dist/{agent-registration-ByMZ_cK5.mjs → agent-registration-a6QYaG3h.mjs} +70 -57
- package/dist/agent-registration-a6QYaG3h.mjs.map +1 -0
- package/dist/capabilities/Approval.mjs +2 -2
- package/dist/capabilities/Approval.mjs.map +1 -1
- package/dist/capabilities/BrowserUse.d.mts +102 -1867
- package/dist/capabilities/BrowserUse.mjs +74 -798
- package/dist/capabilities/BrowserUse.mjs.map +1 -1
- package/dist/capabilities/Budget.mjs +3 -1
- package/dist/capabilities/Budget.mjs.map +1 -1
- package/dist/capabilities/CodeMode.d.mts +1 -1
- package/dist/capabilities/CodeMode.mjs +16 -9
- package/dist/capabilities/CodeMode.mjs.map +1 -1
- package/dist/capabilities/Commands.d.mts +1 -1
- package/dist/capabilities/McpClient.mjs +1 -1
- package/dist/capabilities/McpClient.mjs.map +1 -1
- package/dist/capabilities/Messaging.d.mts +1 -1
- package/dist/capabilities/Messaging.mjs +5 -5
- package/dist/capabilities/Messaging.mjs.map +1 -1
- package/dist/capabilities/ModelContext.d.mts +1 -1
- package/dist/capabilities/ModelContext.mjs +7 -5
- package/dist/capabilities/ModelContext.mjs.map +1 -1
- package/dist/capabilities/Redaction.mjs +49 -19
- package/dist/capabilities/Redaction.mjs.map +1 -1
- package/dist/capabilities/RunHooks.d.mts +2 -2
- package/dist/capabilities/RunHooks.mjs +1 -1
- package/dist/capabilities/RunHooks.mjs.map +1 -1
- package/dist/capabilities/Subagent.d.mts +1 -1
- package/dist/capabilities/Subagent.mjs +1 -1
- package/dist/capabilities/SubagentReservations.mjs +3 -1
- package/dist/capabilities/SubagentReservations.mjs.map +1 -1
- package/dist/capabilities/WebCapture.mjs.map +1 -1
- package/dist/capabilities/WebSearch.mjs +7 -7
- package/dist/capabilities/WebSearch.mjs.map +1 -1
- package/dist/core/Agent.d.mts +1 -1
- package/dist/core/Agent.mjs +1 -1
- package/dist/core/AgentUpdates.mjs +8 -8
- package/dist/core/AgentUpdates.mjs.map +1 -1
- package/dist/core/Thread.d.mts +2 -2
- package/dist/core/Thread.mjs +6 -4
- package/dist/core/Thread.mjs.map +1 -1
- package/dist/core/Usage.mjs +2 -2
- package/dist/core/Usage.mjs.map +1 -1
- package/dist/durable/ActivityStore.mjs +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/Admin.mjs +1 -1
- package/dist/durable/AgentRegistration.d.mts +1 -1
- package/dist/durable/AgentRegistration.mjs +1 -1
- package/dist/durable/Certification.d.mts +1 -1
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/CommittedActivity.mjs +3 -3
- package/dist/durable/Digest.mjs +1 -1
- package/dist/durable/DurableAgentRuntime.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.mjs +2 -2
- package/dist/durable/EventSource.d.mts +1 -1
- package/dist/durable/EventSource.mjs +7 -7
- package/dist/durable/EventSource.mjs.map +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/LifecyclePublication.d.mts +1 -1
- package/dist/durable/LifecyclePublication.mjs +2 -2
- package/dist/durable/MessageDelivery.d.mts +1 -1
- package/dist/durable/MessageDelivery.mjs +2 -2
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +40 -40
- package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
- package/dist/durable/PersistentHistory.d.mts +2 -2
- package/dist/durable/PersistentHistory.mjs +4 -4
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/RecordFormat.mjs +3 -3
- package/dist/durable/RecordFormat.mjs.map +1 -1
- package/dist/durable/Records.mjs +1 -1
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/Recovery.mjs +1 -1
- package/dist/durable/RunContinuation.d.mts +2 -2
- package/dist/durable/RunContinuation.mjs +3 -3
- package/dist/durable/RunJournal.d.mts +2 -2
- package/dist/durable/RunJournal.mjs +2 -2
- package/dist/durable/RunStorage.d.mts +1 -1
- package/dist/durable/RunStorage.mjs +1 -1
- package/dist/durable/Schedule.mjs +1 -1
- package/dist/durable/ScheduleStoreConformance.mjs +1 -1
- package/dist/durable/ScheduleTransition.mjs +1 -1
- package/dist/durable/Scheduling.d.mts +1 -1
- package/dist/durable/Scheduling.mjs +1 -1
- package/dist/durable/SettlementPublisher.d.mts +1 -1
- package/dist/durable/SettlementPublisher.mjs +3 -3
- package/dist/durable/SqlMemoryStore.mjs +5 -3
- package/dist/durable/SqlMemoryStore.mjs.map +1 -1
- package/dist/durable/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedger.mjs +11 -3
- package/dist/durable/SubmissionLedger.mjs.map +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.mjs +6 -6
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/Subscription.mjs +2 -2
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionInput.mjs +4 -4
- package/dist/durable/SubscriptionInput.mjs.map +1 -1
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.mjs +3 -3
- package/dist/durable/SubscriptionStoreConformance.mjs.map +1 -1
- package/dist/durable/SubscriptionTools.d.mts +2 -2
- 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 +3 -3
- package/dist/durable/Subscriptions.mjs.map +1 -1
- package/dist/durable/ThreadArchiveRange.d.mts +1 -1
- package/dist/durable/ThreadArchiveRange.mjs +1 -1
- package/dist/durable/ThreadContextHistory.d.mts +1 -1
- package/dist/durable/ThreadContextHistory.mjs +2 -2
- package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
- package/dist/durable/ThreadImport.d.mts +1 -1
- package/dist/durable/ThreadImport.mjs +1 -1
- package/dist/durable/ThreadInvariants.d.mts +3 -3
- package/dist/durable/ThreadInvariants.mjs +5 -5
- package/dist/durable/ThreadInvariants.mjs.map +1 -1
- package/dist/durable/ThreadProjection.mjs +1 -1
- package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
- package/dist/durable/ThreadReader.d.mts +1 -1
- package/dist/durable/ThreadReader.mjs +1 -1
- package/dist/durable/ThreadStore.d.mts +1 -1
- package/dist/durable/ThreadStore.mjs +2 -2
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/ThreadStoreConformance.mjs +4 -4
- package/dist/durable/ThreadTransfer.d.mts +1 -1
- package/dist/durable/ThreadTransfer.mjs +1 -1
- package/dist/durable/ThreadWork.d.mts +2 -2
- package/dist/durable/ThreadWork.mjs +12 -6
- package/dist/durable/ThreadWork.mjs.map +1 -1
- package/dist/durable/ToolReconciler.mjs +1 -1
- package/dist/durable/WakeScheduler.mjs +23 -39
- package/dist/durable/WakeScheduler.mjs.map +1 -1
- package/dist/durable/WorkerAdmission.d.mts +1 -1
- package/dist/durable/WorkerAdmission.mjs +1 -1
- package/dist/durable/WorkerHost.d.mts +2 -2
- package/dist/engine/AgentRuntime.d.mts +1 -1
- package/dist/engine/AgentRuntime.mjs +1 -1
- package/dist/engine/MessagingHost.d.mts +1 -1
- package/dist/engine/MessagingHost.mjs +1 -1
- package/dist/engine/Output.d.mts +1 -1
- package/dist/engine/Output.mjs +1 -1
- package/dist/engine/RunOptions.d.mts +1 -1
- package/dist/engine/SubagentHost.d.mts +1 -1
- package/dist/engine/ThreadHistory.d.mts +1 -1
- package/dist/engine/ThreadHistory.mjs +3 -2
- package/dist/engine/ThreadHistory.mjs.map +1 -1
- package/dist/index.d.mts +21 -21
- package/dist/index.mjs +16 -16
- package/dist/{output-contract-CMIhu4Cw.mjs → output-contract-DCnLYCdp.mjs} +12 -15
- package/dist/{output-contract-CMIhu4Cw.mjs.map → output-contract-DCnLYCdp.mjs.map} +1 -1
- package/dist/{run-context-reader-OcsZr4bk.d.mts → run-context-reader-DVhJZ1Qe.d.mts} +2 -2
- package/package.json +1 -1
- package/src/capabilities/Approval.ts +58 -60
- package/src/capabilities/BrowserUse.ts +89 -944
- package/src/capabilities/Budget.ts +8 -1
- package/src/capabilities/CodeMode.ts +57 -33
- package/src/capabilities/McpClient.ts +2 -2
- package/src/capabilities/Messaging.ts +51 -55
- package/src/capabilities/ModelContext.ts +5 -2
- package/src/capabilities/Redaction.ts +64 -34
- package/src/capabilities/RunHooks.ts +52 -55
- package/src/capabilities/Subagent.ts +33 -33
- package/src/capabilities/SubagentReservations.ts +9 -1
- package/src/capabilities/WebCapture.ts +6 -0
- package/src/capabilities/WebSearch.ts +10 -7
- package/src/capabilities/internal/subagent-background.ts +202 -158
- package/src/core/Agent.ts +11 -6
- package/src/core/AgentUpdates.ts +8 -8
- package/src/core/Thread.ts +142 -146
- package/src/core/Usage.ts +7 -8
- package/src/core/internal/agent-updates.ts +1 -0
- package/src/durable/DurableAgentRuntime.ts +1024 -829
- package/src/durable/EventSource.ts +15 -9
- package/src/durable/RecordFormat.ts +5 -3
- package/src/durable/RunContinuation.ts +316 -113
- package/src/durable/RunJournal.ts +106 -114
- package/src/durable/RunStorage.ts +129 -124
- package/src/durable/Scheduling.ts +639 -651
- package/src/durable/SqlMemoryStore.ts +8 -6
- package/src/durable/SubmissionLedger.ts +11 -0
- package/src/durable/SubscriptionInput.ts +5 -3
- package/src/durable/SubscriptionStoreConformance.ts +23 -23
- package/src/durable/Subscriptions.ts +18 -19
- package/src/durable/ThreadImport.ts +18 -19
- package/src/durable/ThreadInvariants.ts +2 -1
- package/src/durable/ThreadStore.ts +217 -111
- package/src/durable/ThreadWork.ts +11 -2
- package/src/durable/WakeScheduler.ts +34 -66
- package/src/durable/internal/StorageOwner.ts +10 -4
- package/src/durable/internal/agent-registration.ts +96 -89
- package/src/durable/internal/canonical-json.ts +61 -20
- package/src/durable/internal/prompt-history-cache.ts +13 -4
- package/src/durable/internal/record-encoding.ts +120 -64
- package/src/durable/internal/schedule-time.ts +6 -6
- package/src/durable/internal/usage-totals.ts +35 -0
- package/src/durable/internal/worker-host.ts +69 -74
- package/src/engine/MessagingHost.ts +4 -2
- package/src/engine/ThreadHistory.ts +47 -48
- package/src/engine/internal/agent-runtime.ts +1298 -1217
- package/src/engine/internal/output-contract.ts +15 -15
- package/src/engine/internal/primitive-delta.ts +2 -3
- package/src/engine/internal/provisional-text.ts +12 -10
- package/src/engine/internal/tool-exposure.ts +22 -18
- package/src/engine/internal/tool-telemetry.ts +15 -12
- package/dist/Agent-0WvBWWbM.mjs.map +0 -1
- package/dist/AgentRuntime-CQ-qcETl.mjs.map +0 -1
- package/dist/Digest-DCuPlhNk.mjs.map +0 -1
- package/dist/DurableAgentRuntime-BAmSkWYr.mjs.map +0 -1
- package/dist/MessagingHost-CRUK4JBi.mjs.map +0 -1
- package/dist/RunContinuation-DHYNAQFi.mjs.map +0 -1
- package/dist/RunJournal-kfKVxJj7.mjs.map +0 -1
- package/dist/RunStorage-uqPxps09.mjs.map +0 -1
- package/dist/Scheduling-CVyfXlBR.mjs.map +0 -1
- package/dist/Subagent-vpffemql.mjs.map +0 -1
- package/dist/ThreadImport-DckUMEqJ.mjs.map +0 -1
- package/dist/ThreadStore-CEekRa80.mjs.map +0 -1
- package/dist/agent-registration-ByMZ_cK5.mjs.map +0 -1
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
getToolExecutionKind,
|
|
64
64
|
SubagentParentLink,
|
|
65
65
|
} from "../core/SubagentContract.ts";
|
|
66
|
+
import { Store as ConversationStore, makeMemoryStore } from "../core/Thread.ts";
|
|
66
67
|
import { Selection, Snapshot } from "../core/ToolExposure.ts";
|
|
67
68
|
import type { ToolParameterRejection } from "../core/ToolResult.ts";
|
|
68
69
|
import {
|
|
@@ -280,6 +281,7 @@ import {
|
|
|
280
281
|
modelResponseInterruptedRecordId,
|
|
281
282
|
modelResponseRecordId,
|
|
282
283
|
projectRunJournalStream,
|
|
284
|
+
initialRunJournal,
|
|
283
285
|
type JournalBoundary,
|
|
284
286
|
type RunJournalProjection,
|
|
285
287
|
type RunJournalContext,
|
|
@@ -846,6 +848,11 @@ export class RecoverySweepResult extends Schema.Class<RecoverySweepResult>("Reco
|
|
|
846
848
|
/** Recover one bounded page of native work, optionally confined to one Thread. */
|
|
847
849
|
export interface RecoverySweepOptions {
|
|
848
850
|
readonly threadId?: ThreadId;
|
|
851
|
+
/** Leave this incarnation's untouched root admissions to their first actual claim.
|
|
852
|
+
* A verified sole admission needs no recovery inventory; other owners retain bounded
|
|
853
|
+
* discovery and deferred reports. Neither case supplies ownership or a recovery decision:
|
|
854
|
+
* the Attempt still verifies current controls, journal, fencing and historyDigest. */
|
|
855
|
+
readonly deferUnclaimedAdmissions?: boolean;
|
|
849
856
|
/** Continue the previous pass with the same Thread selection until its result has no cursor. */
|
|
850
857
|
readonly cursor?: string;
|
|
851
858
|
}
|
|
@@ -1374,6 +1381,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1374
1381
|
const reader = ThreadReader.fromStore(store);
|
|
1375
1382
|
const promptHistory = yield* makePromptHistoryCache();
|
|
1376
1383
|
|
|
1384
|
+
// One incarnation remembers only newly accepted root input, never a recovery decision.
|
|
1385
|
+
// A claim consumes the hint before binding/application code can run. Cold owners,
|
|
1386
|
+
// replays, linked inputs, and any changed ledger state retain the recovery path.
|
|
1387
|
+
const unclaimedAdmissions = new Map<SubmissionId, ThreadId>();
|
|
1388
|
+
|
|
1377
1389
|
const workDiscovery = yield* ThreadWorkDiscovery.pipe(
|
|
1378
1390
|
Effect.provide(ThreadWorkDiscovery.layer),
|
|
1379
1391
|
Effect.provideService(ThreadStore, store),
|
|
@@ -1444,6 +1456,17 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1444
1456
|
// observer must never replace this host choice on a fresh or replacement Attempt.
|
|
1445
1457
|
const toolFailureObserver = yield* CurrentToolFailureObserver;
|
|
1446
1458
|
|
|
1459
|
+
const runServices = Context.make(CurrentToolFailureObserver, toolFailureObserver).pipe(
|
|
1460
|
+
Context.add(RunToolVisibility, runToolVisibility),
|
|
1461
|
+
Context.add(RunToolScheduling, runToolScheduling),
|
|
1462
|
+
Context.add(ContextCompactor, compactor),
|
|
1463
|
+
Context.add(RunContextPreparation, runContextPreparation),
|
|
1464
|
+
);
|
|
1465
|
+
|
|
1466
|
+
const journalServices = Context.make(ThreadReader, reader).pipe(
|
|
1467
|
+
Context.add(Crypto.Crypto, crypto),
|
|
1468
|
+
);
|
|
1469
|
+
|
|
1447
1470
|
const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>): Effect.Effect<A, E> =>
|
|
1448
1471
|
Effect.provideService(effect, Crypto.Crypto, crypto);
|
|
1449
1472
|
|
|
@@ -1453,8 +1476,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1453
1476
|
suffix: ReadonlyArray<CanonicalRecordEnvelope>,
|
|
1454
1477
|
) =>
|
|
1455
1478
|
initialContext(original).pipe(
|
|
1456
|
-
Effect.
|
|
1457
|
-
Effect.provideService(Crypto.Crypto, crypto),
|
|
1479
|
+
Effect.provideContext(journalServices),
|
|
1458
1480
|
Effect.map((history) => history.concat(suffix)),
|
|
1459
1481
|
);
|
|
1460
1482
|
|
|
@@ -1662,6 +1684,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1662
1684
|
let context: RunJournalContext | undefined;
|
|
1663
1685
|
let progress: RunContinuation | undefined;
|
|
1664
1686
|
let progressThrough: CanonicalSequence | undefined;
|
|
1687
|
+
let operational: ReadonlyArray<RecordEnvelope> = [];
|
|
1665
1688
|
|
|
1666
1689
|
const invalid = (message: string) =>
|
|
1667
1690
|
ThreadStoreError.make({
|
|
@@ -1707,10 +1730,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1707
1730
|
|
|
1708
1731
|
if (found === undefined) {
|
|
1709
1732
|
// An exact lookup distinguishes missing evidence from an incomplete native index.
|
|
1710
|
-
yield* resolveEvidence(threadId, ref).pipe(
|
|
1711
|
-
Effect.provideService(ThreadReader, reader),
|
|
1712
|
-
Effect.provideService(Crypto.Crypto, crypto),
|
|
1713
|
-
);
|
|
1733
|
+
yield* resolveEvidence(threadId, ref).pipe(Effect.provideContext(journalServices));
|
|
1714
1734
|
|
|
1715
1735
|
return yield* invalid(
|
|
1716
1736
|
"Required Run evidence is absent from its locator; rebuild the index explicitly",
|
|
@@ -1777,8 +1797,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1777
1797
|
} else {
|
|
1778
1798
|
saved = yield* readRunContext(initial, input, cursor.savedContext.recordId).pipe(
|
|
1779
1799
|
Effect.provide(RunContextReader.layer(threadId)),
|
|
1780
|
-
Effect.
|
|
1781
|
-
Effect.provideService(Crypto.Crypto, crypto),
|
|
1800
|
+
Effect.provideContext(journalServices),
|
|
1782
1801
|
Effect.mapError(() =>
|
|
1783
1802
|
invalid("Saved original context cannot be resolved from its evidence"),
|
|
1784
1803
|
),
|
|
@@ -1822,6 +1841,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1822
1841
|
context = saved;
|
|
1823
1842
|
progress = cursor;
|
|
1824
1843
|
progressThrough = envelope.throughSequence;
|
|
1844
|
+
operational = envelope.operational;
|
|
1825
1845
|
}
|
|
1826
1846
|
}
|
|
1827
1847
|
|
|
@@ -1833,7 +1853,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1833
1853
|
).values(),
|
|
1834
1854
|
].sort((left, right) => left.sequence - right.sequence);
|
|
1835
1855
|
|
|
1836
|
-
return {
|
|
1856
|
+
return { records, context, progress, progressThrough, operational };
|
|
1837
1857
|
});
|
|
1838
1858
|
|
|
1839
1859
|
const readControl = Effect.fnUntraced(function* (
|
|
@@ -1843,9 +1863,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1843
1863
|
const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
|
|
1844
1864
|
const view = yield* recoveryView(threadId, tail.tailSequence, submissionIds);
|
|
1845
1865
|
|
|
1846
|
-
return
|
|
1847
|
-
view.canonical.pipe(Stream.filter(controlRecords(submissionIds))),
|
|
1848
|
-
);
|
|
1866
|
+
return view.records.filter(controlRecords(submissionIds));
|
|
1849
1867
|
});
|
|
1850
1868
|
|
|
1851
1869
|
const readAllTolerant = (
|
|
@@ -1888,9 +1906,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1888
1906
|
return { records: [], materialized: false, throughSequence: ZERO_SEQUENCE, submissionIds };
|
|
1889
1907
|
|
|
1890
1908
|
const records = yield* recoveryView(threadId, tail.value.tailSequence, submissionIds).pipe(
|
|
1891
|
-
Effect.
|
|
1892
|
-
Stream.runCollect(view.canonical.pipe(Stream.filter(controlRecords(submissionIds)))),
|
|
1893
|
-
),
|
|
1909
|
+
Effect.map((view) => view.records.filter(controlRecords(submissionIds))),
|
|
1894
1910
|
Effect.catchTag("ThreadNotMaterialized", (error) =>
|
|
1895
1911
|
ThreadStoreError.make({
|
|
1896
1912
|
operation: "read recovery history",
|
|
@@ -2575,8 +2591,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
2575
2591
|
const progress =
|
|
2576
2592
|
active?.writer ??
|
|
2577
2593
|
(yield* makeProgressWriter(writer.threadId, config.deploymentId).pipe(
|
|
2578
|
-
Effect.
|
|
2579
|
-
Effect.provideService(Crypto.Crypto, crypto),
|
|
2594
|
+
Effect.provideContext(journalServices),
|
|
2580
2595
|
));
|
|
2581
2596
|
|
|
2582
2597
|
const owned: RunWriter = {
|
|
@@ -2629,50 +2644,48 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
2629
2644
|
// also covers a destination handoff written before dispatch or after Run settlement.
|
|
2630
2645
|
const progressStore = ThreadStore.of({
|
|
2631
2646
|
...store,
|
|
2632
|
-
append: (request)
|
|
2633
|
-
|
|
2634
|
-
const active = activeProgress.get(request.threadId);
|
|
2647
|
+
append: Effect.fnUntraced(function* (request) {
|
|
2648
|
+
const active = activeProgress.get(request.threadId);
|
|
2635
2649
|
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
));
|
|
2650
|
+
const progress =
|
|
2651
|
+
active?.writer ??
|
|
2652
|
+
(yield* makeProgressWriter(request.threadId, config.deploymentId).pipe(
|
|
2653
|
+
Effect.provideContext(journalServices),
|
|
2654
|
+
));
|
|
2642
2655
|
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2656
|
+
let tail = {
|
|
2657
|
+
sequence: request.expectedTailSequence,
|
|
2658
|
+
};
|
|
2646
2659
|
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2660
|
+
return yield* progress.commit(request.batch).pipe(
|
|
2661
|
+
Effect.provideService(CurrentRunWriter, {
|
|
2662
|
+
threadId: request.threadId,
|
|
2663
|
+
tail: Effect.sync(() => tail),
|
|
2664
|
+
withProgress: (effect) => storageOwner.withProgress(request.threadId, effect),
|
|
2665
|
+
append: (batch, executions) =>
|
|
2666
|
+
store
|
|
2667
|
+
.append(
|
|
2668
|
+
AppendRequest.make({
|
|
2669
|
+
...request,
|
|
2670
|
+
expectedTailSequence: tail.sequence,
|
|
2671
|
+
|
|
2672
|
+
batch,
|
|
2673
|
+
executions,
|
|
2674
|
+
}),
|
|
2675
|
+
)
|
|
2676
|
+
.pipe(
|
|
2677
|
+
Effect.tapErrorTag("AppendConflict", (conflict) =>
|
|
2678
|
+
Effect.sync(() => {
|
|
2679
|
+
if (conflict.reason === "tail" && conflict.actualTailSequence !== undefined)
|
|
2680
|
+
tail = {
|
|
2681
|
+
sequence: conflict.actualTailSequence,
|
|
2682
|
+
};
|
|
2661
2683
|
}),
|
|
2662
|
-
)
|
|
2663
|
-
.pipe(
|
|
2664
|
-
Effect.tapErrorTag("AppendConflict", (conflict) =>
|
|
2665
|
-
Effect.sync(() => {
|
|
2666
|
-
if (conflict.reason === "tail" && conflict.actualTailSequence !== undefined)
|
|
2667
|
-
tail = {
|
|
2668
|
-
sequence: conflict.actualTailSequence,
|
|
2669
|
-
};
|
|
2670
|
-
}),
|
|
2671
|
-
),
|
|
2672
2684
|
),
|
|
2673
|
-
|
|
2674
|
-
)
|
|
2675
|
-
|
|
2685
|
+
),
|
|
2686
|
+
}),
|
|
2687
|
+
);
|
|
2688
|
+
}),
|
|
2676
2689
|
});
|
|
2677
2690
|
|
|
2678
2691
|
const appendBatch = (ctx: Pick<RunWriter, "threadId" | "append">, batch: CanonicalBatch) =>
|
|
@@ -2701,51 +2714,49 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
2701
2714
|
threadId: ctx.threadId,
|
|
2702
2715
|
tail: ctx.tail,
|
|
2703
2716
|
withProgress: ctx.withProgress,
|
|
2704
|
-
publishSettlement: (prepared, executions)
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
.
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
executions,
|
|
2720
|
-
}),
|
|
2721
|
-
}),
|
|
2722
|
-
)
|
|
2723
|
-
.pipe(
|
|
2724
|
-
(effect) =>
|
|
2725
|
-
storageOwner.withMutation(Effect.andThen(ctx.checkActive, effect)),
|
|
2726
|
-
Effect.catchTag("AppendConflict", (conflict) => {
|
|
2727
|
-
if (
|
|
2728
|
-
conflict.reason !== "tail" ||
|
|
2729
|
-
conflict.actualTailSequence === undefined ||
|
|
2730
|
-
retries >= 8
|
|
2731
|
-
)
|
|
2732
|
-
return Effect.fail(conflict);
|
|
2733
|
-
tail = {
|
|
2734
|
-
sequence: conflict.actualTailSequence,
|
|
2735
|
-
};
|
|
2736
|
-
|
|
2737
|
-
if (executions !== undefined && executions.length > 0)
|
|
2738
|
-
return ctx.refresh.pipe(Effect.andThen(Effect.fail(conflict)));
|
|
2739
|
-
|
|
2740
|
-
return Effect.succeed(undefined);
|
|
2717
|
+
publishSettlement: Effect.fnUntraced(function* (prepared, executions) {
|
|
2718
|
+
let tail = yield* ctx.tail;
|
|
2719
|
+
|
|
2720
|
+
for (let retries = 0; ; retries++) {
|
|
2721
|
+
const result = yield* runStorage
|
|
2722
|
+
.publishSettlement(
|
|
2723
|
+
SettlementPublication.make({
|
|
2724
|
+
submissionId,
|
|
2725
|
+
authority,
|
|
2726
|
+
append: AppendRequest.make({
|
|
2727
|
+
threadId: ctx.threadId,
|
|
2728
|
+
expectedTailSequence: tail.sequence,
|
|
2729
|
+
|
|
2730
|
+
batch: prepared,
|
|
2731
|
+
executions,
|
|
2741
2732
|
}),
|
|
2742
|
-
)
|
|
2733
|
+
}),
|
|
2734
|
+
)
|
|
2735
|
+
.pipe(
|
|
2736
|
+
(effect) => storageOwner.withMutation(Effect.andThen(ctx.checkActive, effect)),
|
|
2737
|
+
Effect.catchTag("AppendConflict", (conflict) => {
|
|
2738
|
+
if (
|
|
2739
|
+
conflict.reason !== "tail" ||
|
|
2740
|
+
conflict.actualTailSequence === undefined ||
|
|
2741
|
+
retries >= 8
|
|
2742
|
+
)
|
|
2743
|
+
return Effect.fail(conflict);
|
|
2744
|
+
tail = {
|
|
2745
|
+
sequence: conflict.actualTailSequence,
|
|
2746
|
+
};
|
|
2743
2747
|
|
|
2744
|
-
|
|
2748
|
+
if (executions !== undefined && executions.length > 0)
|
|
2749
|
+
return ctx.refresh.pipe(Effect.andThen(Effect.fail(conflict)));
|
|
2745
2750
|
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2751
|
+
return Effect.succeed(undefined);
|
|
2752
|
+
}),
|
|
2753
|
+
);
|
|
2754
|
+
|
|
2755
|
+
if (result === undefined) continue;
|
|
2756
|
+
|
|
2757
|
+
return result;
|
|
2758
|
+
}
|
|
2759
|
+
}),
|
|
2749
2760
|
}),
|
|
2750
2761
|
);
|
|
2751
2762
|
});
|
|
@@ -3207,15 +3218,23 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
3207
3218
|
yield* ownership.markInputApplied({ recordId, sequence: result.firstSequence });
|
|
3208
3219
|
});
|
|
3209
3220
|
|
|
3210
|
-
const readRunStart = Effect.fnUntraced(function* (
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3221
|
+
const readRunStart = Effect.fnUntraced(function* (
|
|
3222
|
+
threadId: ThreadId,
|
|
3223
|
+
runId: RunId,
|
|
3224
|
+
operational?: ReadonlyArray<RecordEnvelope>,
|
|
3225
|
+
) {
|
|
3226
|
+
if (operational === undefined) {
|
|
3227
|
+
const loaded = yield* reader
|
|
3228
|
+
.readExecution({ threadId, runId })
|
|
3229
|
+
.pipe(Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed(Option.none())));
|
|
3230
|
+
|
|
3231
|
+
if (Option.isNone(loaded)) return undefined;
|
|
3232
|
+
operational = loaded.value.operational;
|
|
3233
|
+
}
|
|
3214
3234
|
|
|
3215
|
-
if (Option.isNone(loaded)) return undefined;
|
|
3216
3235
|
const recordId = runStartedRecordId(runId);
|
|
3217
|
-
const starts =
|
|
3218
|
-
const existing =
|
|
3236
|
+
const starts = operational.filter(({ payload }) => payload._tag === "RunStarted");
|
|
3237
|
+
const existing = operational.find((record) => record.recordId === recordId);
|
|
3219
3238
|
|
|
3220
3239
|
if (
|
|
3221
3240
|
starts.length > 1 ||
|
|
@@ -3234,11 +3253,12 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
3234
3253
|
submission: SubmissionSnapshot,
|
|
3235
3254
|
maxDurationMillis: number,
|
|
3236
3255
|
records: ReadonlyArray<CanonicalRecordEnvelope>,
|
|
3256
|
+
operational: ReadonlyArray<RecordEnvelope>,
|
|
3237
3257
|
) {
|
|
3238
3258
|
const writer = yield* CurrentRunWriter;
|
|
3239
3259
|
const runId = runIdForSubmission(submission.submissionId);
|
|
3240
3260
|
const recordId = runStartedRecordId(runId);
|
|
3241
|
-
const existing = yield* readRunStart(submission.threadId, runId);
|
|
3261
|
+
const existing = yield* readRunStart(submission.threadId, runId, operational);
|
|
3242
3262
|
let start: RecordEnvelope;
|
|
3243
3263
|
let allowance = maxDurationMillis;
|
|
3244
3264
|
let committed = existing !== undefined;
|
|
@@ -4762,70 +4782,71 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
4762
4782
|
knownIds.add(recordId);
|
|
4763
4783
|
});
|
|
4764
4784
|
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
Tools extends Record<string, Tool.Any>,
|
|
4782
|
-
Provider,
|
|
4783
|
-
ModelProvides,
|
|
4784
|
-
ModelRequires,
|
|
4785
|
-
InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
|
|
4786
|
-
InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
|
|
4787
|
-
RunDispositionValue extends
|
|
4788
|
-
| RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
|
|
4789
|
-
| undefined = undefined,
|
|
4790
|
-
InputPromptValue extends InputPromptSource<InputSchema["Type"], unknown, unknown> | undefined =
|
|
4791
|
-
undefined,
|
|
4792
|
-
UpdatesSchema extends Schema.Top | undefined = undefined,
|
|
4793
|
-
>(
|
|
4794
|
-
agent: RuntimeBinding<
|
|
4795
|
-
InputSchema,
|
|
4796
|
-
OutputSchema,
|
|
4785
|
+
const runModel = Effect.fnUntraced(
|
|
4786
|
+
/**
|
|
4787
|
+
* Drive the shared interpreter Effect with history rebuilt by the run journal, commit
|
|
4788
|
+
* each Turn under the local owner, and watch for durable abort intent. Engine Run failures settle `failed`; coordinator failures abort the
|
|
4789
|
+
* Attempt cleanly with the obligation still owed.
|
|
4790
|
+
*
|
|
4791
|
+
* The interpreter supplies validated response, closed Tool-result, and completion facts.
|
|
4792
|
+
* Responses precede approval and effectful dispatch; readonly responses may join their closed
|
|
4793
|
+
* results. Tool results precede input drains and the next Turn. A no-tool response and Run
|
|
4794
|
+
* completion share one batch. A completion Tool's result
|
|
4795
|
+
* precedes its terminal-only completion batch, so recovery can repeat a pure output projection
|
|
4796
|
+
* without replaying the handler. Pending batches resume from their canonical declarations.
|
|
4797
|
+
*/
|
|
4798
|
+
function* <
|
|
4799
|
+
InputSchema extends Schema.Top,
|
|
4800
|
+
OutputSchema extends Schema.Top,
|
|
4797
4801
|
Instructions,
|
|
4798
|
-
Tools,
|
|
4802
|
+
Tools extends Record<string, Tool.Any>,
|
|
4799
4803
|
Provider,
|
|
4800
4804
|
ModelProvides,
|
|
4801
4805
|
ModelRequires,
|
|
4802
|
-
InstructionError,
|
|
4803
|
-
InstructionRequirements,
|
|
4804
|
-
RunDispositionValue
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4806
|
+
InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
|
|
4807
|
+
InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
|
|
4808
|
+
RunDispositionValue extends
|
|
4809
|
+
| RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
|
|
4810
|
+
| undefined = undefined,
|
|
4811
|
+
InputPromptValue extends
|
|
4812
|
+
| InputPromptSource<InputSchema["Type"], unknown, unknown>
|
|
4813
|
+
| undefined = undefined,
|
|
4814
|
+
UpdatesSchema extends Schema.Top | undefined = undefined,
|
|
4815
|
+
>(
|
|
4816
|
+
agent: RuntimeBinding<
|
|
4817
|
+
InputSchema,
|
|
4818
|
+
OutputSchema,
|
|
4819
|
+
Instructions,
|
|
4820
|
+
Tools,
|
|
4821
|
+
Provider,
|
|
4822
|
+
ModelProvides,
|
|
4823
|
+
ModelRequires,
|
|
4824
|
+
InstructionError,
|
|
4825
|
+
InstructionRequirements,
|
|
4826
|
+
RunDispositionValue,
|
|
4827
|
+
InputPromptValue,
|
|
4828
|
+
UpdatesSchema
|
|
4829
|
+
>,
|
|
4830
|
+
ctx: AttemptAppendContext,
|
|
4831
|
+
submission: SubmissionSnapshot,
|
|
4832
|
+
session: RunStorageSession,
|
|
4833
|
+
records: ReadonlyArray<CanonicalRecordEnvelope>,
|
|
4834
|
+
canonical: Stream.Stream<
|
|
4835
|
+
JournalRecordEnvelope,
|
|
4836
|
+
ThreadStoreError | ThreadNotMaterialized | RunJournalError
|
|
4837
|
+
>,
|
|
4838
|
+
initialJournal: InitialJournalSource | undefined,
|
|
4839
|
+
canonicalThrough: CanonicalSequence,
|
|
4840
|
+
progressThrough: CanonicalSequence | undefined,
|
|
4841
|
+
continuation: RunContinuation | undefined,
|
|
4842
|
+
priorContext: RunJournalContext | undefined,
|
|
4843
|
+
journalMetadata: JournalMetadata | undefined,
|
|
4844
|
+
lineage: AttemptLineage,
|
|
4845
|
+
approvalDecisions: ReadonlyArray<ApprovalDecisionIntent>,
|
|
4846
|
+
currentContracts: Readonly<Record<string, Digest>>,
|
|
4847
|
+
runTiming: RunTiming,
|
|
4848
|
+
yieldAfter?: DateTime.Utc,
|
|
4849
|
+
) {
|
|
4829
4850
|
const submissionId = submission.submissionId;
|
|
4830
4851
|
|
|
4831
4852
|
const runId = runIdForSubmission(submissionId);
|
|
@@ -4851,23 +4872,35 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
4851
4872
|
historyEvidence.set(entry.record.recordId, entry);
|
|
4852
4873
|
};
|
|
4853
4874
|
|
|
4875
|
+
// An exact cache prefix followed only by this fresh input has no response,
|
|
4876
|
+
// policy reservation or context to replay. Keep recovery on the full projector.
|
|
4877
|
+
const freshPrefix =
|
|
4878
|
+
priorContext === undefined &&
|
|
4879
|
+
initialJournal?.prefix !== undefined &&
|
|
4880
|
+
initialJournal.records?.length === 1 &&
|
|
4881
|
+
initialJournal.records[0] === originalInput
|
|
4882
|
+
? initialJournal.prefix.prompt
|
|
4883
|
+
: undefined;
|
|
4884
|
+
|
|
4854
4885
|
let journal =
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
cached.
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4886
|
+
freshPrefix !== undefined
|
|
4887
|
+
? initialRunJournal(freshPrefix)
|
|
4888
|
+
: cached !== undefined &&
|
|
4889
|
+
cached.threadId === ctx.threadId &&
|
|
4890
|
+
cached.through === projectionThrough &&
|
|
4891
|
+
cached.runId === runId &&
|
|
4892
|
+
cached.contextRecordId === priorContext?.recordId
|
|
4893
|
+
? (boundaries.push(...cached.boundaries),
|
|
4894
|
+
cached.contextEvidence.forEach(retainHistory),
|
|
4895
|
+
cached.journal)
|
|
4896
|
+
: yield* projectRunJournalStream(
|
|
4897
|
+
canonical,
|
|
4898
|
+
runId,
|
|
4899
|
+
(boundary) => boundaries.push(boundary),
|
|
4900
|
+
priorContext,
|
|
4901
|
+
journalMetadata,
|
|
4902
|
+
priorContext === undefined ? retainHistory : undefined,
|
|
4903
|
+
);
|
|
4871
4904
|
|
|
4872
4905
|
if (initialJournal?.prefix !== undefined) {
|
|
4873
4906
|
const prompt = initialJournal.prefix.prompt;
|
|
@@ -5187,9 +5220,6 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5187
5220
|
),
|
|
5188
5221
|
);
|
|
5189
5222
|
|
|
5190
|
-
const addUsageTotal = (field: string, left: number, right: number) =>
|
|
5191
|
-
checkedUsageTotal(field, left + right);
|
|
5192
|
-
|
|
5193
5223
|
const subtractUsageTotal = (field: string, left: number, right: number) =>
|
|
5194
5224
|
checkedUsageTotal(field, left - right);
|
|
5195
5225
|
|
|
@@ -5199,11 +5229,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5199
5229
|
let unobservedModelCalls = journal.usage.unobservedModelCalls ?? 0;
|
|
5200
5230
|
|
|
5201
5231
|
for (const count of stagedUnobservedCalls.values()) {
|
|
5202
|
-
unobservedModelCalls
|
|
5203
|
-
"unobservedModelCalls",
|
|
5204
|
-
unobservedModelCalls,
|
|
5205
|
-
count,
|
|
5206
|
-
);
|
|
5232
|
+
unobservedModelCalls += count;
|
|
5207
5233
|
}
|
|
5208
5234
|
const detailedCalls = [...journal.usage.modelUsage, ...stagedCalls];
|
|
5209
5235
|
|
|
@@ -5224,17 +5250,15 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5224
5250
|
let costMicrousd = journal.usage.costMicrousd;
|
|
5225
5251
|
|
|
5226
5252
|
for (const usage of stagedUsage.values()) {
|
|
5227
|
-
inputTokens
|
|
5228
|
-
outputTokens
|
|
5229
|
-
costMicrousd
|
|
5253
|
+
inputTokens += usage.inputTokens;
|
|
5254
|
+
outputTokens += usage.outputTokens;
|
|
5255
|
+
costMicrousd += usage.costMicrousd;
|
|
5230
5256
|
}
|
|
5231
5257
|
|
|
5232
|
-
const modelCalls =
|
|
5233
|
-
"modelCalls",
|
|
5234
|
-
journal.usage.modelCalls,
|
|
5235
|
-
stagedCalls.length,
|
|
5236
|
-
);
|
|
5258
|
+
const modelCalls = journal.usage.modelCalls + stagedCalls.length;
|
|
5237
5259
|
|
|
5260
|
+
// Reconciliation must reject negative legacy deltas independently of
|
|
5261
|
+
// the final summary's complete safe-integer and additive checks.
|
|
5238
5262
|
const legacyCalls = yield* subtractUsageTotal(
|
|
5239
5263
|
"legacy.modelCalls",
|
|
5240
5264
|
modelCalls,
|
|
@@ -5278,81 +5302,52 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5278
5302
|
|
|
5279
5303
|
const existing = existingIndex < 0 ? undefined : byModel[existingIndex];
|
|
5280
5304
|
|
|
5281
|
-
const legacyGroup = ModelUsageGroup.
|
|
5305
|
+
const legacyGroup = yield* ModelUsageGroup.makeEffect({
|
|
5282
5306
|
provider: "unknown",
|
|
5283
5307
|
model: "legacy-record",
|
|
5284
|
-
modelCalls:
|
|
5285
|
-
|
|
5286
|
-
existing?.
|
|
5287
|
-
|
|
5288
|
-
),
|
|
5289
|
-
inputTokens: InputTokenUsage.make({
|
|
5290
|
-
total: yield* addUsageTotal(
|
|
5291
|
-
"byModel.inputTokens.total",
|
|
5292
|
-
existing?.inputTokens.total ?? 0,
|
|
5293
|
-
legacyInput,
|
|
5294
|
-
),
|
|
5295
|
-
uncached: yield* addUsageTotal(
|
|
5296
|
-
"byModel.inputTokens.uncached",
|
|
5297
|
-
existing?.inputTokens.uncached ?? 0,
|
|
5298
|
-
legacyInput,
|
|
5299
|
-
),
|
|
5308
|
+
modelCalls: (existing?.modelCalls ?? 0) + legacyCalls,
|
|
5309
|
+
inputTokens: {
|
|
5310
|
+
total: (existing?.inputTokens.total ?? 0) + legacyInput,
|
|
5311
|
+
uncached: (existing?.inputTokens.uncached ?? 0) + legacyInput,
|
|
5300
5312
|
cacheRead: existing?.inputTokens.cacheRead ?? 0,
|
|
5301
5313
|
cacheWrite: existing?.inputTokens.cacheWrite ?? 0,
|
|
5302
|
-
}
|
|
5303
|
-
outputTokens:
|
|
5304
|
-
total:
|
|
5305
|
-
|
|
5306
|
-
existing?.outputTokens.total ?? 0,
|
|
5307
|
-
legacyOutput,
|
|
5308
|
-
),
|
|
5309
|
-
text: yield* addUsageTotal(
|
|
5310
|
-
"byModel.outputTokens.text",
|
|
5311
|
-
existing?.outputTokens.text ?? 0,
|
|
5312
|
-
legacyOutput,
|
|
5313
|
-
),
|
|
5314
|
+
},
|
|
5315
|
+
outputTokens: {
|
|
5316
|
+
total: (existing?.outputTokens.total ?? 0) + legacyOutput,
|
|
5317
|
+
text: (existing?.outputTokens.text ?? 0) + legacyOutput,
|
|
5314
5318
|
reasoning: existing?.outputTokens.reasoning ?? 0,
|
|
5315
|
-
}
|
|
5316
|
-
costMicrousd:
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5319
|
+
},
|
|
5320
|
+
costMicrousd: (existing?.costMicrousd ?? 0) + legacyCost,
|
|
5321
|
+
}).pipe(
|
|
5322
|
+
Effect.mapError((cause) =>
|
|
5323
|
+
RunJournalError.make({
|
|
5324
|
+
message: "Run legacy usage exceeds safe-integer accounting bounds",
|
|
5325
|
+
cause,
|
|
5326
|
+
}),
|
|
5320
5327
|
),
|
|
5321
|
-
|
|
5328
|
+
);
|
|
5322
5329
|
|
|
5323
5330
|
if (existingIndex < 0) byModel.push(legacyGroup);
|
|
5324
5331
|
else byModel[existingIndex] = legacyGroup;
|
|
5325
5332
|
}
|
|
5326
5333
|
|
|
5327
|
-
|
|
5328
|
-
"inputTokens.uncached",
|
|
5329
|
-
detailed.inputTokens.uncached,
|
|
5330
|
-
legacyInput,
|
|
5331
|
-
);
|
|
5332
|
-
|
|
5333
|
-
const text = yield* addUsageTotal(
|
|
5334
|
-
"outputTokens.text",
|
|
5335
|
-
detailed.outputTokens.text,
|
|
5336
|
-
legacyOutput,
|
|
5337
|
-
);
|
|
5338
|
-
|
|
5339
|
-
return RunUsageSummary.make({
|
|
5334
|
+
return yield* RunUsageSummary.makeEffect({
|
|
5340
5335
|
...(legacyCalls === 0 && detailed.webSearchCalls !== undefined
|
|
5341
5336
|
? { webSearchCalls: detailed.webSearchCalls }
|
|
5342
5337
|
: {}),
|
|
5343
5338
|
modelCalls,
|
|
5344
|
-
inputTokens:
|
|
5339
|
+
inputTokens: {
|
|
5345
5340
|
total: inputTokens,
|
|
5346
5341
|
// Legacy aggregate inputs are conservatively classified as uncached.
|
|
5347
|
-
uncached,
|
|
5342
|
+
uncached: detailed.inputTokens.uncached + legacyInput,
|
|
5348
5343
|
cacheRead: detailed.inputTokens.cacheRead,
|
|
5349
5344
|
cacheWrite: detailed.inputTokens.cacheWrite,
|
|
5350
|
-
}
|
|
5351
|
-
outputTokens:
|
|
5345
|
+
},
|
|
5346
|
+
outputTokens: {
|
|
5352
5347
|
total: outputTokens,
|
|
5353
|
-
text,
|
|
5348
|
+
text: detailed.outputTokens.text + legacyOutput,
|
|
5354
5349
|
reasoning: detailed.outputTokens.reasoning,
|
|
5355
|
-
}
|
|
5350
|
+
},
|
|
5356
5351
|
costMicrousd,
|
|
5357
5352
|
byModel,
|
|
5358
5353
|
unobservedModelCalls,
|
|
@@ -5368,7 +5363,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5368
5363
|
? "unknown"
|
|
5369
5364
|
: "partial"
|
|
5370
5365
|
: detailed.pricingStatus,
|
|
5371
|
-
})
|
|
5366
|
+
}).pipe(
|
|
5367
|
+
Effect.mapError((cause) =>
|
|
5368
|
+
RunJournalError.make({
|
|
5369
|
+
message: "Run usage summary exceeds safe-integer accounting bounds",
|
|
5370
|
+
cause,
|
|
5371
|
+
}),
|
|
5372
|
+
),
|
|
5373
|
+
);
|
|
5372
5374
|
});
|
|
5373
5375
|
|
|
5374
5376
|
const activeExecution = yield* reader.readExecution({ threadId: ctx.threadId, runId });
|
|
@@ -5544,37 +5546,38 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5544
5546
|
const recordHalt = <A, R>(
|
|
5545
5547
|
effect: Effect.Effect<A, DurableWorkerFailure, R>,
|
|
5546
5548
|
): Effect.Effect<A, CoordinatorHalt | AgentPersistenceCapacityError, R> =>
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
Cause.map(cause, (failure) =>
|
|
5557
|
-
failure instanceof ThreadStoreError &&
|
|
5558
|
-
failure.cause instanceof AgentPersistenceCapacityError
|
|
5559
|
-
? failure.cause
|
|
5560
|
-
: new CoordinatorHalt(failure),
|
|
5561
|
-
),
|
|
5549
|
+
Effect.catchCause(effect, (cause) => {
|
|
5550
|
+
const first = Cause.findErrorOption(cause);
|
|
5551
|
+
|
|
5552
|
+
const failure = Effect.failCause(
|
|
5553
|
+
Cause.map(cause, (error) =>
|
|
5554
|
+
error instanceof ThreadStoreError &&
|
|
5555
|
+
error.cause instanceof AgentPersistenceCapacityError
|
|
5556
|
+
? error.cause
|
|
5557
|
+
: new CoordinatorHalt(error),
|
|
5562
5558
|
),
|
|
5563
|
-
)
|
|
5564
|
-
|
|
5559
|
+
);
|
|
5560
|
+
|
|
5561
|
+
return Option.isNone(first) ||
|
|
5562
|
+
(first.value instanceof ThreadStoreError &&
|
|
5563
|
+
first.value.cause instanceof AgentPersistenceCapacityError)
|
|
5564
|
+
? failure
|
|
5565
|
+
: Effect.andThen(Ref.set(haltRef, first.value), failure);
|
|
5566
|
+
});
|
|
5565
5567
|
|
|
5566
5568
|
// Infrastructure errors can be wrapped by broker/Tool APIs. The interpreter checks this
|
|
5567
5569
|
// authority before semantic work; progress delivery is not a coordinator checkpoint.
|
|
5568
|
-
const
|
|
5569
|
-
Effect.gen(function* () {
|
|
5570
|
-
const failure = yield* Ref.get(haltRef);
|
|
5570
|
+
const progress = progressWriters.get(ctx);
|
|
5571
5571
|
|
|
5572
|
-
|
|
5573
|
-
|
|
5572
|
+
const checkProgress =
|
|
5573
|
+
progress === undefined
|
|
5574
|
+
? Effect.void
|
|
5575
|
+
: progress.check(runId).pipe(Effect.provideService(CurrentRunWriter, ctx));
|
|
5574
5576
|
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5577
|
+
const checkpoint = recordHalt(
|
|
5578
|
+
Effect.flatMap(Ref.get(haltRef), (failure) =>
|
|
5579
|
+
failure === undefined ? checkProgress : Effect.fail(failure),
|
|
5580
|
+
),
|
|
5578
5581
|
);
|
|
5579
5582
|
|
|
5580
5583
|
// One readonly Turn may own its response without publishing it. Promotion and settlement
|
|
@@ -6065,9 +6068,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6065
6068
|
// The interpreter supplies only closed siblings, in declaration order.
|
|
6066
6069
|
// Per-call identities retain each result before the child suspension.
|
|
6067
6070
|
for (const result of toolResults) {
|
|
6068
|
-
const batch = yield*
|
|
6069
|
-
turnResultsBatch({ ...input, toolResults: [result] }),
|
|
6070
|
-
);
|
|
6071
|
+
const batch = yield* turnResultsBatch({ ...input, toolResults: [result] });
|
|
6071
6072
|
|
|
6072
6073
|
yield* appendBatch(
|
|
6073
6074
|
ctx,
|
|
@@ -6100,47 +6101,45 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6100
6101
|
turnId: commit.turnId,
|
|
6101
6102
|
// The journal owns encoded messages. Detach the remaining small metadata
|
|
6102
6103
|
// trees too: Schema validation alone is not an ownership transfer.
|
|
6103
|
-
batch: yield*
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
toolExposure
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
toolNames: [...input.toolExposure.selection.toolNames],
|
|
6121
|
-
}),
|
|
6104
|
+
batch: yield* turnCanonicalBatch({
|
|
6105
|
+
...input,
|
|
6106
|
+
toolParameterRejections: input.toolParameterRejections?.map((rejection) => ({
|
|
6107
|
+
...rejection,
|
|
6108
|
+
parameters: copyJson(rejection.parameters),
|
|
6109
|
+
error: { ...rejection.error, reason: { ...rejection.error.reason } },
|
|
6110
|
+
})),
|
|
6111
|
+
toolExposure:
|
|
6112
|
+
input.toolExposure === undefined
|
|
6113
|
+
? undefined
|
|
6114
|
+
: Snapshot.make({
|
|
6115
|
+
exposedToolNames: [...input.toolExposure.exposedToolNames],
|
|
6116
|
+
...(input.toolExposure.selection === undefined
|
|
6117
|
+
? {}
|
|
6118
|
+
: {
|
|
6119
|
+
selection: Selection.make({
|
|
6120
|
+
toolNames: [...input.toolExposure.selection.toolNames],
|
|
6122
6121
|
}),
|
|
6123
|
-
}),
|
|
6124
|
-
usage:
|
|
6125
|
-
input.usage === undefined
|
|
6126
|
-
? undefined
|
|
6127
|
-
: {
|
|
6128
|
-
...input.usage,
|
|
6129
|
-
modelUsage: input.usage.modelUsage?.map((usage) =>
|
|
6130
|
-
ModelCallUsage.make({
|
|
6131
|
-
...usage,
|
|
6132
|
-
...(usage.response === undefined
|
|
6133
|
-
? {}
|
|
6134
|
-
: {
|
|
6135
|
-
response: ModelResponseIdentity.make({ ...usage.response }),
|
|
6136
|
-
}),
|
|
6137
|
-
inputTokens: InputTokenUsage.make({ ...usage.inputTokens }),
|
|
6138
|
-
outputTokens: OutputTokenUsage.make({ ...usage.outputTokens }),
|
|
6139
6122
|
}),
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6123
|
+
}),
|
|
6124
|
+
usage:
|
|
6125
|
+
input.usage === undefined
|
|
6126
|
+
? undefined
|
|
6127
|
+
: {
|
|
6128
|
+
...input.usage,
|
|
6129
|
+
modelUsage: input.usage.modelUsage?.map((usage) =>
|
|
6130
|
+
ModelCallUsage.make({
|
|
6131
|
+
...usage,
|
|
6132
|
+
...(usage.response === undefined
|
|
6133
|
+
? {}
|
|
6134
|
+
: {
|
|
6135
|
+
response: ModelResponseIdentity.make({ ...usage.response }),
|
|
6136
|
+
}),
|
|
6137
|
+
inputTokens: InputTokenUsage.make({ ...usage.inputTokens }),
|
|
6138
|
+
outputTokens: OutputTokenUsage.make({ ...usage.outputTokens }),
|
|
6139
|
+
}),
|
|
6140
|
+
),
|
|
6141
|
+
},
|
|
6142
|
+
}),
|
|
6144
6143
|
};
|
|
6145
6144
|
|
|
6146
6145
|
const progress = progressWriters.get(ctx);
|
|
@@ -6163,17 +6162,15 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6163
6162
|
let batch: CanonicalBatch;
|
|
6164
6163
|
|
|
6165
6164
|
if (deferred === undefined) {
|
|
6166
|
-
batch = yield*
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
: turnCanonicalBatch(input),
|
|
6172
|
-
);
|
|
6165
|
+
batch = yield* commit._tag === "Response"
|
|
6166
|
+
? turnResponseBatch(input)
|
|
6167
|
+
: response === undefined
|
|
6168
|
+
? turnResultsBatch(input)
|
|
6169
|
+
: turnCanonicalBatch(input);
|
|
6173
6170
|
} else if (toolResults.length === 0 && runCompletion === undefined) {
|
|
6174
6171
|
batch = deferred.batch;
|
|
6175
6172
|
} else {
|
|
6176
|
-
const results = yield*
|
|
6173
|
+
const results = yield* turnResultsBatch(input);
|
|
6177
6174
|
|
|
6178
6175
|
batch = CanonicalBatch.make({
|
|
6179
6176
|
...deferred.batch,
|
|
@@ -6347,214 +6344,208 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6347
6344
|
.get(ctx)
|
|
6348
6345
|
?.stageUsage(runId, terminalUsageCharge(uncommittedModelUsage()));
|
|
6349
6346
|
}),
|
|
6350
|
-
commitCompaction: (commit)
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
const recordId = compactionRecordId(runId, canonicalTurn, commit.kind);
|
|
6354
|
-
|
|
6355
|
-
if (knownIds.has(recordId)) {
|
|
6356
|
-
return yield* CompactionError.make({
|
|
6357
|
-
message: "Compaction is already committed for this Turn and kind",
|
|
6358
|
-
});
|
|
6359
|
-
}
|
|
6347
|
+
commitCompaction: Effect.fnUntraced(function* (commit) {
|
|
6348
|
+
const canonicalTurn = commit.turn;
|
|
6349
|
+
const recordId = compactionRecordId(runId, canonicalTurn, commit.kind);
|
|
6360
6350
|
|
|
6361
|
-
|
|
6362
|
-
|
|
6351
|
+
if (knownIds.has(recordId)) {
|
|
6352
|
+
return yield* CompactionError.make({
|
|
6353
|
+
message: "Compaction is already committed for this Turn and kind",
|
|
6354
|
+
});
|
|
6355
|
+
}
|
|
6363
6356
|
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
initialJournal.history = undefined;
|
|
6367
|
-
}
|
|
6368
|
-
if (commit.kind !== "summarize" || initialJournal?.prefix !== undefined) {
|
|
6369
|
-
// Results must be canonical before pruning or rollover can cover them. Newly committed
|
|
6370
|
-
// compactions remain overlays on this Attempt's append-only source, so omit those
|
|
6371
|
-
// overlays while reconstructing the exact source-to-record mapping.
|
|
6372
|
-
const tail = yield* ctx.tail;
|
|
6373
|
-
|
|
6374
|
-
const source =
|
|
6375
|
-
initialJournal === undefined
|
|
6376
|
-
? canonical
|
|
6377
|
-
: Stream.fromIterable(
|
|
6378
|
-
yield* recordHalt(
|
|
6379
|
-
readInitialJournal(initialJournal.original, initialJournal.suffix),
|
|
6380
|
-
),
|
|
6381
|
-
);
|
|
6357
|
+
let sourceJournal = journal;
|
|
6358
|
+
let sourceBoundaries = boundaries;
|
|
6382
6359
|
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6360
|
+
if (initialJournal !== undefined) {
|
|
6361
|
+
initialJournal.cacheable = false;
|
|
6362
|
+
initialJournal.history = undefined;
|
|
6363
|
+
}
|
|
6364
|
+
if (commit.kind !== "summarize" || initialJournal?.prefix !== undefined) {
|
|
6365
|
+
// Results must be canonical before pruning or rollover can cover them. Newly committed
|
|
6366
|
+
// compactions remain overlays on this Attempt's append-only source, so omit those
|
|
6367
|
+
// overlays while reconstructing the exact source-to-record mapping.
|
|
6368
|
+
const tail = yield* ctx.tail;
|
|
6369
|
+
|
|
6370
|
+
const source =
|
|
6371
|
+
initialJournal === undefined
|
|
6372
|
+
? canonical
|
|
6373
|
+
: Stream.fromIterable(
|
|
6374
|
+
yield* recordHalt(
|
|
6375
|
+
readInitialJournal(initialJournal.original, initialJournal.suffix),
|
|
6397
6376
|
),
|
|
6398
|
-
)
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6377
|
+
);
|
|
6378
|
+
|
|
6379
|
+
sourceBoundaries = [];
|
|
6380
|
+
sourceJournal = yield* recordHalt(
|
|
6381
|
+
projectRunJournalStream(
|
|
6382
|
+
Stream.concat(
|
|
6383
|
+
source,
|
|
6384
|
+
Stream.fromIterable(
|
|
6385
|
+
yield* recordHalt(
|
|
6386
|
+
selectedRange(ctx.threadId, [submissionId], tail.sequence, canonicalThrough),
|
|
6403
6387
|
),
|
|
6404
6388
|
),
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6389
|
+
).pipe(
|
|
6390
|
+
Stream.filter(
|
|
6391
|
+
(envelope) =>
|
|
6392
|
+
envelope.record.payload._tag !== "CompactionCreated" ||
|
|
6393
|
+
envelope.sequence <= canonicalThrough,
|
|
6394
|
+
),
|
|
6408
6395
|
),
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
// complete current-Run boundaries from the fresh canonical source above.
|
|
6414
|
-
const coverable = sourceBoundaries.filter(
|
|
6415
|
-
(boundary) =>
|
|
6416
|
-
commit.kind !== "summarize" ||
|
|
6417
|
-
ownerFirstSequence === undefined ||
|
|
6418
|
-
boundary.sequence < ownerFirstSequence,
|
|
6419
|
-
);
|
|
6420
|
-
|
|
6421
|
-
if (coverable.length === 0) {
|
|
6422
|
-
return yield* CompactionError.make({
|
|
6423
|
-
message:
|
|
6424
|
-
commit.kind === "rollover"
|
|
6425
|
-
? "Durable rollover requires complete canonical records"
|
|
6426
|
-
: "Durable compaction requires eligible canonical records",
|
|
6427
|
-
});
|
|
6428
|
-
}
|
|
6429
|
-
|
|
6430
|
-
const comparisonView = instructionView(
|
|
6431
|
-
sourceJournal.prompt.content,
|
|
6432
|
-
initialInstructions,
|
|
6433
|
-
false,
|
|
6434
|
-
sourceJournal.historyBefore.content.length,
|
|
6396
|
+
runId,
|
|
6397
|
+
(boundary) => sourceBoundaries.push(boundary),
|
|
6398
|
+
priorContext,
|
|
6399
|
+
),
|
|
6435
6400
|
);
|
|
6401
|
+
}
|
|
6436
6402
|
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6403
|
+
// Summaries use the initial prior-Run boundaries; pruning and rollover also admit
|
|
6404
|
+
// complete current-Run boundaries from the fresh canonical source above.
|
|
6405
|
+
const coverable = sourceBoundaries.filter(
|
|
6406
|
+
(boundary) =>
|
|
6407
|
+
commit.kind !== "summarize" ||
|
|
6408
|
+
ownerFirstSequence === undefined ||
|
|
6409
|
+
boundary.sequence < ownerFirstSequence,
|
|
6410
|
+
);
|
|
6442
6411
|
|
|
6443
|
-
|
|
6444
|
-
|
|
6412
|
+
if (coverable.length === 0) {
|
|
6413
|
+
return yield* CompactionError.make({
|
|
6414
|
+
message:
|
|
6415
|
+
commit.kind === "rollover"
|
|
6416
|
+
? "Durable rollover requires complete canonical records"
|
|
6417
|
+
: "Durable compaction requires eligible canonical records",
|
|
6418
|
+
});
|
|
6419
|
+
}
|
|
6445
6420
|
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
requiredThrough = index + 1;
|
|
6453
|
-
if (index >= comparisonLimit || index !== matchingPrefix || message === undefined)
|
|
6454
|
-
continue;
|
|
6455
|
-
const canonicalMessage = comparisonView.messages[index];
|
|
6456
|
-
|
|
6457
|
-
if (canonicalMessage === undefined) continue;
|
|
6458
|
-
|
|
6459
|
-
// Preserve Prompt's JSON projection while ignoring persisted object-key order.
|
|
6460
|
-
const encode = (entry: Prompt.Message) =>
|
|
6461
|
-
encodeCompactionMessageJson(entry).pipe(
|
|
6462
|
-
Effect.flatMap(decodeCompactionMessageJson),
|
|
6463
|
-
Effect.map(canonicalJson),
|
|
6464
|
-
Effect.mapError((cause) =>
|
|
6465
|
-
CompactionError.make({
|
|
6466
|
-
message: "Could not encode the canonical compaction prefix",
|
|
6467
|
-
cause,
|
|
6468
|
-
}),
|
|
6469
|
-
),
|
|
6470
|
-
);
|
|
6421
|
+
const comparisonView = instructionView(
|
|
6422
|
+
sourceJournal.prompt.content,
|
|
6423
|
+
initialInstructions,
|
|
6424
|
+
false,
|
|
6425
|
+
sourceJournal.historyBefore.content.length,
|
|
6426
|
+
);
|
|
6471
6427
|
|
|
6472
|
-
|
|
6473
|
-
|
|
6428
|
+
// Compare each visible message once. A transformed source can authorize only its
|
|
6429
|
+
// exact canonical prefix; no per-candidate rereads or full encoded Prompt copies.
|
|
6430
|
+
let matchingPrefix = 0;
|
|
6431
|
+
let requiredThrough = 0;
|
|
6432
|
+
const comparisonLimit = Math.min(comparisonView.messages.length, commit.through);
|
|
6474
6433
|
|
|
6475
|
-
|
|
6476
|
-
|
|
6434
|
+
for (let index = 0; index < commit.through; index += 1) {
|
|
6435
|
+
const message = commit.source.content[index];
|
|
6477
6436
|
|
|
6478
|
-
|
|
6437
|
+
if (
|
|
6438
|
+
message !== undefined &&
|
|
6439
|
+
(commit.kind === "clear-tool-results"
|
|
6440
|
+
? message.role === "tool"
|
|
6441
|
+
: message.role !== "system")
|
|
6442
|
+
)
|
|
6443
|
+
requiredThrough = index + 1;
|
|
6444
|
+
if (index >= comparisonLimit || index !== matchingPrefix || message === undefined)
|
|
6445
|
+
continue;
|
|
6446
|
+
const canonicalMessage = comparisonView.messages[index];
|
|
6447
|
+
|
|
6448
|
+
if (canonicalMessage === undefined) continue;
|
|
6449
|
+
|
|
6450
|
+
// Preserve Prompt's JSON projection while ignoring persisted object-key order.
|
|
6451
|
+
const encode = (entry: Prompt.Message) =>
|
|
6452
|
+
encodeCompactionMessageJson(entry).pipe(
|
|
6453
|
+
Effect.flatMap(decodeCompactionMessageJson),
|
|
6454
|
+
Effect.map(canonicalJson),
|
|
6455
|
+
Effect.mapError((cause) =>
|
|
6456
|
+
CompactionError.make({
|
|
6457
|
+
message: "Could not encode the canonical compaction prefix",
|
|
6458
|
+
cause,
|
|
6459
|
+
}),
|
|
6460
|
+
),
|
|
6461
|
+
);
|
|
6479
6462
|
|
|
6480
|
-
|
|
6481
|
-
|
|
6463
|
+
const left = yield* encode(canonicalMessage);
|
|
6464
|
+
const right = yield* encode(message);
|
|
6482
6465
|
|
|
6483
|
-
|
|
6484
|
-
|
|
6466
|
+
if (left === right) matchingPrefix += 1;
|
|
6467
|
+
}
|
|
6485
6468
|
|
|
6486
|
-
|
|
6469
|
+
let lastCovered: JournalBoundary | undefined;
|
|
6487
6470
|
|
|
6488
|
-
|
|
6471
|
+
for (let index = 0; index < coverable.length; index += 1) {
|
|
6472
|
+
const candidate = coverable[index];
|
|
6489
6473
|
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
lastCovered = candidate;
|
|
6493
|
-
}
|
|
6494
|
-
if (lastCovered === undefined) {
|
|
6495
|
-
return yield* CompactionError.make({
|
|
6496
|
-
message:
|
|
6497
|
-
commit.kind === "rollover"
|
|
6498
|
-
? "Rollover coverage cannot be mapped to complete canonical records"
|
|
6499
|
-
: "Compaction coverage cannot be mapped to complete canonical records",
|
|
6500
|
-
});
|
|
6501
|
-
}
|
|
6502
|
-
const coveredSequence = lastCovered.sequence;
|
|
6474
|
+
if (candidate === undefined) continue;
|
|
6475
|
+
const following = coverable[index + 1];
|
|
6503
6476
|
|
|
6504
|
-
if (
|
|
6505
|
-
commit.kind !== "summarize" &&
|
|
6506
|
-
sourceBoundaries.some(
|
|
6507
|
-
(boundary) =>
|
|
6508
|
-
boundary.incomplete === true &&
|
|
6509
|
-
boundary.terminalPriorRun !== true &&
|
|
6510
|
-
boundary.sequence <= coveredSequence,
|
|
6511
|
-
)
|
|
6512
|
-
) {
|
|
6513
|
-
return yield* CompactionError.make({
|
|
6514
|
-
message: "Compaction cannot cover an incomplete Tool batch",
|
|
6515
|
-
});
|
|
6516
|
-
}
|
|
6517
|
-
if (commit.kind === "summarize" && (commit.summary ?? "").trim().length === 0) {
|
|
6518
|
-
return yield* CompactionError.make({
|
|
6519
|
-
message: "A summarize compaction commit carried no summary",
|
|
6520
|
-
});
|
|
6521
|
-
}
|
|
6477
|
+
if (following?.tag === "ToolCallSettled") continue;
|
|
6522
6478
|
|
|
6523
|
-
const
|
|
6524
|
-
_tag: "CompactionCreated",
|
|
6525
|
-
runId,
|
|
6526
|
-
turn: canonicalTurn,
|
|
6527
|
-
kind: commit.kind,
|
|
6528
|
-
coversThrough: lastCovered.sequence,
|
|
6529
|
-
...(commit.kind === "summarize" ? { summary: commit.summary } : {}),
|
|
6530
|
-
...(commit.kind === "rollover" && commit.handoff !== undefined
|
|
6531
|
-
? { handoff: commit.handoff }
|
|
6532
|
-
: {}),
|
|
6533
|
-
}).pipe(
|
|
6534
|
-
Effect.mapError((cause) =>
|
|
6535
|
-
CompactionError.make({
|
|
6536
|
-
message: "Compaction decision exceeds canonical persistence bounds",
|
|
6537
|
-
cause,
|
|
6538
|
-
}),
|
|
6539
|
-
),
|
|
6540
|
-
);
|
|
6479
|
+
const length = comparisonView.prefixLength(candidate.promptLength);
|
|
6541
6480
|
|
|
6542
|
-
|
|
6481
|
+
if (length === 0 || length > commit.through) continue;
|
|
6482
|
+
if (length < requiredThrough || length > matchingPrefix) continue;
|
|
6483
|
+
lastCovered = candidate;
|
|
6484
|
+
}
|
|
6485
|
+
if (lastCovered === undefined) {
|
|
6486
|
+
return yield* CompactionError.make({
|
|
6487
|
+
message:
|
|
6488
|
+
commit.kind === "rollover"
|
|
6489
|
+
? "Rollover coverage cannot be mapped to complete canonical records"
|
|
6490
|
+
: "Compaction coverage cannot be mapped to complete canonical records",
|
|
6491
|
+
});
|
|
6492
|
+
}
|
|
6493
|
+
const coveredSequence = lastCovered.sequence;
|
|
6543
6494
|
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6495
|
+
if (
|
|
6496
|
+
commit.kind !== "summarize" &&
|
|
6497
|
+
sourceBoundaries.some(
|
|
6498
|
+
(boundary) =>
|
|
6499
|
+
boundary.incomplete === true &&
|
|
6500
|
+
boundary.terminalPriorRun !== true &&
|
|
6501
|
+
boundary.sequence <= coveredSequence,
|
|
6502
|
+
)
|
|
6503
|
+
) {
|
|
6504
|
+
return yield* CompactionError.make({
|
|
6505
|
+
message: "Compaction cannot cover an incomplete Tool batch",
|
|
6506
|
+
});
|
|
6507
|
+
}
|
|
6508
|
+
if (commit.kind === "summarize" && (commit.summary ?? "").trim().length === 0) {
|
|
6509
|
+
return yield* CompactionError.make({
|
|
6510
|
+
message: "A summarize compaction commit carried no summary",
|
|
6511
|
+
});
|
|
6512
|
+
}
|
|
6513
|
+
|
|
6514
|
+
const payload = yield* Schema.decodeEffect(CompactionCreated)({
|
|
6515
|
+
_tag: "CompactionCreated",
|
|
6516
|
+
runId,
|
|
6517
|
+
turn: canonicalTurn,
|
|
6518
|
+
kind: commit.kind,
|
|
6519
|
+
coversThrough: lastCovered.sequence,
|
|
6520
|
+
...(commit.kind === "summarize" ? { summary: commit.summary } : {}),
|
|
6521
|
+
...(commit.kind === "rollover" && commit.handoff !== undefined
|
|
6522
|
+
? { handoff: commit.handoff }
|
|
6523
|
+
: {}),
|
|
6524
|
+
}).pipe(
|
|
6525
|
+
Effect.mapError((cause) =>
|
|
6526
|
+
CompactionError.make({
|
|
6527
|
+
message: "Compaction decision exceeds canonical persistence bounds",
|
|
6528
|
+
cause,
|
|
6529
|
+
}),
|
|
6530
|
+
),
|
|
6531
|
+
);
|
|
6532
|
+
|
|
6533
|
+
const envelope = yield* recordHalt(makeEnvelope(recordId, payload));
|
|
6534
|
+
|
|
6535
|
+
yield* recordHalt(hit("compaction:before-canonical-append"));
|
|
6536
|
+
yield* recordHalt(
|
|
6537
|
+
appendBatch(
|
|
6538
|
+
ctx,
|
|
6539
|
+
CanonicalBatch.make({
|
|
6540
|
+
batchId: compactionBatchId(runId, canonicalTurn, commit.kind),
|
|
6541
|
+
producerId: config.producerId,
|
|
6542
|
+
records: [envelope],
|
|
6543
|
+
}),
|
|
6544
|
+
),
|
|
6545
|
+
);
|
|
6546
|
+
knownIds.add(recordId);
|
|
6547
|
+
yield* recordHalt(hit("compaction:after-canonical-append"));
|
|
6548
|
+
}),
|
|
6558
6549
|
};
|
|
6559
6550
|
|
|
6560
6551
|
/** Digest of one declared call's canonical encoded parameters (the original normalized wire). */
|
|
@@ -6803,37 +6794,34 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6803
6794
|
* so the next Turn's response commit makes it model-visible canonically — exactly the
|
|
6804
6795
|
* prompt-coverage rule recovery relies on.
|
|
6805
6796
|
*/
|
|
6806
|
-
const renderJoinedInput = (
|
|
6807
|
-
|
|
6808
|
-
messageAdmission,
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
messageAdmission,
|
|
6814
|
-
).pipe(
|
|
6815
|
-
Effect.mapError(() =>
|
|
6816
|
-
AgentInputError.make({ message: "Invalid worker completion message" }),
|
|
6817
|
-
),
|
|
6818
|
-
);
|
|
6819
|
-
const inputPrompt = agent.definition.inputPrompt;
|
|
6820
|
-
|
|
6821
|
-
if (inputPrompt === undefined) {
|
|
6822
|
-
return yield* renderInputPrompt(undefined, encodedInput, encodedInput);
|
|
6823
|
-
}
|
|
6824
|
-
|
|
6825
|
-
const decodedInput = yield* Schema.decodeEffect(agent.definition.input)(
|
|
6826
|
-
encodedInput,
|
|
6797
|
+
const renderJoinedInput = Effect.fnUntraced(function* (
|
|
6798
|
+
encodedInput: UserInputRecorded["input"],
|
|
6799
|
+
messageAdmission: UserInputRecorded["messageAdmission"],
|
|
6800
|
+
) {
|
|
6801
|
+
if (Schema.is(FrameworkMessage)(messageAdmission))
|
|
6802
|
+
return yield* Schema.encodeEffect(Schema.fromJsonString(FrameworkMessage))(
|
|
6803
|
+
messageAdmission,
|
|
6827
6804
|
).pipe(
|
|
6828
|
-
Effect.mapError((
|
|
6829
|
-
|
|
6830
|
-
message: cause.message,
|
|
6831
|
-
}),
|
|
6805
|
+
Effect.mapError(() =>
|
|
6806
|
+
AgentInputError.make({ message: "Invalid worker completion message" }),
|
|
6832
6807
|
),
|
|
6833
6808
|
);
|
|
6809
|
+
const inputPrompt = agent.definition.inputPrompt;
|
|
6834
6810
|
|
|
6835
|
-
|
|
6836
|
-
|
|
6811
|
+
if (inputPrompt === undefined) {
|
|
6812
|
+
return yield* renderInputPrompt(undefined, encodedInput, encodedInput);
|
|
6813
|
+
}
|
|
6814
|
+
|
|
6815
|
+
const decodedInput = yield* Schema.decodeEffect(agent.definition.input)(encodedInput).pipe(
|
|
6816
|
+
Effect.mapError((cause) =>
|
|
6817
|
+
AgentInputDecodeError.make({
|
|
6818
|
+
message: cause.message,
|
|
6819
|
+
}),
|
|
6820
|
+
),
|
|
6821
|
+
);
|
|
6822
|
+
|
|
6823
|
+
return yield* renderInputPrompt(inputPrompt, decodedInput, encodedInput);
|
|
6824
|
+
});
|
|
6837
6825
|
|
|
6838
6826
|
// Claims are authority, wakes are hints. A cancelled waiter retains every claim
|
|
6839
6827
|
// for the seam drain; it never appends input while an old response can still commit.
|
|
@@ -6892,7 +6880,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6892
6880
|
: { messageAdmission: snapshot.submission.messageAdmission }),
|
|
6893
6881
|
};
|
|
6894
6882
|
|
|
6895
|
-
const rendered = yield* Effect.result(
|
|
6883
|
+
const rendered = yield* Effect.result(
|
|
6884
|
+
renderJoinedInput(input.input, input.messageAdmission),
|
|
6885
|
+
);
|
|
6896
6886
|
|
|
6897
6887
|
if (Result.isFailure(rendered)) {
|
|
6898
6888
|
// A rejected prompt and its suffix retain their own Runs in queue order.
|
|
@@ -6932,118 +6922,117 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6932
6922
|
Agent.DefinitionRequirements<(typeof agent)["definition"]>
|
|
6933
6923
|
> = {
|
|
6934
6924
|
awaitJoin,
|
|
6935
|
-
drain: (policy)
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
const limit = policy === "one" ? 1 : MAX_JOIN_DRAIN;
|
|
6925
|
+
drain: Effect.fnUntraced(function* (policy) {
|
|
6926
|
+
const joinedInputs = yield* recordHalt(
|
|
6927
|
+
Effect.gen(function* () {
|
|
6928
|
+
const limit = policy === "one" ? 1 : MAX_JOIN_DRAIN;
|
|
6940
6929
|
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6930
|
+
const joinedInputs: Array<
|
|
6931
|
+
Pick<UserInputRecorded, "input" | "messageAdmission"> & {
|
|
6932
|
+
readonly rendered?: Prompt.RawInput;
|
|
6933
|
+
}
|
|
6934
|
+
> = [];
|
|
6946
6935
|
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6936
|
+
if (joinBacklog === undefined) {
|
|
6937
|
+
const hostSnapshot = yield* ledger.loadRecoverySnapshot(
|
|
6938
|
+
RecoverySnapshotRequest.make({ submissionId }),
|
|
6939
|
+
);
|
|
6951
6940
|
|
|
6952
|
-
|
|
6941
|
+
joinBacklog = hostSnapshot.joins;
|
|
6942
|
+
}
|
|
6943
|
+
// These inputs were already consumed by an earlier attempt. Restore the
|
|
6944
|
+
// entire uncovered prompt; only fresh claims are subject to the drain bound.
|
|
6945
|
+
for (const join of joinBacklog) {
|
|
6946
|
+
const joinId = join.submissionId;
|
|
6947
|
+
|
|
6948
|
+
if (deliveredJoinInputs.has(joinId)) continue;
|
|
6949
|
+
if (join.state !== "joining" && join.state !== "joined") continue;
|
|
6950
|
+
const existing = joinedInputEnvelopes.get(joinId);
|
|
6951
|
+
|
|
6952
|
+
if (existing === undefined) continue;
|
|
6953
|
+
if (join.state === "joining") {
|
|
6954
|
+
// Canonical input without its joined marker (crash between the append and
|
|
6955
|
+
// `markJoined`): repair the marker from history before reattaching.
|
|
6956
|
+
yield* session.markJoined(joinId, {
|
|
6957
|
+
recordId: existing.record.recordId,
|
|
6958
|
+
sequence: existing.sequence,
|
|
6959
|
+
});
|
|
6953
6960
|
}
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
if (join.state !== "joining" && join.state !== "joined") continue;
|
|
6961
|
-
const existing = joinedInputEnvelopes.get(joinId);
|
|
6962
|
-
|
|
6963
|
-
if (existing === undefined) continue;
|
|
6964
|
-
if (join.state === "joining") {
|
|
6965
|
-
// Canonical input without its joined marker (crash between the append and
|
|
6966
|
-
// `markJoined`): repair the marker from history before reattaching.
|
|
6967
|
-
yield* session.markJoined(joinId, {
|
|
6968
|
-
recordId: existing.record.recordId,
|
|
6969
|
-
sequence: existing.sequence,
|
|
6970
|
-
});
|
|
6971
|
-
}
|
|
6972
|
-
deliveredJoinInputs.add(joinId);
|
|
6973
|
-
if (
|
|
6974
|
-
lastHostResponseSequence !== undefined &&
|
|
6975
|
-
lastHostResponseSequence > existing.sequence
|
|
6976
|
-
) {
|
|
6977
|
-
continue;
|
|
6978
|
-
}
|
|
6979
|
-
const payload = existing.record.payload;
|
|
6980
|
-
|
|
6981
|
-
if (payload._tag !== "UserInputRecorded") continue;
|
|
6982
|
-
joinedInputs.push(payload);
|
|
6961
|
+
deliveredJoinInputs.add(joinId);
|
|
6962
|
+
if (
|
|
6963
|
+
lastHostResponseSequence !== undefined &&
|
|
6964
|
+
lastHostResponseSequence > existing.sequence
|
|
6965
|
+
) {
|
|
6966
|
+
continue;
|
|
6983
6967
|
}
|
|
6984
|
-
const
|
|
6985
|
-
|
|
6986
|
-
pendingJoinClaims.splice(0, claims.length);
|
|
6987
|
-
for (const { claim, input: payload, rendered } of claims) {
|
|
6988
|
-
const recordId = submissionInputRecordId(claim.submissionId);
|
|
6989
|
-
let sequence: CanonicalSequence;
|
|
6990
|
-
const existing = joinedInputEnvelopes.get(claim.submissionId);
|
|
6991
|
-
|
|
6992
|
-
if (existing !== undefined) {
|
|
6993
|
-
// Defensive reattach: the exact record is already canonical, so only the
|
|
6994
|
-
// marker and the delivery remain (DUR-016 — never a duplicate append).
|
|
6995
|
-
sequence = existing.sequence;
|
|
6996
|
-
} else {
|
|
6997
|
-
const envelope = yield* makeEnvelope(
|
|
6998
|
-
recordId,
|
|
6999
|
-
UserInputRecorded.make({
|
|
7000
|
-
submissionId: claim.submissionId,
|
|
7001
|
-
kind: "steering",
|
|
7002
|
-
runId,
|
|
7003
|
-
input: claim.inputPayload,
|
|
7004
|
-
...(payload.messageAdmission === undefined
|
|
7005
|
-
? {}
|
|
7006
|
-
: { messageAdmission: payload.messageAdmission }),
|
|
7007
|
-
}),
|
|
7008
|
-
);
|
|
6968
|
+
const payload = existing.record.payload;
|
|
7009
6969
|
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
6970
|
+
if (payload._tag !== "UserInputRecorded") continue;
|
|
6971
|
+
joinedInputs.push(payload);
|
|
6972
|
+
}
|
|
6973
|
+
const claims = yield* prepareInputs(limit);
|
|
6974
|
+
|
|
6975
|
+
pendingJoinClaims.splice(0, claims.length);
|
|
6976
|
+
for (const { claim, input: payload, rendered } of claims) {
|
|
6977
|
+
const recordId = submissionInputRecordId(claim.submissionId);
|
|
6978
|
+
let sequence: CanonicalSequence;
|
|
6979
|
+
const existing = joinedInputEnvelopes.get(claim.submissionId);
|
|
6980
|
+
|
|
6981
|
+
if (existing !== undefined) {
|
|
6982
|
+
// Defensive reattach: the exact record is already canonical, so only the
|
|
6983
|
+
// marker and the delivery remain (DUR-016 — never a duplicate append).
|
|
6984
|
+
sequence = existing.sequence;
|
|
6985
|
+
} else {
|
|
6986
|
+
const envelope = yield* makeEnvelope(
|
|
6987
|
+
recordId,
|
|
6988
|
+
UserInputRecorded.make({
|
|
6989
|
+
submissionId: claim.submissionId,
|
|
6990
|
+
kind: "steering",
|
|
6991
|
+
runId,
|
|
6992
|
+
input: claim.inputPayload,
|
|
6993
|
+
...(payload.messageAdmission === undefined
|
|
6994
|
+
? {}
|
|
6995
|
+
: { messageAdmission: payload.messageAdmission }),
|
|
6996
|
+
}),
|
|
6997
|
+
);
|
|
7018
6998
|
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
6999
|
+
const result = yield* appendBatch(
|
|
7000
|
+
ctx,
|
|
7001
|
+
CanonicalBatch.make({
|
|
7002
|
+
batchId: submissionInputBatchId(claim.submissionId),
|
|
7003
|
+
producerId: config.producerId,
|
|
7004
|
+
records: [envelope],
|
|
7005
|
+
}),
|
|
7006
|
+
);
|
|
7007
|
+
|
|
7008
|
+
sequence = result.firstSequence;
|
|
7009
|
+
knownIds.add(recordId);
|
|
7010
|
+
yield* hit("join:after-canonical-append");
|
|
7029
7011
|
}
|
|
7012
|
+
yield* session.markJoined(claim.submissionId, { recordId, sequence });
|
|
7013
|
+
deliveredJoinInputs.add(claim.submissionId);
|
|
7014
|
+
joinedInputs.push({
|
|
7015
|
+
...payload,
|
|
7016
|
+
rendered,
|
|
7017
|
+
});
|
|
7018
|
+
}
|
|
7030
7019
|
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7020
|
+
return joinedInputs;
|
|
7021
|
+
}),
|
|
7022
|
+
);
|
|
7034
7023
|
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7024
|
+
return yield* Effect.forEach(joinedInputs, (joinedInput) =>
|
|
7025
|
+
(joinedInput.rendered === undefined
|
|
7026
|
+
? renderJoinedInput(joinedInput.input, joinedInput.messageAdmission)
|
|
7027
|
+
: Effect.succeed(joinedInput.rendered)
|
|
7028
|
+
).pipe(
|
|
7029
|
+
Effect.map((input): RunInputCommand => ({
|
|
7030
|
+
kind: "steering",
|
|
7031
|
+
input,
|
|
7032
|
+
})),
|
|
7033
|
+
),
|
|
7034
|
+
);
|
|
7035
|
+
}),
|
|
7047
7036
|
};
|
|
7048
7037
|
|
|
7049
7038
|
/**
|
|
@@ -7726,50 +7715,49 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
7726
7715
|
},
|
|
7727
7716
|
}),
|
|
7728
7717
|
...(preparedContext === undefined ? {} : { context: preparedContext }),
|
|
7729
|
-
beforeTurn: ()
|
|
7730
|
-
|
|
7718
|
+
beforeTurn: Effect.fnUntraced(function* () {
|
|
7719
|
+
if (
|
|
7720
|
+
yieldAfter !== undefined &&
|
|
7721
|
+
(yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(yieldAfter)
|
|
7722
|
+
) {
|
|
7723
|
+
yield* Deferred.succeed(yieldSignal, undefined);
|
|
7724
|
+
|
|
7725
|
+
// The successful signal wins the outer race and closes the execution Scope;
|
|
7726
|
+
// no synthetic engine RunFailed event or terminal Settlement is produced.
|
|
7727
|
+
return yield* Effect.never;
|
|
7728
|
+
}
|
|
7729
|
+
if (submissionScheduling.yieldTo !== undefined && (yield* Ref.get(turnCounter)) > 0) {
|
|
7730
|
+
const candidate = yield* recordHalt(
|
|
7731
|
+
eligibleThreadHead(submission.threadId, submission.queueSequence),
|
|
7732
|
+
);
|
|
7733
|
+
|
|
7731
7734
|
if (
|
|
7732
|
-
|
|
7733
|
-
(
|
|
7735
|
+
Option.isSome(candidate) &&
|
|
7736
|
+
(candidate.value.state === "ready" || candidate.value.state === "input-applied")
|
|
7734
7737
|
) {
|
|
7735
|
-
yield*
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
if (submissionScheduling.yieldTo !== undefined && (yield* Ref.get(turnCounter)) > 0) {
|
|
7742
|
-
const candidate = yield* recordHalt(
|
|
7743
|
-
eligibleThreadHead(submission.threadId, submission.queueSequence),
|
|
7738
|
+
const next = yield* recordHalt(
|
|
7739
|
+
ledger.lookup(
|
|
7740
|
+
SubmissionLookupById.make({
|
|
7741
|
+
submissionId: candidate.value.submissionId,
|
|
7742
|
+
}),
|
|
7743
|
+
),
|
|
7744
7744
|
);
|
|
7745
7745
|
|
|
7746
7746
|
if (
|
|
7747
|
-
Option.isSome(
|
|
7748
|
-
|
|
7747
|
+
Option.isSome(next) &&
|
|
7748
|
+
next.value.agentId === submission.agentId &&
|
|
7749
|
+
(next.value.state === "ready" || next.value.state === "input-applied") &&
|
|
7750
|
+
(yield* recordHalt(
|
|
7751
|
+
submissionScheduling.yieldTo({ active: submission, next: next.value }),
|
|
7752
|
+
))
|
|
7749
7753
|
) {
|
|
7750
|
-
|
|
7751
|
-
ledger.lookup(
|
|
7752
|
-
SubmissionLookupById.make({
|
|
7753
|
-
submissionId: candidate.value.submissionId,
|
|
7754
|
-
}),
|
|
7755
|
-
),
|
|
7756
|
-
);
|
|
7754
|
+
yield* Deferred.succeed(yieldSignal, next.value.submissionId);
|
|
7757
7755
|
|
|
7758
|
-
|
|
7759
|
-
Option.isSome(next) &&
|
|
7760
|
-
next.value.agentId === submission.agentId &&
|
|
7761
|
-
(next.value.state === "ready" || next.value.state === "input-applied") &&
|
|
7762
|
-
(yield* recordHalt(
|
|
7763
|
-
submissionScheduling.yieldTo({ active: submission, next: next.value }),
|
|
7764
|
-
))
|
|
7765
|
-
) {
|
|
7766
|
-
yield* Deferred.succeed(yieldSignal, next.value.submissionId);
|
|
7767
|
-
|
|
7768
|
-
return yield* Effect.never;
|
|
7769
|
-
}
|
|
7756
|
+
return yield* Effect.never;
|
|
7770
7757
|
}
|
|
7771
7758
|
}
|
|
7772
|
-
}
|
|
7759
|
+
}
|
|
7760
|
+
}),
|
|
7773
7761
|
...(runContextPreparation.transientContext === undefined
|
|
7774
7762
|
? {}
|
|
7775
7763
|
: { transientContext: runContextPreparation.transientContext }),
|
|
@@ -7844,35 +7832,39 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
7844
7832
|
stagedUnobservedCalls.set(turn, (stagedUnobservedCalls.get(turn) ?? 0) + 1);
|
|
7845
7833
|
}),
|
|
7846
7834
|
}),
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7835
|
+
(body) =>
|
|
7836
|
+
body.pipe(
|
|
7837
|
+
Effect.provideServiceEffect(ThreadHistory, ThreadHistory.makeFromStore),
|
|
7838
|
+
Effect.provideServiceEffect(ConversationStore, makeMemoryStore),
|
|
7839
|
+
Effect.scoped,
|
|
7840
|
+
),
|
|
7841
|
+
Effect.provideContext(
|
|
7842
|
+
runServices.pipe(
|
|
7843
|
+
Context.add(SubagentHost.forTool, (source) =>
|
|
7844
|
+
source.threadId !== submission.threadId ||
|
|
7845
|
+
source.agentId !== submission.agentId ||
|
|
7846
|
+
source.runId !== runId
|
|
7847
|
+
? SubagentHost.unavailable
|
|
7848
|
+
: workerRuntime.facet(
|
|
7849
|
+
{
|
|
7850
|
+
source,
|
|
7851
|
+
policy: agent.definition.policy,
|
|
7852
|
+
depth: delegationDepth,
|
|
7853
|
+
...(inheritedGrant === undefined ? {} : { grant: inheritedGrant }),
|
|
7854
|
+
},
|
|
7855
|
+
submission.principal,
|
|
7856
|
+
submission.submissionId,
|
|
7857
|
+
),
|
|
7858
|
+
),
|
|
7859
|
+
Context.add(MessagingHost.forTool, (source) =>
|
|
7860
|
+
source.threadId !== submission.threadId ||
|
|
7861
|
+
source.agentId !== submission.agentId ||
|
|
7862
|
+
source.runId !== runId
|
|
7863
|
+
? MessagingHost.unavailable
|
|
7864
|
+
: messagingRuntime.forTool(source, submission.principal),
|
|
7865
|
+
),
|
|
7866
|
+
),
|
|
7870
7867
|
),
|
|
7871
|
-
Effect.provideService(CurrentToolFailureObserver, toolFailureObserver),
|
|
7872
|
-
Effect.provideService(RunToolVisibility, runToolVisibility),
|
|
7873
|
-
Effect.provideService(RunToolScheduling, runToolScheduling),
|
|
7874
|
-
Effect.provideService(ContextCompactor, compactor),
|
|
7875
|
-
Effect.provideService(RunContextPreparation, runContextPreparation),
|
|
7876
7868
|
Effect.andThen(checkpoint),
|
|
7877
7869
|
// A wrapped Tool/approval failure cannot outrank retained infrastructure authority.
|
|
7878
7870
|
// Preserve independent defects, interruptions and already-marked coordinator failures.
|
|
@@ -7919,21 +7911,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
7919
7911
|
}),
|
|
7920
7912
|
);
|
|
7921
7913
|
|
|
7922
|
-
const
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
yieldAfter === undefined && submissionScheduling.yieldTo === undefined
|
|
7926
|
-
?
|
|
7927
|
-
:
|
|
7928
|
-
execution,
|
|
7914
|
+
const raced = Effect.raceAllFirst([
|
|
7915
|
+
consume,
|
|
7916
|
+
abortWatcher,
|
|
7917
|
+
...(yieldAfter === undefined && submissionScheduling.yieldTo === undefined
|
|
7918
|
+
? []
|
|
7919
|
+
: [
|
|
7929
7920
|
Deferred.await(yieldSignal).pipe(
|
|
7930
7921
|
Effect.map((nextSubmissionId) => ({
|
|
7931
7922
|
_tag: "yielded" as const,
|
|
7932
7923
|
...(nextSubmissionId === undefined ? {} : { nextSubmissionId }),
|
|
7933
7924
|
})),
|
|
7934
7925
|
),
|
|
7935
|
-
)
|
|
7936
|
-
).pipe(Effect.provideService(IdGenerator, idGenerator));
|
|
7926
|
+
]),
|
|
7927
|
+
]).pipe(Effect.provideService(IdGenerator, idGenerator));
|
|
7937
7928
|
|
|
7938
7929
|
const result = yield* raced.pipe(
|
|
7939
7930
|
Effect.catchCauseFilter(
|
|
@@ -8112,7 +8103,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8112
8103
|
};
|
|
8113
8104
|
|
|
8114
8105
|
return completed;
|
|
8115
|
-
}
|
|
8106
|
+
},
|
|
8107
|
+
);
|
|
8116
8108
|
|
|
8117
8109
|
/**
|
|
8118
8110
|
* One ownership period over the claimed lane head. The step order mirrors the pure recovery
|
|
@@ -8121,53 +8113,51 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8121
8113
|
* (Unknown Outcomes, or a durable approval suspension) — no settlement occurs and the
|
|
8122
8114
|
* obligation stays owed.
|
|
8123
8115
|
*/
|
|
8124
|
-
const runAttempt =
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
Tools extends Record<string, Tool.Any>,
|
|
8129
|
-
Provider,
|
|
8130
|
-
ModelProvides,
|
|
8131
|
-
ModelRequires,
|
|
8132
|
-
InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
|
|
8133
|
-
InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
|
|
8134
|
-
RunDispositionValue extends
|
|
8135
|
-
| RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
|
|
8136
|
-
| undefined = undefined,
|
|
8137
|
-
InputPromptValue extends InputPromptSource<InputSchema["Type"], unknown, unknown> | undefined =
|
|
8138
|
-
undefined,
|
|
8139
|
-
UpdatesSchema extends Schema.Top | undefined = undefined,
|
|
8140
|
-
>(
|
|
8141
|
-
registeredAgent: RuntimeBinding<
|
|
8142
|
-
InputSchema,
|
|
8143
|
-
OutputSchema,
|
|
8116
|
+
const runAttempt = Effect.fnUntraced(
|
|
8117
|
+
function* <
|
|
8118
|
+
InputSchema extends Schema.Top,
|
|
8119
|
+
OutputSchema extends Schema.Top,
|
|
8144
8120
|
Instructions,
|
|
8145
|
-
Tools,
|
|
8121
|
+
Tools extends Record<string, Tool.Any>,
|
|
8146
8122
|
Provider,
|
|
8147
8123
|
ModelProvides,
|
|
8148
8124
|
ModelRequires,
|
|
8149
|
-
InstructionError,
|
|
8150
|
-
InstructionRequirements,
|
|
8151
|
-
RunDispositionValue
|
|
8152
|
-
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8125
|
+
InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
|
|
8126
|
+
InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
|
|
8127
|
+
RunDispositionValue extends
|
|
8128
|
+
| RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
|
|
8129
|
+
| undefined = undefined,
|
|
8130
|
+
InputPromptValue extends
|
|
8131
|
+
| InputPromptSource<InputSchema["Type"], unknown, unknown>
|
|
8132
|
+
| undefined = undefined,
|
|
8133
|
+
UpdatesSchema extends Schema.Top | undefined = undefined,
|
|
8134
|
+
>(
|
|
8135
|
+
registeredAgent: RuntimeBinding<
|
|
8136
|
+
InputSchema,
|
|
8137
|
+
OutputSchema,
|
|
8138
|
+
Instructions,
|
|
8139
|
+
Tools,
|
|
8140
|
+
Provider,
|
|
8141
|
+
ModelProvides,
|
|
8142
|
+
ModelRequires,
|
|
8143
|
+
InstructionError,
|
|
8144
|
+
InstructionRequirements,
|
|
8145
|
+
RunDispositionValue,
|
|
8146
|
+
InputPromptValue,
|
|
8147
|
+
UpdatesSchema
|
|
8148
|
+
>,
|
|
8149
|
+
session: RunStorageSession,
|
|
8150
|
+
resumeAfterRetention: () => void,
|
|
8151
|
+
onHandoff: (nextSubmissionId: SubmissionId) => void,
|
|
8152
|
+
yieldAfter?: DateTime.Utc,
|
|
8153
|
+
freshlyAdmitted = false,
|
|
8154
|
+
) {
|
|
8161
8155
|
// Recheck ownership outside the disposable cache's failure recovery.
|
|
8162
8156
|
yield* session.checkActive;
|
|
8163
8157
|
|
|
8164
8158
|
const checkpointPrompt = yield* promptHistory
|
|
8165
8159
|
.take({ threadId: session.threadId })
|
|
8166
|
-
.pipe(
|
|
8167
|
-
Effect.provideService(ThreadStore, store),
|
|
8168
|
-
Effect.provideService(ThreadReader, reader),
|
|
8169
|
-
Effect.provideService(Crypto.Crypto, crypto),
|
|
8170
|
-
);
|
|
8160
|
+
.pipe(Effect.provideService(ThreadStore, store), Effect.provideContext(journalServices));
|
|
8171
8161
|
|
|
8172
8162
|
// withProgressSession exposes only progress.commit/publish. Their prepare step owns and
|
|
8173
8163
|
// freezes each canonical fact before reaching this raw writer. Snapshot the private array
|
|
@@ -8181,7 +8171,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8181
8171
|
committedPromptSuffix = undefined;
|
|
8182
8172
|
}),
|
|
8183
8173
|
);
|
|
8184
|
-
|
|
8174
|
+
|
|
8175
|
+
const promptSession: RunStorageSession = {
|
|
8185
8176
|
...storageSession,
|
|
8186
8177
|
append: (batch, executions) => {
|
|
8187
8178
|
const suffix = committedPromptSuffix;
|
|
@@ -8216,17 +8207,117 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8216
8207
|
Effect.tapCause(() => Effect.sync(() => suffix.discard())),
|
|
8217
8208
|
);
|
|
8218
8209
|
},
|
|
8219
|
-
}
|
|
8210
|
+
};
|
|
8211
|
+
|
|
8212
|
+
session = yield* withProgressSession(promptSession);
|
|
8220
8213
|
const { claim, threadId } = session;
|
|
8221
8214
|
const submissionId = claim.submissionId;
|
|
8222
8215
|
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8216
|
+
// The hint only selects this preparation. A real claim, current controls, exact
|
|
8217
|
+
// evidence and checkpoint absence are verified under its owner before any reuse.
|
|
8218
|
+
// No binding, instructions, model, Tool or cross-Thread operation runs in the window.
|
|
8219
|
+
let prepared =
|
|
8220
|
+
freshlyAdmitted && session.withInitialization !== undefined
|
|
8221
|
+
? yield* session.withInitialization(
|
|
8222
|
+
Effect.gen(function* () {
|
|
8223
|
+
const snapshot = yield* ledger.loadRecoverySnapshot(
|
|
8224
|
+
RecoverySnapshotRequest.make({ submissionId }),
|
|
8225
|
+
);
|
|
8226
|
+
|
|
8227
|
+
const submission = snapshot.submission;
|
|
8228
|
+
|
|
8229
|
+
if (
|
|
8230
|
+
submission.state !== "ready" ||
|
|
8231
|
+
submission.workerAdmission !== undefined ||
|
|
8232
|
+
submission.messageAdmission !== undefined ||
|
|
8233
|
+
submission.parentLinkage !== undefined ||
|
|
8234
|
+
snapshot.inputApplied !== undefined ||
|
|
8235
|
+
snapshot.abortIntent !== undefined ||
|
|
8236
|
+
snapshot.approvalDecisions.length !== 0
|
|
8237
|
+
)
|
|
8238
|
+
return undefined;
|
|
8239
|
+
yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
|
|
8240
|
+
yield* session.refresh;
|
|
8241
|
+
const before = (yield* session.tail).sequence;
|
|
8242
|
+
|
|
8243
|
+
const view = yield* recoveryView(
|
|
8244
|
+
threadId,
|
|
8245
|
+
before,
|
|
8246
|
+
[submissionId],
|
|
8247
|
+
runIdForSubmission(submissionId),
|
|
8248
|
+
);
|
|
8249
|
+
|
|
8250
|
+
if (
|
|
8251
|
+
view.progress !== undefined ||
|
|
8252
|
+
view.context !== undefined ||
|
|
8253
|
+
view.operational.length !== 0 ||
|
|
8254
|
+
view.records.some(({ record }) => record.payload._tag !== "ThreadCreated")
|
|
8255
|
+
)
|
|
8256
|
+
return undefined;
|
|
8257
|
+
const progress = progressWriters.get(session);
|
|
8258
|
+
|
|
8259
|
+
const applyInput = applyCanonicalInput(
|
|
8260
|
+
session,
|
|
8261
|
+
submission,
|
|
8262
|
+
session,
|
|
8263
|
+
view.records,
|
|
8264
|
+
undefined,
|
|
8265
|
+
);
|
|
8266
|
+
|
|
8267
|
+
yield* progress === undefined
|
|
8268
|
+
? applyInput
|
|
8269
|
+
: progress
|
|
8270
|
+
.withVerifiedEmptyRun(runIdForSubmission(submissionId), before, applyInput)
|
|
8271
|
+
.pipe(Effect.provideService(CurrentRunWriter, promptSession));
|
|
8272
|
+
const through = (yield* session.tail).sequence;
|
|
8273
|
+
const added = yield* selectedRange(threadId, [submissionId], through, before);
|
|
8274
|
+
const records = [...view.records, ...added];
|
|
8275
|
+
|
|
8276
|
+
const accepted = progressWriters
|
|
8277
|
+
.get(session)
|
|
8278
|
+
?.acceptedCheckpoint(runIdForSubmission(submissionId));
|
|
8279
|
+
|
|
8280
|
+
if (
|
|
8281
|
+
accepted === undefined ||
|
|
8282
|
+
accepted.throughSequence !== through ||
|
|
8283
|
+
accepted.progress.runId !== runIdForSubmission(submissionId) ||
|
|
8284
|
+
accepted.progress.submissionId !== submissionId
|
|
8285
|
+
)
|
|
8286
|
+
return undefined;
|
|
8287
|
+
|
|
8288
|
+
const start = yield* makeRunStart(
|
|
8289
|
+
submission,
|
|
8290
|
+
Duration.toMillis(registeredAgent.definition.policy.maxDuration),
|
|
8291
|
+
records,
|
|
8292
|
+
view.operational,
|
|
8293
|
+
).pipe(Effect.provideService(CurrentRunWriter, session));
|
|
8294
|
+
|
|
8295
|
+
return {
|
|
8296
|
+
snapshot,
|
|
8297
|
+
through,
|
|
8298
|
+
view: {
|
|
8299
|
+
...view,
|
|
8300
|
+
records,
|
|
8301
|
+
progress: accepted.progress,
|
|
8302
|
+
progressThrough: accepted.throughSequence,
|
|
8303
|
+
operational: accepted.operational,
|
|
8304
|
+
},
|
|
8305
|
+
start,
|
|
8306
|
+
};
|
|
8307
|
+
}),
|
|
8308
|
+
)
|
|
8309
|
+
: undefined;
|
|
8310
|
+
|
|
8311
|
+
const preparedInput = prepared !== undefined;
|
|
8312
|
+
|
|
8313
|
+
const snapshot =
|
|
8314
|
+
prepared?.snapshot ??
|
|
8315
|
+
(yield* ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId })));
|
|
8226
8316
|
|
|
8227
8317
|
const submission = snapshot.submission;
|
|
8228
8318
|
|
|
8229
|
-
|
|
8319
|
+
if (prepared === undefined)
|
|
8320
|
+
yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
|
|
8230
8321
|
if (submission.workerAdmission?.origin.reporting?.mode === "standard")
|
|
8231
8322
|
yield* updateRuntime.repairRun(threadId, submissionId);
|
|
8232
8323
|
if (submission.workerAdmission !== undefined) {
|
|
@@ -8241,18 +8332,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8241
8332
|
if (submission.state === "admitted") {
|
|
8242
8333
|
yield* ledger.markReady(MarkReadyRequest.make({ submissionId }));
|
|
8243
8334
|
}
|
|
8244
|
-
yield* session.refresh;
|
|
8335
|
+
if (prepared === undefined) yield* session.refresh;
|
|
8245
8336
|
const ctx: AttemptAppendContext = session;
|
|
8246
|
-
let controlThrough = (yield* session.tail).sequence;
|
|
8337
|
+
let controlThrough = prepared?.through ?? (yield* session.tail).sequence;
|
|
8247
8338
|
|
|
8248
8339
|
const initialThrough = controlThrough;
|
|
8249
8340
|
|
|
8250
|
-
const initialView =
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8341
|
+
const initialView =
|
|
8342
|
+
prepared?.view ??
|
|
8343
|
+
(yield* recoveryView(
|
|
8344
|
+
threadId,
|
|
8345
|
+
initialThrough,
|
|
8346
|
+
[submissionId],
|
|
8347
|
+
runIdForSubmission(submissionId),
|
|
8348
|
+
));
|
|
8256
8349
|
|
|
8257
8350
|
let journalMetadata: ReturnType<typeof makeJournalMetadata> | undefined;
|
|
8258
8351
|
|
|
@@ -8273,9 +8366,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8273
8366
|
return metadata;
|
|
8274
8367
|
};
|
|
8275
8368
|
|
|
8276
|
-
let records: ReadonlyArray<CanonicalRecordEnvelope> =
|
|
8277
|
-
initialView.
|
|
8278
|
-
);
|
|
8369
|
+
let records: ReadonlyArray<CanonicalRecordEnvelope> =
|
|
8370
|
+
initialView.records.filter(collectControl);
|
|
8279
8371
|
|
|
8280
8372
|
// The append-only prefix remains valid for this Attempt. Retain only this Run's control
|
|
8281
8373
|
// evidence and validate each newly visible suffix against its own captured tail.
|
|
@@ -8376,13 +8468,17 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8376
8468
|
),
|
|
8377
8469
|
);
|
|
8378
8470
|
}
|
|
8379
|
-
|
|
8471
|
+
if (prepared === undefined)
|
|
8472
|
+
yield* applyCanonicalInput(ctx, submission, session, records, snapshot.inputApplied);
|
|
8380
8473
|
|
|
8381
|
-
const start =
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8474
|
+
const start =
|
|
8475
|
+
prepared?.start ??
|
|
8476
|
+
(yield* makeRunStart(
|
|
8477
|
+
submission,
|
|
8478
|
+
Duration.toMillis(agent.definition.policy.maxDuration),
|
|
8479
|
+
yield* refreshControl(),
|
|
8480
|
+
initialView.operational,
|
|
8481
|
+
).pipe(Effect.provideService(CurrentRunWriter, ctx)));
|
|
8386
8482
|
|
|
8387
8483
|
const savedRunTiming = start.timing;
|
|
8388
8484
|
|
|
@@ -8404,8 +8500,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8404
8500
|
};
|
|
8405
8501
|
|
|
8406
8502
|
let expiredChildObligation = false;
|
|
8503
|
+
let preparedControl = prepared !== undefined;
|
|
8407
8504
|
|
|
8408
8505
|
if ((yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(runTiming.deadline)) {
|
|
8506
|
+
prepared = undefined;
|
|
8507
|
+
preparedControl = false;
|
|
8409
8508
|
yield* commitStart;
|
|
8410
8509
|
yield* reconcileRetainedChildren(ctx, submission, session);
|
|
8411
8510
|
expiredChildObligation = yield* completeJoinedReleases(submission);
|
|
@@ -8413,7 +8512,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8413
8512
|
|
|
8414
8513
|
// Recheck after duration interruption too: that Attempt may have declared new ordinary calls.
|
|
8415
8514
|
const ordinaryCallsResolved = Effect.gen(function* () {
|
|
8416
|
-
const reconciliationRecords = yield* refreshControl();
|
|
8515
|
+
const reconciliationRecords = preparedControl ? records : yield* refreshControl();
|
|
8516
|
+
|
|
8517
|
+
preparedControl = false;
|
|
8417
8518
|
|
|
8418
8519
|
const reconciliationEvidence = yield* evidenceFor(
|
|
8419
8520
|
reconciliationRecords,
|
|
@@ -8463,7 +8564,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8463
8564
|
if (!expiredChildObligation && !(yield* ordinaryCallsResolved))
|
|
8464
8565
|
return Option.none<Settlement>();
|
|
8465
8566
|
|
|
8466
|
-
const continuationRecords = yield* refreshControl();
|
|
8567
|
+
const continuationRecords = prepared === undefined ? yield* refreshControl() : records;
|
|
8467
8568
|
|
|
8468
8569
|
const continuation = yield* pendingToolBatchFor(
|
|
8469
8570
|
continuationRecords,
|
|
@@ -8603,29 +8704,35 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8603
8704
|
|
|
8604
8705
|
const lineage: AttemptLineage = {
|
|
8605
8706
|
attemptId: claim.attemptId,
|
|
8606
|
-
inputWasRecorded: evidence.inputRecorded,
|
|
8707
|
+
inputWasRecorded: !preparedInput && evidence.inputRecorded,
|
|
8607
8708
|
};
|
|
8608
8709
|
|
|
8609
8710
|
let approvalDecisionIntents = snapshot.approvalDecisions;
|
|
8610
8711
|
|
|
8611
8712
|
while (true) {
|
|
8612
|
-
|
|
8713
|
+
const initialization = prepared;
|
|
8714
|
+
|
|
8715
|
+
prepared = undefined;
|
|
8716
|
+
if (initialization === undefined) yield* session.refresh;
|
|
8613
8717
|
const tail = yield* session.tail;
|
|
8614
8718
|
|
|
8615
|
-
const currentRecords =
|
|
8719
|
+
const currentRecords =
|
|
8720
|
+
initialization === undefined ? yield* refreshControl(tail.sequence) : records;
|
|
8616
8721
|
|
|
8617
|
-
const view =
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8722
|
+
const view =
|
|
8723
|
+
initialization?.view ??
|
|
8724
|
+
(yield* recoveryView(
|
|
8725
|
+
threadId,
|
|
8726
|
+
tail.sequence,
|
|
8727
|
+
[submissionId],
|
|
8728
|
+
runIdForSubmission(submissionId),
|
|
8729
|
+
currentRecords,
|
|
8730
|
+
));
|
|
8624
8731
|
|
|
8625
8732
|
let canonical: Stream.Stream<
|
|
8626
8733
|
JournalRecordEnvelope,
|
|
8627
8734
|
ThreadStoreError | ThreadNotMaterialized | RunJournalError
|
|
8628
|
-
> = view.
|
|
8735
|
+
> = Stream.fromIterable(view.records);
|
|
8629
8736
|
|
|
8630
8737
|
let initialJournal: InitialJournalSource | undefined;
|
|
8631
8738
|
|
|
@@ -8885,7 +8992,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8885
8992
|
|
|
8886
8993
|
return Option.some(settlement);
|
|
8887
8994
|
}
|
|
8888
|
-
}
|
|
8995
|
+
},
|
|
8996
|
+
(effect) => effect.pipe(Effect.scoped),
|
|
8997
|
+
);
|
|
8889
8998
|
|
|
8890
8999
|
/** A missing current binding leaves both roots and children owed, with their claims released. */
|
|
8891
9000
|
type CapturedWorkerBinding = Effect.Success<ReturnType<typeof makeLegacyWorkerBinding>>;
|
|
@@ -8938,6 +9047,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8938
9047
|
const session = claimed.value;
|
|
8939
9048
|
const { claim } = session;
|
|
8940
9049
|
|
|
9050
|
+
const freshlyAdmitted = unclaimedAdmissions.get(claim.submissionId) === threadId;
|
|
9051
|
+
|
|
9052
|
+
unclaimedAdmissions.delete(claim.submissionId);
|
|
8941
9053
|
firstClaimedSubmissionId ??= claim.submissionId;
|
|
8942
9054
|
|
|
8943
9055
|
const attributes = {
|
|
@@ -9022,6 +9134,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
9022
9134
|
});
|
|
9023
9135
|
},
|
|
9024
9136
|
options?.yieldAfter,
|
|
9137
|
+
freshlyAdmitted,
|
|
9025
9138
|
),
|
|
9026
9139
|
threadId,
|
|
9027
9140
|
claim,
|
|
@@ -9094,7 +9207,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
9094
9207
|
}
|
|
9095
9208
|
}).pipe((body) => withProcessing(threadId, body));
|
|
9096
9209
|
|
|
9097
|
-
const processThreadImpl = <
|
|
9210
|
+
const processThreadImpl = Effect.fnUntraced(function* <
|
|
9098
9211
|
InputSchema extends Schema.Top,
|
|
9099
9212
|
OutputSchema extends Schema.Top,
|
|
9100
9213
|
Instructions,
|
|
@@ -9126,26 +9239,25 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
9126
9239
|
UpdatesSchema
|
|
9127
9240
|
>,
|
|
9128
9241
|
threadId: ThreadId,
|
|
9129
|
-
)
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
});
|
|
9242
|
+
) {
|
|
9243
|
+
// The legacy single-binding worker is a singleton resolver (plan §1.7): identity-exact —
|
|
9244
|
+
// a claimed head with a different Agent never runs against this binding (the latent P4
|
|
9245
|
+
// gap) — and digest-transparent, because this call site registers no digest authority.
|
|
9246
|
+
const binding = yield* makeLegacyWorkerBinding(agent);
|
|
9247
|
+
|
|
9248
|
+
return yield* drainThread(
|
|
9249
|
+
(submission) =>
|
|
9250
|
+
submission.agentId === binding.agentId
|
|
9251
|
+
? Effect.succeed(binding)
|
|
9252
|
+
: Effect.fail(
|
|
9253
|
+
BindingUnavailable.make({
|
|
9254
|
+
agentId: submission.agentId,
|
|
9255
|
+
message: `This worker is bound to Agent ${binding.agentId}; the claimed head belongs to Agent ${submission.agentId} (SUB-023)`,
|
|
9256
|
+
}),
|
|
9257
|
+
),
|
|
9258
|
+
threadId,
|
|
9259
|
+
);
|
|
9260
|
+
});
|
|
9149
9261
|
|
|
9150
9262
|
const processThreadResolvedImpl = (
|
|
9151
9263
|
threadId: ThreadId,
|
|
@@ -10057,9 +10169,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
10057
10169
|
const hostId = snapshot.hostSubmissionId;
|
|
10058
10170
|
const hostView = yield* recoveryView(submission.threadId, history.throughSequence, [hostId]);
|
|
10059
10171
|
|
|
10060
|
-
const hostRecords =
|
|
10061
|
-
hostView.canonical.pipe(Stream.filter(controlRecords([hostId]))),
|
|
10062
|
-
);
|
|
10172
|
+
const hostRecords = hostView.records.filter(controlRecords([hostId]));
|
|
10063
10173
|
|
|
10064
10174
|
const merged = new Map(history.records.map((entry) => [entry.sequence, entry]));
|
|
10065
10175
|
|
|
@@ -10403,6 +10513,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
10403
10513
|
const recoverOwnedWork = Effect.fnUntraced(function* (
|
|
10404
10514
|
request: WorkRecoveryRequest,
|
|
10405
10515
|
recoveredAdmissions = new Map<SubmissionId, RecoveryReport>(),
|
|
10516
|
+
deferUnclaimedAdmissions = false,
|
|
10406
10517
|
): Effect.fn.Return<WorkRecoveryReport, DurableWorkerFailure> {
|
|
10407
10518
|
const { threadId, work: entry } = yield* Schema.decodeEffect(WorkRecoveryRequest)(request).pipe(
|
|
10408
10519
|
Effect.mapError((cause) =>
|
|
@@ -10456,6 +10567,21 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
10456
10567
|
const original = yield* lookupKnownSubmission("recover work", owner.submissionId);
|
|
10457
10568
|
|
|
10458
10569
|
if (original.threadId !== threadId) return yield* invalid();
|
|
10570
|
+
if (
|
|
10571
|
+
deferUnclaimedAdmissions &&
|
|
10572
|
+
unclaimedAdmissions.get(owner.submissionId) === threadId &&
|
|
10573
|
+
original.state === "ready" &&
|
|
10574
|
+
!storageOwner.hasOwner(threadId) &&
|
|
10575
|
+
(yield* ledger.readAbortIntent(
|
|
10576
|
+
AbortIntentRequest.make({ submissionId: owner.submissionId }),
|
|
10577
|
+
)) === undefined
|
|
10578
|
+
) {
|
|
10579
|
+
// The worker's first scoped claim performs the authoritative control/journal
|
|
10580
|
+
// read. This hint skips only a redundant pre-claim recovery traversal; it grants
|
|
10581
|
+
// no claim, synthesizes no decision, and supplies no model-visible facts.
|
|
10582
|
+
return report("deferred");
|
|
10583
|
+
}
|
|
10584
|
+
unclaimedAdmissions.delete(owner.submissionId);
|
|
10459
10585
|
const submission = yield* recoverAdmission(owner.submissionId);
|
|
10460
10586
|
|
|
10461
10587
|
return report(submission.disposition, submission);
|
|
@@ -10934,6 +11060,29 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
10934
11060
|
|
|
10935
11061
|
const outcome = yield* isolateRecovery(
|
|
10936
11062
|
Effect.gen(function* () {
|
|
11063
|
+
// This incarnation admitted the sole untouched root. The native snapshot proves
|
|
11064
|
+
// there is no old owner to repair; claim and live controls still run normally.
|
|
11065
|
+
// New source mutations retain their dirty generation for the next maintenance scan.
|
|
11066
|
+
if (
|
|
11067
|
+
workCursor === undefined &&
|
|
11068
|
+
options?.deferUnclaimedAdmissions === true &&
|
|
11069
|
+
workDiscovery.isOnlyFreshAdmission !== undefined &&
|
|
11070
|
+
!storageOwner.hasOwner(threadId)
|
|
11071
|
+
) {
|
|
11072
|
+
const fresh = [...unclaimedAdmissions].find(([, owner]) => owner === threadId);
|
|
11073
|
+
|
|
11074
|
+
if (
|
|
11075
|
+
fresh !== undefined &&
|
|
11076
|
+
(yield* workDiscovery.isOnlyFreshAdmission({
|
|
11077
|
+
threadId,
|
|
11078
|
+
submissionId: fresh[0],
|
|
11079
|
+
}))
|
|
11080
|
+
) {
|
|
11081
|
+
complete = true;
|
|
11082
|
+
|
|
11083
|
+
return;
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
10937
11086
|
for (
|
|
10938
11087
|
let pages = 0;
|
|
10939
11088
|
pages < MAX_RECOVERY_PAGES && workReports.length < MAX_RECOVERY_WORK_ITEMS;
|
|
@@ -10949,7 +11098,11 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
10949
11098
|
|
|
10950
11099
|
phase = "recovery";
|
|
10951
11100
|
for (const work of workPage.entries) {
|
|
10952
|
-
const repaired = yield* recoverOwnedWork(
|
|
11101
|
+
const repaired = yield* recoverOwnedWork(
|
|
11102
|
+
{ threadId, work },
|
|
11103
|
+
recoveredAdmissions,
|
|
11104
|
+
options?.deferUnclaimedAdmissions === true,
|
|
11105
|
+
);
|
|
10953
11106
|
|
|
10954
11107
|
workReports.push(repaired);
|
|
10955
11108
|
if (
|
|
@@ -11173,7 +11326,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
11173
11326
|
queueSequence: admitted.queueSequence,
|
|
11174
11327
|
});
|
|
11175
11328
|
}
|
|
11176
|
-
const admitted = yield* ledger.admit(request);
|
|
11329
|
+
const admitted = yield* ledger.admit(request, { readyIfInitialized: true });
|
|
11177
11330
|
|
|
11178
11331
|
yield* hit("submit:after-admit");
|
|
11179
11332
|
|
|
@@ -11189,10 +11342,24 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
11189
11342
|
if (admitted.replayed && admitted.state !== "admitted") {
|
|
11190
11343
|
return receipt;
|
|
11191
11344
|
}
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11345
|
+
if (admitted.state === "admitted") {
|
|
11346
|
+
yield* materializeThread(options.threadId);
|
|
11347
|
+
yield* ensureThreadCreated(options.threadId, agent.definition.id, options.definitions);
|
|
11348
|
+
yield* hit("submit:after-materialize");
|
|
11349
|
+
yield* ledger.markReady(MarkReadyRequest.make({ submissionId: admitted.submissionId }));
|
|
11350
|
+
}
|
|
11351
|
+
if (
|
|
11352
|
+
!admitted.replayed &&
|
|
11353
|
+
messageAdmission === undefined &&
|
|
11354
|
+
options.workerAdmission === undefined
|
|
11355
|
+
) {
|
|
11356
|
+
unclaimedAdmissions.set(admitted.submissionId, options.threadId);
|
|
11357
|
+
if (unclaimedAdmissions.size > 128) {
|
|
11358
|
+
const oldest = unclaimedAdmissions.keys().next().value;
|
|
11359
|
+
|
|
11360
|
+
if (oldest !== undefined) unclaimedAdmissions.delete(oldest);
|
|
11361
|
+
}
|
|
11362
|
+
}
|
|
11196
11363
|
yield* wake.notify(options.threadId);
|
|
11197
11364
|
|
|
11198
11365
|
return receipt;
|
|
@@ -11248,14 +11415,8 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
11248
11415
|
}
|
|
11249
11416
|
if (snapshot.value.state !== "settled") return Option.none();
|
|
11250
11417
|
|
|
11251
|
-
// Finalization replay reads the canonical outcome without
|
|
11252
|
-
|
|
11253
|
-
SettlementFinalization.make({
|
|
11254
|
-
submissionId: receipt.submissionId,
|
|
11255
|
-
settlementId: submissionSettlementId(receipt.submissionId),
|
|
11256
|
-
}),
|
|
11257
|
-
);
|
|
11258
|
-
|
|
11418
|
+
// Finalization replay reads the canonical outcome for adapters without finalized
|
|
11419
|
+
// metadata. Native projections already retain the first settledAt and exact record.
|
|
11259
11420
|
const canonical = yield* getRecord({
|
|
11260
11421
|
threadId: receipt.threadId,
|
|
11261
11422
|
recordId: submissionSettlementRecordId(receipt.submissionId),
|
|
@@ -11278,6 +11439,40 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
11278
11439
|
}
|
|
11279
11440
|
|
|
11280
11441
|
const record = yield* settlementPayloadFromRecord(canonical.value.record, receipt.submissionId);
|
|
11442
|
+
const finalized = snapshot.value;
|
|
11443
|
+
|
|
11444
|
+
const retainedFinalization =
|
|
11445
|
+
finalized.settledAt !== undefined &&
|
|
11446
|
+
finalized.settledRecordId !== undefined &&
|
|
11447
|
+
finalized.settledOutcome !== undefined;
|
|
11448
|
+
|
|
11449
|
+
if (
|
|
11450
|
+
retainedFinalization &&
|
|
11451
|
+
(finalized.settledRecordId !== canonical.value.record.recordId ||
|
|
11452
|
+
finalized.settledOutcome !== record.outcome ||
|
|
11453
|
+
canonical.value.batchId !== submissionSettlementBatchId(receipt.submissionId))
|
|
11454
|
+
) {
|
|
11455
|
+
return yield* LedgerError.make({
|
|
11456
|
+
operation: "awaitSettlement",
|
|
11457
|
+
message: "Settled projection disagrees with its canonical Settlement",
|
|
11458
|
+
});
|
|
11459
|
+
}
|
|
11460
|
+
|
|
11461
|
+
const settlement = retainedFinalization
|
|
11462
|
+
? Settlement.make({
|
|
11463
|
+
submissionId: receipt.submissionId,
|
|
11464
|
+
settlementId: record.settlementId,
|
|
11465
|
+
receiptId: record.receiptId,
|
|
11466
|
+
outcome: record.outcome,
|
|
11467
|
+
...(record.outcome === "failed" ? { failure: record.result } : {}),
|
|
11468
|
+
settledAt: finalized.settledAt,
|
|
11469
|
+
})
|
|
11470
|
+
: yield* ledger.finalizeSettlement(
|
|
11471
|
+
SettlementFinalization.make({
|
|
11472
|
+
submissionId: receipt.submissionId,
|
|
11473
|
+
settlementId: submissionSettlementId(receipt.submissionId),
|
|
11474
|
+
}),
|
|
11475
|
+
);
|
|
11281
11476
|
|
|
11282
11477
|
if (
|
|
11283
11478
|
record.receiptId !== receipt.receiptId ||
|