@yielded/agent 0.1.0-beta.181 → 0.1.0-beta.183
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-NvXFPDIM.d.mts → Admin-BgCHCGSl.d.mts} +8 -8
- package/dist/{AgentRuntime-Dx5u_FJD.d.mts → AgentRuntime-DhRchFzv.d.mts} +7 -4
- package/dist/{AgentRuntime-BAExRlgu.mjs → AgentRuntime-ILtyHuQG.mjs} +61 -45
- package/dist/AgentRuntime-ILtyHuQG.mjs.map +1 -0
- package/dist/{Digest-DjUgS5PG.d.mts → Digest-1iVcDLVm.d.mts} +2 -2
- package/dist/{Digest-ft4ku9pj.mjs → Digest-CzLJcsWv.mjs} +2 -2
- package/dist/{Digest-ft4ku9pj.mjs.map → Digest-CzLJcsWv.mjs.map} +1 -1
- package/dist/{DurableAgentRuntime-HH2muFrT.mjs → DurableAgentRuntime-Cj263rW9.mjs} +27 -57
- package/dist/DurableAgentRuntime-Cj263rW9.mjs.map +1 -0
- package/dist/{EventSource-DNyaWmyQ.d.mts → EventSource-CaA6RAtC.d.mts} +3 -3
- package/dist/InMemory.d.mts +1 -1
- package/dist/{MessageDelivery-CLzWJImC.d.mts → MessageDelivery-rlLpakO8.d.mts} +43 -43
- package/dist/{PreparedInputAdmission-BQ9UCLFS.d.mts → PreparedInputAdmission-DjbWz2lW.d.mts} +4 -4
- package/dist/{RecordFormat-BNiLpe43.d.mts → RecordFormat-CjMl0uu8.d.mts} +2 -2
- package/dist/{Records-BTnGRdcf.d.mts → Records-BP67Yz6n.d.mts} +20 -8
- package/dist/{Records-CZRedCE7.mjs → Records-bbGVHprv.mjs} +14 -10
- package/dist/Records-bbGVHprv.mjs.map +1 -0
- package/dist/{RunContinuation-CL0wBsQE.d.mts → RunContinuation-CIJRN_vC.d.mts} +5 -4
- package/dist/{RunContinuation-BF95Ncv2.mjs → RunContinuation-DJ6kkbQG.mjs} +22 -58
- package/dist/RunContinuation-DJ6kkbQG.mjs.map +1 -0
- package/dist/{RunEvent-C5jVQZ6K.d.mts → RunEvent-Bh1p4zAO.d.mts} +2 -2
- package/dist/{RunJournal-oJLo-pjS.mjs → RunJournal-DQr2dUgg.mjs} +4 -7
- package/dist/RunJournal-DQr2dUgg.mjs.map +1 -0
- package/dist/{RunOptions-BANtohGk.d.mts → RunOptions-aBHR-Eqm.d.mts} +6 -3
- package/dist/{RunStorage-DlfvMfHM.mjs → RunStorage-DblN-cmM.mjs} +2 -2
- package/dist/{RunStorage-DlfvMfHM.mjs.map → RunStorage-DblN-cmM.mjs.map} +1 -1
- package/dist/{Schedule-CDgTz0vj.d.mts → Schedule-CVLtl5rP.d.mts} +2 -2
- package/dist/{Scheduling-BebkRVc2.mjs → Scheduling-B6l0lWdr.mjs} +3 -3
- package/dist/{Scheduling-BebkRVc2.mjs.map → Scheduling-B6l0lWdr.mjs.map} +1 -1
- package/dist/{Subagent-D68fe7mr.mjs → Subagent-Bm842K7J.mjs} +43 -17
- package/dist/Subagent-Bm842K7J.mjs.map +1 -0
- package/dist/{Subagent-5ulnV6An.d.mts → Subagent-CWWmA1UQ.d.mts} +17 -5
- package/dist/{Subscription-D2yn6Eo3.d.mts → Subscription-BRsNy0TA.d.mts} +4 -4
- package/dist/{SubscriptionInput-Cog4x7Rd.d.mts → SubscriptionInput-ClCaW_kX.d.mts} +3 -3
- package/dist/{ThreadHistory-KPPBJLJb.d.mts → ThreadHistory-ajQhqZDY.d.mts} +2 -2
- package/dist/{ThreadImport-BoTfjjrz.mjs → ThreadImport-CkPAxXU5.mjs} +7 -7
- package/dist/{ThreadImport-BoTfjjrz.mjs.map → ThreadImport-CkPAxXU5.mjs.map} +1 -1
- package/dist/{ThreadImport-em89zmM-.d.mts → ThreadImport-DtRNoHIr.d.mts} +5 -5
- package/dist/{ThreadStore-D2rqtt7k.mjs → ThreadStore-C_ofHc7h.mjs} +4 -4
- package/dist/{ThreadStore-D2rqtt7k.mjs.map → ThreadStore-C_ofHc7h.mjs.map} +1 -1
- package/dist/{ThreadTransfer-BFolkpEp.mjs → ThreadTransfer-C_TRuo_9.mjs} +4 -4
- package/dist/{ThreadTransfer-BFolkpEp.mjs.map → ThreadTransfer-C_TRuo_9.mjs.map} +1 -1
- package/dist/{WorkerAdmission-DrxDrLII.mjs → WorkerAdmission-9Ka3edXj.mjs} +5 -5
- package/dist/{WorkerAdmission-DrxDrLII.mjs.map → WorkerAdmission-9Ka3edXj.mjs.map} +1 -1
- package/dist/{agent-registration-BovXT8jc.mjs → agent-registration-7kp1dHT0.mjs} +4 -4
- package/dist/{agent-registration-BovXT8jc.mjs.map → agent-registration-7kp1dHT0.mjs.map} +1 -1
- package/dist/capabilities/Commands.d.mts +1 -1
- package/dist/capabilities/Redaction.d.mts +1 -1
- package/dist/capabilities/RunHooks.d.mts +1 -1
- package/dist/capabilities/Subagent.d.mts +1 -1
- package/dist/capabilities/Subagent.mjs +1 -1
- package/dist/capabilities/SubagentReservations.d.mts +4 -1
- package/dist/capabilities/SubagentReservations.mjs +4 -1
- package/dist/capabilities/SubagentReservations.mjs.map +1 -1
- package/dist/core/RunEvent.d.mts +1 -1
- package/dist/core/RunEvent.mjs +1 -1
- package/dist/core/RunEvent.mjs.map +1 -1
- package/dist/durable/ActivityStore.d.mts +1 -1
- package/dist/durable/ActivityStore.mjs +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/Admin.mjs +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 +3 -3
- package/dist/durable/CommittedActivity.mjs +3 -3
- package/dist/durable/Digest.d.mts +1 -1
- package/dist/durable/Digest.mjs +1 -1
- package/dist/durable/DurableAgentRuntime.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.mjs +2 -2
- package/dist/durable/EventSource.d.mts +1 -1
- package/dist/durable/EventSource.mjs +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/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 +42 -42
- 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.d.mts +1 -1
- package/dist/durable/RecordFormat.mjs +1 -1
- package/dist/durable/Records.d.mts +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 +3 -3
- package/dist/durable/RunContinuation.mjs +3 -3
- package/dist/durable/RunJournal.d.mts +1 -1
- package/dist/durable/RunJournal.mjs +1 -1
- package/dist/durable/RunStorage.d.mts +1 -1
- package/dist/durable/RunStorage.mjs +1 -1
- package/dist/durable/Schedule.d.mts +1 -1
- package/dist/durable/Schedule.mjs +1 -1
- package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
- package/dist/durable/ScheduleStoreConformance.mjs +1 -1
- package/dist/durable/ScheduleTransition.d.mts +1 -1
- package/dist/durable/ScheduleTransition.mjs +1 -1
- package/dist/durable/Scheduling.d.mts +3 -3
- 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/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedger.mjs +2 -2
- package/dist/durable/SubmissionLedgerConformance.d.mts +2 -2
- package/dist/durable/SubmissionLedgerConformance.mjs +5 -5
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/Subscription.mjs +1 -1
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionInput.mjs +2 -2
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
- package/dist/durable/SubscriptionTools.d.mts +4 -4
- package/dist/durable/SubscriptionTransition.d.mts +1 -1
- package/dist/durable/SubscriptionTransition.mjs +1 -1
- package/dist/durable/Subscriptions.d.mts +6 -6
- package/dist/durable/Subscriptions.mjs +2 -2
- 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.d.mts +1 -1
- 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 +4 -4
- package/dist/durable/ThreadProjection.d.mts +1 -1
- package/dist/durable/ThreadProjection.mjs +1 -1
- package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
- package/dist/durable/ThreadReader.d.mts +1 -1
- package/dist/durable/ThreadReader.mjs +1 -1
- package/dist/durable/ThreadStore.d.mts +2 -2
- package/dist/durable/ThreadStore.mjs +2 -2
- package/dist/durable/ThreadStoreConformance.d.mts +2 -2
- package/dist/durable/ThreadStoreConformance.mjs +4 -4
- package/dist/durable/ThreadTransfer.d.mts +2 -2
- package/dist/durable/ThreadTransfer.mjs +1 -1
- package/dist/durable/ThreadWork.d.mts +1 -1
- package/dist/durable/ThreadWork.mjs +5 -5
- package/dist/durable/ToolReconciler.mjs +1 -1
- package/dist/durable/WorkerAdmission.d.mts +2 -2
- 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/RunOptions.d.mts +1 -1
- package/dist/engine/RunOptions.mjs.map +1 -1
- package/dist/engine/ThreadHistory.d.mts +1 -1
- package/dist/index.d.mts +17 -17
- package/dist/index.mjs +12 -12
- package/dist/{run-context-reader-kpO9xhP2.d.mts → run-context-reader-DEU4REfD.d.mts} +3 -3
- package/package.json +1 -1
- package/src/capabilities/Subagent.ts +83 -28
- package/src/capabilities/SubagentReservations.ts +4 -1
- package/src/capabilities/internal/subagent-contract.ts +23 -3
- package/src/core/RunEvent.ts +1 -1
- package/src/durable/DurableAgentRuntime.ts +64 -14
- package/src/durable/Records.ts +21 -15
- package/src/durable/RunContinuation.ts +64 -125
- package/src/durable/RunJournal.ts +4 -12
- package/src/durable/internal/history-prompt.ts +10 -4
- package/src/durable/internal/initial-context.ts +1 -1
- package/src/durable/internal/prompt-history-cache.ts +8 -63
- package/src/engine/RunOptions.ts +4 -1
- package/src/engine/internal/agent-runtime.ts +422 -387
- package/dist/AgentRuntime-BAExRlgu.mjs.map +0 -1
- package/dist/DurableAgentRuntime-HH2muFrT.mjs.map +0 -1
- package/dist/Records-CZRedCE7.mjs.map +0 -1
- package/dist/RunContinuation-BF95Ncv2.mjs.map +0 -1
- package/dist/RunJournal-oJLo-pjS.mjs.map +0 -1
- package/dist/Subagent-D68fe7mr.mjs.map +0 -1
- package/dist/json-BlJVQZxN.mjs +0 -15
- package/dist/json-BlJVQZxN.mjs.map +0 -1
|
@@ -757,7 +757,7 @@ const acceptTurnCommit = <E, R>(
|
|
|
757
757
|
trace: TurnTrace,
|
|
758
758
|
durability: RunDurabilityHook<E, R> | undefined,
|
|
759
759
|
commit: RunTurnCommit,
|
|
760
|
-
): Effect.Effect<void, E | ModelProtocolError | AgentPolicyError, R> =>
|
|
760
|
+
): Effect.Effect<void | "capacity-refused", E | ModelProtocolError | AgentPolicyError, R> =>
|
|
761
761
|
Effect.suspend(() => {
|
|
762
762
|
if (trace.commitFailed) return Effect.void;
|
|
763
763
|
|
|
@@ -767,6 +767,15 @@ const acceptTurnCommit = <E, R>(
|
|
|
767
767
|
|
|
768
768
|
return Effect.onExitPrimitive(
|
|
769
769
|
Effect.flatMap(accepted, (receipt) => {
|
|
770
|
+
if (receipt === "capacity-refused")
|
|
771
|
+
return commit._tag === "Response" && trace.responseCommitted !== true
|
|
772
|
+
? Effect.succeed(receipt)
|
|
773
|
+
: Effect.fail(
|
|
774
|
+
ModelProtocolError.make({
|
|
775
|
+
message: "Durability refused capacity after response acceptance",
|
|
776
|
+
}),
|
|
777
|
+
);
|
|
778
|
+
|
|
770
779
|
if (receipt === "deferred")
|
|
771
780
|
return commit._tag === "Response" && commit.defer === true
|
|
772
781
|
? Effect.void
|
|
@@ -837,7 +846,7 @@ const settleTurn = <E, R>(
|
|
|
837
846
|
...(response === undefined ? {} : { response }),
|
|
838
847
|
results,
|
|
839
848
|
...(completion === undefined ? {} : { completion }),
|
|
840
|
-
}),
|
|
849
|
+
}).pipe(Effect.asVoid),
|
|
841
850
|
);
|
|
842
851
|
});
|
|
843
852
|
|
|
@@ -1020,22 +1029,20 @@ const inspectModelResponsePartCapacity = (
|
|
|
1020
1029
|
part: unknown,
|
|
1021
1030
|
limits: EffectiveRunBufferLimits,
|
|
1022
1031
|
knownSafePrototypes: ReadonlySet<object> = knownSafeModelResponsePrototypes,
|
|
1023
|
-
):
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
);
|
|
1032
|
+
): number | ModelProtocolError => {
|
|
1033
|
+
const bytes = boundedValueFootprint(
|
|
1034
|
+
part,
|
|
1035
|
+
limits.maxModelResponseBytes - usage.responsePartBytes,
|
|
1036
|
+
knownSafePrototypes,
|
|
1037
|
+
);
|
|
1030
1038
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
});
|
|
1039
|
+
return (
|
|
1040
|
+
bytes ??
|
|
1041
|
+
ModelProtocolError.make({
|
|
1042
|
+
message: `Model response exceeded the ${limits.maxModelResponseBytes}-byte retained response limit`,
|
|
1043
|
+
})
|
|
1044
|
+
);
|
|
1045
|
+
};
|
|
1039
1046
|
|
|
1040
1047
|
/**
|
|
1041
1048
|
* LanguageModel validates encoded parameters even with automatic resolution disabled.
|
|
@@ -1128,7 +1135,7 @@ const modelResponseParsersFor = (toolkit: Toolkit.Any) => {
|
|
|
1128
1135
|
return parsers;
|
|
1129
1136
|
};
|
|
1130
1137
|
|
|
1131
|
-
const captureModelResponsePartGeneral = Effect.
|
|
1138
|
+
const captureModelResponsePartGeneral = Effect.fnUntracedEager(function* <
|
|
1132
1139
|
Tools extends Record<string, Tool.Any>,
|
|
1133
1140
|
>(
|
|
1134
1141
|
part: unknown,
|
|
@@ -1155,19 +1162,23 @@ const captureModelResponsePartGeneral = Effect.fnUntraced(function* <
|
|
|
1155
1162
|
if (directInputBytes === undefined) {
|
|
1156
1163
|
const preflight = schemaClassToolPartPreflight(part, toolkit);
|
|
1157
1164
|
|
|
1158
|
-
|
|
1165
|
+
const capacity = inspectModelResponsePartCapacity(usage, preflight ?? part, limits);
|
|
1166
|
+
|
|
1167
|
+
if (typeof capacity !== "number") return yield* capacity;
|
|
1159
1168
|
}
|
|
1160
1169
|
const parsers = modelResponseParsersFor(toolkit);
|
|
1161
1170
|
|
|
1162
1171
|
const encoded = yield* parsers
|
|
1163
1172
|
.encode(part)
|
|
1164
1173
|
.pipe(
|
|
1165
|
-
Effect.
|
|
1174
|
+
Effect.mapErrorEager(() =>
|
|
1166
1175
|
ModelProtocolError.make({ message: "Model response part failed canonical encoding" }),
|
|
1167
1176
|
),
|
|
1168
1177
|
);
|
|
1169
1178
|
|
|
1170
|
-
const retainedBytes =
|
|
1179
|
+
const retainedBytes = inspectModelResponsePartCapacity(usage, encoded, limits);
|
|
1180
|
+
|
|
1181
|
+
if (typeof retainedBytes !== "number") return yield* retainedBytes;
|
|
1171
1182
|
|
|
1172
1183
|
if (typeof structuredCloneFunction !== "function") {
|
|
1173
1184
|
return yield* ModelProtocolError.make({
|
|
@@ -1175,17 +1186,15 @@ const captureModelResponsePartGeneral = Effect.fnUntraced(function* <
|
|
|
1175
1186
|
});
|
|
1176
1187
|
}
|
|
1177
1188
|
|
|
1178
|
-
|
|
1179
|
-
try: () => {
|
|
1180
|
-
const cloned: unknown = Reflect.apply(structuredCloneFunction, globalThis, [encoded]);
|
|
1189
|
+
let ownedEncoded: unknown;
|
|
1181
1190
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
}
|
|
1191
|
+
try {
|
|
1192
|
+
ownedEncoded = Reflect.apply(structuredCloneFunction, globalThis, [encoded]);
|
|
1193
|
+
} catch {
|
|
1194
|
+
return yield* ModelProtocolError.make({
|
|
1195
|
+
message: "Model response part could not be converted into engine-owned data",
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1189
1198
|
|
|
1190
1199
|
return { encodedPart: ownedEncoded, retainedBytes };
|
|
1191
1200
|
});
|
|
@@ -1208,49 +1217,51 @@ const decodePrimitiveModelResponsePart = Schema.decodeUnknownEffect(
|
|
|
1208
1217
|
),
|
|
1209
1218
|
);
|
|
1210
1219
|
|
|
1211
|
-
/**
|
|
1220
|
+
/**
|
|
1221
|
+
* Called once by the executing consumer. Pure capture stays in that step; application
|
|
1222
|
+
* codecs may suspend in the same fiber. Reject trailing content before either path.
|
|
1223
|
+
*/
|
|
1212
1224
|
const ownModelResponsePart = <Tools extends Record<string, Tool.Any>>(
|
|
1213
1225
|
part: unknown,
|
|
1214
1226
|
toolkit: Toolkit.Toolkit<Tools>,
|
|
1215
1227
|
usage: ModelResponseBufferUsage,
|
|
1216
1228
|
limits: EffectiveRunBufferLimits,
|
|
1217
1229
|
finished: boolean,
|
|
1218
|
-
): Effect.Effect<OwnedModelResponsePart, ModelProtocolError> =>
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
}
|
|
1230
|
+
): Effect.Effect<OwnedModelResponsePart, ModelProtocolError> => {
|
|
1231
|
+
if (finished) {
|
|
1232
|
+
return Effect.fail(
|
|
1233
|
+
ModelProtocolError.make({
|
|
1234
|
+
message: "Model response emitted content after its finish part",
|
|
1235
|
+
}),
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1227
1238
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1239
|
+
const primitive =
|
|
1240
|
+
usage.responsePartCount < limits.maxModelResponseParts
|
|
1241
|
+
? capturePrimitiveTextPart(part, limits.maxModelResponseBytes - usage.responsePartBytes)
|
|
1242
|
+
: undefined;
|
|
1243
|
+
|
|
1244
|
+
// Complete primitive snapshots need validation, but no defaults or transformation.
|
|
1245
|
+
// Schema.toType retains every constraint from the native Response part schemas.
|
|
1246
|
+
if (primitive !== undefined) {
|
|
1247
|
+
return Effect.mapBothEager(decodePrimitiveModelResponsePart(primitive.part), {
|
|
1248
|
+
onFailure: () =>
|
|
1249
|
+
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1250
|
+
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes: primitive.retainedBytes }),
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1232
1253
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1254
|
+
// Pending application codecs keep normal Effect execution in this same consumer fiber.
|
|
1255
|
+
return Effect.flatMapEager(
|
|
1256
|
+
captureModelResponsePartGeneral(part, toolkit, usage, limits),
|
|
1257
|
+
(captured) =>
|
|
1258
|
+
Effect.mapBothEager(modelResponseParsersFor(toolkit).decode(captured.encodedPart), {
|
|
1237
1259
|
onFailure: () =>
|
|
1238
1260
|
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1239
|
-
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes:
|
|
1240
|
-
})
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
// Pending application codecs keep normal Effect execution in this same consumer fiber.
|
|
1244
|
-
return Effect.flatMapEager(
|
|
1245
|
-
captureModelResponsePartGeneral(part, toolkit, usage, limits),
|
|
1246
|
-
(captured) =>
|
|
1247
|
-
Effect.mapBothEager(modelResponseParsersFor(toolkit).decode(captured.encodedPart), {
|
|
1248
|
-
onFailure: () =>
|
|
1249
|
-
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1250
|
-
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes: captured.retainedBytes }),
|
|
1251
|
-
}),
|
|
1252
|
-
);
|
|
1253
|
-
});
|
|
1261
|
+
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes: captured.retainedBytes }),
|
|
1262
|
+
}),
|
|
1263
|
+
);
|
|
1264
|
+
};
|
|
1254
1265
|
|
|
1255
1266
|
type PartLifecycle = "open" | "closed";
|
|
1256
1267
|
|
|
@@ -1307,10 +1318,12 @@ const endPart = (
|
|
|
1307
1318
|
parts: Map<string, PartLifecycle>,
|
|
1308
1319
|
id: string,
|
|
1309
1320
|
description: string,
|
|
1310
|
-
): Effect.Effect<void, ModelProtocolError> =>
|
|
1311
|
-
continuePart(parts, id, `${description} end`)
|
|
1312
|
-
|
|
1313
|
-
|
|
1321
|
+
): Effect.Effect<void, ModelProtocolError> => {
|
|
1322
|
+
if (parts.get(id) !== "open") return continuePart(parts, id, `${description} end`);
|
|
1323
|
+
parts.set(id, "closed");
|
|
1324
|
+
|
|
1325
|
+
return Effect.void;
|
|
1326
|
+
};
|
|
1314
1327
|
|
|
1315
1328
|
const firstOpenPart = (trace: TurnTrace): string | undefined => {
|
|
1316
1329
|
for (const [id, state] of trace.textParts) {
|
|
@@ -1663,7 +1676,7 @@ const makeToolFailedEvent = Effect.fnUntraced(function* (
|
|
|
1663
1676
|
/**
|
|
1664
1677
|
* Settle a rejected Tool batch without starting any handler: every open
|
|
1665
1678
|
* application call receives the encoded failure as its model-visible result.
|
|
1666
|
-
*
|
|
1679
|
+
* Policy and persistence budget rejections are exempt from repeated-failure folding;
|
|
1667
1680
|
* correctable model declarations count as ordinary failed calls. Emit one
|
|
1668
1681
|
* `ToolCallFailed` per call with no `ToolCallStarted` (the
|
|
1669
1682
|
* approval-denied precedent). The caller hands the trace to
|
|
@@ -1676,10 +1689,10 @@ const settleRejectedBatch = Effect.fnUntraced(function* (
|
|
|
1676
1689
|
context: RunContext,
|
|
1677
1690
|
turnId: TurnId,
|
|
1678
1691
|
trace: TurnTrace,
|
|
1679
|
-
error: AgentPolicyError | ModelProtocolError,
|
|
1692
|
+
error: AgentPolicyError | AgentPersistenceCapacityError | ModelProtocolError,
|
|
1680
1693
|
alreadySettled?: ReadonlySet<string>,
|
|
1681
1694
|
): Effect.fn.Return<ReadonlyArray<RunEvent>, ModelProtocolError> {
|
|
1682
|
-
const budgetRejected = error._tag
|
|
1695
|
+
const budgetRejected = error._tag !== "ModelProtocolError" ? true : undefined;
|
|
1683
1696
|
|
|
1684
1697
|
const encodedResult = {
|
|
1685
1698
|
_tag: error._tag,
|
|
@@ -3349,9 +3362,13 @@ const decodeUsageTotal = Schema.decodeEffect(Schema.Natural);
|
|
|
3349
3362
|
const decodeProviderUsage = Schema.decodeEffect(ProviderUsage);
|
|
3350
3363
|
|
|
3351
3364
|
const decodeProviderUsageTotal = (value: number): Effect.Effect<number, ModelProtocolError> =>
|
|
3352
|
-
decodeUsageTotal(value).pipe(Effect.
|
|
3365
|
+
decodeUsageTotal(value).pipe(Effect.mapErrorEager(() => invalidProviderUsage()));
|
|
3353
3366
|
|
|
3354
|
-
const consumeUsage =
|
|
3367
|
+
const consumeUsage = Effect.fnUntracedEager(function* <
|
|
3368
|
+
AgentValue extends Agent.Any,
|
|
3369
|
+
HookError,
|
|
3370
|
+
HookRequirements,
|
|
3371
|
+
>(
|
|
3355
3372
|
agent: AgentValue,
|
|
3356
3373
|
context: RunContext,
|
|
3357
3374
|
usage: Response.Usage | undefined,
|
|
@@ -3362,337 +3379,332 @@ const consumeUsage = <AgentValue extends Agent.Any, HookError, HookRequirements>
|
|
|
3362
3379
|
RunCostEstimateRequest,
|
|
3363
3380
|
"response" | "finishMetadata" | "purpose" | "webSearchCalls"
|
|
3364
3381
|
> = {},
|
|
3365
|
-
): Effect.
|
|
3382
|
+
): Effect.fn.Return<
|
|
3366
3383
|
ConsumedUsage,
|
|
3367
3384
|
AgentPolicyError | ModelProtocolError | HookError,
|
|
3368
3385
|
HookRequirements | Model.ProviderName | Model.ModelName
|
|
3369
|
-
>
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
}
|
|
3386
|
+
> {
|
|
3387
|
+
if (usage === undefined) {
|
|
3388
|
+
return yield* AgentPolicyError.make({
|
|
3389
|
+
limit: "usage",
|
|
3390
|
+
message: "A completed model response did not report usage",
|
|
3391
|
+
});
|
|
3392
|
+
}
|
|
3377
3393
|
|
|
3378
|
-
|
|
3394
|
+
const webSearchCalls = yield* decodeProviderUsageTotal(response.webSearchCalls ?? 0);
|
|
3379
3395
|
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3396
|
+
const providerUsage = yield* decodeProviderUsage(usage).pipe(
|
|
3397
|
+
Effect.mapErrorEager(() => invalidProviderUsage()),
|
|
3398
|
+
);
|
|
3383
3399
|
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
providerUsage.inputTokens.cacheWrite !== undefined;
|
|
3400
|
+
const reportedUncached = providerUsage.inputTokens.uncached ?? 0;
|
|
3401
|
+
const cacheRead = providerUsage.inputTokens.cacheRead ?? 0;
|
|
3402
|
+
const cacheWrite = providerUsage.inputTokens.cacheWrite ?? 0;
|
|
3403
|
+
const reportedText = providerUsage.outputTokens.text ?? 0;
|
|
3404
|
+
const reasoning = providerUsage.outputTokens.reasoning ?? 0;
|
|
3405
|
+
// Gross token accounting never discounts cache activity. An omitted
|
|
3406
|
+
// aggregate is derived from present components; an omitted component may
|
|
3407
|
+
// receive the aggregate remainder. Explicitly contradictory fields fail.
|
|
3408
|
+
// Some providers include cache writes in `uncached` while also reporting
|
|
3409
|
+
// them separately. Separate that overlap before constructing the canonical,
|
|
3410
|
+
// additive input components, but retain the raw provider usage for pricing.
|
|
3411
|
+
const reportedInputTotal = providerUsage.inputTokens.total;
|
|
3412
|
+
const reportedInputWithoutWrite = reportedUncached + cacheRead;
|
|
3413
|
+
|
|
3414
|
+
const cacheWriteOverlapsUncached =
|
|
3415
|
+
reportedInputTotal !== undefined &&
|
|
3416
|
+
providerUsage.inputTokens.uncached !== undefined &&
|
|
3417
|
+
providerUsage.inputTokens.cacheWrite !== undefined &&
|
|
3418
|
+
cacheWrite <= reportedUncached &&
|
|
3419
|
+
reportedInputWithoutWrite <= reportedInputTotal &&
|
|
3420
|
+
cacheWrite > reportedInputTotal - reportedInputWithoutWrite;
|
|
3421
|
+
|
|
3422
|
+
const disjointReportedUncached = reportedUncached - (cacheWriteOverlapsUncached ? cacheWrite : 0);
|
|
3423
|
+
|
|
3424
|
+
const reportedInputComponents = disjointReportedUncached + cacheRead + cacheWrite;
|
|
3425
|
+
|
|
3426
|
+
const allInputComponentsReported =
|
|
3427
|
+
providerUsage.inputTokens.uncached !== undefined &&
|
|
3428
|
+
providerUsage.inputTokens.cacheRead !== undefined &&
|
|
3429
|
+
providerUsage.inputTokens.cacheWrite !== undefined;
|
|
3415
3430
|
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3431
|
+
if (
|
|
3432
|
+
reportedInputTotal !== undefined &&
|
|
3433
|
+
(reportedInputTotal < reportedInputComponents ||
|
|
3434
|
+
(allInputComponentsReported && reportedInputTotal !== reportedInputComponents))
|
|
3435
|
+
) {
|
|
3436
|
+
return yield* invalidProviderUsage();
|
|
3437
|
+
}
|
|
3438
|
+
const inputTokens = reportedInputTotal ?? reportedInputComponents;
|
|
3439
|
+
const reportedOutputComponents = reportedText + reasoning;
|
|
3440
|
+
const reportedOutputTotal = providerUsage.outputTokens.total;
|
|
3426
3441
|
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3442
|
+
const allOutputComponentsReported =
|
|
3443
|
+
providerUsage.outputTokens.text !== undefined &&
|
|
3444
|
+
providerUsage.outputTokens.reasoning !== undefined;
|
|
3430
3445
|
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
const model = yield* Model.ModelName;
|
|
3492
|
-
|
|
3493
|
-
const estimate =
|
|
3494
|
-
options.estimateCostMicrousd === undefined
|
|
3495
|
-
? 0
|
|
3496
|
-
: yield* options.estimateCostMicrousd(usage, {
|
|
3497
|
-
provider,
|
|
3498
|
-
model,
|
|
3499
|
-
usage,
|
|
3500
|
-
...response,
|
|
3501
|
-
webSearchCalls,
|
|
3502
|
-
});
|
|
3446
|
+
if (
|
|
3447
|
+
reportedOutputTotal !== undefined &&
|
|
3448
|
+
(reportedOutputTotal < reportedOutputComponents ||
|
|
3449
|
+
(allOutputComponentsReported && reportedOutputTotal !== reportedOutputComponents))
|
|
3450
|
+
) {
|
|
3451
|
+
return yield* invalidProviderUsage();
|
|
3452
|
+
}
|
|
3453
|
+
const outputTokens = reportedOutputTotal ?? reportedOutputComponents;
|
|
3454
|
+
// When a provider reports only aggregates, classify the unexplained input
|
|
3455
|
+
// conservatively as uncached and non-reasoning output as text when those
|
|
3456
|
+
// fields are absent. If either was explicit, assign the remainder to the
|
|
3457
|
+
// first genuinely omitted component so no provider-supplied value changes.
|
|
3458
|
+
const inputRemainder = inputTokens - reportedInputComponents;
|
|
3459
|
+
const outputRemainder = outputTokens - reportedOutputComponents;
|
|
3460
|
+
|
|
3461
|
+
const uncached =
|
|
3462
|
+
disjointReportedUncached +
|
|
3463
|
+
(providerUsage.inputTokens.uncached === undefined ? inputRemainder : 0);
|
|
3464
|
+
|
|
3465
|
+
const normalizedCacheRead =
|
|
3466
|
+
cacheRead +
|
|
3467
|
+
(providerUsage.inputTokens.uncached !== undefined &&
|
|
3468
|
+
providerUsage.inputTokens.cacheRead === undefined
|
|
3469
|
+
? inputRemainder
|
|
3470
|
+
: 0);
|
|
3471
|
+
|
|
3472
|
+
const normalizedCacheWrite =
|
|
3473
|
+
cacheWrite +
|
|
3474
|
+
(providerUsage.inputTokens.uncached !== undefined &&
|
|
3475
|
+
providerUsage.inputTokens.cacheRead !== undefined &&
|
|
3476
|
+
providerUsage.inputTokens.cacheWrite === undefined
|
|
3477
|
+
? inputRemainder
|
|
3478
|
+
: 0);
|
|
3479
|
+
|
|
3480
|
+
const text = reportedText + (providerUsage.outputTokens.text === undefined ? outputRemainder : 0);
|
|
3481
|
+
|
|
3482
|
+
const normalizedReasoning =
|
|
3483
|
+
reasoning +
|
|
3484
|
+
(providerUsage.outputTokens.text !== undefined &&
|
|
3485
|
+
providerUsage.outputTokens.reasoning === undefined
|
|
3486
|
+
? outputRemainder
|
|
3487
|
+
: 0);
|
|
3488
|
+
|
|
3489
|
+
// Validate the complete additive components before pricing or staging usage.
|
|
3490
|
+
const inputUsage = yield* InputTokenUsage.makeEffect({
|
|
3491
|
+
total: inputTokens,
|
|
3492
|
+
uncached,
|
|
3493
|
+
cacheRead: normalizedCacheRead,
|
|
3494
|
+
cacheWrite: normalizedCacheWrite,
|
|
3495
|
+
}).pipe(Effect.mapErrorEager(() => invalidProviderUsage()));
|
|
3496
|
+
|
|
3497
|
+
const outputUsage = yield* OutputTokenUsage.makeEffect({
|
|
3498
|
+
total: outputTokens,
|
|
3499
|
+
text,
|
|
3500
|
+
reasoning: normalizedReasoning,
|
|
3501
|
+
}).pipe(Effect.mapErrorEager(() => invalidProviderUsage()));
|
|
3502
|
+
|
|
3503
|
+
const totalTokens = yield* decodeProviderUsageTotal(inputTokens + outputTokens);
|
|
3504
|
+
const provider = yield* Model.ProviderName;
|
|
3505
|
+
const model = yield* Model.ModelName;
|
|
3503
3506
|
|
|
3504
|
-
|
|
3507
|
+
const estimate =
|
|
3508
|
+
options.estimateCostMicrousd === undefined
|
|
3509
|
+
? 0
|
|
3510
|
+
: yield* options.estimateCostMicrousd(usage, {
|
|
3511
|
+
provider,
|
|
3512
|
+
model,
|
|
3513
|
+
usage,
|
|
3514
|
+
...response,
|
|
3515
|
+
webSearchCalls,
|
|
3516
|
+
});
|
|
3505
3517
|
|
|
3506
|
-
|
|
3507
|
-
return yield* AgentPolicyError.make({
|
|
3508
|
-
limit: "cost",
|
|
3509
|
-
message: "Model cost estimation must produce a non-negative integer number of microdollars",
|
|
3510
|
-
});
|
|
3511
|
-
}
|
|
3512
|
-
const serviceTier = typeof estimate === "number" ? undefined : estimate.serviceTier;
|
|
3513
|
-
const pricingVersion = typeof estimate === "number" ? undefined : estimate.pricingVersion;
|
|
3518
|
+
const costMicrousd = typeof estimate === "number" ? estimate : estimate.costMicrousd;
|
|
3514
3519
|
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3520
|
+
if (!Number.isSafeInteger(costMicrousd) || costMicrousd < 0) {
|
|
3521
|
+
return yield* AgentPolicyError.make({
|
|
3522
|
+
limit: "cost",
|
|
3523
|
+
message: "Model cost estimation must produce a non-negative integer number of microdollars",
|
|
3524
|
+
});
|
|
3525
|
+
}
|
|
3526
|
+
const serviceTier = typeof estimate === "number" ? undefined : estimate.serviceTier;
|
|
3527
|
+
const pricingVersion = typeof estimate === "number" ? undefined : estimate.pricingVersion;
|
|
3521
3528
|
|
|
3522
|
-
|
|
3523
|
-
|
|
3529
|
+
const pricingStatus =
|
|
3530
|
+
options.estimateCostMicrousd === undefined
|
|
3531
|
+
? "unknown"
|
|
3532
|
+
: typeof estimate === "number"
|
|
3533
|
+
? "estimated"
|
|
3534
|
+
: (estimate.pricingStatus ?? "estimated");
|
|
3524
3535
|
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
limit: "cost",
|
|
3528
|
-
message: "Model cost estimation returned an invalid service tier or pricing version",
|
|
3529
|
-
});
|
|
3530
|
-
}
|
|
3536
|
+
const validPricingIdentity = (value: string | undefined): boolean =>
|
|
3537
|
+
value === undefined || (value.length > 0 && value.length <= 256);
|
|
3531
3538
|
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
...(response.response === undefined
|
|
3537
|
-
? {}
|
|
3538
|
-
: { response: ModelResponseIdentity.make(response.response) }),
|
|
3539
|
-
purpose: response.purpose ?? "turn",
|
|
3540
|
-
usageStatus:
|
|
3541
|
-
Object.values(providerUsage.inputTokens).every((value) => value === undefined) &&
|
|
3542
|
-
Object.values(providerUsage.outputTokens).every((value) => value === undefined)
|
|
3543
|
-
? "unknown"
|
|
3544
|
-
: [
|
|
3545
|
-
providerUsage.inputTokens.uncached,
|
|
3546
|
-
providerUsage.inputTokens.cacheRead,
|
|
3547
|
-
providerUsage.inputTokens.cacheWrite,
|
|
3548
|
-
providerUsage.outputTokens.text,
|
|
3549
|
-
providerUsage.outputTokens.reasoning,
|
|
3550
|
-
].every((value) => value !== undefined)
|
|
3551
|
-
? "complete"
|
|
3552
|
-
: "partial",
|
|
3553
|
-
pricingStatus,
|
|
3554
|
-
...(serviceTier === undefined ? {} : { serviceTier }),
|
|
3555
|
-
...(pricingVersion === undefined ? {} : { pricingVersion }),
|
|
3556
|
-
inputTokens: inputUsage,
|
|
3557
|
-
outputTokens: outputUsage,
|
|
3558
|
-
costMicrousd,
|
|
3539
|
+
if (!validPricingIdentity(serviceTier) || !validPricingIdentity(pricingVersion)) {
|
|
3540
|
+
return yield* AgentPolicyError.make({
|
|
3541
|
+
limit: "cost",
|
|
3542
|
+
message: "Model cost estimation returned an invalid service tier or pricing version",
|
|
3559
3543
|
});
|
|
3544
|
+
}
|
|
3560
3545
|
|
|
3561
|
-
|
|
3546
|
+
const modelUsage = ModelCallUsage.make({
|
|
3547
|
+
webSearchCalls,
|
|
3548
|
+
provider,
|
|
3549
|
+
model,
|
|
3550
|
+
...(response.response === undefined
|
|
3551
|
+
? {}
|
|
3552
|
+
: { response: ModelResponseIdentity.make(response.response) }),
|
|
3553
|
+
purpose: response.purpose ?? "turn",
|
|
3554
|
+
usageStatus:
|
|
3555
|
+
Object.values(providerUsage.inputTokens).every((value) => value === undefined) &&
|
|
3556
|
+
Object.values(providerUsage.outputTokens).every((value) => value === undefined)
|
|
3557
|
+
? "unknown"
|
|
3558
|
+
: [
|
|
3559
|
+
providerUsage.inputTokens.uncached,
|
|
3560
|
+
providerUsage.inputTokens.cacheRead,
|
|
3561
|
+
providerUsage.inputTokens.cacheWrite,
|
|
3562
|
+
providerUsage.outputTokens.text,
|
|
3563
|
+
providerUsage.outputTokens.reasoning,
|
|
3564
|
+
].every((value) => value !== undefined)
|
|
3565
|
+
? "complete"
|
|
3566
|
+
: "partial",
|
|
3567
|
+
pricingStatus,
|
|
3568
|
+
...(serviceTier === undefined ? {} : { serviceTier }),
|
|
3569
|
+
...(pricingVersion === undefined ? {} : { pricingVersion }),
|
|
3570
|
+
inputTokens: inputUsage,
|
|
3571
|
+
outputTokens: outputUsage,
|
|
3572
|
+
costMicrousd,
|
|
3573
|
+
});
|
|
3562
3574
|
|
|
3563
|
-
|
|
3564
|
-
context.webSearchCalls === undefined
|
|
3565
|
-
? undefined
|
|
3566
|
-
: yield* decodeProviderUsageTotal(context.webSearchCalls + webSearchCalls);
|
|
3575
|
+
const modelCalls = yield* decodeProviderUsageTotal(context.modelCalls + 1);
|
|
3567
3576
|
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3577
|
+
const cumulativeWebSearchCalls =
|
|
3578
|
+
context.webSearchCalls === undefined
|
|
3579
|
+
? undefined
|
|
3580
|
+
: yield* decodeProviderUsageTotal(context.webSearchCalls + webSearchCalls);
|
|
3571
3581
|
|
|
3572
|
-
|
|
3573
|
-
context.outputTokens + outputTokens,
|
|
3574
|
-
);
|
|
3582
|
+
const cumulativeInputTokens = yield* decodeProviderUsageTotal(context.inputTokens + inputTokens);
|
|
3575
3583
|
|
|
3576
|
-
|
|
3584
|
+
const cumulativeOutputTokens = yield* decodeProviderUsageTotal(
|
|
3585
|
+
context.outputTokens + outputTokens,
|
|
3586
|
+
);
|
|
3577
3587
|
|
|
3578
|
-
|
|
3579
|
-
return yield* AgentPolicyError.make({
|
|
3580
|
-
limit: "cost",
|
|
3581
|
-
message: "Cumulative model cost exceeds safe-integer accounting capacity",
|
|
3582
|
-
});
|
|
3583
|
-
}
|
|
3584
|
-
const firstObserved = context.modelCalls === 0 && context.unobservedModelCalls === 0;
|
|
3588
|
+
const cumulativeCostMicrousd = context.costMicrousd + costMicrousd;
|
|
3585
3589
|
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
+
if (!Number.isSafeInteger(cumulativeCostMicrousd)) {
|
|
3591
|
+
return yield* AgentPolicyError.make({
|
|
3592
|
+
limit: "cost",
|
|
3593
|
+
message: "Cumulative model cost exceeds safe-integer accounting capacity",
|
|
3594
|
+
});
|
|
3595
|
+
}
|
|
3596
|
+
const firstObserved = context.modelCalls === 0 && context.unobservedModelCalls === 0;
|
|
3590
3597
|
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
);
|
|
3596
|
-
context.modelCalls = modelCalls;
|
|
3597
|
-
context.webSearchCalls = cumulativeWebSearchCalls;
|
|
3598
|
-
context.inputTokens = cumulativeInputTokens;
|
|
3599
|
-
context.outputTokens = cumulativeOutputTokens;
|
|
3600
|
-
context.lastInputTokens = inputTokens;
|
|
3601
|
-
context.lastOutputTokens = outputTokens;
|
|
3602
|
-
context.costMicrousd = cumulativeCostMicrousd;
|
|
3603
|
-
context.lastCostMicrousd = costMicrousd;
|
|
3604
|
-
|
|
3605
|
-
if (options.durability !== undefined) {
|
|
3606
|
-
// Stage before enforcing hard rails: a response that spends past the
|
|
3607
|
-
// cost/token budget still becomes auditable in its canonical Turn and
|
|
3608
|
-
// terminal settlement.
|
|
3609
|
-
yield* options.durability.noteTurnUsage({ turn, usage: modelUsage });
|
|
3610
|
-
}
|
|
3598
|
+
const combineStatus = (
|
|
3599
|
+
prior: typeof UsageCompleteness.Type,
|
|
3600
|
+
next: typeof UsageCompleteness.Type,
|
|
3601
|
+
) => (firstObserved && prior === "complete" ? next : prior === next ? prior : "partial");
|
|
3611
3602
|
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3603
|
+
context.usageStatus = combineStatus(context.usageStatus, modelUsage.usageStatus ?? "unknown");
|
|
3604
|
+
context.pricingStatus = combineStatus(
|
|
3605
|
+
context.pricingStatus,
|
|
3606
|
+
pricingStatus === "estimated" ? "complete" : "unknown",
|
|
3607
|
+
);
|
|
3608
|
+
context.modelCalls = modelCalls;
|
|
3609
|
+
context.webSearchCalls = cumulativeWebSearchCalls;
|
|
3610
|
+
context.inputTokens = cumulativeInputTokens;
|
|
3611
|
+
context.outputTokens = cumulativeOutputTokens;
|
|
3612
|
+
context.lastInputTokens = inputTokens;
|
|
3613
|
+
context.lastOutputTokens = outputTokens;
|
|
3614
|
+
context.costMicrousd = cumulativeCostMicrousd;
|
|
3615
|
+
context.lastCostMicrousd = costMicrousd;
|
|
3616
|
+
|
|
3617
|
+
if (options.durability !== undefined) {
|
|
3618
|
+
// Stage before enforcing hard rails: a response that spends past the
|
|
3619
|
+
// cost/token budget still becomes auditable in its canonical Turn and
|
|
3620
|
+
// terminal settlement.
|
|
3621
|
+
yield* options.durability.noteTurnUsage({ turn, usage: modelUsage });
|
|
3622
|
+
}
|
|
3616
3623
|
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3624
|
+
const policy = agent.definition.policy;
|
|
3625
|
+
const consumedTokens = context.inputTokens + context.outputTokens;
|
|
3626
|
+
const tokenBudget = policy.tokenBudget;
|
|
3627
|
+
let breach: AgentPolicyError | undefined;
|
|
3628
|
+
|
|
3629
|
+
if (
|
|
3630
|
+
!context.finalizing &&
|
|
3631
|
+
!context.tokenExhausted &&
|
|
3632
|
+
tokenBudget !== undefined &&
|
|
3633
|
+
consumedTokens > tokenBudget
|
|
3634
|
+
) {
|
|
3635
|
+
breach = AgentPolicyError.make({
|
|
3636
|
+
limit: "tokens",
|
|
3637
|
+
message: `Agent exceeded its ${tokenBudget} token budget`,
|
|
3638
|
+
});
|
|
3639
|
+
// Fail mode rejects before any declared application Handler starts.
|
|
3640
|
+
if (policy.onExhaustion === "fail") {
|
|
3641
|
+
return yield* breach;
|
|
3642
|
+
}
|
|
3643
|
+
// One-shot (RUN-025): the flag joins the final-answer derivation so the
|
|
3644
|
+
// grace Turn's own usage accumulates and charges without re-breaching.
|
|
3645
|
+
context.tokenExhausted = true;
|
|
3646
|
+
context.exhaustedDimension ??= "tokens";
|
|
3647
|
+
}
|
|
3648
|
+
// Cost is an unconditional hard rail (runtime spec §3): it is enforced on
|
|
3649
|
+
// every response, including one that just soft-breached the token budget —
|
|
3650
|
+
// a simultaneous breach fails typed instead of soft-landing on overspend.
|
|
3651
|
+
const costBudget = policy.costBudgetMicrousd;
|
|
3652
|
+
|
|
3653
|
+
if (costBudget !== undefined) {
|
|
3654
|
+
if (options.estimateCostMicrousd === undefined) {
|
|
3655
|
+
return yield* AgentPolicyError.make({
|
|
3656
|
+
limit: "cost",
|
|
3657
|
+
message: "Agent cost budget requires a model cost estimator",
|
|
3626
3658
|
});
|
|
3627
|
-
// Fail mode rejects before any declared application Handler starts.
|
|
3628
|
-
if (policy.onExhaustion === "fail") {
|
|
3629
|
-
return yield* breach;
|
|
3630
|
-
}
|
|
3631
|
-
// One-shot (RUN-025): the flag joins the final-answer derivation so the
|
|
3632
|
-
// grace Turn's own usage accumulates and charges without re-breaching.
|
|
3633
|
-
context.tokenExhausted = true;
|
|
3634
|
-
context.exhaustedDimension ??= "tokens";
|
|
3635
3659
|
}
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
if (costBudget !== undefined) {
|
|
3642
|
-
if (options.estimateCostMicrousd === undefined) {
|
|
3643
|
-
return yield* AgentPolicyError.make({
|
|
3644
|
-
limit: "cost",
|
|
3645
|
-
message: "Agent cost budget requires a model cost estimator",
|
|
3646
|
-
});
|
|
3647
|
-
}
|
|
3648
|
-
if (pricingStatus === "unknown") {
|
|
3649
|
-
return yield* AgentPolicyError.make({
|
|
3650
|
-
limit: "cost",
|
|
3651
|
-
message: "Agent cost budget requires a known model cost estimate",
|
|
3652
|
-
});
|
|
3653
|
-
}
|
|
3654
|
-
if (context.costMicrousd > costBudget) {
|
|
3655
|
-
// Cost is spend, not context: it never earns a grace Turn.
|
|
3656
|
-
return yield* AgentPolicyError.make({
|
|
3657
|
-
limit: "cost",
|
|
3658
|
-
message: `Agent exceeded its ${costBudget} microdollar cost budget`,
|
|
3659
|
-
});
|
|
3660
|
-
}
|
|
3660
|
+
if (pricingStatus === "unknown") {
|
|
3661
|
+
return yield* AgentPolicyError.make({
|
|
3662
|
+
limit: "cost",
|
|
3663
|
+
message: "Agent cost budget requires a known model cost estimate",
|
|
3664
|
+
});
|
|
3661
3665
|
}
|
|
3662
|
-
if (
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
toolCalls: toolCallCount,
|
|
3669
|
-
costMicrousd,
|
|
3670
|
-
usage,
|
|
3671
|
-
modelUsage,
|
|
3672
|
-
};
|
|
3673
|
-
|
|
3674
|
-
yield* options.budget.consume(delta);
|
|
3666
|
+
if (context.costMicrousd > costBudget) {
|
|
3667
|
+
// Cost is spend, not context: it never earns a grace Turn.
|
|
3668
|
+
return yield* AgentPolicyError.make({
|
|
3669
|
+
limit: "cost",
|
|
3670
|
+
message: `Agent exceeded its ${costBudget} microdollar cost budget`,
|
|
3671
|
+
});
|
|
3675
3672
|
}
|
|
3676
|
-
|
|
3673
|
+
}
|
|
3674
|
+
if (options.budget !== undefined) {
|
|
3675
|
+
const delta: RunUsageDelta = {
|
|
3676
|
+
modelCalls: 1,
|
|
3677
|
+
inputTokens,
|
|
3678
|
+
outputTokens,
|
|
3679
|
+
totalTokens,
|
|
3680
|
+
toolCalls: toolCallCount,
|
|
3681
|
+
costMicrousd,
|
|
3682
|
+
usage,
|
|
3683
|
+
modelUsage,
|
|
3684
|
+
};
|
|
3677
3685
|
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
nearingLimit(consumedTokens, tokenBudget)
|
|
3682
|
-
) {
|
|
3683
|
-
context.warnedLimits.add("tokens");
|
|
3684
|
-
warnings?.push(
|
|
3685
|
-
BudgetWarning.make({
|
|
3686
|
-
...(yield* eventBase(context)),
|
|
3687
|
-
limit: "tokens",
|
|
3688
|
-
consumed: consumedTokens,
|
|
3689
|
-
limitValue: tokenBudget,
|
|
3690
|
-
}),
|
|
3691
|
-
);
|
|
3692
|
-
}
|
|
3686
|
+
yield* options.budget.consume(delta);
|
|
3687
|
+
}
|
|
3688
|
+
const warnings: Array<RunEvent> | undefined = context.publish === undefined ? undefined : [];
|
|
3693
3689
|
|
|
3694
|
-
|
|
3695
|
-
|
|
3690
|
+
if (
|
|
3691
|
+
tokenBudget !== undefined &&
|
|
3692
|
+
!context.warnedLimits.has("tokens") &&
|
|
3693
|
+
nearingLimit(consumedTokens, tokenBudget)
|
|
3694
|
+
) {
|
|
3695
|
+
context.warnedLimits.add("tokens");
|
|
3696
|
+
warnings?.push(
|
|
3697
|
+
BudgetWarning.make({
|
|
3698
|
+
...(yield* eventBase(context)),
|
|
3699
|
+
limit: "tokens",
|
|
3700
|
+
consumed: consumedTokens,
|
|
3701
|
+
limitValue: tokenBudget,
|
|
3702
|
+
}),
|
|
3703
|
+
);
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
return { breach, warnings: warnings ?? noEvents, modelUsage };
|
|
3707
|
+
});
|
|
3696
3708
|
|
|
3697
3709
|
// `Effect.fnUntraced`: this helper runs for every streamed Response Part, so a
|
|
3698
3710
|
// named span here would emit one span per TextDelta/ReasoningDelta. Spans stay
|
|
@@ -5765,13 +5777,6 @@ const makeTurn = <
|
|
|
5765
5777
|
|
|
5766
5778
|
const started = Effect.gen(function* () {
|
|
5767
5779
|
yield* Metric.update(modelCounter, 1);
|
|
5768
|
-
yield* Effect.logDebug("agent model call started").pipe(
|
|
5769
|
-
Effect.annotateLogs({
|
|
5770
|
-
agentId: context.agentId,
|
|
5771
|
-
runId: context.runId,
|
|
5772
|
-
turnId,
|
|
5773
|
-
}),
|
|
5774
|
-
);
|
|
5775
5780
|
|
|
5776
5781
|
if (context.publish === undefined) return;
|
|
5777
5782
|
|
|
@@ -5791,7 +5796,7 @@ const makeTurn = <
|
|
|
5791
5796
|
]);
|
|
5792
5797
|
|
|
5793
5798
|
return;
|
|
5794
|
-
})
|
|
5799
|
+
});
|
|
5795
5800
|
|
|
5796
5801
|
// Final-answer mode (RUN-018/RUN-019, extended to tokens by RUN-025):
|
|
5797
5802
|
// once the Turn, Tool Call, or token budget is
|
|
@@ -6548,8 +6553,8 @@ const makeTurn = <
|
|
|
6548
6553
|
activeProvisionalText = provisionalText;
|
|
6549
6554
|
}),
|
|
6550
6555
|
),
|
|
6551
|
-
Stream.runForEachArray(
|
|
6552
|
-
Effect.
|
|
6556
|
+
Stream.runForEachArray(
|
|
6557
|
+
Effect.fnUntracedEager(function* (parts) {
|
|
6553
6558
|
for (const part of parts) {
|
|
6554
6559
|
const owned = yield* ownModelResponsePart(
|
|
6555
6560
|
part,
|
|
@@ -7349,9 +7354,10 @@ const makeTurn = <
|
|
|
7349
7354
|
return yield* ModelProtocolError.make({
|
|
7350
7355
|
message: "Tool dispatch has no validated response facts",
|
|
7351
7356
|
});
|
|
7357
|
+
|
|
7352
7358
|
// Idempotency only permits replay of the original operation. Re-asking the
|
|
7353
7359
|
// model could choose different arguments or a new key, so it keeps the barrier.
|
|
7354
|
-
yield* acceptTurnCommit(context, trace, options.durability, {
|
|
7360
|
+
const admission = yield* acceptTurnCommit(context, trace, options.durability, {
|
|
7355
7361
|
_tag: "Response",
|
|
7356
7362
|
turn,
|
|
7357
7363
|
turnId,
|
|
@@ -7370,6 +7376,33 @@ const makeTurn = <
|
|
|
7370
7376
|
? { defer: true as const }
|
|
7371
7377
|
: {}),
|
|
7372
7378
|
});
|
|
7379
|
+
|
|
7380
|
+
if (admission === "capacity-refused") {
|
|
7381
|
+
const rejection = yield* settleRejectedBatch(
|
|
7382
|
+
context,
|
|
7383
|
+
turnId,
|
|
7384
|
+
trace,
|
|
7385
|
+
AgentPersistenceCapacityError.make({
|
|
7386
|
+
message:
|
|
7387
|
+
"This application tool batch was not dispatched because its durable results exceed available capacity. Retry the needed application tools in smaller batches. Earlier tool results remain valid.",
|
|
7388
|
+
}),
|
|
7389
|
+
);
|
|
7390
|
+
|
|
7391
|
+
return publishEvents(context, rejection).pipe(
|
|
7392
|
+
Effect.andThen(
|
|
7393
|
+
toolBatchContinuation(
|
|
7394
|
+
agent,
|
|
7395
|
+
context,
|
|
7396
|
+
trace,
|
|
7397
|
+
prompt,
|
|
7398
|
+
turn,
|
|
7399
|
+
turnId,
|
|
7400
|
+
toolCalls,
|
|
7401
|
+
options,
|
|
7402
|
+
),
|
|
7403
|
+
),
|
|
7404
|
+
);
|
|
7405
|
+
}
|
|
7373
7406
|
}
|
|
7374
7407
|
|
|
7375
7408
|
const toolResults = executeToolBatch(
|
|
@@ -8839,13 +8872,10 @@ function executeWithCompletion<
|
|
|
8839
8872
|
|
|
8840
8873
|
const started = Effect.gen(function* () {
|
|
8841
8874
|
yield* Metric.update(runCounter, 1);
|
|
8842
|
-
yield* Effect.logDebug("agent run started").pipe(
|
|
8843
|
-
Effect.annotateLogs({ agentId: context.agentId, runId: context.runId }),
|
|
8844
|
-
);
|
|
8845
8875
|
yield* publishEvent(context, () =>
|
|
8846
8876
|
eventBase(context).pipe(Effect.map((base) => RunStarted.make(base))),
|
|
8847
8877
|
);
|
|
8848
|
-
})
|
|
8878
|
+
});
|
|
8849
8879
|
|
|
8850
8880
|
const execution = Effect.flatten(
|
|
8851
8881
|
Effect.gen(function* () {
|
|
@@ -9156,6 +9186,7 @@ function executeWithCompletion<
|
|
|
9156
9186
|
history,
|
|
9157
9187
|
preparation,
|
|
9158
9188
|
agent.definition.policy,
|
|
9189
|
+
DateTime.makeUnsafe(context.durationDeadlineMillis),
|
|
9159
9190
|
options.subagentGrant,
|
|
9160
9191
|
options.subagentBudget,
|
|
9161
9192
|
options.subagentBudgetScope,
|
|
@@ -11091,6 +11122,8 @@ const spawnWithParent = (
|
|
|
11091
11122
|
export interface AgentSpawnerService {
|
|
11092
11123
|
/** Resolved defaults for this parent Run; never includes its tools or handlers. */
|
|
11093
11124
|
readonly policy: AgentPolicy;
|
|
11125
|
+
/** Effective deadline of this active parent execution, including RunOptions.durationDeadline. */
|
|
11126
|
+
readonly durationDeadline: DateTime.Utc;
|
|
11094
11127
|
readonly depth: number;
|
|
11095
11128
|
readonly grant?: SubagentGrant;
|
|
11096
11129
|
readonly budget?: SubagentBudgetReservation;
|
|
@@ -11199,6 +11232,7 @@ const makeAgentSpawner = (
|
|
|
11199
11232
|
history: ThreadHistory["Service"],
|
|
11200
11233
|
preparation: RunContextPreparation["Service"],
|
|
11201
11234
|
policy: AgentPolicy,
|
|
11235
|
+
durationDeadline: DateTime.Utc,
|
|
11202
11236
|
grant?: SubagentGrant,
|
|
11203
11237
|
budget?: SubagentBudgetReservation,
|
|
11204
11238
|
budgetScope?: WorkerBudgetScope,
|
|
@@ -11208,6 +11242,7 @@ const makeAgentSpawner = (
|
|
|
11208
11242
|
...(budget === undefined ? {} : { budget }),
|
|
11209
11243
|
...(budgetScope === undefined ? {} : { budgetScope }),
|
|
11210
11244
|
policy,
|
|
11245
|
+
durationDeadline,
|
|
11211
11246
|
depth,
|
|
11212
11247
|
parent,
|
|
11213
11248
|
spawn: spawnWithParent(parent, depth, history, preparation, onChild),
|