@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
|
@@ -763,8 +763,8 @@ const acceptTurnCommit = <E, R>(
|
|
|
763
763
|
Effect.andThen(() => durability?.commitTurn(commit) ?? Effect.succeed("committed" as const)),
|
|
764
764
|
);
|
|
765
765
|
|
|
766
|
-
return
|
|
767
|
-
Effect.flatMap((receipt) => {
|
|
766
|
+
return Effect.onExitPrimitive(
|
|
767
|
+
Effect.flatMap(accepted, (receipt) => {
|
|
768
768
|
if (receipt === "deferred")
|
|
769
769
|
return commit._tag === "Response" && commit.defer === true
|
|
770
770
|
? Effect.void
|
|
@@ -774,21 +774,19 @@ const acceptTurnCommit = <E, R>(
|
|
|
774
774
|
}),
|
|
775
775
|
);
|
|
776
776
|
|
|
777
|
-
|
|
778
|
-
if (commit.
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
trace.responseCommitted = true;
|
|
782
|
-
}
|
|
783
|
-
if (commit._tag === "Settled") trace.resultsCommitted = true;
|
|
777
|
+
if (commit._tag !== "Partial") {
|
|
778
|
+
if (commit.response !== undefined) {
|
|
779
|
+
context.pendingCommitInputs.splice(0, trace.commitInputCount ?? 0);
|
|
780
|
+
trace.responseCommitted = true;
|
|
784
781
|
}
|
|
785
|
-
|
|
782
|
+
if (commit._tag === "Settled") trace.resultsCommitted = true;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return Effect.void;
|
|
786
786
|
}),
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
}),
|
|
791
|
-
),
|
|
787
|
+
(exit) => {
|
|
788
|
+
if (Exit.isFailure(exit)) trace.commitFailed = true;
|
|
789
|
+
},
|
|
792
790
|
);
|
|
793
791
|
});
|
|
794
792
|
|
|
@@ -961,24 +959,20 @@ const schemaClassToolPartPreflight = <Tools extends Record<string, Tool.Any>>(
|
|
|
961
959
|
try {
|
|
962
960
|
if (part === null || typeof part !== "object") return undefined;
|
|
963
961
|
|
|
964
|
-
const read = (key: string):
|
|
962
|
+
const read = (key: string): PropertyDescriptor | undefined => {
|
|
965
963
|
const descriptor = Object.getOwnPropertyDescriptor(part, key);
|
|
966
964
|
|
|
967
|
-
|
|
968
|
-
throw new TypeError(`response part ${key} must be an own data property`);
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
return descriptor.value;
|
|
965
|
+
return descriptor !== undefined && "value" in descriptor ? descriptor : undefined;
|
|
972
966
|
};
|
|
973
967
|
|
|
974
|
-
const type = read("type");
|
|
968
|
+
const type = read("type")?.value;
|
|
975
969
|
|
|
976
970
|
if (type !== "tool-call" && type !== "tool-result") return undefined;
|
|
977
|
-
const name = read("name");
|
|
971
|
+
const name = read("name")?.value;
|
|
978
972
|
|
|
979
973
|
if (typeof name !== "string" || !hasTool(toolkit.tools, name)) return undefined;
|
|
980
974
|
const tool = toolkit.tools[name];
|
|
981
|
-
const payload = read(type === "tool-call" ? "params" : "result");
|
|
975
|
+
const payload: unknown = read(type === "tool-call" ? "params" : "result")?.value;
|
|
982
976
|
|
|
983
977
|
if (payload === null || typeof payload !== "object") return undefined;
|
|
984
978
|
const payloadPrototype = Object.getPrototypeOf(payload);
|
|
@@ -1007,7 +1001,10 @@ const schemaClassToolPartPreflight = <Tools extends Record<string, Tool.Any>>(
|
|
|
1007
1001
|
];
|
|
1008
1002
|
|
|
1009
1003
|
for (const key of retainedKeys) {
|
|
1010
|
-
|
|
1004
|
+
const descriptor = read(key);
|
|
1005
|
+
|
|
1006
|
+
if (descriptor === undefined) return undefined;
|
|
1007
|
+
preflight[key] = descriptor.value;
|
|
1011
1008
|
}
|
|
1012
1009
|
|
|
1013
1010
|
return preflight;
|
|
@@ -1170,11 +1167,14 @@ const captureModelResponsePartGeneral = Effect.fnUntraced(function* <
|
|
|
1170
1167
|
|
|
1171
1168
|
const retainedBytes = yield* inspectModelResponsePartCapacity(usage, encoded, limits);
|
|
1172
1169
|
|
|
1170
|
+
if (typeof structuredCloneFunction !== "function") {
|
|
1171
|
+
return yield* ModelProtocolError.make({
|
|
1172
|
+
message: "Model response part could not be converted into engine-owned data",
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1173
1176
|
const ownedEncoded = yield* Effect.try({
|
|
1174
1177
|
try: () => {
|
|
1175
|
-
if (typeof structuredCloneFunction !== "function") {
|
|
1176
|
-
throw new TypeError("structuredClone is unavailable");
|
|
1177
|
-
}
|
|
1178
1178
|
const cloned: unknown = Reflect.apply(structuredCloneFunction, globalThis, [encoded]);
|
|
1179
1179
|
|
|
1180
1180
|
return cloned;
|
|
@@ -1188,53 +1188,67 @@ const captureModelResponsePartGeneral = Effect.fnUntraced(function* <
|
|
|
1188
1188
|
return { encodedPart: ownedEncoded, retainedBytes };
|
|
1189
1189
|
});
|
|
1190
1190
|
|
|
1191
|
-
const captureModelResponsePart = <Tools extends Record<string, Tool.Any>>(
|
|
1192
|
-
part: unknown,
|
|
1193
|
-
toolkit: Toolkit.Toolkit<Tools>,
|
|
1194
|
-
usage: ModelResponseBufferUsage,
|
|
1195
|
-
limits: EffectiveRunBufferLimits,
|
|
1196
|
-
): ReturnType<typeof captureModelResponsePartGeneral> =>
|
|
1197
|
-
Effect.suspend((): ReturnType<typeof captureModelResponsePartGeneral> => {
|
|
1198
|
-
const primitive =
|
|
1199
|
-
usage.responsePartCount < limits.maxModelResponseParts
|
|
1200
|
-
? capturePrimitiveTextPart(part, limits.maxModelResponseBytes - usage.responsePartBytes)
|
|
1201
|
-
: undefined;
|
|
1202
|
-
|
|
1203
|
-
return primitive === undefined
|
|
1204
|
-
? captureModelResponsePartGeneral(part, toolkit, usage, limits)
|
|
1205
|
-
: Effect.succeed(primitive);
|
|
1206
|
-
});
|
|
1207
|
-
|
|
1208
1191
|
interface OwnedModelResponsePart {
|
|
1209
1192
|
readonly ownedPart: Response.AnyPart;
|
|
1210
1193
|
readonly retainedBytes: number;
|
|
1211
1194
|
}
|
|
1212
1195
|
|
|
1196
|
+
const decodePrimitiveModelResponsePart = Schema.decodeUnknownEffect(
|
|
1197
|
+
Schema.toType(
|
|
1198
|
+
Schema.Union([
|
|
1199
|
+
Response.TextStartPart,
|
|
1200
|
+
Response.TextDeltaPart,
|
|
1201
|
+
Response.TextEndPart,
|
|
1202
|
+
Response.ReasoningStartPart,
|
|
1203
|
+
Response.ReasoningDeltaPart,
|
|
1204
|
+
Response.ReasoningEndPart,
|
|
1205
|
+
]),
|
|
1206
|
+
),
|
|
1207
|
+
);
|
|
1208
|
+
|
|
1213
1209
|
/** Reject trailing content before its capture can run an application codec or suspend. */
|
|
1214
|
-
const ownModelResponsePart =
|
|
1210
|
+
const ownModelResponsePart = <Tools extends Record<string, Tool.Any>>(
|
|
1215
1211
|
part: unknown,
|
|
1216
1212
|
toolkit: Toolkit.Toolkit<Tools>,
|
|
1217
1213
|
usage: ModelResponseBufferUsage,
|
|
1218
1214
|
limits: EffectiveRunBufferLimits,
|
|
1219
1215
|
finished: boolean,
|
|
1220
|
-
): Effect.
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1216
|
+
): Effect.Effect<OwnedModelResponsePart, ModelProtocolError> =>
|
|
1217
|
+
Effect.suspend((): Effect.Effect<OwnedModelResponsePart, ModelProtocolError> => {
|
|
1218
|
+
if (finished) {
|
|
1219
|
+
return Effect.fail(
|
|
1220
|
+
ModelProtocolError.make({
|
|
1221
|
+
message: "Model response emitted content after its finish part",
|
|
1222
|
+
}),
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1227
1225
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1233
|
-
),
|
|
1234
|
-
);
|
|
1226
|
+
const primitive =
|
|
1227
|
+
usage.responsePartCount < limits.maxModelResponseParts
|
|
1228
|
+
? capturePrimitiveTextPart(part, limits.maxModelResponseBytes - usage.responsePartBytes)
|
|
1229
|
+
: undefined;
|
|
1235
1230
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1231
|
+
// Complete primitive snapshots need validation, but no defaults or transformation.
|
|
1232
|
+
// Schema.toType retains every constraint from the native Response part schemas.
|
|
1233
|
+
if (primitive !== undefined) {
|
|
1234
|
+
return Effect.mapBothEager(decodePrimitiveModelResponsePart(primitive.part), {
|
|
1235
|
+
onFailure: () =>
|
|
1236
|
+
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1237
|
+
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes: primitive.retainedBytes }),
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// Pending application codecs keep normal Effect execution in this same consumer fiber.
|
|
1242
|
+
return Effect.flatMapEager(
|
|
1243
|
+
captureModelResponsePartGeneral(part, toolkit, usage, limits),
|
|
1244
|
+
(captured) =>
|
|
1245
|
+
Effect.mapBothEager(modelResponseParsersFor(toolkit).decode(captured.encodedPart), {
|
|
1246
|
+
onFailure: () =>
|
|
1247
|
+
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1248
|
+
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes: captured.retainedBytes }),
|
|
1249
|
+
}),
|
|
1250
|
+
);
|
|
1251
|
+
});
|
|
1238
1252
|
|
|
1239
1253
|
type PartLifecycle = "open" | "closed";
|
|
1240
1254
|
|
|
@@ -1244,7 +1258,10 @@ interface PreparedToolCall<Tools extends Record<string, Tool.Any>> {
|
|
|
1244
1258
|
readonly call: Response.ToolCallPart<string, Schema.Json>;
|
|
1245
1259
|
readonly name: keyof Tools & string;
|
|
1246
1260
|
readonly toolCallId: ToolCallId;
|
|
1247
|
-
readonly validation: Result.Result<
|
|
1261
|
+
readonly validation: Result.Result<
|
|
1262
|
+
Option.Option<Tool.Parameters<ToolUnion<Tools>>>,
|
|
1263
|
+
ToolParameterRejection
|
|
1264
|
+
>;
|
|
1248
1265
|
readonly tool: ToolUnion<Tools>;
|
|
1249
1266
|
readonly declarationIndex: number;
|
|
1250
1267
|
}
|
|
@@ -1375,9 +1392,15 @@ const decodeToolCallParameters = <Tools extends Record<string, Tool.Any>>(
|
|
|
1375
1392
|
);
|
|
1376
1393
|
};
|
|
1377
1394
|
|
|
1395
|
+
/** Native identity proves encoding does not transform the already validated parameters. */
|
|
1396
|
+
const reusesValidatedToolParameters = (tool: Tool.Any): boolean =>
|
|
1397
|
+
(tool.needsApproval === undefined || tool.needsApproval === false) &&
|
|
1398
|
+
SchemaAST.toEncoded(tool.parametersSchema.ast) === tool.parametersSchema.ast;
|
|
1399
|
+
|
|
1378
1400
|
/**
|
|
1379
|
-
*
|
|
1380
|
-
*
|
|
1401
|
+
* Fresh responses and resumed batches validate every call before reaching this boundary.
|
|
1402
|
+
* Identity encodings with no approval need no second decoded value. Other codecs retain the
|
|
1403
|
+
* complete batch preflight, including validation of their canonical round trip, before dispatch.
|
|
1381
1404
|
*/
|
|
1382
1405
|
const prepareToolCall = <Tools extends Record<string, Tool.Any>>(
|
|
1383
1406
|
toolkit: Toolkit.WithHandler<Tools>,
|
|
@@ -1398,17 +1421,26 @@ const prepareToolCall = <Tools extends Record<string, Tool.Any>>(
|
|
|
1398
1421
|
}
|
|
1399
1422
|
const tool = toolkit.tools[name] as ToolUnion<Tools>;
|
|
1400
1423
|
|
|
1401
|
-
return Effect.
|
|
1402
|
-
const
|
|
1424
|
+
return Effect.flatMap(decodeToolCallId(call.id), (toolCallId) => {
|
|
1425
|
+
const prepared = (
|
|
1426
|
+
validation: PreparedToolCall<Tools>["validation"],
|
|
1427
|
+
): PreparedToolCall<Tools> => ({
|
|
1428
|
+
call,
|
|
1429
|
+
name,
|
|
1430
|
+
toolCallId,
|
|
1431
|
+
validation,
|
|
1432
|
+
tool,
|
|
1433
|
+
declarationIndex,
|
|
1434
|
+
});
|
|
1403
1435
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
yield* decodeToolCallParameters<Tools>(tool, call.name, copyJson(call.params)),
|
|
1408
|
-
)
|
|
1409
|
-
: Result.fail(rejection);
|
|
1436
|
+
if (rejection !== undefined) return Effect.succeed(prepared(Result.fail(rejection)));
|
|
1437
|
+
if (reusesValidatedToolParameters(tool))
|
|
1438
|
+
return Effect.succeed(prepared(Result.succeed(Option.none())));
|
|
1410
1439
|
|
|
1411
|
-
return
|
|
1440
|
+
return Effect.map(
|
|
1441
|
+
decodeToolCallParameters<Tools>(tool, call.name, copyJson(call.params)),
|
|
1442
|
+
(parameters) => prepared(Result.succeed(Option.some(parameters))),
|
|
1443
|
+
);
|
|
1412
1444
|
});
|
|
1413
1445
|
};
|
|
1414
1446
|
|
|
@@ -1453,47 +1485,56 @@ const effectiveRunBounds = (
|
|
|
1453
1485
|
maxToolCalls: boundedAllowance(policy.maxToolCalls, options.toolCallAllowance),
|
|
1454
1486
|
});
|
|
1455
1487
|
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
if (descriptor === undefined || !("value" in descriptor)) {
|
|
1467
|
-
throw new TypeError(`settled Tool Call ${key} must be an own data property`);
|
|
1468
|
-
}
|
|
1488
|
+
const readSnapshotProperty = Effect.fnUntraced(function* (
|
|
1489
|
+
input: object,
|
|
1490
|
+
key: string,
|
|
1491
|
+
invalid: () => ModelProtocolError,
|
|
1492
|
+
optional = false,
|
|
1493
|
+
): Effect.fn.Return<unknown, ModelProtocolError> {
|
|
1494
|
+
const descriptor = yield* Effect.try({
|
|
1495
|
+
try: () => Object.getOwnPropertyDescriptor(input, key),
|
|
1496
|
+
catch: invalid,
|
|
1497
|
+
});
|
|
1469
1498
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1499
|
+
if (descriptor === undefined && optional) return undefined;
|
|
1500
|
+
if (descriptor === undefined || !("value" in descriptor)) return yield* invalid();
|
|
1472
1501
|
|
|
1473
|
-
|
|
1502
|
+
return descriptor.value;
|
|
1503
|
+
});
|
|
1474
1504
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1505
|
+
const decodeResumedSettledCall = Effect.fnUntraced(function* (input: unknown) {
|
|
1506
|
+
const invalid = () =>
|
|
1507
|
+
ModelProtocolError.make({
|
|
1508
|
+
message: "Turn resume contains an invalid settled Tool Call",
|
|
1509
|
+
});
|
|
1478
1510
|
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1511
|
+
if (input === null || typeof input !== "object") return yield* invalid();
|
|
1512
|
+
const id = yield* readSnapshotProperty(input, "id", invalid);
|
|
1513
|
+
const resultValue = yield* readSnapshotProperty(input, "result", invalid);
|
|
1514
|
+
const isFailure = yield* readSnapshotProperty(input, "isFailure", invalid);
|
|
1482
1515
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
};
|
|
1490
|
-
},
|
|
1491
|
-
catch: () =>
|
|
1492
|
-
ModelProtocolError.make({
|
|
1493
|
-
message: "Turn resume contains an invalid settled Tool Call",
|
|
1494
|
-
}),
|
|
1516
|
+
const { selection, rejected } = yield* Effect.try({
|
|
1517
|
+
try: () => ({
|
|
1518
|
+
selection: Object.getOwnPropertyDescriptor(input, "toolSelection"),
|
|
1519
|
+
rejected: Object.getOwnPropertyDescriptor(input, "budgetRejected"),
|
|
1520
|
+
}),
|
|
1521
|
+
catch: invalid,
|
|
1495
1522
|
});
|
|
1496
1523
|
|
|
1524
|
+
if (
|
|
1525
|
+
(selection !== undefined && !("value" in selection)) ||
|
|
1526
|
+
(rejected !== undefined && !("value" in rejected))
|
|
1527
|
+
)
|
|
1528
|
+
return yield* invalid();
|
|
1529
|
+
|
|
1530
|
+
const raw = {
|
|
1531
|
+
id,
|
|
1532
|
+
result: resultValue,
|
|
1533
|
+
isFailure,
|
|
1534
|
+
...(rejected === undefined ? {} : { budgetRejected: rejected.value }),
|
|
1535
|
+
...(selection === undefined ? {} : { toolSelection: selection.value }),
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1497
1538
|
const result = boundedCanonicalJsonSnapshot(raw.result, MAX_RESUMED_RESULT_BYTES);
|
|
1498
1539
|
|
|
1499
1540
|
if (result === undefined) {
|
|
@@ -1514,106 +1555,73 @@ const decodeResumedSettledCall = Effect.fnUntraced(function* (input: unknown) {
|
|
|
1514
1555
|
);
|
|
1515
1556
|
});
|
|
1516
1557
|
|
|
1517
|
-
const snapshotResumedSettledCalls = (resume: unknown, maximum: number)
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
const settledDescriptor = Object.getOwnPropertyDescriptor(resume, "settled");
|
|
1524
|
-
|
|
1525
|
-
if (settledDescriptor === undefined || !("value" in settledDescriptor)) {
|
|
1526
|
-
throw new TypeError("Turn resume settled must be an own data property");
|
|
1527
|
-
}
|
|
1528
|
-
const settled = settledDescriptor.value;
|
|
1558
|
+
const snapshotResumedSettledCalls = Effect.fnUntraced(function* (resume: unknown, maximum: number) {
|
|
1559
|
+
const invalid = () =>
|
|
1560
|
+
ModelProtocolError.make({
|
|
1561
|
+
message: "Turn resume contains an invalid settled Tool Call collection",
|
|
1562
|
+
});
|
|
1529
1563
|
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
const lengthDescriptor = Object.getOwnPropertyDescriptor(settled, "length");
|
|
1564
|
+
if (resume === null || typeof resume !== "object") return yield* invalid();
|
|
1565
|
+
const settled = yield* readSnapshotProperty(resume, "settled", invalid);
|
|
1566
|
+
const isArray = yield* Effect.try({ try: () => Array.isArray(settled), catch: invalid });
|
|
1534
1567
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
!("value" in lengthDescriptor) ||
|
|
1538
|
-
!Number.isSafeInteger(lengthDescriptor.value) ||
|
|
1539
|
-
lengthDescriptor.value < 0 ||
|
|
1540
|
-
lengthDescriptor.value > maximum
|
|
1541
|
-
) {
|
|
1542
|
-
throw new TypeError("Turn resume settled has an invalid length");
|
|
1543
|
-
}
|
|
1544
|
-
const snapshot: Array<unknown> = [];
|
|
1568
|
+
if (!isArray || settled === null || typeof settled !== "object") return yield* invalid();
|
|
1569
|
+
const length = yield* readSnapshotProperty(settled, "length", invalid);
|
|
1545
1570
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1571
|
+
if (
|
|
1572
|
+
typeof length !== "number" ||
|
|
1573
|
+
!Number.isSafeInteger(length) ||
|
|
1574
|
+
length < 0 ||
|
|
1575
|
+
length > maximum
|
|
1576
|
+
) {
|
|
1577
|
+
return yield* invalid();
|
|
1578
|
+
}
|
|
1579
|
+
const snapshot: Array<unknown> = [];
|
|
1548
1580
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
snapshot.push(entryDescriptor.value);
|
|
1553
|
-
}
|
|
1581
|
+
for (let index = 0; index < length; index += 1) {
|
|
1582
|
+
snapshot.push(yield* readSnapshotProperty(settled, String(index), invalid));
|
|
1583
|
+
}
|
|
1554
1584
|
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
catch: () =>
|
|
1558
|
-
ModelProtocolError.make({
|
|
1559
|
-
message: "Turn resume contains an invalid settled Tool Call collection",
|
|
1560
|
-
}),
|
|
1561
|
-
});
|
|
1585
|
+
return snapshot;
|
|
1586
|
+
});
|
|
1562
1587
|
|
|
1563
1588
|
const decodeResumeUsage = Effect.fnUntraced(function* (input: unknown) {
|
|
1564
|
-
const
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
const read = (key: keyof RunResumeUsage, optional = false): unknown => {
|
|
1571
|
-
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
1572
|
-
|
|
1573
|
-
if (descriptor === undefined && optional) return undefined;
|
|
1574
|
-
if (descriptor === undefined || !("value" in descriptor)) {
|
|
1575
|
-
throw new TypeError(`Run resume usage ${key} must be an own data property`);
|
|
1576
|
-
}
|
|
1589
|
+
const invalid = () =>
|
|
1590
|
+
ModelProtocolError.make({
|
|
1591
|
+
message:
|
|
1592
|
+
"Run resume usage requires own data properties with non-negative safe-integer totals and last-call tokens no greater than their cumulative totals",
|
|
1593
|
+
});
|
|
1577
1594
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1595
|
+
if (input === null || typeof input !== "object") return yield* invalid();
|
|
1596
|
+
const snapshot: Record<string, unknown> = {};
|
|
1580
1597
|
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
] as const
|
|
1591
|
-
)
|
|
1592
|
-
.map((key) => [key, read(key, true)])
|
|
1593
|
-
.filter(([, value]) => value !== undefined),
|
|
1594
|
-
);
|
|
1598
|
+
for (const key of [
|
|
1599
|
+
"usageStatus",
|
|
1600
|
+
"pricingStatus",
|
|
1601
|
+
"unobservedModelCalls",
|
|
1602
|
+
"children",
|
|
1603
|
+
"modelRestarts",
|
|
1604
|
+
"webSearchCalls",
|
|
1605
|
+
] satisfies ReadonlyArray<keyof RunResumeUsage>) {
|
|
1606
|
+
const value = yield* readSnapshotProperty(input, key, invalid, true);
|
|
1595
1607
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
message:
|
|
1614
|
-
"Run resume usage requires own data properties with non-negative safe-integer totals and last-call tokens no greater than their cumulative totals",
|
|
1615
|
-
}),
|
|
1616
|
-
});
|
|
1608
|
+
if (value !== undefined) snapshot[key] = value;
|
|
1609
|
+
}
|
|
1610
|
+
for (const key of [
|
|
1611
|
+
"modelCalls",
|
|
1612
|
+
"inputTokens",
|
|
1613
|
+
"outputTokens",
|
|
1614
|
+
"lastInputTokens",
|
|
1615
|
+
"lastOutputTokens",
|
|
1616
|
+
"costMicrousd",
|
|
1617
|
+
"committedTurns",
|
|
1618
|
+
"toolCalls",
|
|
1619
|
+
"programmaticToolCalls",
|
|
1620
|
+
"consecutiveToolFailures",
|
|
1621
|
+
"finalizationUsed",
|
|
1622
|
+
] satisfies ReadonlyArray<keyof RunResumeUsage>) {
|
|
1623
|
+
snapshot[key] = yield* readSnapshotProperty(input, key, invalid);
|
|
1624
|
+
}
|
|
1617
1625
|
|
|
1618
1626
|
return yield* Schema.decodeUnknownEffect(RunResumeUsageSchema)(snapshot).pipe(
|
|
1619
1627
|
Effect.mapError(() =>
|
|
@@ -1802,7 +1810,8 @@ const prepareApprovalRequest = <Tools extends Record<string, Tool.Any>>(
|
|
|
1802
1810
|
context,
|
|
1803
1811
|
Effect.gen(function* () {
|
|
1804
1812
|
if (Result.isFailure(prepared.validation)) return undefined;
|
|
1805
|
-
|
|
1813
|
+
if (Option.isNone(prepared.validation.success)) return undefined;
|
|
1814
|
+
const decodedParams = prepared.validation.success.value;
|
|
1806
1815
|
const approval = prepared.tool.needsApproval;
|
|
1807
1816
|
|
|
1808
1817
|
const required =
|
|
@@ -2169,6 +2178,7 @@ const executePreparedToolCall = Effect.fnUntraced(function* <
|
|
|
2169
2178
|
| AiError.AiError
|
|
2170
2179
|
| Tool.HandlerError<ToolUnion<Tools>>;
|
|
2171
2180
|
const call = prepared.call;
|
|
2181
|
+
const toolCallId = prepared.toolCallId;
|
|
2172
2182
|
const observer = context.toolFailureObserver;
|
|
2173
2183
|
const telemetryToolCallId = isTelemetryToolCallId(call.id) ? call.id : undefined;
|
|
2174
2184
|
const executionClass = getToolExecutionClass(prepared.tool);
|
|
@@ -2208,8 +2218,6 @@ const executePreparedToolCall = Effect.fnUntraced(function* <
|
|
|
2208
2218
|
const started = Result.isFailure(prepared.validation)
|
|
2209
2219
|
? Effect.void
|
|
2210
2220
|
: Effect.gen(function* () {
|
|
2211
|
-
const toolCallId = yield* decodeToolCallId(call.id);
|
|
2212
|
-
|
|
2213
2221
|
yield* Effect.logDebug("agent tool handler started").pipe(
|
|
2214
2222
|
Effect.annotateLogs({
|
|
2215
2223
|
agentId: context.agentId,
|
|
@@ -2268,7 +2276,6 @@ const executePreparedToolCall = Effect.fnUntraced(function* <
|
|
|
2268
2276
|
return yield* ModelProtocolError.make({
|
|
2269
2277
|
message: `Tool Call ${call.id} produced more than one terminal result`,
|
|
2270
2278
|
});
|
|
2271
|
-
const toolCallId = yield* decodeToolCallId(call.id);
|
|
2272
2279
|
const encodedResult = toolResultForJson(prepared.tool, result);
|
|
2273
2280
|
|
|
2274
2281
|
if (result.preliminary) {
|
|
@@ -2305,7 +2312,6 @@ const executePreparedToolCall = Effect.fnUntraced(function* <
|
|
|
2305
2312
|
message: `Tool Call ${call.id} completed without a terminal result`,
|
|
2306
2313
|
});
|
|
2307
2314
|
const result = terminalResult;
|
|
2308
|
-
const toolCallId = yield* decodeToolCallId(call.id);
|
|
2309
2315
|
let toolSelection: Selection | undefined;
|
|
2310
2316
|
|
|
2311
2317
|
if (!result.isFailure && Context.get(prepared.tool.annotations, DiscoveryTool)) {
|
|
@@ -2321,7 +2327,7 @@ const executePreparedToolCall = Effect.fnUntraced(function* <
|
|
|
2321
2327
|
context.toolSchemaTransformer,
|
|
2322
2328
|
);
|
|
2323
2329
|
}
|
|
2324
|
-
const encodedResult = boundEncodedToolResult(result.encodedResult, resultBounds);
|
|
2330
|
+
const encodedResult = yield* boundEncodedToolResult(result.encodedResult, resultBounds);
|
|
2325
2331
|
|
|
2326
2332
|
// The ordinary JSON boundary remains required; only the public Class projection is optional.
|
|
2327
2333
|
const successResult = result.isFailure
|
|
@@ -2392,22 +2398,21 @@ const executePreparedToolCall = Effect.fnUntraced(function* <
|
|
|
2392
2398
|
if (result.isFailure) return yield* failureMarker();
|
|
2393
2399
|
});
|
|
2394
2400
|
|
|
2395
|
-
const failTerminalResult = (cause: Cause.Cause<ToolExecutionError>)
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
);
|
|
2401
|
+
const failTerminalResult = Effect.fnUntraced(function* (cause: Cause.Cause<ToolExecutionError>) {
|
|
2402
|
+
terminal = true;
|
|
2403
|
+
terminalResult = undefined;
|
|
2404
|
+
propagatedFailure = cause;
|
|
2405
|
+
// The raw failure has no canonical success/result, matching the prior propagated path.
|
|
2406
|
+
yield* decodeToolCallId(call.id);
|
|
2407
|
+
trace.finalToolResultIds.add(call.id);
|
|
2408
|
+
terminalResultCommitted = true;
|
|
2409
|
+
yield* terminalTelemetry("failure", "propagated");
|
|
2410
|
+
yield* publishEvent(context, () =>
|
|
2411
|
+
makeToolFailedEvent(context, turnId, call, Cause.squash(cause), "propagated"),
|
|
2412
|
+
);
|
|
2408
2413
|
|
|
2409
|
-
|
|
2410
|
-
|
|
2414
|
+
return yield* failureMarker();
|
|
2415
|
+
});
|
|
2411
2416
|
|
|
2412
2417
|
const measured = started.pipe(
|
|
2413
2418
|
Effect.andThen(results),
|
|
@@ -2491,6 +2496,7 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2491
2496
|
| AgentUpdateAcceptance
|
|
2492
2497
|
| ToolSpanTelemetry
|
|
2493
2498
|
| ProgrammaticToolAuthorization
|
|
2499
|
+
| Scope.Scope
|
|
2494
2500
|
| Tool.HandlerServices<ToolUnion<Tools>>
|
|
2495
2501
|
> {
|
|
2496
2502
|
const exposed = context.toolExposure?.exposedToolNames;
|
|
@@ -2513,22 +2519,38 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2513
2519
|
message: "Tool batch calls a Tool outside its request exposure or current visibility",
|
|
2514
2520
|
});
|
|
2515
2521
|
|
|
2516
|
-
const
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
calls.flatMap((call, declarationIndex) =>
|
|
2520
|
-
settledCallIds?.has(call.id) ? [] : [{ call, declarationIndex }],
|
|
2521
|
-
),
|
|
2522
|
-
({ call, declarationIndex }) =>
|
|
2523
|
-
prepareToolCall(
|
|
2524
|
-
toolkit,
|
|
2525
|
-
call,
|
|
2526
|
-
declarationIndex,
|
|
2527
|
-
trace.toolParameterRejections.get(call.id),
|
|
2528
|
-
),
|
|
2522
|
+
const prepareCalls = Effect.forEach(
|
|
2523
|
+
calls.flatMap((call, declarationIndex) =>
|
|
2524
|
+
settledCallIds?.has(call.id) ? [] : [{ call, declarationIndex }],
|
|
2529
2525
|
),
|
|
2526
|
+
({ call, declarationIndex }) =>
|
|
2527
|
+
prepareToolCall(toolkit, call, declarationIndex, trace.toolParameterRejections.get(call.id)),
|
|
2528
|
+
);
|
|
2529
|
+
|
|
2530
|
+
const needsParameterDecoding = calls.some(
|
|
2531
|
+
(call) =>
|
|
2532
|
+
!settledCallIds?.has(call.id) &&
|
|
2533
|
+
!trace.toolParameterRejections.has(call.id) &&
|
|
2534
|
+
hasTool(toolkit.tools, call.name) &&
|
|
2535
|
+
!reusesValidatedToolParameters(toolkit.tools[call.name]),
|
|
2530
2536
|
);
|
|
2531
2537
|
|
|
2538
|
+
const prepared = yield* needsParameterDecoding
|
|
2539
|
+
? prepareWithinDeadline(context, prepareCalls)
|
|
2540
|
+
: Effect.flatMap(
|
|
2541
|
+
Clock.currentTimeMillis,
|
|
2542
|
+
(
|
|
2543
|
+
now,
|
|
2544
|
+
): Effect.Effect<
|
|
2545
|
+
Array<PreparedToolCall<Tools>>,
|
|
2546
|
+
ModelProtocolError | AgentPolicyError,
|
|
2547
|
+
Tool.HandlerServices<ToolUnion<Tools>>
|
|
2548
|
+
> =>
|
|
2549
|
+
context.durationDeadlineMillis - now <= 0
|
|
2550
|
+
? Effect.fail(context.durationFailure)
|
|
2551
|
+
: prepareCalls,
|
|
2552
|
+
);
|
|
2553
|
+
|
|
2532
2554
|
if (
|
|
2533
2555
|
prepared.some((call) => Context.get(call.tool.annotations, ContextRolloverTool)) &&
|
|
2534
2556
|
calls.length !== 1
|
|
@@ -2692,7 +2714,7 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2692
2714
|
"SubagentInterrupted",
|
|
2693
2715
|
"SubagentJoined",
|
|
2694
2716
|
].includes(payload._tag)
|
|
2695
|
-
? Effect.fail(
|
|
2717
|
+
? Effect.fail(RunEventSinkClosedError.make({ message: "Unsupported Tool event payload" }))
|
|
2696
2718
|
: emitPayload(payload),
|
|
2697
2719
|
};
|
|
2698
2720
|
|
|
@@ -2702,28 +2724,28 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2702
2724
|
emit: (request) =>
|
|
2703
2725
|
context.updatePermits.withPermit(
|
|
2704
2726
|
Effect.gen(function* () {
|
|
2705
|
-
if (!updatesOpen) return yield*
|
|
2727
|
+
if (!updatesOpen) return yield* UpdateError.make({ reason: "unavailable" });
|
|
2706
2728
|
if (
|
|
2707
2729
|
request.target.id !== context.agentId ||
|
|
2708
2730
|
request.target.updates !== context.definition.updates ||
|
|
2709
2731
|
context.definition.updates === undefined
|
|
2710
2732
|
)
|
|
2711
|
-
return yield*
|
|
2733
|
+
return yield* UpdateError.make({ reason: "identity" });
|
|
2712
2734
|
|
|
2713
2735
|
const updateId = yield* Schema.decodeEffect(IdempotencyKey)(request.updateId).pipe(
|
|
2714
|
-
Effect.mapError(() =>
|
|
2736
|
+
Effect.mapError(() => UpdateError.make({ reason: "validation" })),
|
|
2715
2737
|
);
|
|
2716
2738
|
|
|
2717
2739
|
const value = yield* Schema.decodeEffect(Schema.fromJsonString(Schema.Json))(
|
|
2718
2740
|
JSON.stringify(request.value),
|
|
2719
|
-
).pipe(Effect.mapError(() =>
|
|
2741
|
+
).pipe(Effect.mapError(() => UpdateError.make({ reason: "validation" })));
|
|
2720
2742
|
|
|
2721
2743
|
yield* context.validateUpdate(value);
|
|
2722
2744
|
const existing = context.updates.get(updateId);
|
|
2723
2745
|
|
|
2724
2746
|
if (existing !== undefined) {
|
|
2725
2747
|
if (!Schema.toEquivalence(Schema.Json)(existing.value, value))
|
|
2726
|
-
return yield*
|
|
2748
|
+
return yield* UpdateError.make({ reason: "conflict" });
|
|
2727
2749
|
|
|
2728
2750
|
return existing;
|
|
2729
2751
|
}
|
|
@@ -2739,11 +2761,11 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2739
2761
|
context.updates.size >= maxCount ||
|
|
2740
2762
|
context.updateBytes + bytes > maxBytes
|
|
2741
2763
|
)
|
|
2742
|
-
return yield*
|
|
2764
|
+
return yield* UpdateError.make({ reason: "capacity" });
|
|
2743
2765
|
|
|
2744
2766
|
const snapshot = boundedCanonicalJsonSnapshot(value, bytes);
|
|
2745
2767
|
|
|
2746
|
-
if (snapshot === undefined) return yield*
|
|
2768
|
+
if (snapshot === undefined) return yield* UpdateError.make({ reason: "validation" });
|
|
2747
2769
|
|
|
2748
2770
|
const accepted = yield* updateAcceptance.accept(
|
|
2749
2771
|
Object.freeze(
|
|
@@ -2760,7 +2782,7 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2760
2782
|
);
|
|
2761
2783
|
|
|
2762
2784
|
const validated = yield* Schema.decodeEffect(Update)(accepted).pipe(
|
|
2763
|
-
Effect.mapError(() =>
|
|
2785
|
+
Effect.mapError(() => UpdateError.make({ reason: "validation" })),
|
|
2764
2786
|
);
|
|
2765
2787
|
|
|
2766
2788
|
if (
|
|
@@ -2770,7 +2792,7 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2770
2792
|
validated.updateId !== request.updateId ||
|
|
2771
2793
|
!Schema.toEquivalence(Schema.Json)(validated.value, value)
|
|
2772
2794
|
)
|
|
2773
|
-
return yield*
|
|
2795
|
+
return yield* UpdateError.make({ reason: "identity" });
|
|
2774
2796
|
|
|
2775
2797
|
// Keep acknowledgements, replay, and retries on the same owned value even when
|
|
2776
2798
|
// the accepting host retains its own mutable Update instance.
|
|
@@ -2780,7 +2802,7 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2780
2802
|
context.updateBytes += bytes;
|
|
2781
2803
|
|
|
2782
2804
|
yield* emitPayload({ _tag: "AgentUpdateEmitted", update }).pipe(
|
|
2783
|
-
Effect.mapError(() =>
|
|
2805
|
+
Effect.mapError(() => UpdateError.make({ reason: "unavailable" })),
|
|
2784
2806
|
);
|
|
2785
2807
|
|
|
2786
2808
|
return update;
|
|
@@ -2788,9 +2810,9 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2788
2810
|
),
|
|
2789
2811
|
};
|
|
2790
2812
|
|
|
2791
|
-
//
|
|
2792
|
-
//
|
|
2793
|
-
//
|
|
2813
|
+
// The permit covers the child's complete lifetime, including descendant cleanup. The call
|
|
2814
|
+
// owns its resource Scope and its fiber belongs to the enclosing Turn Scope. The native bracket
|
|
2815
|
+
// joins or interrupts it before releasing the permit, without another supervisor Scope.
|
|
2794
2816
|
const permits = yield* Semaphore.make(concurrency);
|
|
2795
2817
|
|
|
2796
2818
|
const handlers = Effect.gen(function* () {
|
|
@@ -2799,9 +2821,9 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2799
2821
|
group,
|
|
2800
2822
|
(call) =>
|
|
2801
2823
|
permits.withPermit(
|
|
2802
|
-
Effect.
|
|
2803
|
-
Effect.
|
|
2804
|
-
|
|
2824
|
+
Effect.acquireUseRelease(
|
|
2825
|
+
Effect.forkScoped(
|
|
2826
|
+
Effect.scoped(
|
|
2805
2827
|
Effect.gen(function* () {
|
|
2806
2828
|
yield* checkpointExecution(context, durability);
|
|
2807
2829
|
const subagentHost = yield* SubagentHost.forTool;
|
|
@@ -2827,6 +2849,11 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2827
2849
|
hookServices,
|
|
2828
2850
|
});
|
|
2829
2851
|
|
|
2852
|
+
const step = stepServiceFor(call);
|
|
2853
|
+
const subagent = subagentHost(source);
|
|
2854
|
+
const messaging = messagingHost(source);
|
|
2855
|
+
const catalog = { entries: context.toolCatalog };
|
|
2856
|
+
|
|
2830
2857
|
return yield* executePreparedToolCall(
|
|
2831
2858
|
context,
|
|
2832
2859
|
turnId,
|
|
@@ -2838,30 +2865,52 @@ const executeToolBatch = Effect.fnUntraced(function* <
|
|
|
2838
2865
|
waitingByDeclaration.set(call.declarationIndex, waiting);
|
|
2839
2866
|
},
|
|
2840
2867
|
).pipe(
|
|
2841
|
-
|
|
2842
|
-
Effect.
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2868
|
+
// Match the original providers' outer-to-inner order with one restoration.
|
|
2869
|
+
Effect.updateContext(
|
|
2870
|
+
(
|
|
2871
|
+
services: Context.Context<
|
|
2872
|
+
ToolSpanTelemetry | Tool.HandlerServices<ToolUnion<Tools>>
|
|
2873
|
+
>,
|
|
2874
|
+
) =>
|
|
2875
|
+
services.pipe(
|
|
2876
|
+
Context.add(CurrentToolCatalog, catalog),
|
|
2877
|
+
Context.add(ToolBroker, broker.service),
|
|
2878
|
+
Context.add(MessagingHost, messaging),
|
|
2879
|
+
Context.add(SubagentHost, subagent),
|
|
2880
|
+
Context.add(DurableStep, step),
|
|
2881
|
+
),
|
|
2882
|
+
),
|
|
2846
2883
|
Effect.ensuring(Effect.sync(() => broker.close())),
|
|
2847
2884
|
);
|
|
2848
2885
|
}),
|
|
2849
|
-
)
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
return yield* Fiber.join(fiber);
|
|
2854
|
-
}),
|
|
2886
|
+
),
|
|
2887
|
+
),
|
|
2888
|
+
Fiber.join,
|
|
2889
|
+
Fiber.interrupt,
|
|
2855
2890
|
),
|
|
2856
2891
|
),
|
|
2857
|
-
{ concurrency: "unbounded", discard: true },
|
|
2892
|
+
{ concurrency: group.length === 1 ? 1 : "unbounded", discard: true },
|
|
2858
2893
|
);
|
|
2859
|
-
|
|
2894
|
+
// Each following call checks before dispatch; the batch owner checks again
|
|
2895
|
+
// after the final group, its cleanup, and the optional budget guard.
|
|
2860
2896
|
}
|
|
2861
2897
|
}).pipe(
|
|
2862
|
-
Effect.
|
|
2863
|
-
|
|
2864
|
-
|
|
2898
|
+
Effect.updateContext(
|
|
2899
|
+
(
|
|
2900
|
+
services: Context.Context<
|
|
2901
|
+
| HookRequirements
|
|
2902
|
+
| ToolSpanTelemetry
|
|
2903
|
+
| ProgrammaticToolAuthorization
|
|
2904
|
+
| Scope.Scope
|
|
2905
|
+
| Tool.HandlerServices<ToolUnion<Tools>>
|
|
2906
|
+
>,
|
|
2907
|
+
) =>
|
|
2908
|
+
services.pipe(
|
|
2909
|
+
Context.add(SubagentDurability, batchSubagentDurability),
|
|
2910
|
+
Context.add(Emitter, updateEmitter),
|
|
2911
|
+
Context.add(RunEventSink, batchSink),
|
|
2912
|
+
),
|
|
2913
|
+
),
|
|
2865
2914
|
Effect.ensuring(
|
|
2866
2915
|
Effect.sync(() => {
|
|
2867
2916
|
updatesOpen = false;
|
|
@@ -3118,13 +3167,17 @@ const toolResultForJson = (tool: Tool.Any, result: Tool.HandlerResult<Tool.Any>)
|
|
|
3118
3167
|
return result.encodedResult;
|
|
3119
3168
|
};
|
|
3120
3169
|
|
|
3170
|
+
const decodeBoundedToolResult = Schema.decodeEffect(Schema.fromJsonString(Schema.Unknown));
|
|
3171
|
+
|
|
3121
3172
|
/**
|
|
3122
3173
|
* RUN-022: bound one application Tool result at the settle seam. The bounded
|
|
3123
3174
|
* value is what official history, the settled record, and the success event
|
|
3124
|
-
* all carry, so every downstream surface agrees
|
|
3125
|
-
* passes through with its identity preserved.
|
|
3175
|
+
* all carry, so every downstream surface agrees on the measured representation.
|
|
3126
3176
|
*/
|
|
3127
|
-
const boundEncodedToolResult = (
|
|
3177
|
+
const boundEncodedToolResult = (
|
|
3178
|
+
encodedResult: unknown,
|
|
3179
|
+
bounds: ToolResultBounds,
|
|
3180
|
+
): Effect.Effect<unknown> => {
|
|
3128
3181
|
let text: string | undefined;
|
|
3129
3182
|
|
|
3130
3183
|
try {
|
|
@@ -3132,10 +3185,10 @@ const boundEncodedToolResult = (encodedResult: unknown, bounds: ToolResultBounds
|
|
|
3132
3185
|
} catch (cause) {
|
|
3133
3186
|
// Fail closed (runtime spec §9): an unserializable result is unbounded by
|
|
3134
3187
|
// construction, so the sentinel replaces it before history or records.
|
|
3135
|
-
return unserializableToolResult(cause);
|
|
3188
|
+
return Effect.succeed(unserializableToolResult(cause));
|
|
3136
3189
|
}
|
|
3137
3190
|
if (text === undefined) {
|
|
3138
|
-
return unserializableToolResult("the encoded result is not a JSON value");
|
|
3191
|
+
return Effect.succeed(unserializableToolResult("the encoded result is not a JSON value"));
|
|
3139
3192
|
}
|
|
3140
3193
|
const bounded = applyToolResultBounds(text, bounds);
|
|
3141
3194
|
|
|
@@ -3143,12 +3196,8 @@ const boundEncodedToolResult = (encodedResult: unknown, bounds: ToolResultBounds
|
|
|
3143
3196
|
// truncated and unmodified cases: returning the original object would let a
|
|
3144
3197
|
// stateful `toJSON` pass the byte check small and expand or throw on later
|
|
3145
3198
|
// canonical serialization, carrying unchecked state past the boundary.
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
} catch (cause) {
|
|
3149
|
-
// Defensive only: `applyToolResultBounds` always emits valid JSON.
|
|
3150
|
-
return unserializableToolResult(cause);
|
|
3151
|
-
}
|
|
3199
|
+
// Stay in the caller's Effect; a synchronous decoder would start another runtime fiber.
|
|
3200
|
+
return decodeBoundedToolResult(bounded).pipe(Effect.orElseSucceed(unserializableToolResult));
|
|
3152
3201
|
};
|
|
3153
3202
|
|
|
3154
3203
|
const durableResultBounds = <E, R>(
|
|
@@ -3337,7 +3386,7 @@ const consumeUsage = <AgentValue extends Agent.Any, HookError, HookRequirements>
|
|
|
3337
3386
|
// them separately. Separate that overlap before constructing the canonical,
|
|
3338
3387
|
// additive input components, but retain the raw provider usage for pricing.
|
|
3339
3388
|
const reportedInputTotal = providerUsage.inputTokens.total;
|
|
3340
|
-
const reportedInputWithoutWrite =
|
|
3389
|
+
const reportedInputWithoutWrite = reportedUncached + cacheRead;
|
|
3341
3390
|
|
|
3342
3391
|
const cacheWriteOverlapsUncached =
|
|
3343
3392
|
reportedInputTotal !== undefined &&
|
|
@@ -3350,9 +3399,7 @@ const consumeUsage = <AgentValue extends Agent.Any, HookError, HookRequirements>
|
|
|
3350
3399
|
const disjointReportedUncached =
|
|
3351
3400
|
reportedUncached - (cacheWriteOverlapsUncached ? cacheWrite : 0);
|
|
3352
3401
|
|
|
3353
|
-
const reportedInputComponents =
|
|
3354
|
-
disjointReportedUncached + cacheRead + cacheWrite,
|
|
3355
|
-
);
|
|
3402
|
+
const reportedInputComponents = disjointReportedUncached + cacheRead + cacheWrite;
|
|
3356
3403
|
|
|
3357
3404
|
const allInputComponentsReported =
|
|
3358
3405
|
providerUsage.inputTokens.uncached !== undefined &&
|
|
@@ -3367,7 +3414,7 @@ const consumeUsage = <AgentValue extends Agent.Any, HookError, HookRequirements>
|
|
|
3367
3414
|
return yield* invalidProviderUsage();
|
|
3368
3415
|
}
|
|
3369
3416
|
const inputTokens = reportedInputTotal ?? reportedInputComponents;
|
|
3370
|
-
const reportedOutputComponents =
|
|
3417
|
+
const reportedOutputComponents = reportedText + reasoning;
|
|
3371
3418
|
const reportedOutputTotal = providerUsage.outputTokens.total;
|
|
3372
3419
|
|
|
3373
3420
|
const allOutputComponentsReported =
|
|
@@ -3418,6 +3465,20 @@ const consumeUsage = <AgentValue extends Agent.Any, HookError, HookRequirements>
|
|
|
3418
3465
|
? outputRemainder
|
|
3419
3466
|
: 0);
|
|
3420
3467
|
|
|
3468
|
+
// Validate the complete additive components before pricing or staging usage.
|
|
3469
|
+
const inputUsage = yield* InputTokenUsage.makeEffect({
|
|
3470
|
+
total: inputTokens,
|
|
3471
|
+
uncached,
|
|
3472
|
+
cacheRead: normalizedCacheRead,
|
|
3473
|
+
cacheWrite: normalizedCacheWrite,
|
|
3474
|
+
}).pipe(Effect.mapError(() => invalidProviderUsage()));
|
|
3475
|
+
|
|
3476
|
+
const outputUsage = yield* OutputTokenUsage.makeEffect({
|
|
3477
|
+
total: outputTokens,
|
|
3478
|
+
text,
|
|
3479
|
+
reasoning: normalizedReasoning,
|
|
3480
|
+
}).pipe(Effect.mapError(() => invalidProviderUsage()));
|
|
3481
|
+
|
|
3421
3482
|
const totalTokens = yield* decodeProviderUsageTotal(inputTokens + outputTokens);
|
|
3422
3483
|
const provider = yield* Model.ProviderName;
|
|
3423
3484
|
const model = yield* Model.ModelName;
|
|
@@ -3485,17 +3546,8 @@ const consumeUsage = <AgentValue extends Agent.Any, HookError, HookRequirements>
|
|
|
3485
3546
|
pricingStatus,
|
|
3486
3547
|
...(serviceTier === undefined ? {} : { serviceTier }),
|
|
3487
3548
|
...(pricingVersion === undefined ? {} : { pricingVersion }),
|
|
3488
|
-
inputTokens:
|
|
3489
|
-
|
|
3490
|
-
uncached,
|
|
3491
|
-
cacheRead: normalizedCacheRead,
|
|
3492
|
-
cacheWrite: normalizedCacheWrite,
|
|
3493
|
-
}),
|
|
3494
|
-
outputTokens: OutputTokenUsage.make({
|
|
3495
|
-
total: outputTokens,
|
|
3496
|
-
text,
|
|
3497
|
-
reasoning: normalizedReasoning,
|
|
3498
|
-
}),
|
|
3549
|
+
inputTokens: inputUsage,
|
|
3550
|
+
outputTokens: outputUsage,
|
|
3499
3551
|
costMicrousd,
|
|
3500
3552
|
});
|
|
3501
3553
|
|
|
@@ -4127,76 +4179,75 @@ const compactContext = <AgentValue extends Agent.Any, HookError, HookRequirement
|
|
|
4127
4179
|
const textParts = new Map<string, PartLifecycle>();
|
|
4128
4180
|
const reasoningParts = new Map<string, PartLifecycle>();
|
|
4129
4181
|
|
|
4130
|
-
const consumeSummaryPart = (owned: OwnedModelResponsePart)
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
summaryFinishMetadata = ownedPart.metadata;
|
|
4142
|
-
}
|
|
4143
|
-
} else if (ownedPart.type === "response-metadata") {
|
|
4144
|
-
summaryResponse = yield* responseIdentity(ownedPart, summaryResponse);
|
|
4182
|
+
const consumeSummaryPart = Effect.fnUntraced(function* (owned: OwnedModelResponsePart) {
|
|
4183
|
+
responseUsage.responsePartCount++;
|
|
4184
|
+
responseUsage.responsePartBytes += owned.retainedBytes;
|
|
4185
|
+
const ownedPart = owned.ownedPart;
|
|
4186
|
+
|
|
4187
|
+
if (ownedPart.type === "text-delta") {
|
|
4188
|
+
pieces.push(ownedPart.delta);
|
|
4189
|
+
} else if (ownedPart.type === "finish") {
|
|
4190
|
+
if (summaryUsage === undefined) {
|
|
4191
|
+
summaryUsage = ownedPart.usage;
|
|
4192
|
+
summaryFinishMetadata = ownedPart.metadata;
|
|
4145
4193
|
}
|
|
4146
|
-
|
|
4147
|
-
yield*
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
});
|
|
4175
|
-
}
|
|
4176
|
-
|
|
4177
|
-
return;
|
|
4178
|
-
}
|
|
4179
|
-
case "tool-params-start":
|
|
4180
|
-
case "tool-params-delta":
|
|
4181
|
-
case "tool-params-end":
|
|
4182
|
-
case "tool-approval-request":
|
|
4183
|
-
case "error":
|
|
4194
|
+
} else if (ownedPart.type === "response-metadata") {
|
|
4195
|
+
summaryResponse = yield* responseIdentity(ownedPart, summaryResponse);
|
|
4196
|
+
}
|
|
4197
|
+
// Drain malformed responses within the buffer bounds so reported usage is charged.
|
|
4198
|
+
yield* Effect.gen(function* () {
|
|
4199
|
+
switch (ownedPart.type) {
|
|
4200
|
+
case "text-start":
|
|
4201
|
+
return yield* startPart(textParts, ownedPart.id, "compaction text");
|
|
4202
|
+
case "text-delta":
|
|
4203
|
+
return yield* continuePart(textParts, ownedPart.id, "compaction text delta");
|
|
4204
|
+
case "text-end":
|
|
4205
|
+
return yield* endPart(textParts, ownedPart.id, "compaction text");
|
|
4206
|
+
case "reasoning-start":
|
|
4207
|
+
return yield* startPart(reasoningParts, ownedPart.id, "compaction reasoning");
|
|
4208
|
+
case "reasoning-delta":
|
|
4209
|
+
return yield* continuePart(
|
|
4210
|
+
reasoningParts,
|
|
4211
|
+
ownedPart.id,
|
|
4212
|
+
"compaction reasoning delta",
|
|
4213
|
+
);
|
|
4214
|
+
case "reasoning-end":
|
|
4215
|
+
return yield* endPart(reasoningParts, ownedPart.id, "compaction reasoning");
|
|
4216
|
+
case "finish": {
|
|
4217
|
+
summaryFinished = true;
|
|
4218
|
+
if (
|
|
4219
|
+
ownedPart.reason !== "stop" ||
|
|
4220
|
+
[...textParts.values(), ...reasoningParts.values()].includes("open")
|
|
4221
|
+
) {
|
|
4184
4222
|
return yield* ModelProtocolError.make({
|
|
4185
|
-
message:
|
|
4223
|
+
message:
|
|
4224
|
+
"Compaction response did not finish with complete text and a stop reason",
|
|
4186
4225
|
});
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
return;
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
return;
|
|
4191
4229
|
}
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4230
|
+
case "tool-params-start":
|
|
4231
|
+
case "tool-params-delta":
|
|
4232
|
+
case "tool-params-end":
|
|
4233
|
+
case "tool-approval-request":
|
|
4234
|
+
case "error":
|
|
4235
|
+
return yield* ModelProtocolError.make({
|
|
4236
|
+
message: `Compaction response contained an unusable ${ownedPart.type} part`,
|
|
4237
|
+
});
|
|
4238
|
+
case "file":
|
|
4239
|
+
case "response-metadata":
|
|
4240
|
+
case "source":
|
|
4241
|
+
return;
|
|
4242
|
+
}
|
|
4243
|
+
}).pipe(
|
|
4244
|
+
Effect.catch((error) =>
|
|
4245
|
+
Effect.sync(() => {
|
|
4246
|
+
summaryFailure ??= error;
|
|
4247
|
+
}),
|
|
4248
|
+
),
|
|
4249
|
+
);
|
|
4250
|
+
});
|
|
4200
4251
|
|
|
4201
4252
|
const summaryExit = yield* enforceDurationDeadline(
|
|
4202
4253
|
guardBudgetStream(LanguageModel.streamText({ prompt: summarizerPrompt }), options.budget),
|
|
@@ -4449,7 +4500,7 @@ const decodeInput = <AgentValue extends Agent.Any>(
|
|
|
4449
4500
|
AgentInputDecodeError,
|
|
4450
4501
|
AgentValue["definition"]["input"]["DecodingServices"]
|
|
4451
4502
|
> =>
|
|
4452
|
-
Schema.decodeUnknownEffect(agent.definition.input)(input).pipe(
|
|
4503
|
+
Schema.decodeUnknownEffect<AgentValue["definition"]["input"]>(agent.definition.input)(input).pipe(
|
|
4453
4504
|
Effect.mapError((cause) =>
|
|
4454
4505
|
AgentInputDecodeError.make({
|
|
4455
4506
|
message: cause.message,
|
|
@@ -4475,7 +4526,7 @@ const encodeInput = <AgentValue extends Agent.Any>(
|
|
|
4475
4526
|
AgentInputError,
|
|
4476
4527
|
AgentValue["definition"]["input"]["EncodingServices"]
|
|
4477
4528
|
> =>
|
|
4478
|
-
Schema.encodeEffect(agent.definition.input)(input).pipe(
|
|
4529
|
+
Schema.encodeEffect<AgentValue["definition"]["input"]>(agent.definition.input)(input).pipe(
|
|
4479
4530
|
Effect.mapError((cause) =>
|
|
4480
4531
|
AgentInputError.make({
|
|
4481
4532
|
message: `Unable to encode Agent input: ${cause.message}`,
|
|
@@ -4489,20 +4540,21 @@ const renderInputPromptEffect = <Input, Error, Services>(
|
|
|
4489
4540
|
encodedInput: unknown,
|
|
4490
4541
|
): Effect.Effect<Prompt.RawInput, Error | AgentInputError, Services> =>
|
|
4491
4542
|
inputPrompt === undefined
|
|
4492
|
-
? Effect.
|
|
4493
|
-
|
|
4494
|
-
|
|
4543
|
+
? Effect.gen(function* () {
|
|
4544
|
+
const encoded = yield* Effect.try({
|
|
4545
|
+
try: () => JSON.stringify(encodedInput),
|
|
4546
|
+
catch: (cause) =>
|
|
4547
|
+
AgentInputError.make({
|
|
4548
|
+
message: `Unable to materialize Agent input: ${errorMessage(cause)}`,
|
|
4549
|
+
}),
|
|
4550
|
+
});
|
|
4495
4551
|
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4552
|
+
if (encoded === undefined)
|
|
4553
|
+
return yield* AgentInputError.make({
|
|
4554
|
+
message: "Unable to materialize Agent input: Agent input cannot be represented as JSON",
|
|
4555
|
+
});
|
|
4499
4556
|
|
|
4500
|
-
|
|
4501
|
-
},
|
|
4502
|
-
catch: (cause) =>
|
|
4503
|
-
AgentInputError.make({
|
|
4504
|
-
message: `Unable to materialize Agent input: ${errorMessage(cause)}`,
|
|
4505
|
-
}),
|
|
4557
|
+
return encoded;
|
|
4506
4558
|
})
|
|
4507
4559
|
: Effect.suspend(() => {
|
|
4508
4560
|
const result = inputPrompt(decodedInput);
|
|
@@ -4524,11 +4576,11 @@ export function renderInputPrompt<
|
|
|
4524
4576
|
InputPromptRequirementsOf<InputPromptValue, Input>
|
|
4525
4577
|
>;
|
|
4526
4578
|
|
|
4527
|
-
export function renderInputPrompt<Input>(
|
|
4528
|
-
inputPrompt: InputPromptSource<Input,
|
|
4579
|
+
export function renderInputPrompt<Input, Error, Requirements>(
|
|
4580
|
+
inputPrompt: InputPromptSource<Input, Error, Requirements> | undefined,
|
|
4529
4581
|
decodedInput: Input,
|
|
4530
4582
|
encodedInput: unknown,
|
|
4531
|
-
): Effect.Effect<Prompt.RawInput,
|
|
4583
|
+
): Effect.Effect<Prompt.RawInput, Error | AgentInputError, Requirements> {
|
|
4532
4584
|
return renderInputPromptEffect(inputPrompt, decodedInput, encodedInput);
|
|
4533
4585
|
}
|
|
4534
4586
|
|
|
@@ -4579,30 +4631,26 @@ const decodeToolCallId = (id: string) =>
|
|
|
4579
4631
|
);
|
|
4580
4632
|
|
|
4581
4633
|
const decodeProviderToolCallId = (id: string) =>
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
}),
|
|
4589
|
-
),
|
|
4634
|
+
decodeProviderToolCallIdValue(id).pipe(
|
|
4635
|
+
Effect.mapErrorEager(() =>
|
|
4636
|
+
ModelProtocolError.make({
|
|
4637
|
+
message:
|
|
4638
|
+
"Model supplied an invalid Tool Call ID; expected 1-128 ASCII letters, digits, dots, underscores, colons, or hyphens",
|
|
4639
|
+
}),
|
|
4590
4640
|
),
|
|
4591
4641
|
);
|
|
4592
4642
|
|
|
4593
4643
|
const decodeProviderResponsePartId = (id: string) =>
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
}),
|
|
4601
|
-
),
|
|
4644
|
+
decodeProviderResponsePartIdValue(id).pipe(
|
|
4645
|
+
Effect.mapErrorEager(() =>
|
|
4646
|
+
ModelProtocolError.make({
|
|
4647
|
+
message:
|
|
4648
|
+
"Model supplied an invalid response part ID; expected 1-128 ASCII letters, digits, dots, underscores, colons, or hyphens",
|
|
4649
|
+
}),
|
|
4602
4650
|
),
|
|
4603
4651
|
);
|
|
4604
4652
|
|
|
4605
|
-
const validateProviderPartIdentifiers = Effect.
|
|
4653
|
+
const validateProviderPartIdentifiers = Effect.fnUntracedEager(function* (part: Response.AnyPart) {
|
|
4606
4654
|
switch (part.type) {
|
|
4607
4655
|
case "text-start":
|
|
4608
4656
|
case "text-delta":
|
|
@@ -4735,10 +4783,10 @@ const promptFromTurnParts = (trace: TurnTrace): Prompt.Prompt => {
|
|
|
4735
4783
|
return Prompt.fromMessages(messages);
|
|
4736
4784
|
};
|
|
4737
4785
|
|
|
4738
|
-
//
|
|
4739
|
-
//
|
|
4740
|
-
//
|
|
4741
|
-
const processModelPart = Effect.
|
|
4786
|
+
// Yield the returned Effect immediately and exactly once from the executing stream
|
|
4787
|
+
// consumer: construction runs the synchronous prefix, mutating trace counters and maps.
|
|
4788
|
+
// Application codecs and events still suspend. The enclosing model request owns the span.
|
|
4789
|
+
const processModelPart = Effect.fnUntracedEager(function* <Tools extends Record<string, Tool.Any>>(
|
|
4742
4790
|
context: RunContext,
|
|
4743
4791
|
turnId: TurnId,
|
|
4744
4792
|
turn: number,
|
|
@@ -4908,7 +4956,7 @@ const processModelPart = Effect.fnUntraced(function* <Tools extends Record<strin
|
|
|
4908
4956
|
const error = AiError.make({
|
|
4909
4957
|
module: "Toolkit",
|
|
4910
4958
|
method: `${part.name}.handle`,
|
|
4911
|
-
reason:
|
|
4959
|
+
reason: AiError.ToolParameterValidationError.make({
|
|
4912
4960
|
toolName: part.name,
|
|
4913
4961
|
description: decoded.failure.message,
|
|
4914
4962
|
}),
|
|
@@ -5142,7 +5190,9 @@ const decodeFinalOutput = Effect.fnUntraced(function* <AgentValue extends Agent.
|
|
|
5142
5190
|
|
|
5143
5191
|
const candidateOutput: unknown = eventJson;
|
|
5144
5192
|
|
|
5145
|
-
const decoded = yield* Schema.decodeUnknownEffect
|
|
5193
|
+
const decoded = yield* Schema.decodeUnknownEffect<AgentValue["definition"]["output"]>(
|
|
5194
|
+
agent.definition.output,
|
|
5195
|
+
)(candidateOutput).pipe(
|
|
5146
5196
|
Effect.mapError((cause) =>
|
|
5147
5197
|
AgentOutputError.make({
|
|
5148
5198
|
message: cause.message,
|
|
@@ -5157,7 +5207,9 @@ const encodeOutputCandidate = Effect.fnUntraced(function* <AgentValue extends Ag
|
|
|
5157
5207
|
agent: AgentValue,
|
|
5158
5208
|
candidate: unknown,
|
|
5159
5209
|
) {
|
|
5160
|
-
const encoded = yield* Schema.encodeUnknownEffect
|
|
5210
|
+
const encoded = yield* Schema.encodeUnknownEffect<AgentValue["definition"]["output"]>(
|
|
5211
|
+
agent.definition.output,
|
|
5212
|
+
)(candidate).pipe(
|
|
5161
5213
|
Effect.mapError((cause) =>
|
|
5162
5214
|
AgentOutputError.make({
|
|
5163
5215
|
message: `Completion Tool output failed Schema encoding: ${cause.message}`,
|
|
@@ -5165,7 +5217,9 @@ const encodeOutputCandidate = Effect.fnUntraced(function* <AgentValue extends Ag
|
|
|
5165
5217
|
),
|
|
5166
5218
|
);
|
|
5167
5219
|
|
|
5168
|
-
const decoded = yield* Schema.decodeUnknownEffect
|
|
5220
|
+
const decoded = yield* Schema.decodeUnknownEffect<AgentValue["definition"]["output"]>(
|
|
5221
|
+
agent.definition.output,
|
|
5222
|
+
)(encoded).pipe(
|
|
5169
5223
|
Effect.mapError((cause) =>
|
|
5170
5224
|
AgentOutputError.make({
|
|
5171
5225
|
message: `Completion Tool output failed canonical decoding: ${cause.message}`,
|
|
@@ -5194,7 +5248,8 @@ const projectToolResult = Effect.fnUntraced(function* <AgentValue extends Agent.
|
|
|
5194
5248
|
AgentOutputError | ModelProtocolError,
|
|
5195
5249
|
AgentCompletionProjectionRequirements<AgentValue>
|
|
5196
5250
|
> {
|
|
5197
|
-
const
|
|
5251
|
+
const tools: AgentValue["definition"]["toolkit"]["tools"] = agent.definition.toolkit.tools;
|
|
5252
|
+
const tool = tools[declaration.tool];
|
|
5198
5253
|
|
|
5199
5254
|
if (tool === undefined) {
|
|
5200
5255
|
return yield* ModelProtocolError.make({
|
|
@@ -5202,9 +5257,9 @@ const projectToolResult = Effect.fnUntraced(function* <AgentValue extends Agent.
|
|
|
5202
5257
|
});
|
|
5203
5258
|
}
|
|
5204
5259
|
|
|
5205
|
-
const decodedParameters = yield* Schema.decodeUnknownEffect
|
|
5206
|
-
|
|
5207
|
-
).pipe(
|
|
5260
|
+
const decodedParameters = yield* Schema.decodeUnknownEffect<
|
|
5261
|
+
AgentValue["definition"]["toolkit"]["tools"][string]["parametersSchema"]
|
|
5262
|
+
>(tool.parametersSchema)(parameters).pipe(
|
|
5208
5263
|
Effect.mapError((cause) =>
|
|
5209
5264
|
AgentOutputError.make({
|
|
5210
5265
|
message: `Completion Tool parameters failed canonical decoding: ${cause.message}`,
|
|
@@ -5212,7 +5267,9 @@ const projectToolResult = Effect.fnUntraced(function* <AgentValue extends Agent.
|
|
|
5212
5267
|
),
|
|
5213
5268
|
);
|
|
5214
5269
|
|
|
5215
|
-
const decodedResult = yield* Schema.decodeUnknownEffect
|
|
5270
|
+
const decodedResult = yield* Schema.decodeUnknownEffect<
|
|
5271
|
+
AgentValue["definition"]["toolkit"]["tools"][string]["successSchema"]
|
|
5272
|
+
>(tool.successSchema)(result).pipe(
|
|
5216
5273
|
Effect.mapError((cause) =>
|
|
5217
5274
|
AgentOutputError.make({
|
|
5218
5275
|
message: `Completion Tool result failed canonical decoding: ${cause.message}`,
|
|
@@ -5512,10 +5569,10 @@ const makeTurn = <
|
|
|
5512
5569
|
: yield* prepareWithinDeadline(context, options.context.prepare(contextRequest));
|
|
5513
5570
|
|
|
5514
5571
|
if (context.resolvedModel && modelContext.modelCall !== undefined) {
|
|
5515
|
-
return yield*
|
|
5572
|
+
return yield* AiError.AiError.make({
|
|
5516
5573
|
module: "AgentRuntime",
|
|
5517
5574
|
method: "resolveModel",
|
|
5518
|
-
reason:
|
|
5575
|
+
reason: AiError.InvalidRequestError.make({
|
|
5519
5576
|
description: "Context preparation cannot replace a thread-resolved model",
|
|
5520
5577
|
}),
|
|
5521
5578
|
});
|
|
@@ -6337,13 +6394,20 @@ const makeTurn = <
|
|
|
6337
6394
|
if (Exit.isFailure(exit)) failedUsageCause = exit.cause;
|
|
6338
6395
|
});
|
|
6339
6396
|
|
|
6340
|
-
const attempt = (
|
|
6397
|
+
const attempt = (
|
|
6398
|
+
basis: Prompt.Prompt,
|
|
6399
|
+
): Effect.Effect<
|
|
6400
|
+
void,
|
|
6401
|
+
AgentRuntimeFailure<typeof agent, HookError, InstructionError>,
|
|
6402
|
+
| InterpreterRequirements<typeof agent, HookRequirements, InstructionRequirements>
|
|
6403
|
+
| Scope.Scope
|
|
6404
|
+
> =>
|
|
6341
6405
|
Effect.flatten(
|
|
6342
6406
|
outgoingModelPrompt(
|
|
6343
6407
|
policy,
|
|
6344
6408
|
context,
|
|
6345
6409
|
prepareModelPrompt(
|
|
6346
|
-
Prompt.
|
|
6410
|
+
Prompt.concat(basis, transientContext),
|
|
6347
6411
|
outputContract._tag === "rendered" ? outputContract.part : undefined,
|
|
6348
6412
|
systemMessagesInHistory,
|
|
6349
6413
|
staticInstructions,
|
|
@@ -6430,6 +6494,8 @@ const makeTurn = <
|
|
|
6430
6494
|
|
|
6431
6495
|
return yield* trackContext.pipe(
|
|
6432
6496
|
Effect.as(
|
|
6497
|
+
// Transport-only tools retain existential schema services; the enclosing attempt keeps the Agent's declared requirements.
|
|
6498
|
+
// @effect-diagnostics-next-line anyUnknownInErrorContext:off
|
|
6433
6499
|
guardBudgetStream(
|
|
6434
6500
|
LanguageModel.streamText({
|
|
6435
6501
|
// Every attempt, including an overflow retry, places the
|
|
@@ -6475,43 +6541,47 @@ const makeTurn = <
|
|
|
6475
6541
|
activeProvisionalText = provisionalText;
|
|
6476
6542
|
}),
|
|
6477
6543
|
),
|
|
6478
|
-
Stream.
|
|
6544
|
+
Stream.runForEachArray((parts) =>
|
|
6479
6545
|
Effect.gen(function* () {
|
|
6480
|
-
const
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6546
|
+
for (const part of parts) {
|
|
6547
|
+
const owned = yield* ownModelResponsePart(
|
|
6548
|
+
part,
|
|
6549
|
+
agent.definition.toolkit,
|
|
6550
|
+
trace,
|
|
6551
|
+
context.bufferLimits,
|
|
6552
|
+
trace.finished,
|
|
6553
|
+
);
|
|
6554
|
+
|
|
6555
|
+
const events = context.publish === undefined ? undefined : [];
|
|
6556
|
+
|
|
6557
|
+
yield* processModelPart(
|
|
6558
|
+
context,
|
|
6559
|
+
turnId,
|
|
6560
|
+
turn,
|
|
6561
|
+
agent.definition.toolkit.tools,
|
|
6562
|
+
trace,
|
|
6563
|
+
owned.ownedPart,
|
|
6564
|
+
owned.retainedBytes,
|
|
6565
|
+
events,
|
|
6566
|
+
);
|
|
6567
|
+
if (provisionalText !== undefined) {
|
|
6568
|
+
const part = owned.ownedPart;
|
|
6569
|
+
|
|
6570
|
+
if (
|
|
6571
|
+
part.type === "text-start" ||
|
|
6572
|
+
part.type === "text-delta" ||
|
|
6573
|
+
part.type === "text-end"
|
|
6574
|
+
)
|
|
6575
|
+
provisionalText.offerUnsafe(part);
|
|
6576
|
+
}
|
|
6577
|
+
// Publish before accepting the next part. Backpressure stays interruptible.
|
|
6578
|
+
if (events !== undefined) yield* publishEvents(context, events);
|
|
6509
6579
|
}
|
|
6510
|
-
// Publish before accepting the next part. Backpressure stays interruptible.
|
|
6511
|
-
if (events !== undefined) yield* publishEvents(context, events);
|
|
6512
6580
|
}),
|
|
6513
6581
|
),
|
|
6514
|
-
|
|
6582
|
+
<Value, Error, Requirements>(
|
|
6583
|
+
effect: Effect.Effect<Value, Error, Requirements>,
|
|
6584
|
+
) => prepareWithinDeadline(context, effect),
|
|
6515
6585
|
Effect.onExit((exit) => {
|
|
6516
6586
|
if (Exit.isSuccess(exit)) return Effect.void;
|
|
6517
6587
|
provisionalText?.discard();
|
|
@@ -6529,63 +6599,71 @@ const makeTurn = <
|
|
|
6529
6599
|
// Retry the closed native provider operation. Admitted content makes the response
|
|
6530
6600
|
// ineligible for retry; canonical acceptance belongs to the continuation below.
|
|
6531
6601
|
const attemptWithRetries = Effect.suspend(() => attempt(compactedOutgoing())).pipe(
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6602
|
+
(operation) =>
|
|
6603
|
+
(policy.modelRetries ?? 0) === 0
|
|
6604
|
+
? operation
|
|
6605
|
+
: Effect.retry(operation, ($) =>
|
|
6606
|
+
$(Schedule.exponential(MODEL_RETRY_BASE)).pipe(
|
|
6607
|
+
Schedule.upTo({ times: policy.modelRetries ?? 0 }),
|
|
6608
|
+
Schedule.while(({ input: error }) => {
|
|
6609
|
+
const lastPart = trace.parts.at(-1);
|
|
6610
|
+
const providerError: unknown = error;
|
|
6611
|
+
|
|
6612
|
+
return (
|
|
6613
|
+
canRepeatModelCall &&
|
|
6614
|
+
!hasStreamedContent(trace) &&
|
|
6615
|
+
((lastPart?.type === "error" && isTransientErrorPayload(lastPart.error)) ||
|
|
6616
|
+
(AiError.isAiError(providerError) && providerError.isRetryable))
|
|
6617
|
+
);
|
|
6618
|
+
}),
|
|
6619
|
+
Schedule.modifyDelay(({ input: error, duration }) =>
|
|
6620
|
+
Effect.gen(function* () {
|
|
6621
|
+
const backoff = Duration.min(duration, MODEL_RETRY_MAX_DELAY);
|
|
6622
|
+
const providerError: unknown = error;
|
|
6549
6623
|
|
|
6550
|
-
|
|
6551
|
-
|
|
6624
|
+
const retryAfter = AiError.isAiError(providerError)
|
|
6625
|
+
? providerError.retryAfter
|
|
6626
|
+
: undefined;
|
|
6552
6627
|
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
const remaining = Math.max(
|
|
6556
|
-
0,
|
|
6557
|
-
context.durationDeadlineMillis - (yield* Clock.currentTimeMillis),
|
|
6558
|
-
);
|
|
6628
|
+
const requested =
|
|
6629
|
+
retryAfter === undefined ? backoff : Duration.max(backoff, retryAfter);
|
|
6559
6630
|
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
trace.responsePartCount = 0;
|
|
6567
|
-
trace.responsePartBytes = 0;
|
|
6568
|
-
// The failed attempt's usage was already retained on exit.
|
|
6569
|
-
trace.usage = undefined;
|
|
6570
|
-
delete trace.response;
|
|
6571
|
-
delete trace.finishMetadata;
|
|
6572
|
-
yield* Effect.logWarning("agent model call retrying").pipe(
|
|
6573
|
-
Effect.annotateLogs({
|
|
6574
|
-
runId: context.runId,
|
|
6575
|
-
turnId,
|
|
6576
|
-
retry,
|
|
6577
|
-
delayMillis: Duration.toMillis(duration),
|
|
6578
|
-
error: errorMessage(error),
|
|
6579
|
-
}),
|
|
6580
|
-
);
|
|
6581
|
-
}),
|
|
6582
|
-
),
|
|
6583
|
-
),
|
|
6584
|
-
),
|
|
6585
|
-
);
|
|
6631
|
+
// Expiration is rejected by the next native attempt. Capping only this delay
|
|
6632
|
+
// keeps failed-attempt accounting and canonical hooks outside interruption timers.
|
|
6633
|
+
const remaining = Math.max(
|
|
6634
|
+
0,
|
|
6635
|
+
context.durationDeadlineMillis - (yield* Clock.currentTimeMillis),
|
|
6636
|
+
);
|
|
6586
6637
|
|
|
6587
|
-
|
|
6588
|
-
|
|
6638
|
+
return Duration.min(requested, Duration.millis(remaining));
|
|
6639
|
+
}),
|
|
6640
|
+
),
|
|
6641
|
+
Schedule.tap(({ input: error, attempt: retry, duration }) =>
|
|
6642
|
+
Effect.gen(function* () {
|
|
6643
|
+
trace.parts.length = 0;
|
|
6644
|
+
trace.responsePartCount = 0;
|
|
6645
|
+
trace.responsePartBytes = 0;
|
|
6646
|
+
// The failed attempt's usage was already retained on exit.
|
|
6647
|
+
trace.usage = undefined;
|
|
6648
|
+
delete trace.response;
|
|
6649
|
+
delete trace.finishMetadata;
|
|
6650
|
+
yield* Effect.logWarning("agent model call retrying").pipe(
|
|
6651
|
+
Effect.annotateLogs({
|
|
6652
|
+
runId: context.runId,
|
|
6653
|
+
turnId,
|
|
6654
|
+
retry,
|
|
6655
|
+
delayMillis: Duration.toMillis(duration),
|
|
6656
|
+
error: errorMessage(error),
|
|
6657
|
+
}),
|
|
6658
|
+
);
|
|
6659
|
+
}),
|
|
6660
|
+
),
|
|
6661
|
+
),
|
|
6662
|
+
),
|
|
6663
|
+
);
|
|
6664
|
+
|
|
6665
|
+
// RUN-027: one summarize-and-retry for a classified provider context
|
|
6666
|
+
// overflow when compaction is configured; every other provider error
|
|
6589
6667
|
// propagates unchanged. A response that already streamed parts mutated
|
|
6590
6668
|
// the trace, so it is never retried.
|
|
6591
6669
|
const response = attemptWithRetries.pipe(
|
|
@@ -7031,16 +7109,15 @@ const makeTurn = <
|
|
|
7031
7109
|
* steering at the safe seam, and start the next Turn. The `maxTurns`
|
|
7032
7110
|
* guard already ran for these Tool Call sites.
|
|
7033
7111
|
*/
|
|
7034
|
-
const continueTurn = (history: Prompt.Prompt)
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
});
|
|
7112
|
+
const continueTurn = Effect.fnUntraced(function* (history: Prompt.Prompt) {
|
|
7113
|
+
trace.historyAccepted = true;
|
|
7114
|
+
yield* advanceHistory(context, history, options);
|
|
7115
|
+
yield* settleTurn(context, trace, turn, turnId, options);
|
|
7116
|
+
const steering = yield* drainInputs(context, options);
|
|
7117
|
+
const nextPrompt = yield* appendInputs(context, history, steering, options);
|
|
7118
|
+
|
|
7119
|
+
return nextTurn(nextPrompt, turn + 1, toolCalls);
|
|
7120
|
+
});
|
|
7044
7121
|
|
|
7045
7122
|
/**
|
|
7046
7123
|
* The otherwise-stop seam: advance official history, drain steering,
|
|
@@ -7048,86 +7125,81 @@ const makeTurn = <
|
|
|
7048
7125
|
* under the `maxTurns` and repeated-failure Stop Policy bounds or
|
|
7049
7126
|
* complete the Run with the Turn's decoded final output.
|
|
7050
7127
|
*/
|
|
7051
|
-
const settleOrFollowUp = (history: Prompt.Prompt)
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
yield* advanceHistory(context, history, options);
|
|
7128
|
+
const settleOrFollowUp = Effect.fnUntraced(function* (history: Prompt.Prompt) {
|
|
7129
|
+
trace.historyAccepted = true;
|
|
7130
|
+
yield* advanceHistory(context, history, options);
|
|
7055
7131
|
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7132
|
+
// Do not consume durable receipts that this completed Turn cannot cover.
|
|
7133
|
+
// Final-answer mode permits one grace Turn, but never a second finalization.
|
|
7134
|
+
const turnsBlocked =
|
|
7135
|
+
policy.onExhaustion === "fail" ? turn >= bounds.maxTurns : turn > bounds.maxTurns;
|
|
7060
7136
|
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
? []
|
|
7064
|
-
: yield* drainInputs(context, options);
|
|
7065
|
-
|
|
7066
|
-
const queued =
|
|
7067
|
-
steering.length > 0
|
|
7068
|
-
? steering
|
|
7069
|
-
: takeFollowUps(context, options.commandDrainPolicy ?? "one");
|
|
7070
|
-
|
|
7071
|
-
if (queued.length > 0) {
|
|
7072
|
-
// Final-answer mode admits exactly one grace Turn past
|
|
7073
|
-
// `maxTurns` (RUN-019): `turn > maxTurns` can only be
|
|
7074
|
-
// `maxTurns + 1`, so a second grace is structurally
|
|
7075
|
-
// impossible.
|
|
7076
|
-
if (turnsBlocked) {
|
|
7077
|
-
return failExecution(
|
|
7078
|
-
AgentPolicyError.make({
|
|
7079
|
-
limit: "turns",
|
|
7080
|
-
message: `Agent exceeded its ${bounds.maxTurns} Turn limit`,
|
|
7081
|
-
}),
|
|
7082
|
-
);
|
|
7083
|
-
}
|
|
7084
|
-
yield* applyRepeatedFailurePolicy(
|
|
7085
|
-
context,
|
|
7086
|
-
trace,
|
|
7087
|
-
agent.definition.policy.repeatedFailureLimit,
|
|
7088
|
-
);
|
|
7089
|
-
yield* settleTurn(context, trace, turn, turnId, options);
|
|
7090
|
-
const nextPrompt = yield* appendInputs(context, history, queued, options);
|
|
7137
|
+
const steering =
|
|
7138
|
+
turnsBlocked || context.finalizationUsed ? [] : yield* drainInputs(context, options);
|
|
7091
7139
|
|
|
7092
|
-
|
|
7093
|
-
|
|
7140
|
+
const queued =
|
|
7141
|
+
steering.length > 0
|
|
7142
|
+
? steering
|
|
7143
|
+
: takeFollowUps(context, options.commandDrainPolicy ?? "one");
|
|
7094
7144
|
|
|
7095
|
-
|
|
7145
|
+
if (queued.length > 0) {
|
|
7146
|
+
// Final-answer mode admits exactly one grace Turn past
|
|
7147
|
+
// `maxTurns` (RUN-019): `turn > maxTurns` can only be
|
|
7148
|
+
// `maxTurns + 1`, so a second grace is structurally
|
|
7149
|
+
// impossible.
|
|
7150
|
+
if (turnsBlocked) {
|
|
7151
|
+
return failExecution(
|
|
7152
|
+
AgentPolicyError.make({
|
|
7153
|
+
limit: "turns",
|
|
7154
|
+
message: `Agent exceeded its ${bounds.maxTurns} Turn limit`,
|
|
7155
|
+
}),
|
|
7156
|
+
);
|
|
7157
|
+
}
|
|
7158
|
+
yield* applyRepeatedFailurePolicy(
|
|
7096
7159
|
context,
|
|
7097
|
-
|
|
7160
|
+
trace,
|
|
7161
|
+
agent.definition.policy.repeatedFailureLimit,
|
|
7098
7162
|
);
|
|
7163
|
+
yield* settleTurn(context, trace, turn, turnId, options);
|
|
7164
|
+
const nextPrompt = yield* appendInputs(context, history, queued, options);
|
|
7165
|
+
|
|
7166
|
+
return nextTurn(nextPrompt, turn + 1, toolCalls);
|
|
7167
|
+
}
|
|
7099
7168
|
|
|
7100
|
-
|
|
7169
|
+
const output = yield* prepareWithinDeadline(
|
|
7170
|
+
context,
|
|
7171
|
+
decodeFinalOutput(agent, trace.text.join("")),
|
|
7172
|
+
);
|
|
7101
7173
|
|
|
7102
|
-
|
|
7103
|
-
finalAnswerOnly || declaration === undefined
|
|
7104
|
-
? undefined
|
|
7105
|
-
: yield* prepareWithinDeadline(
|
|
7106
|
-
context,
|
|
7107
|
-
encodeRunDisposition(agent, output.decoded),
|
|
7108
|
-
);
|
|
7174
|
+
const declaration = agent.definition.runDisposition;
|
|
7109
7175
|
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7176
|
+
const runDisposition =
|
|
7177
|
+
finalAnswerOnly || declaration === undefined
|
|
7178
|
+
? undefined
|
|
7179
|
+
: yield* prepareWithinDeadline(
|
|
7180
|
+
context,
|
|
7181
|
+
encodeRunDisposition(agent, output.decoded),
|
|
7182
|
+
);
|
|
7183
|
+
|
|
7184
|
+
const completionUsage = yield* usageReportOf(context);
|
|
7185
|
+
|
|
7186
|
+
return Effect.map(eventBase(context), (base) =>
|
|
7187
|
+
RunCompleted.make({
|
|
7188
|
+
...base,
|
|
7189
|
+
output: output.encoded,
|
|
7190
|
+
...(runDisposition === undefined ? {} : { runDisposition }),
|
|
7191
|
+
turns: turn,
|
|
7192
|
+
// A Run settled under the final-answer constraint reports
|
|
7193
|
+
// the exhaustion honestly (RUN-011), never a plain model
|
|
7194
|
+
// stop, and carries the dimension that bound.
|
|
7195
|
+
finishReason: finalAnswerOnly ? "budget-exhausted" : "model-stop",
|
|
7196
|
+
...(finalAnswerOnly && context.exhaustedDimension !== undefined
|
|
7197
|
+
? { exhausted: context.exhaustedDimension }
|
|
7198
|
+
: {}),
|
|
7199
|
+
...completionUsage,
|
|
7200
|
+
}),
|
|
7201
|
+
).pipe(Effect.tap((event) => settleTurn(context, trace, turn, turnId, options, event)));
|
|
7202
|
+
});
|
|
7131
7203
|
|
|
7132
7204
|
if (providerOnly && trace.finishReason === "stop") {
|
|
7133
7205
|
if (agent.definition.completion?.required === true) {
|
|
@@ -7889,7 +7961,7 @@ const makeResumeTurn = <
|
|
|
7889
7961
|
id: call.id,
|
|
7890
7962
|
isFailure: true,
|
|
7891
7963
|
result: yield* decodeEventJson(
|
|
7892
|
-
boundEncodedToolResult(
|
|
7964
|
+
yield* boundEncodedToolResult(
|
|
7893
7965
|
rejection.error,
|
|
7894
7966
|
durableResultBounds(agent.definition.policy.toolResultBounds, options, turn),
|
|
7895
7967
|
),
|
|
@@ -8533,10 +8605,9 @@ function executeWithCompletion<
|
|
|
8533
8605
|
|
|
8534
8606
|
// Normalize the selected host policy at the Run boundary. Inner calls project typed
|
|
8535
8607
|
// override failures into broker outcomes; native calls retain their original E channel.
|
|
8536
|
-
const programmaticAuthorization =
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
const services = yield* Effect.context<HookRequirements>();
|
|
8608
|
+
const programmaticAuthorization = Effect.map(
|
|
8609
|
+
Effect.context<HookRequirements>(),
|
|
8610
|
+
(services) => {
|
|
8540
8611
|
const selected = options.toolAuthorization;
|
|
8541
8612
|
|
|
8542
8613
|
return ProgrammaticToolAuthorization.of({
|
|
@@ -8549,7 +8620,7 @@ function executeWithCompletion<
|
|
|
8549
8620
|
),
|
|
8550
8621
|
),
|
|
8551
8622
|
});
|
|
8552
|
-
}
|
|
8623
|
+
},
|
|
8553
8624
|
);
|
|
8554
8625
|
|
|
8555
8626
|
const interpreted = Effect.flatten(
|
|
@@ -8601,12 +8672,12 @@ function executeWithCompletion<
|
|
|
8601
8672
|
|
|
8602
8673
|
const validateUpdate =
|
|
8603
8674
|
updateSchema === undefined
|
|
8604
|
-
? () => Effect.fail(
|
|
8675
|
+
? () => Effect.fail(UpdateError.make({ reason: "unavailable" }))
|
|
8605
8676
|
: (value: Schema.Json) =>
|
|
8606
8677
|
Schema.decodeEffect(updateSchema)(value).pipe(
|
|
8607
8678
|
Effect.provide(updateContext),
|
|
8608
8679
|
Effect.asVoid,
|
|
8609
|
-
Effect.mapError(() =>
|
|
8680
|
+
Effect.mapError(() => UpdateError.make({ reason: "validation" })),
|
|
8610
8681
|
);
|
|
8611
8682
|
|
|
8612
8683
|
const languageModel = yield* LanguageModel.LanguageModel;
|
|
@@ -8961,8 +9032,13 @@ function executeWithCompletion<
|
|
|
8961
9032
|
),
|
|
8962
9033
|
);
|
|
8963
9034
|
|
|
8964
|
-
|
|
8965
|
-
|
|
9035
|
+
if (output._tag !== "NextTurn") {
|
|
9036
|
+
yield* checkpointExecution(context, options.durability);
|
|
9037
|
+
|
|
9038
|
+
return output;
|
|
9039
|
+
}
|
|
9040
|
+
// Both Turn entry points check before any hook or dispatch. The
|
|
9041
|
+
// preceding Scope has closed, so that check also sees finalizer failures.
|
|
8966
9042
|
request = output;
|
|
8967
9043
|
}
|
|
8968
9044
|
|
|
@@ -8983,15 +9059,14 @@ function executeWithCompletion<
|
|
|
8983
9059
|
const selectionPrompt = yield* Schema.encodeEffect(
|
|
8984
9060
|
Schema.fromJsonString(Prompt.Prompt),
|
|
8985
9061
|
)(pending.prompt).pipe(
|
|
8986
|
-
Effect.mapError(
|
|
8987
|
-
(
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
description: `Cannot encode model selection context: ${cause.message}`,
|
|
8993
|
-
}),
|
|
9062
|
+
Effect.mapError((cause) =>
|
|
9063
|
+
AiError.AiError.make({
|
|
9064
|
+
module: "AgentRuntime",
|
|
9065
|
+
method: "resolveModel",
|
|
9066
|
+
reason: AiError.InvalidRequestError.make({
|
|
9067
|
+
description: `Cannot encode model selection context: ${cause.message}`,
|
|
8994
9068
|
}),
|
|
9069
|
+
}),
|
|
8995
9070
|
),
|
|
8996
9071
|
);
|
|
8997
9072
|
|
|
@@ -9030,34 +9105,36 @@ function executeWithCompletion<
|
|
|
9030
9105
|
agent.definition.updates === undefined || nativeUpdateTool === undefined
|
|
9031
9106
|
? Context.empty()
|
|
9032
9107
|
: yield* Toolkit.make(nativeUpdateTool).toHandlers({
|
|
9033
|
-
emit_update: (
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9108
|
+
emit_update: Effect.fnUntraced(function* (
|
|
9109
|
+
parameters: unknown,
|
|
9110
|
+
call: Toolkit.HandlerContext<Tool.Any>,
|
|
9111
|
+
) {
|
|
9112
|
+
if (call.toolCallId === undefined)
|
|
9113
|
+
return yield* UpdateError.make({ reason: "unavailable" });
|
|
9037
9114
|
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9115
|
+
const decoded = yield* Schema.decodeUnknownEffect(
|
|
9116
|
+
Schema.Struct({ value: Schema.Unknown }),
|
|
9117
|
+
)(parameters).pipe(
|
|
9118
|
+
Effect.mapError(() => UpdateError.make({ reason: "validation" })),
|
|
9119
|
+
);
|
|
9043
9120
|
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9121
|
+
const value = yield* Schema.encodeUnknownEffect(agent.definition.updates!)(
|
|
9122
|
+
decoded.value,
|
|
9123
|
+
).pipe(
|
|
9124
|
+
Effect.flatMap(Schema.decodeUnknownEffect(Schema.Json)),
|
|
9125
|
+
Effect.mapError(() => UpdateError.make({ reason: "validation" })),
|
|
9126
|
+
);
|
|
9050
9127
|
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9128
|
+
const updateId = yield* Schema.decodeEffect(IdempotencyKey)(
|
|
9129
|
+
`tool:${context.runId}:${call.toolCallId}`,
|
|
9130
|
+
).pipe(Effect.mapError(() => UpdateError.make({ reason: "validation" })));
|
|
9054
9131
|
|
|
9055
|
-
|
|
9132
|
+
const emitter = yield* Emitter;
|
|
9056
9133
|
|
|
9057
|
-
|
|
9134
|
+
yield* emitter.emit({ target: agent.definition, updateId, value });
|
|
9058
9135
|
|
|
9059
|
-
|
|
9060
|
-
|
|
9136
|
+
return { emitted: true };
|
|
9137
|
+
}),
|
|
9061
9138
|
});
|
|
9062
9139
|
|
|
9063
9140
|
const engineToolServices = Context.make(
|
|
@@ -9114,7 +9191,7 @@ function executeWithCompletion<
|
|
|
9114
9191
|
}),
|
|
9115
9192
|
Context.merge(updateHandlers),
|
|
9116
9193
|
Context.add(Emitter, {
|
|
9117
|
-
emit: () => Effect.fail(
|
|
9194
|
+
emit: () => Effect.fail(UpdateError.make({ reason: "unavailable" })),
|
|
9118
9195
|
}),
|
|
9119
9196
|
Context.add(CurrentToolCatalog, { entries: [] }),
|
|
9120
9197
|
Context.add(RunToolVisibility, visibility),
|
|
@@ -9208,7 +9285,8 @@ function executeWithCompletion<
|
|
|
9208
9285
|
const events = modeled.pipe(
|
|
9209
9286
|
// The engine composition boundary owns span-lifecycle isolation while preserving the host's
|
|
9210
9287
|
// ambient Tracer/Logger configuration. Individual Tool executions consume this capability.
|
|
9211
|
-
Effect.
|
|
9288
|
+
Effect.provideServiceEffect(ToolSpanTelemetry, ToolSpanTelemetry.make),
|
|
9289
|
+
Effect.provideServiceEffect(ProgrammaticToolAuthorization, programmaticAuthorization),
|
|
9212
9290
|
);
|
|
9213
9291
|
|
|
9214
9292
|
// Closing execution first joins every owned child and finalizer before validation,
|
|
@@ -9330,9 +9408,9 @@ const completeRun = <AgentValue extends Agent.Any, Error, Requirements>(
|
|
|
9330
9408
|
Effect.gen(function* () {
|
|
9331
9409
|
const candidateOutput: unknown = completed.output;
|
|
9332
9410
|
|
|
9333
|
-
const output = yield* Schema.decodeUnknownEffect
|
|
9334
|
-
|
|
9335
|
-
).pipe(
|
|
9411
|
+
const output = yield* Schema.decodeUnknownEffect<AgentValue["definition"]["output"]>(
|
|
9412
|
+
agent.definition.output,
|
|
9413
|
+
)(candidateOutput).pipe(
|
|
9336
9414
|
Effect.mapError((cause) =>
|
|
9337
9415
|
AgentOutputError.make({
|
|
9338
9416
|
message: cause.message,
|
|
@@ -9577,9 +9655,11 @@ function runUnknown<H = never, R = never>(
|
|
|
9577
9655
|
input: unknown,
|
|
9578
9656
|
options: RunOptions<H, R> = {},
|
|
9579
9657
|
) {
|
|
9580
|
-
const program = "definition" in agent ? agent :
|
|
9658
|
+
const program = { definition: "definition" in agent ? agent.definition : agent };
|
|
9581
9659
|
|
|
9582
9660
|
return runProgram(program, (onCompleted) =>
|
|
9661
|
+
// The overload retains each Agent's R; this implementation executes an existential model binding without narrowing its services.
|
|
9662
|
+
// @effect-diagnostics-next-line anyUnknownInErrorContext:off
|
|
9583
9663
|
executeWithCompletion(agent, input, options, onCompleted).pipe(
|
|
9584
9664
|
Effect.provide(
|
|
9585
9665
|
Layer.mergeAll(ModelUsageAccounting.layerEphemeral, AgentUpdateAcceptance.layerEphemeral),
|
|
@@ -9614,11 +9694,13 @@ function startUnknown<H = never, R = never>(
|
|
|
9614
9694
|
input: unknown,
|
|
9615
9695
|
options: RunOptions<H, R> = {},
|
|
9616
9696
|
) {
|
|
9617
|
-
const program = "definition" in agent ? agent :
|
|
9697
|
+
const program = { definition: "definition" in agent ? agent.definition : agent };
|
|
9618
9698
|
|
|
9619
9699
|
return startProgram(
|
|
9620
9700
|
program,
|
|
9621
9701
|
(executionOptions, onCompleted, onUsage, publish) =>
|
|
9702
|
+
// The overload retains each Agent's R; detached execution captures the same existential binding inside the caller's Scope.
|
|
9703
|
+
// @effect-diagnostics-next-line anyUnknownInErrorContext:off
|
|
9622
9704
|
executeWithCompletion(agent, input, executionOptions, onCompleted, onUsage, publish).pipe(
|
|
9623
9705
|
Effect.provide(
|
|
9624
9706
|
Layer.mergeAll(ModelUsageAccounting.layerEphemeral, AgentUpdateAcceptance.layerEphemeral),
|
|
@@ -9888,588 +9970,588 @@ const makeToolBrokerService = Effect.fnUntraced(function* <HookError, HookRequir
|
|
|
9888
9970
|
observer === undefined ? undefined : { observer, permits: Semaphore.makeUnsafe(1) };
|
|
9889
9971
|
|
|
9890
9972
|
const service: ToolBrokerService = {
|
|
9891
|
-
openPass: (
|
|
9892
|
-
|
|
9893
|
-
|
|
9973
|
+
openPass: Effect.fnUntraced(function* <Tools extends Record<string, Tool.Any>>(
|
|
9974
|
+
toolkit: Toolkit.WithHandler<Tools>,
|
|
9975
|
+
passOptions: Parameters<ToolBrokerService["openPass"]>[1],
|
|
9976
|
+
) {
|
|
9977
|
+
const inheritedAuthority = yield* CurrentSubagentAuthority;
|
|
9894
9978
|
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
|
|
9952
|
-
|
|
9979
|
+
if (lifecycle.closed) {
|
|
9980
|
+
return yield* ToolBrokerUnavailableError.make({
|
|
9981
|
+
message: "The outer Tool Call for this broker has already settled",
|
|
9982
|
+
});
|
|
9983
|
+
}
|
|
9984
|
+
// A malformed result bound would fail open (`NaN` defeats every
|
|
9985
|
+
// comparison), so it is rejected typed before the pass opens.
|
|
9986
|
+
if (
|
|
9987
|
+
passOptions === undefined ||
|
|
9988
|
+
!Number.isSafeInteger(passOptions.maxResultBytes) ||
|
|
9989
|
+
passOptions.maxResultBytes <= 0
|
|
9990
|
+
) {
|
|
9991
|
+
return yield* ToolBrokerConfigurationError.make({
|
|
9992
|
+
message: `maxResultBytes must be a positive safe integer; received ${String(passOptions?.maxResultBytes)}`,
|
|
9993
|
+
});
|
|
9994
|
+
}
|
|
9995
|
+
// Capture the declared handler services at the pass boundary once.
|
|
9996
|
+
const handlerServices = yield* Effect.context<Tool.HandlerServices<Tools[keyof Tools]>>();
|
|
9997
|
+
|
|
9998
|
+
// Toolkit.handle decodes unknown parameters itself. Widen only that input boundary;
|
|
9999
|
+
// retain its declared schema failures and handler services for this exact Toolkit.
|
|
10000
|
+
const handle = toolkit.handle as (
|
|
10001
|
+
name: keyof Tools,
|
|
10002
|
+
parameters: unknown,
|
|
10003
|
+
id: string,
|
|
10004
|
+
) => ReturnType<Toolkit.WithHandler<Tools>["handle"]>;
|
|
10005
|
+
|
|
10006
|
+
const concurrency = passOptions.concurrency ?? 4;
|
|
10007
|
+
|
|
10008
|
+
if (!Number.isSafeInteger(concurrency) || concurrency < 1 || concurrency > 64) {
|
|
10009
|
+
return yield* ToolBrokerConfigurationError.make({
|
|
10010
|
+
message: "concurrency must be an integer between 1 and 64",
|
|
10011
|
+
});
|
|
10012
|
+
}
|
|
10013
|
+
const permits = yield* Semaphore.make(concurrency);
|
|
10014
|
+
const records: Array<ProgrammaticCallRecord> = [];
|
|
10015
|
+
|
|
10016
|
+
const preflightFailure = (
|
|
10017
|
+
input: ProgrammaticToolInput,
|
|
10018
|
+
kind: "infrastructure" | "protocol",
|
|
10019
|
+
tag: string,
|
|
10020
|
+
message: string,
|
|
10021
|
+
cause?: Cause.Cause<unknown>,
|
|
10022
|
+
): Effect.Effect<ProgrammaticCallOutcome> => {
|
|
10023
|
+
const outcome = programmaticOutcomeError(undefined, tag, message);
|
|
10024
|
+
|
|
10025
|
+
if (preflightObserver === undefined) return Effect.succeed(outcome);
|
|
10026
|
+
|
|
10027
|
+
return deliverToolFailure(preflightObserver.observer, {
|
|
10028
|
+
_tag: "ProgrammaticPreflightFailure",
|
|
10029
|
+
agentId: binding.context.agentId,
|
|
10030
|
+
threadId: binding.context.threadId,
|
|
10031
|
+
runId: binding.context.runId,
|
|
10032
|
+
turnId: binding.turnId,
|
|
10033
|
+
parentToolCallId: binding.outerToolCallId,
|
|
10034
|
+
toolName: input.toolName,
|
|
10035
|
+
...(hasTool(toolkit.tools, input.toolName)
|
|
10036
|
+
? { executionClass: getToolExecutionClass(toolkit.tools[input.toolName]) }
|
|
10037
|
+
: {}),
|
|
10038
|
+
kind,
|
|
10039
|
+
tag,
|
|
10040
|
+
message: toolFailureMessage(message),
|
|
10041
|
+
...(cause === undefined ? {} : { cause }),
|
|
10042
|
+
}).pipe(preflightObserver.permits.withPermits(1), Effect.as(outcome));
|
|
10043
|
+
};
|
|
9953
10044
|
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
10045
|
+
const body = (
|
|
10046
|
+
input: ProgrammaticToolInput,
|
|
10047
|
+
sequenceIndex: number,
|
|
10048
|
+
): Effect.Effect<ProgrammaticCallOutcome> =>
|
|
10049
|
+
Effect.gen(function* () {
|
|
10050
|
+
if (!hasTool(toolkit.tools, input.toolName)) {
|
|
10051
|
+
return yield* preflightFailure(
|
|
10052
|
+
input,
|
|
10053
|
+
"infrastructure",
|
|
10054
|
+
"ProgrammaticToolUnknownError",
|
|
10055
|
+
`Tool ${input.toolName} is not part of this pass's allowlisted Toolkit`,
|
|
10056
|
+
);
|
|
10057
|
+
}
|
|
10058
|
+
const tool = toolkit.tools[input.toolName];
|
|
9968
10059
|
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
10060
|
+
const advertised = Object.values(binding.context.definition.toolkit.tools).some((outer) =>
|
|
10061
|
+
Context.get(outer.annotations, AdditionalToolCatalog).some(
|
|
10062
|
+
(entry) => entry.tool.name === input.toolName,
|
|
10063
|
+
),
|
|
10064
|
+
);
|
|
10065
|
+
|
|
10066
|
+
if (
|
|
10067
|
+
advertised &&
|
|
10068
|
+
!binding.context.toolCatalog.some(
|
|
10069
|
+
(entry) => entry.kind === "code-mode" && entry.tool.name === input.toolName,
|
|
10070
|
+
)
|
|
10071
|
+
)
|
|
10072
|
+
return yield* preflightFailure(
|
|
10073
|
+
input,
|
|
10074
|
+
"infrastructure",
|
|
10075
|
+
"ProgrammaticToolAuthorizationDenied",
|
|
10076
|
+
"Tool is excluded by the current host visibility policy",
|
|
9974
10077
|
);
|
|
9975
10078
|
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
10079
|
+
if (
|
|
10080
|
+
!isSubagentToolAllowed(
|
|
10081
|
+
inheritedAuthority?.grant,
|
|
10082
|
+
inheritedAuthority?.depth ?? 0,
|
|
10083
|
+
input.toolName,
|
|
10084
|
+
tool.annotations,
|
|
9981
10085
|
)
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
10086
|
+
) {
|
|
10087
|
+
return yield* preflightFailure(
|
|
10088
|
+
input,
|
|
10089
|
+
"infrastructure",
|
|
10090
|
+
"ProgrammaticToolAuthorizationDenied",
|
|
10091
|
+
"Tool exceeds the inherited subagent grant",
|
|
10092
|
+
);
|
|
10093
|
+
}
|
|
9988
10094
|
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10095
|
+
if (Context.get(tool.annotations, ContextRolloverTool)) {
|
|
10096
|
+
return yield* preflightFailure(
|
|
10097
|
+
input,
|
|
10098
|
+
"infrastructure",
|
|
10099
|
+
"ProgrammaticContextRolloverUnsupportedError",
|
|
10100
|
+
"Context rollover requires a direct, singleton model Tool Call",
|
|
10101
|
+
);
|
|
10102
|
+
}
|
|
10103
|
+
const approval = tool.needsApproval;
|
|
10104
|
+
|
|
10105
|
+
if (approval !== undefined && approval !== false) {
|
|
10106
|
+
return yield* preflightFailure(
|
|
10107
|
+
input,
|
|
10108
|
+
"infrastructure",
|
|
10109
|
+
"ProgrammaticApprovalUnsupportedError",
|
|
10110
|
+
`Tool ${input.toolName} requires approval; approval-requiring Tools never start programmatically in the ephemeral slice`,
|
|
10111
|
+
);
|
|
10112
|
+
}
|
|
10004
10113
|
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
}
|
|
10013
|
-
const approval = tool.needsApproval;
|
|
10114
|
+
// Validate the encoded arguments against the owning parameter
|
|
10115
|
+
// Schema before the handler can start; the pinned `Toolkit.handle`
|
|
10116
|
+
// decodes internally, so the validated encoded form passes through
|
|
10117
|
+
// (the same inversion as `prepareToolCall`).
|
|
10118
|
+
const decodeParameters = Schema.decodeUnknownEffect(tool.parametersSchema) as (
|
|
10119
|
+
value: unknown,
|
|
10120
|
+
) => Effect.Effect<unknown, Schema.SchemaError>;
|
|
10014
10121
|
|
|
10015
|
-
|
|
10016
|
-
|
|
10122
|
+
const invalidParameters = yield* decodeParameters(input.encodedArguments).pipe(
|
|
10123
|
+
Effect.map(() => undefined),
|
|
10124
|
+
Effect.catchCauseFilter(Cause.findError, (error, cause) =>
|
|
10125
|
+
preflightFailure(
|
|
10017
10126
|
input,
|
|
10018
|
-
"
|
|
10019
|
-
"
|
|
10020
|
-
`Tool ${input.toolName}
|
|
10021
|
-
|
|
10022
|
-
|
|
10127
|
+
"protocol",
|
|
10128
|
+
"ModelProtocolError",
|
|
10129
|
+
`Invalid parameters for Tool ${input.toolName}: ${error.message}`,
|
|
10130
|
+
cause,
|
|
10131
|
+
),
|
|
10132
|
+
),
|
|
10133
|
+
);
|
|
10134
|
+
|
|
10135
|
+
if (invalidParameters !== undefined) {
|
|
10136
|
+
return invalidParameters;
|
|
10137
|
+
}
|
|
10138
|
+
|
|
10139
|
+
const handleId = ToolCallId.make(`${binding.outerToolCallId}#${sequenceIndex}`);
|
|
10023
10140
|
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10141
|
+
const denied = yield* authorization
|
|
10142
|
+
.authorize({
|
|
10143
|
+
threadId: binding.context.threadId,
|
|
10144
|
+
runId: binding.context.runId,
|
|
10145
|
+
turnId: binding.turnId,
|
|
10146
|
+
turn: binding.turn,
|
|
10147
|
+
input: binding.context.input,
|
|
10148
|
+
programmatic: { parentToolCallId: binding.outerToolCallId, sequenceIndex },
|
|
10149
|
+
call: {
|
|
10150
|
+
toolCallId: handleId,
|
|
10151
|
+
toolName: input.toolName,
|
|
10152
|
+
parameters: input.encodedArguments,
|
|
10153
|
+
executionClass: getToolExecutionClass(tool),
|
|
10154
|
+
executionKind: getToolExecutionKind(tool.annotations),
|
|
10155
|
+
},
|
|
10156
|
+
})
|
|
10157
|
+
.pipe(
|
|
10158
|
+
Effect.flatMap((decision) =>
|
|
10159
|
+
decision._tag === "allowed"
|
|
10160
|
+
? Effect.succeed(undefined)
|
|
10161
|
+
: preflightFailure(
|
|
10162
|
+
input,
|
|
10163
|
+
"infrastructure",
|
|
10164
|
+
"ProgrammaticToolAuthorizationDenied",
|
|
10165
|
+
decision.reason,
|
|
10166
|
+
),
|
|
10167
|
+
),
|
|
10168
|
+
Effect.catch((failure) =>
|
|
10035
10169
|
preflightFailure(
|
|
10036
10170
|
input,
|
|
10037
|
-
"
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
cause,
|
|
10171
|
+
"infrastructure",
|
|
10172
|
+
errorTag(failure.error),
|
|
10173
|
+
errorMessage(failure.error),
|
|
10174
|
+
failure.cause,
|
|
10041
10175
|
),
|
|
10042
10176
|
),
|
|
10043
10177
|
);
|
|
10044
10178
|
|
|
10045
|
-
|
|
10046
|
-
return invalidParameters;
|
|
10047
|
-
}
|
|
10179
|
+
if (denied !== undefined) return denied;
|
|
10048
10180
|
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
turnId: binding.turnId,
|
|
10056
|
-
turn: binding.turn,
|
|
10057
|
-
input: binding.context.input,
|
|
10058
|
-
programmatic: { parentToolCallId: binding.outerToolCallId, sequenceIndex },
|
|
10059
|
-
call: {
|
|
10060
|
-
toolCallId: handleId,
|
|
10061
|
-
toolName: input.toolName,
|
|
10062
|
-
parameters: input.encodedArguments,
|
|
10063
|
-
executionClass: getToolExecutionClass(tool),
|
|
10064
|
-
executionKind: getToolExecutionKind(tool.annotations),
|
|
10065
|
-
},
|
|
10066
|
-
})
|
|
10067
|
-
.pipe(
|
|
10068
|
-
Effect.flatMap((decision) =>
|
|
10069
|
-
decision._tag === "allowed"
|
|
10070
|
-
? Effect.succeed(undefined)
|
|
10071
|
-
: preflightFailure(
|
|
10072
|
-
input,
|
|
10073
|
-
"infrastructure",
|
|
10074
|
-
"ProgrammaticToolAuthorizationDenied",
|
|
10075
|
-
decision.reason,
|
|
10076
|
-
),
|
|
10077
|
-
),
|
|
10078
|
-
Effect.catch((failure) =>
|
|
10079
|
-
preflightFailure(
|
|
10080
|
-
input,
|
|
10081
|
-
"infrastructure",
|
|
10082
|
-
errorTag(failure.error),
|
|
10083
|
-
errorMessage(failure.error),
|
|
10084
|
-
failure.cause,
|
|
10085
|
-
),
|
|
10086
|
-
),
|
|
10087
|
-
);
|
|
10088
|
-
|
|
10089
|
-
if (denied !== undefined) return denied;
|
|
10181
|
+
// Serialize admission and durable reservation across outer handlers.
|
|
10182
|
+
// Once reserved, a slot is never refunded: ownership may be lost
|
|
10183
|
+
// after the append but before the Handler starts.
|
|
10184
|
+
const rejected = yield* binding.context.policyReservations.withPermit(
|
|
10185
|
+
Effect.gen(function* () {
|
|
10186
|
+
const used = binding.declaredToolCalls + binding.context.programmaticToolCalls;
|
|
10090
10187
|
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10095
|
-
|
|
10096
|
-
|
|
10188
|
+
if (used + 1 > binding.maxToolCalls) {
|
|
10189
|
+
return yield* preflightFailure(
|
|
10190
|
+
input,
|
|
10191
|
+
"infrastructure",
|
|
10192
|
+
"AgentPolicyError",
|
|
10193
|
+
`Agent exceeded its ${binding.maxToolCalls} Tool Call limit`,
|
|
10194
|
+
);
|
|
10195
|
+
}
|
|
10196
|
+
if (binding.budget !== undefined) {
|
|
10197
|
+
const exhausted = yield* provideHookServices(
|
|
10198
|
+
binding.budget.consume({
|
|
10199
|
+
modelCalls: 0,
|
|
10200
|
+
inputTokens: 0,
|
|
10201
|
+
outputTokens: 0,
|
|
10202
|
+
totalTokens: 0,
|
|
10203
|
+
toolCalls: 1,
|
|
10204
|
+
costMicrousd: 0,
|
|
10205
|
+
usage: emptyProgrammaticUsage,
|
|
10206
|
+
}),
|
|
10207
|
+
binding.hookServices,
|
|
10208
|
+
).pipe(
|
|
10209
|
+
Effect.map(() => undefined),
|
|
10210
|
+
Effect.catchCauseFilter(Cause.findError, (error, cause) =>
|
|
10211
|
+
Effect.succeed({ error, cause }),
|
|
10212
|
+
),
|
|
10213
|
+
);
|
|
10097
10214
|
|
|
10098
|
-
if (
|
|
10215
|
+
if (exhausted !== undefined) {
|
|
10099
10216
|
return yield* preflightFailure(
|
|
10100
10217
|
input,
|
|
10101
10218
|
"infrastructure",
|
|
10102
|
-
|
|
10103
|
-
|
|
10219
|
+
errorTag(exhausted.error),
|
|
10220
|
+
errorMessage(exhausted.error),
|
|
10221
|
+
exhausted.cause,
|
|
10104
10222
|
);
|
|
10105
10223
|
}
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
Effect.map(() => undefined),
|
|
10120
|
-
Effect.catchCauseFilter(Cause.findError, (error, cause) =>
|
|
10121
|
-
Effect.succeed({ error, cause }),
|
|
10122
|
-
),
|
|
10123
|
-
);
|
|
10124
|
-
|
|
10125
|
-
if (exhausted !== undefined) {
|
|
10126
|
-
return yield* preflightFailure(
|
|
10224
|
+
}
|
|
10225
|
+
binding.context.programmaticToolCalls += 1;
|
|
10226
|
+
if (binding.reservePolicyUsage !== undefined) {
|
|
10227
|
+
return yield* provideHookServices(
|
|
10228
|
+
binding.reservePolicyUsage({
|
|
10229
|
+
programmaticToolCalls: binding.context.programmaticToolCalls,
|
|
10230
|
+
finalizationUsed: binding.context.finalizationUsed,
|
|
10231
|
+
}),
|
|
10232
|
+
binding.hookServices,
|
|
10233
|
+
).pipe(
|
|
10234
|
+
Effect.as(undefined),
|
|
10235
|
+
Effect.catchCauseFilter(Cause.findError, (error, cause) =>
|
|
10236
|
+
preflightFailure(
|
|
10127
10237
|
input,
|
|
10128
10238
|
"infrastructure",
|
|
10129
|
-
errorTag(
|
|
10130
|
-
errorMessage(
|
|
10131
|
-
|
|
10132
|
-
);
|
|
10133
|
-
}
|
|
10134
|
-
}
|
|
10135
|
-
binding.context.programmaticToolCalls += 1;
|
|
10136
|
-
if (binding.reservePolicyUsage !== undefined) {
|
|
10137
|
-
return yield* provideHookServices(
|
|
10138
|
-
binding.reservePolicyUsage({
|
|
10139
|
-
programmaticToolCalls: binding.context.programmaticToolCalls,
|
|
10140
|
-
finalizationUsed: binding.context.finalizationUsed,
|
|
10141
|
-
}),
|
|
10142
|
-
binding.hookServices,
|
|
10143
|
-
).pipe(
|
|
10144
|
-
Effect.as(undefined),
|
|
10145
|
-
Effect.catchCauseFilter(Cause.findError, (error, cause) =>
|
|
10146
|
-
preflightFailure(
|
|
10147
|
-
input,
|
|
10148
|
-
"infrastructure",
|
|
10149
|
-
errorTag(error),
|
|
10150
|
-
errorMessage(error),
|
|
10151
|
-
cause,
|
|
10152
|
-
),
|
|
10239
|
+
errorTag(error),
|
|
10240
|
+
errorMessage(error),
|
|
10241
|
+
cause,
|
|
10153
10242
|
),
|
|
10154
|
-
)
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
return undefined;
|
|
10158
|
-
}),
|
|
10159
|
-
);
|
|
10160
|
-
|
|
10161
|
-
if (rejected !== undefined) return rejected;
|
|
10243
|
+
),
|
|
10244
|
+
);
|
|
10245
|
+
}
|
|
10162
10246
|
|
|
10163
|
-
|
|
10247
|
+
return undefined;
|
|
10248
|
+
}),
|
|
10249
|
+
);
|
|
10164
10250
|
|
|
10165
|
-
|
|
10166
|
-
sequenceIndex,
|
|
10167
|
-
toolName: input.toolName,
|
|
10168
|
-
status: "uncertain",
|
|
10169
|
-
};
|
|
10170
|
-
const executionClass = getToolExecutionClass(tool);
|
|
10171
|
-
let failureObservation: ProgrammaticToolFailure | undefined;
|
|
10172
|
-
|
|
10173
|
-
const startedFailure = (
|
|
10174
|
-
kind: "infrastructure" | "protocol",
|
|
10175
|
-
tag: string,
|
|
10176
|
-
message: string,
|
|
10177
|
-
): Effect.Effect<ProgrammaticCallOutcome> => {
|
|
10178
|
-
const outcome = programmaticOutcomeError(index, tag, message);
|
|
10179
|
-
|
|
10180
|
-
if (observer === undefined) return Effect.succeed(outcome);
|
|
10181
|
-
failureObservation = {
|
|
10182
|
-
_tag: "ProgrammaticToolFailure",
|
|
10183
|
-
agentId: binding.context.agentId,
|
|
10184
|
-
threadId: binding.context.threadId,
|
|
10185
|
-
runId: binding.context.runId,
|
|
10186
|
-
turnId: binding.turnId,
|
|
10187
|
-
toolCallId: handleId,
|
|
10188
|
-
parentToolCallId: binding.outerToolCallId,
|
|
10189
|
-
sequenceIndex: index,
|
|
10190
|
-
toolName: input.toolName,
|
|
10191
|
-
executionClass,
|
|
10192
|
-
kind,
|
|
10193
|
-
tag,
|
|
10194
|
-
message: toolFailureMessage(message),
|
|
10195
|
-
};
|
|
10251
|
+
if (rejected !== undefined) return rejected;
|
|
10196
10252
|
|
|
10197
|
-
|
|
10198
|
-
};
|
|
10253
|
+
const index = sequenceIndex;
|
|
10199
10254
|
|
|
10200
|
-
|
|
10201
|
-
|
|
10255
|
+
records[sequenceIndex] = {
|
|
10256
|
+
sequenceIndex,
|
|
10257
|
+
toolName: input.toolName,
|
|
10258
|
+
status: "uncertain",
|
|
10259
|
+
};
|
|
10260
|
+
const executionClass = getToolExecutionClass(tool);
|
|
10261
|
+
let failureObservation: ProgrammaticToolFailure | undefined;
|
|
10262
|
+
|
|
10263
|
+
const startedFailure = (
|
|
10264
|
+
kind: "infrastructure" | "protocol",
|
|
10265
|
+
tag: string,
|
|
10266
|
+
message: string,
|
|
10267
|
+
): Effect.Effect<ProgrammaticCallOutcome> => {
|
|
10268
|
+
const outcome = programmaticOutcomeError(index, tag, message);
|
|
10269
|
+
|
|
10270
|
+
if (observer === undefined) return Effect.succeed(outcome);
|
|
10271
|
+
failureObservation = {
|
|
10272
|
+
_tag: "ProgrammaticToolFailure",
|
|
10273
|
+
agentId: binding.context.agentId,
|
|
10274
|
+
threadId: binding.context.threadId,
|
|
10275
|
+
runId: binding.context.runId,
|
|
10202
10276
|
turnId: binding.turnId,
|
|
10203
10277
|
toolCallId: handleId,
|
|
10204
|
-
toolName: input.toolName,
|
|
10205
|
-
executionClass,
|
|
10206
|
-
invocationKind: "programmatic",
|
|
10207
|
-
failureMode: tool.failureMode,
|
|
10208
10278
|
parentToolCallId: binding.outerToolCallId,
|
|
10209
10279
|
sequenceIndex: index,
|
|
10280
|
+
toolName: input.toolName,
|
|
10281
|
+
executionClass,
|
|
10282
|
+
kind,
|
|
10283
|
+
tag,
|
|
10284
|
+
message: toolFailureMessage(message),
|
|
10210
10285
|
};
|
|
10211
10286
|
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10287
|
+
return Effect.succeed(outcome);
|
|
10288
|
+
};
|
|
10289
|
+
|
|
10290
|
+
const telemetryDescriptor: ToolTelemetryDescriptor = {
|
|
10291
|
+
context: binding.context,
|
|
10292
|
+
turnId: binding.turnId,
|
|
10293
|
+
toolCallId: handleId,
|
|
10294
|
+
toolName: input.toolName,
|
|
10295
|
+
executionClass,
|
|
10296
|
+
invocationKind: "programmatic",
|
|
10297
|
+
failureMode: tool.failureMode,
|
|
10298
|
+
parentToolCallId: binding.outerToolCallId,
|
|
10299
|
+
sequenceIndex: index,
|
|
10300
|
+
};
|
|
10301
|
+
|
|
10302
|
+
const execution = measureProgrammaticToolCall(
|
|
10303
|
+
toolSpanTelemetry,
|
|
10304
|
+
telemetryDescriptor,
|
|
10305
|
+
Effect.gen(function* () {
|
|
10306
|
+
yield* Effect.logDebug("agent programmatic tool handler started").pipe(
|
|
10307
|
+
Effect.annotateLogs({
|
|
10308
|
+
agentId: binding.context.agentId,
|
|
10309
|
+
runId: binding.context.runId,
|
|
10310
|
+
turnId: binding.turnId,
|
|
10311
|
+
toolCallId: handleId,
|
|
10312
|
+
parentToolCallId: binding.outerToolCallId,
|
|
10313
|
+
toolName: input.toolName,
|
|
10314
|
+
sequenceIndex: index,
|
|
10315
|
+
}),
|
|
10316
|
+
);
|
|
10317
|
+
yield* Metric.update(toolCounter, 1);
|
|
10318
|
+
|
|
10319
|
+
let terminal:
|
|
10320
|
+
| {
|
|
10321
|
+
readonly encodedResult: unknown;
|
|
10322
|
+
readonly isFailure: boolean;
|
|
10323
|
+
readonly tag: string | undefined;
|
|
10324
|
+
}
|
|
10325
|
+
| undefined;
|
|
10326
|
+
|
|
10327
|
+
let resultAfterTerminal = false;
|
|
10328
|
+
|
|
10329
|
+
const handlerFailed = yield* Stream.unwrap(
|
|
10330
|
+
toolSpanTelemetry.isolateToolkitHandle(
|
|
10331
|
+
handle(input.toolName, input.encodedArguments, handleId),
|
|
10332
|
+
),
|
|
10333
|
+
).pipe(
|
|
10334
|
+
Stream.runForEach((result) =>
|
|
10335
|
+
Effect.sync(() => {
|
|
10336
|
+
// The direct path rejects a second result after the terminal
|
|
10337
|
+
// one; the broker preserves that protocol violation instead
|
|
10338
|
+
// of silently keeping the last value.
|
|
10339
|
+
if (terminal !== undefined) {
|
|
10340
|
+
resultAfterTerminal = true;
|
|
10341
|
+
|
|
10342
|
+
return;
|
|
10343
|
+
}
|
|
10344
|
+
if (!result.preliminary) {
|
|
10345
|
+
terminal = {
|
|
10346
|
+
encodedResult: toolResultForJson(tool, result),
|
|
10347
|
+
isFailure: result.isFailure,
|
|
10348
|
+
tag:
|
|
10349
|
+
observer === undefined || !result.isFailure
|
|
10350
|
+
? undefined
|
|
10351
|
+
: errorTag(result.result),
|
|
10352
|
+
};
|
|
10353
|
+
}
|
|
10354
|
+
}),
|
|
10355
|
+
),
|
|
10356
|
+
Effect.map(() => undefined),
|
|
10357
|
+
Effect.catchCauseFilter(Cause.findError, (error, cause) =>
|
|
10358
|
+
Effect.succeed(new BrokerCallFailure(error, cause)),
|
|
10359
|
+
),
|
|
10360
|
+
);
|
|
10361
|
+
|
|
10362
|
+
if (handlerFailed instanceof BrokerCallFailure) {
|
|
10363
|
+
const tag = errorTag(handlerFailed.error);
|
|
10364
|
+
|
|
10365
|
+
if (observer !== undefined) {
|
|
10366
|
+
failureObservation = {
|
|
10367
|
+
_tag: "ProgrammaticToolFailure",
|
|
10218
10368
|
agentId: binding.context.agentId,
|
|
10369
|
+
threadId: binding.context.threadId,
|
|
10219
10370
|
runId: binding.context.runId,
|
|
10220
10371
|
turnId: binding.turnId,
|
|
10221
10372
|
toolCallId: handleId,
|
|
10222
10373
|
parentToolCallId: binding.outerToolCallId,
|
|
10223
|
-
toolName: input.toolName,
|
|
10224
10374
|
sequenceIndex: index,
|
|
10225
|
-
|
|
10375
|
+
toolName: input.toolName,
|
|
10376
|
+
executionClass,
|
|
10377
|
+
kind: "handler-error",
|
|
10378
|
+
tag,
|
|
10379
|
+
cause: handlerFailed.cause,
|
|
10380
|
+
};
|
|
10381
|
+
}
|
|
10382
|
+
|
|
10383
|
+
return programmaticOutcomeError(index, tag, errorMessage(handlerFailed.error));
|
|
10384
|
+
}
|
|
10385
|
+
if (resultAfterTerminal) {
|
|
10386
|
+
return yield* startedFailure(
|
|
10387
|
+
"protocol",
|
|
10388
|
+
"ModelProtocolError",
|
|
10389
|
+
`Tool Call ${handleId} produced more than one terminal result`,
|
|
10390
|
+
);
|
|
10391
|
+
}
|
|
10392
|
+
if (terminal === undefined) {
|
|
10393
|
+
return yield* startedFailure(
|
|
10394
|
+
"protocol",
|
|
10395
|
+
"ModelProtocolError",
|
|
10396
|
+
`Tool Call ${handleId} completed without a terminal result`,
|
|
10226
10397
|
);
|
|
10227
|
-
|
|
10398
|
+
}
|
|
10399
|
+
if (terminal.isFailure) {
|
|
10400
|
+
if (observer !== undefined) {
|
|
10401
|
+
failureObservation = {
|
|
10402
|
+
_tag: "ProgrammaticToolFailure",
|
|
10403
|
+
agentId: binding.context.agentId,
|
|
10404
|
+
threadId: binding.context.threadId,
|
|
10405
|
+
runId: binding.context.runId,
|
|
10406
|
+
turnId: binding.turnId,
|
|
10407
|
+
toolCallId: handleId,
|
|
10408
|
+
parentToolCallId: binding.outerToolCallId,
|
|
10409
|
+
sequenceIndex: index,
|
|
10410
|
+
toolName: input.toolName,
|
|
10411
|
+
executionClass,
|
|
10412
|
+
kind: "declared-failure",
|
|
10413
|
+
tag: terminal.tag ?? "UnknownError",
|
|
10414
|
+
};
|
|
10415
|
+
}
|
|
10228
10416
|
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
toolSpanTelemetry.isolateToolkitHandle(
|
|
10241
|
-
(
|
|
10242
|
-
toolkit.handle as (
|
|
10243
|
-
name: string,
|
|
10244
|
-
params: unknown,
|
|
10245
|
-
id: string,
|
|
10246
|
-
) => Effect.Effect<
|
|
10247
|
-
Stream.Stream<Tool.HandlerResult<Tool.Any>, unknown, unknown>,
|
|
10248
|
-
unknown,
|
|
10249
|
-
unknown
|
|
10250
|
-
>
|
|
10251
|
-
)(input.toolName, input.encodedArguments, handleId),
|
|
10252
|
-
),
|
|
10253
|
-
).pipe(
|
|
10254
|
-
Stream.runForEach((result) =>
|
|
10255
|
-
Effect.sync(() => {
|
|
10256
|
-
// The direct path rejects a second result after the terminal
|
|
10257
|
-
// one; the broker preserves that protocol violation instead
|
|
10258
|
-
// of silently keeping the last value.
|
|
10259
|
-
if (terminal !== undefined) {
|
|
10260
|
-
resultAfterTerminal = true;
|
|
10261
|
-
|
|
10262
|
-
return;
|
|
10263
|
-
}
|
|
10264
|
-
if (!result.preliminary) {
|
|
10265
|
-
terminal = {
|
|
10266
|
-
encodedResult: toolResultForJson(tool, result),
|
|
10267
|
-
isFailure: result.isFailure,
|
|
10268
|
-
tag:
|
|
10269
|
-
observer === undefined || !result.isFailure
|
|
10270
|
-
? undefined
|
|
10271
|
-
: errorTag(result.result),
|
|
10272
|
-
};
|
|
10273
|
-
}
|
|
10274
|
-
}),
|
|
10275
|
-
),
|
|
10276
|
-
Effect.map(() => undefined),
|
|
10277
|
-
Effect.catchCauseFilter(Cause.findError, (error, cause) =>
|
|
10278
|
-
Effect.succeed(new BrokerCallFailure(error, cause)),
|
|
10279
|
-
),
|
|
10417
|
+
return {
|
|
10418
|
+
_tag: "ProgrammaticCallFailure",
|
|
10419
|
+
index,
|
|
10420
|
+
encodedResult: terminal.encodedResult,
|
|
10421
|
+
} as const;
|
|
10422
|
+
}
|
|
10423
|
+
if (Option.isNone(brokerDecodeJson(terminal.encodedResult))) {
|
|
10424
|
+
return yield* startedFailure(
|
|
10425
|
+
"protocol",
|
|
10426
|
+
"ModelProtocolError",
|
|
10427
|
+
`Tool ${input.toolName} produced a success encoding outside JSON`,
|
|
10280
10428
|
);
|
|
10429
|
+
}
|
|
10430
|
+
let encodedResult = terminal.encodedResult;
|
|
10281
10431
|
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
failureObservation = {
|
|
10287
|
-
_tag: "ProgrammaticToolFailure",
|
|
10288
|
-
agentId: binding.context.agentId,
|
|
10289
|
-
threadId: binding.context.threadId,
|
|
10290
|
-
runId: binding.context.runId,
|
|
10291
|
-
turnId: binding.turnId,
|
|
10292
|
-
toolCallId: handleId,
|
|
10293
|
-
parentToolCallId: binding.outerToolCallId,
|
|
10294
|
-
sequenceIndex: index,
|
|
10295
|
-
toolName: input.toolName,
|
|
10296
|
-
executionClass,
|
|
10297
|
-
kind: "handler-error",
|
|
10298
|
-
tag,
|
|
10299
|
-
cause: handlerFailed.cause,
|
|
10300
|
-
};
|
|
10301
|
-
}
|
|
10432
|
+
if (passOptions.redactResult !== undefined) {
|
|
10433
|
+
// A redactor is a substitution point: its replacement re-crosses
|
|
10434
|
+
// the JSON boundary or the call fails closed.
|
|
10435
|
+
const redacted = brokerDecodeJson(yield* passOptions.redactResult(encodedResult));
|
|
10302
10436
|
|
|
10303
|
-
|
|
10304
|
-
}
|
|
10305
|
-
if (resultAfterTerminal) {
|
|
10306
|
-
return yield* startedFailure(
|
|
10307
|
-
"protocol",
|
|
10308
|
-
"ModelProtocolError",
|
|
10309
|
-
`Tool Call ${handleId} produced more than one terminal result`,
|
|
10310
|
-
);
|
|
10311
|
-
}
|
|
10312
|
-
if (terminal === undefined) {
|
|
10437
|
+
if (Option.isNone(redacted)) {
|
|
10313
10438
|
return yield* startedFailure(
|
|
10314
10439
|
"protocol",
|
|
10315
10440
|
"ModelProtocolError",
|
|
10316
|
-
`Tool
|
|
10441
|
+
`The redacted result for Tool ${input.toolName} is outside the JSON surface`,
|
|
10317
10442
|
);
|
|
10318
10443
|
}
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
_tag: "ProgrammaticToolFailure",
|
|
10323
|
-
agentId: binding.context.agentId,
|
|
10324
|
-
threadId: binding.context.threadId,
|
|
10325
|
-
runId: binding.context.runId,
|
|
10326
|
-
turnId: binding.turnId,
|
|
10327
|
-
toolCallId: handleId,
|
|
10328
|
-
parentToolCallId: binding.outerToolCallId,
|
|
10329
|
-
sequenceIndex: index,
|
|
10330
|
-
toolName: input.toolName,
|
|
10331
|
-
executionClass,
|
|
10332
|
-
kind: "declared-failure",
|
|
10333
|
-
tag: terminal.tag ?? "UnknownError",
|
|
10334
|
-
};
|
|
10335
|
-
}
|
|
10444
|
+
encodedResult = redacted.value;
|
|
10445
|
+
}
|
|
10446
|
+
const snapshot = brokerSerializeJson(encodedResult);
|
|
10336
10447
|
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
}
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
return yield* startedFailure(
|
|
10345
|
-
"protocol",
|
|
10346
|
-
"ModelProtocolError",
|
|
10347
|
-
`Tool ${input.toolName} produced a success encoding outside JSON`,
|
|
10348
|
-
);
|
|
10349
|
-
}
|
|
10350
|
-
let encodedResult = terminal.encodedResult;
|
|
10351
|
-
|
|
10352
|
-
if (passOptions.redactResult !== undefined) {
|
|
10353
|
-
// A redactor is a substitution point: its replacement re-crosses
|
|
10354
|
-
// the JSON boundary or the call fails closed.
|
|
10355
|
-
const redacted = brokerDecodeJson(yield* passOptions.redactResult(encodedResult));
|
|
10356
|
-
|
|
10357
|
-
if (Option.isNone(redacted)) {
|
|
10358
|
-
return yield* startedFailure(
|
|
10359
|
-
"protocol",
|
|
10360
|
-
"ModelProtocolError",
|
|
10361
|
-
`The redacted result for Tool ${input.toolName} is outside the JSON surface`,
|
|
10362
|
-
);
|
|
10363
|
-
}
|
|
10364
|
-
encodedResult = redacted.value;
|
|
10365
|
-
}
|
|
10366
|
-
const snapshot = brokerSerializeJson(encodedResult);
|
|
10448
|
+
if (snapshot === undefined || snapshot.bytes > passOptions.maxResultBytes) {
|
|
10449
|
+
return yield* startedFailure(
|
|
10450
|
+
"infrastructure",
|
|
10451
|
+
"ProgrammaticResultLimitError",
|
|
10452
|
+
`Tool ${input.toolName} result of ${snapshot?.bytes ?? "unencodable"} bytes exceeds the ${passOptions.maxResultBytes}-byte broker bound`,
|
|
10453
|
+
);
|
|
10454
|
+
}
|
|
10367
10455
|
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
"ProgrammaticResultLimitError",
|
|
10372
|
-
`Tool ${input.toolName} result of ${snapshot?.bytes ?? "unencodable"} bytes exceeds the ${passOptions.maxResultBytes}-byte broker bound`,
|
|
10373
|
-
);
|
|
10374
|
-
}
|
|
10456
|
+
// Retain the exact representation admitted above. Reusing the handler or redactor
|
|
10457
|
+
// value would let later mutation or stateful getters escape the byte bound.
|
|
10458
|
+
const owned = Schema.decodeOption(Schema.fromJsonString(Schema.Json))(snapshot.text);
|
|
10375
10459
|
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10460
|
+
if (Option.isNone(owned)) {
|
|
10461
|
+
return yield* startedFailure(
|
|
10462
|
+
"protocol",
|
|
10463
|
+
"ModelProtocolError",
|
|
10464
|
+
`Tool ${input.toolName} produced a success encoding outside JSON`,
|
|
10380
10465
|
);
|
|
10466
|
+
}
|
|
10381
10467
|
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10468
|
+
return {
|
|
10469
|
+
_tag: "ProgrammaticCallSuccess",
|
|
10470
|
+
index,
|
|
10471
|
+
encodedResult: owned.value,
|
|
10472
|
+
} as const;
|
|
10473
|
+
}),
|
|
10474
|
+
);
|
|
10389
10475
|
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10476
|
+
// Fix the broker outcome and terminal telemetry before delivery. Interruption of an
|
|
10477
|
+
// observer cannot turn a settled inner failure into an interrupted Handler attempt.
|
|
10478
|
+
// This still runs inline under the outer call's permit, before pass.invoke returns.
|
|
10479
|
+
const recorded = execution.pipe(
|
|
10480
|
+
Effect.tap((outcome) =>
|
|
10481
|
+
Effect.sync(() => {
|
|
10482
|
+
records[sequenceIndex] = {
|
|
10483
|
+
sequenceIndex,
|
|
10484
|
+
toolName: input.toolName,
|
|
10485
|
+
status:
|
|
10486
|
+
outcome._tag === "ProgrammaticCallSuccess"
|
|
10487
|
+
? "succeeded"
|
|
10488
|
+
: outcome._tag === "ProgrammaticCallFailure"
|
|
10489
|
+
? "failed"
|
|
10490
|
+
: "uncertain",
|
|
10491
|
+
...(outcome._tag === "ProgrammaticCallError"
|
|
10492
|
+
? { errorTag: outcome.errorTag }
|
|
10493
|
+
: {}),
|
|
10494
|
+
};
|
|
10395
10495
|
}),
|
|
10396
|
-
)
|
|
10496
|
+
),
|
|
10497
|
+
);
|
|
10397
10498
|
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
: "uncertain",
|
|
10413
|
-
...(outcome._tag === "ProgrammaticCallError"
|
|
10414
|
-
? { errorTag: outcome.errorTag }
|
|
10415
|
-
: {}),
|
|
10416
|
-
};
|
|
10417
|
-
}),
|
|
10418
|
-
),
|
|
10419
|
-
);
|
|
10499
|
+
return yield* observer === undefined
|
|
10500
|
+
? recorded
|
|
10501
|
+
: recorded.pipe(
|
|
10502
|
+
Effect.tap(() =>
|
|
10503
|
+
failureObservation === undefined
|
|
10504
|
+
? Effect.void
|
|
10505
|
+
: deliverToolFailure(observer, failureObservation),
|
|
10506
|
+
),
|
|
10507
|
+
);
|
|
10508
|
+
}).pipe(
|
|
10509
|
+
Effect.updateContext((current: Context.Context<never>) =>
|
|
10510
|
+
Context.merge(current, handlerServices),
|
|
10511
|
+
),
|
|
10512
|
+
);
|
|
10420
10513
|
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
? Effect.void
|
|
10427
|
-
: deliverToolFailure(observer, failureObservation),
|
|
10428
|
-
),
|
|
10429
|
-
);
|
|
10430
|
-
}).pipe(Effect.provideContext(handlerServices)) as Effect.Effect<ProgrammaticCallOutcome>;
|
|
10431
|
-
|
|
10432
|
-
const pass: ToolBrokerPass = {
|
|
10433
|
-
snapshot: Effect.sync(() => records.map((record) => ({ ...record }))),
|
|
10434
|
-
invoke: (input) =>
|
|
10435
|
-
Effect.suspend(() => {
|
|
10436
|
-
const sequenceIndex = records.length;
|
|
10437
|
-
|
|
10438
|
-
records.push({ sequenceIndex, toolName: input.toolName, status: "not-started" });
|
|
10439
|
-
|
|
10440
|
-
// Check after acquiring the permit too: queued work cannot start after its owner exits.
|
|
10441
|
-
return permits
|
|
10442
|
-
.withPermit(
|
|
10443
|
-
Effect.suspend(() =>
|
|
10444
|
-
lifecycle.closed
|
|
10445
|
-
? preflightFailure(
|
|
10446
|
-
input,
|
|
10447
|
-
"infrastructure",
|
|
10448
|
-
"ToolBrokerUnavailableError",
|
|
10449
|
-
"The outer Tool Call for this pass has already settled",
|
|
10450
|
-
)
|
|
10451
|
-
: body(input, sequenceIndex),
|
|
10452
|
-
),
|
|
10453
|
-
)
|
|
10454
|
-
.pipe(
|
|
10455
|
-
Effect.tap((outcome) =>
|
|
10456
|
-
Effect.sync(() => {
|
|
10457
|
-
if (outcome._tag === "ProgrammaticCallError" && outcome.index === undefined) {
|
|
10458
|
-
records[sequenceIndex] = {
|
|
10459
|
-
sequenceIndex,
|
|
10460
|
-
toolName: input.toolName,
|
|
10461
|
-
status: "not-started",
|
|
10462
|
-
errorTag: outcome.errorTag,
|
|
10463
|
-
};
|
|
10464
|
-
}
|
|
10465
|
-
}),
|
|
10466
|
-
),
|
|
10467
|
-
);
|
|
10468
|
-
}),
|
|
10469
|
-
};
|
|
10514
|
+
const pass: ToolBrokerPass = {
|
|
10515
|
+
snapshot: Effect.sync(() => records.map((record) => ({ ...record }))),
|
|
10516
|
+
invoke: (input) =>
|
|
10517
|
+
Effect.suspend(() => {
|
|
10518
|
+
const sequenceIndex = records.length;
|
|
10470
10519
|
|
|
10471
|
-
|
|
10472
|
-
|
|
10520
|
+
records.push({ sequenceIndex, toolName: input.toolName, status: "not-started" });
|
|
10521
|
+
|
|
10522
|
+
// Check after acquiring the permit too: queued work cannot start after its owner exits.
|
|
10523
|
+
return permits
|
|
10524
|
+
.withPermit(
|
|
10525
|
+
Effect.suspend(() =>
|
|
10526
|
+
lifecycle.closed
|
|
10527
|
+
? preflightFailure(
|
|
10528
|
+
input,
|
|
10529
|
+
"infrastructure",
|
|
10530
|
+
"ToolBrokerUnavailableError",
|
|
10531
|
+
"The outer Tool Call for this pass has already settled",
|
|
10532
|
+
)
|
|
10533
|
+
: body(input, sequenceIndex),
|
|
10534
|
+
),
|
|
10535
|
+
)
|
|
10536
|
+
.pipe(
|
|
10537
|
+
Effect.tap((outcome) =>
|
|
10538
|
+
Effect.sync(() => {
|
|
10539
|
+
if (outcome._tag === "ProgrammaticCallError" && outcome.index === undefined) {
|
|
10540
|
+
records[sequenceIndex] = {
|
|
10541
|
+
sequenceIndex,
|
|
10542
|
+
toolName: input.toolName,
|
|
10543
|
+
status: "not-started",
|
|
10544
|
+
errorTag: outcome.errorTag,
|
|
10545
|
+
};
|
|
10546
|
+
}
|
|
10547
|
+
}),
|
|
10548
|
+
),
|
|
10549
|
+
);
|
|
10550
|
+
}),
|
|
10551
|
+
};
|
|
10552
|
+
|
|
10553
|
+
return pass;
|
|
10554
|
+
}),
|
|
10473
10555
|
};
|
|
10474
10556
|
|
|
10475
10557
|
return {
|
|
@@ -10553,98 +10635,97 @@ const makeDurableStepService = <HookError, HookRequirements>(
|
|
|
10553
10635
|
const usedNames = new Set<string>();
|
|
10554
10636
|
|
|
10555
10637
|
return {
|
|
10556
|
-
do: <Output extends Schema.Top, BodyError, BodyServices>(
|
|
10638
|
+
do: Effect.fnUntraced(function* <Output extends Schema.Top, BodyError, BodyServices>(
|
|
10557
10639
|
name: string,
|
|
10558
10640
|
output: Output,
|
|
10559
10641
|
execute: Effect.Effect<Output["Type"], BodyError, BodyServices>,
|
|
10560
|
-
)
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10642
|
+
) {
|
|
10643
|
+
if (usedNames.has(name)) {
|
|
10644
|
+
return yield* DurableStepError.make({
|
|
10645
|
+
toolCallId,
|
|
10646
|
+
stepName: name,
|
|
10647
|
+
reason: "duplicate-step-name",
|
|
10648
|
+
message: "Durable Step name was reused within one Tool Call",
|
|
10649
|
+
});
|
|
10650
|
+
}
|
|
10651
|
+
usedNames.add(name);
|
|
10652
|
+
const key: RunStepKey = { toolCallId, stepName: name };
|
|
10653
|
+
|
|
10654
|
+
const recorded = yield* provideHookServices(hook.lookup(key), hookServices).pipe(
|
|
10655
|
+
Effect.mapError((cause) =>
|
|
10656
|
+
DurableStepError.make({
|
|
10564
10657
|
toolCallId,
|
|
10565
10658
|
stepName: name,
|
|
10566
|
-
reason: "
|
|
10567
|
-
message: "Durable Step
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10659
|
+
reason: "lookup-failed",
|
|
10660
|
+
message: "Durable Step lookup failed",
|
|
10661
|
+
cause,
|
|
10662
|
+
}),
|
|
10663
|
+
),
|
|
10664
|
+
);
|
|
10572
10665
|
|
|
10573
|
-
|
|
10574
|
-
|
|
10666
|
+
if (Option.isSome(recorded)) {
|
|
10667
|
+
return yield* Schema.decodeUnknownEffect(output)(recorded.value.encodedOutput).pipe(
|
|
10668
|
+
Effect.mapError(() =>
|
|
10575
10669
|
DurableStepError.make({
|
|
10576
10670
|
toolCallId,
|
|
10577
10671
|
stepName: name,
|
|
10578
|
-
reason: "
|
|
10579
|
-
message: "Durable Step
|
|
10580
|
-
cause,
|
|
10672
|
+
reason: "recorded-result-invalid",
|
|
10673
|
+
message: "Recorded Durable Step result failed the declared output Schema",
|
|
10581
10674
|
}),
|
|
10582
10675
|
),
|
|
10583
10676
|
);
|
|
10677
|
+
}
|
|
10584
10678
|
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
DurableStepError.make({
|
|
10602
|
-
toolCallId,
|
|
10603
|
-
stepName: name,
|
|
10604
|
-
reason:
|
|
10605
|
-
cause instanceof AgentPersistenceCapacityError
|
|
10606
|
-
? "capacity-exhausted"
|
|
10607
|
-
: "reservation-failed",
|
|
10608
|
-
message:
|
|
10609
|
-
cause instanceof AgentPersistenceCapacityError
|
|
10610
|
-
? "Durable Step has no room for a valid saved result"
|
|
10611
|
-
: "Durable Step capacity reservation failed",
|
|
10612
|
-
cause,
|
|
10613
|
-
}),
|
|
10614
|
-
),
|
|
10679
|
+
return yield* Effect.acquireUseRelease(
|
|
10680
|
+
provideHookServices(hook.reserve(key), hookServices).pipe(
|
|
10681
|
+
Effect.mapError((cause) =>
|
|
10682
|
+
DurableStepError.make({
|
|
10683
|
+
toolCallId,
|
|
10684
|
+
stepName: name,
|
|
10685
|
+
reason:
|
|
10686
|
+
cause instanceof AgentPersistenceCapacityError
|
|
10687
|
+
? "capacity-exhausted"
|
|
10688
|
+
: "reservation-failed",
|
|
10689
|
+
message:
|
|
10690
|
+
cause instanceof AgentPersistenceCapacityError
|
|
10691
|
+
? "Durable Step has no room for a valid saved result"
|
|
10692
|
+
: "Durable Step capacity reservation failed",
|
|
10693
|
+
cause,
|
|
10694
|
+
}),
|
|
10615
10695
|
),
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
),
|
|
10629
|
-
)
|
|
10696
|
+
),
|
|
10697
|
+
() =>
|
|
10698
|
+
Effect.gen(function* () {
|
|
10699
|
+
const value = yield* execute;
|
|
10700
|
+
|
|
10701
|
+
const encodedOutput = yield* Schema.encodeEffect(output)(value).pipe(
|
|
10702
|
+
Effect.mapError(() =>
|
|
10703
|
+
DurableStepError.make({
|
|
10704
|
+
toolCallId,
|
|
10705
|
+
stepName: name,
|
|
10706
|
+
reason: "output-encoding-failed",
|
|
10707
|
+
message: "Durable Step output failed the declared output Schema",
|
|
10708
|
+
}),
|
|
10709
|
+
),
|
|
10710
|
+
);
|
|
10630
10711
|
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10712
|
+
yield* provideHookServices(hook.commit(key, encodedOutput), hookServices).pipe(
|
|
10713
|
+
Effect.mapError((cause) =>
|
|
10714
|
+
DurableStepError.make({
|
|
10715
|
+
toolCallId,
|
|
10716
|
+
stepName: name,
|
|
10717
|
+
reason: "commit-failed",
|
|
10718
|
+
message: "Durable Step commit failed",
|
|
10719
|
+
cause,
|
|
10720
|
+
}),
|
|
10721
|
+
),
|
|
10722
|
+
);
|
|
10642
10723
|
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10724
|
+
return value;
|
|
10725
|
+
}),
|
|
10726
|
+
(release) => release,
|
|
10727
|
+
);
|
|
10728
|
+
}),
|
|
10648
10729
|
};
|
|
10649
10730
|
};
|
|
10650
10731
|
|