@yielded/agent 0.1.0-beta.182 → 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-C4mPa0M2.d.mts → Admin-BgCHCGSl.d.mts} +5 -5
- package/dist/{AgentRuntime-CVsnQBPe.d.mts → AgentRuntime-DhRchFzv.d.mts} +4 -1
- package/dist/{AgentRuntime-1OjlOCa9.mjs → AgentRuntime-ILtyHuQG.mjs} +52 -40
- 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-D2I_T2Yt.mjs → DurableAgentRuntime-Cj263rW9.mjs} +20 -55
- package/dist/DurableAgentRuntime-Cj263rW9.mjs.map +1 -0
- package/dist/{EventSource-gc82d-f6.d.mts → EventSource-CaA6RAtC.d.mts} +3 -3
- package/dist/{MessageDelivery-DrQKvh93.d.mts → MessageDelivery-rlLpakO8.d.mts} +43 -43
- package/dist/{PreparedInputAdmission-CrqdFUd8.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-DMbA81sZ.d.mts → RunContinuation-CIJRN_vC.d.mts} +4 -4
- package/dist/{RunContinuation-DWiO2K9D.mjs → RunContinuation-DJ6kkbQG.mjs} +17 -58
- package/dist/RunContinuation-DJ6kkbQG.mjs.map +1 -0
- package/dist/{RunJournal-oJLo-pjS.mjs → RunJournal-DQr2dUgg.mjs} +4 -7
- package/dist/RunJournal-DQr2dUgg.mjs.map +1 -0
- 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-BRiDyUS4.mjs → Subagent-Bm842K7J.mjs} +43 -17
- package/dist/Subagent-Bm842K7J.mjs.map +1 -0
- package/dist/{Subagent-FypBXZP1.d.mts → Subagent-CWWmA1UQ.d.mts} +15 -3
- package/dist/{Subscription-Bx0LiX3x.d.mts → Subscription-BRsNy0TA.d.mts} +4 -4
- package/dist/{SubscriptionInput-CBFCfuOF.d.mts → SubscriptionInput-ClCaW_kX.d.mts} +3 -3
- package/dist/{ThreadImport-DlCLjRHK.mjs → ThreadImport-CkPAxXU5.mjs} +7 -7
- package/dist/{ThreadImport-DlCLjRHK.mjs.map → ThreadImport-CkPAxXU5.mjs.map} +1 -1
- package/dist/{ThreadImport-DTV_ario.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-Cxg_REhE.mjs → WorkerAdmission-9Ka3edXj.mjs} +5 -5
- package/dist/{WorkerAdmission-Cxg_REhE.mjs.map → WorkerAdmission-9Ka3edXj.mjs.map} +1 -1
- package/dist/{agent-registration-C6tkQz8r.mjs → agent-registration-7kp1dHT0.mjs} +4 -4
- package/dist/{agent-registration-C6tkQz8r.mjs.map → agent-registration-7kp1dHT0.mjs.map} +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/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 +4 -4
- package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
- package/dist/durable/PersistentHistory.d.mts +1 -1
- 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/index.d.mts +14 -14
- package/dist/index.mjs +12 -12
- package/dist/{run-context-reader-CIdG5tdJ.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/durable/DurableAgentRuntime.ts +24 -10
- package/src/durable/Records.ts +21 -15
- package/src/durable/RunContinuation.ts +54 -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/internal/agent-runtime.ts +379 -381
- package/dist/AgentRuntime-1OjlOCa9.mjs.map +0 -1
- package/dist/DurableAgentRuntime-D2I_T2Yt.mjs.map +0 -1
- package/dist/Records-CZRedCE7.mjs.map +0 -1
- package/dist/RunContinuation-DWiO2K9D.mjs.map +0 -1
- package/dist/RunJournal-oJLo-pjS.mjs.map +0 -1
- package/dist/Subagent-BRiDyUS4.mjs.map +0 -1
- package/dist/json-BlJVQZxN.mjs +0 -15
- package/dist/json-BlJVQZxN.mjs.map +0 -1
|
@@ -1029,22 +1029,20 @@ const inspectModelResponsePartCapacity = (
|
|
|
1029
1029
|
part: unknown,
|
|
1030
1030
|
limits: EffectiveRunBufferLimits,
|
|
1031
1031
|
knownSafePrototypes: ReadonlySet<object> = knownSafeModelResponsePrototypes,
|
|
1032
|
-
):
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
);
|
|
1032
|
+
): number | ModelProtocolError => {
|
|
1033
|
+
const bytes = boundedValueFootprint(
|
|
1034
|
+
part,
|
|
1035
|
+
limits.maxModelResponseBytes - usage.responsePartBytes,
|
|
1036
|
+
knownSafePrototypes,
|
|
1037
|
+
);
|
|
1039
1038
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
});
|
|
1039
|
+
return (
|
|
1040
|
+
bytes ??
|
|
1041
|
+
ModelProtocolError.make({
|
|
1042
|
+
message: `Model response exceeded the ${limits.maxModelResponseBytes}-byte retained response limit`,
|
|
1043
|
+
})
|
|
1044
|
+
);
|
|
1045
|
+
};
|
|
1048
1046
|
|
|
1049
1047
|
/**
|
|
1050
1048
|
* LanguageModel validates encoded parameters even with automatic resolution disabled.
|
|
@@ -1137,7 +1135,7 @@ const modelResponseParsersFor = (toolkit: Toolkit.Any) => {
|
|
|
1137
1135
|
return parsers;
|
|
1138
1136
|
};
|
|
1139
1137
|
|
|
1140
|
-
const captureModelResponsePartGeneral = Effect.
|
|
1138
|
+
const captureModelResponsePartGeneral = Effect.fnUntracedEager(function* <
|
|
1141
1139
|
Tools extends Record<string, Tool.Any>,
|
|
1142
1140
|
>(
|
|
1143
1141
|
part: unknown,
|
|
@@ -1164,19 +1162,23 @@ const captureModelResponsePartGeneral = Effect.fnUntraced(function* <
|
|
|
1164
1162
|
if (directInputBytes === undefined) {
|
|
1165
1163
|
const preflight = schemaClassToolPartPreflight(part, toolkit);
|
|
1166
1164
|
|
|
1167
|
-
|
|
1165
|
+
const capacity = inspectModelResponsePartCapacity(usage, preflight ?? part, limits);
|
|
1166
|
+
|
|
1167
|
+
if (typeof capacity !== "number") return yield* capacity;
|
|
1168
1168
|
}
|
|
1169
1169
|
const parsers = modelResponseParsersFor(toolkit);
|
|
1170
1170
|
|
|
1171
1171
|
const encoded = yield* parsers
|
|
1172
1172
|
.encode(part)
|
|
1173
1173
|
.pipe(
|
|
1174
|
-
Effect.
|
|
1174
|
+
Effect.mapErrorEager(() =>
|
|
1175
1175
|
ModelProtocolError.make({ message: "Model response part failed canonical encoding" }),
|
|
1176
1176
|
),
|
|
1177
1177
|
);
|
|
1178
1178
|
|
|
1179
|
-
const retainedBytes =
|
|
1179
|
+
const retainedBytes = inspectModelResponsePartCapacity(usage, encoded, limits);
|
|
1180
|
+
|
|
1181
|
+
if (typeof retainedBytes !== "number") return yield* retainedBytes;
|
|
1180
1182
|
|
|
1181
1183
|
if (typeof structuredCloneFunction !== "function") {
|
|
1182
1184
|
return yield* ModelProtocolError.make({
|
|
@@ -1184,17 +1186,15 @@ const captureModelResponsePartGeneral = Effect.fnUntraced(function* <
|
|
|
1184
1186
|
});
|
|
1185
1187
|
}
|
|
1186
1188
|
|
|
1187
|
-
|
|
1188
|
-
try: () => {
|
|
1189
|
-
const cloned: unknown = Reflect.apply(structuredCloneFunction, globalThis, [encoded]);
|
|
1189
|
+
let ownedEncoded: unknown;
|
|
1190
1190
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
}
|
|
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
|
+
}
|
|
1198
1198
|
|
|
1199
1199
|
return { encodedPart: ownedEncoded, retainedBytes };
|
|
1200
1200
|
});
|
|
@@ -1217,49 +1217,51 @@ const decodePrimitiveModelResponsePart = Schema.decodeUnknownEffect(
|
|
|
1217
1217
|
),
|
|
1218
1218
|
);
|
|
1219
1219
|
|
|
1220
|
-
/**
|
|
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
|
+
*/
|
|
1221
1224
|
const ownModelResponsePart = <Tools extends Record<string, Tool.Any>>(
|
|
1222
1225
|
part: unknown,
|
|
1223
1226
|
toolkit: Toolkit.Toolkit<Tools>,
|
|
1224
1227
|
usage: ModelResponseBufferUsage,
|
|
1225
1228
|
limits: EffectiveRunBufferLimits,
|
|
1226
1229
|
finished: boolean,
|
|
1227
|
-
): Effect.Effect<OwnedModelResponsePart, ModelProtocolError> =>
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
}
|
|
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
|
+
}
|
|
1236
1238
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
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
|
+
}
|
|
1241
1253
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
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), {
|
|
1246
1259
|
onFailure: () =>
|
|
1247
1260
|
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1248
|
-
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes:
|
|
1249
|
-
})
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
// Pending application codecs keep normal Effect execution in this same consumer fiber.
|
|
1253
|
-
return Effect.flatMapEager(
|
|
1254
|
-
captureModelResponsePartGeneral(part, toolkit, usage, limits),
|
|
1255
|
-
(captured) =>
|
|
1256
|
-
Effect.mapBothEager(modelResponseParsersFor(toolkit).decode(captured.encodedPart), {
|
|
1257
|
-
onFailure: () =>
|
|
1258
|
-
ModelProtocolError.make({ message: "Model response part failed canonical decoding" }),
|
|
1259
|
-
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes: captured.retainedBytes }),
|
|
1260
|
-
}),
|
|
1261
|
-
);
|
|
1262
|
-
});
|
|
1261
|
+
onSuccess: (ownedPart) => ({ ownedPart, retainedBytes: captured.retainedBytes }),
|
|
1262
|
+
}),
|
|
1263
|
+
);
|
|
1264
|
+
};
|
|
1263
1265
|
|
|
1264
1266
|
type PartLifecycle = "open" | "closed";
|
|
1265
1267
|
|
|
@@ -1316,10 +1318,12 @@ const endPart = (
|
|
|
1316
1318
|
parts: Map<string, PartLifecycle>,
|
|
1317
1319
|
id: string,
|
|
1318
1320
|
description: string,
|
|
1319
|
-
): Effect.Effect<void, ModelProtocolError> =>
|
|
1320
|
-
continuePart(parts, id, `${description} end`)
|
|
1321
|
-
|
|
1322
|
-
|
|
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
|
+
};
|
|
1323
1327
|
|
|
1324
1328
|
const firstOpenPart = (trace: TurnTrace): string | undefined => {
|
|
1325
1329
|
for (const [id, state] of trace.textParts) {
|
|
@@ -3358,9 +3362,13 @@ const decodeUsageTotal = Schema.decodeEffect(Schema.Natural);
|
|
|
3358
3362
|
const decodeProviderUsage = Schema.decodeEffect(ProviderUsage);
|
|
3359
3363
|
|
|
3360
3364
|
const decodeProviderUsageTotal = (value: number): Effect.Effect<number, ModelProtocolError> =>
|
|
3361
|
-
decodeUsageTotal(value).pipe(Effect.
|
|
3365
|
+
decodeUsageTotal(value).pipe(Effect.mapErrorEager(() => invalidProviderUsage()));
|
|
3362
3366
|
|
|
3363
|
-
const consumeUsage =
|
|
3367
|
+
const consumeUsage = Effect.fnUntracedEager(function* <
|
|
3368
|
+
AgentValue extends Agent.Any,
|
|
3369
|
+
HookError,
|
|
3370
|
+
HookRequirements,
|
|
3371
|
+
>(
|
|
3364
3372
|
agent: AgentValue,
|
|
3365
3373
|
context: RunContext,
|
|
3366
3374
|
usage: Response.Usage | undefined,
|
|
@@ -3371,337 +3379,332 @@ const consumeUsage = <AgentValue extends Agent.Any, HookError, HookRequirements>
|
|
|
3371
3379
|
RunCostEstimateRequest,
|
|
3372
3380
|
"response" | "finishMetadata" | "purpose" | "webSearchCalls"
|
|
3373
3381
|
> = {},
|
|
3374
|
-
): Effect.
|
|
3382
|
+
): Effect.fn.Return<
|
|
3375
3383
|
ConsumedUsage,
|
|
3376
3384
|
AgentPolicyError | ModelProtocolError | HookError,
|
|
3377
3385
|
HookRequirements | Model.ProviderName | Model.ModelName
|
|
3378
|
-
>
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
}
|
|
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
|
+
}
|
|
3386
3393
|
|
|
3387
|
-
|
|
3394
|
+
const webSearchCalls = yield* decodeProviderUsageTotal(response.webSearchCalls ?? 0);
|
|
3388
3395
|
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3396
|
+
const providerUsage = yield* decodeProviderUsage(usage).pipe(
|
|
3397
|
+
Effect.mapErrorEager(() => invalidProviderUsage()),
|
|
3398
|
+
);
|
|
3392
3399
|
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
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;
|
|
3424
3430
|
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
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;
|
|
3435
3441
|
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3442
|
+
const allOutputComponentsReported =
|
|
3443
|
+
providerUsage.outputTokens.text !== undefined &&
|
|
3444
|
+
providerUsage.outputTokens.reasoning !== undefined;
|
|
3439
3445
|
|
|
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
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
const model = yield* Model.ModelName;
|
|
3501
|
-
|
|
3502
|
-
const estimate =
|
|
3503
|
-
options.estimateCostMicrousd === undefined
|
|
3504
|
-
? 0
|
|
3505
|
-
: yield* options.estimateCostMicrousd(usage, {
|
|
3506
|
-
provider,
|
|
3507
|
-
model,
|
|
3508
|
-
usage,
|
|
3509
|
-
...response,
|
|
3510
|
-
webSearchCalls,
|
|
3511
|
-
});
|
|
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;
|
|
3512
3506
|
|
|
3513
|
-
|
|
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
|
+
});
|
|
3514
3517
|
|
|
3515
|
-
|
|
3516
|
-
return yield* AgentPolicyError.make({
|
|
3517
|
-
limit: "cost",
|
|
3518
|
-
message: "Model cost estimation must produce a non-negative integer number of microdollars",
|
|
3519
|
-
});
|
|
3520
|
-
}
|
|
3521
|
-
const serviceTier = typeof estimate === "number" ? undefined : estimate.serviceTier;
|
|
3522
|
-
const pricingVersion = typeof estimate === "number" ? undefined : estimate.pricingVersion;
|
|
3518
|
+
const costMicrousd = typeof estimate === "number" ? estimate : estimate.costMicrousd;
|
|
3523
3519
|
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
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;
|
|
3530
3528
|
|
|
3531
|
-
|
|
3532
|
-
|
|
3529
|
+
const pricingStatus =
|
|
3530
|
+
options.estimateCostMicrousd === undefined
|
|
3531
|
+
? "unknown"
|
|
3532
|
+
: typeof estimate === "number"
|
|
3533
|
+
? "estimated"
|
|
3534
|
+
: (estimate.pricingStatus ?? "estimated");
|
|
3533
3535
|
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
limit: "cost",
|
|
3537
|
-
message: "Model cost estimation returned an invalid service tier or pricing version",
|
|
3538
|
-
});
|
|
3539
|
-
}
|
|
3536
|
+
const validPricingIdentity = (value: string | undefined): boolean =>
|
|
3537
|
+
value === undefined || (value.length > 0 && value.length <= 256);
|
|
3540
3538
|
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
...(response.response === undefined
|
|
3546
|
-
? {}
|
|
3547
|
-
: { response: ModelResponseIdentity.make(response.response) }),
|
|
3548
|
-
purpose: response.purpose ?? "turn",
|
|
3549
|
-
usageStatus:
|
|
3550
|
-
Object.values(providerUsage.inputTokens).every((value) => value === undefined) &&
|
|
3551
|
-
Object.values(providerUsage.outputTokens).every((value) => value === undefined)
|
|
3552
|
-
? "unknown"
|
|
3553
|
-
: [
|
|
3554
|
-
providerUsage.inputTokens.uncached,
|
|
3555
|
-
providerUsage.inputTokens.cacheRead,
|
|
3556
|
-
providerUsage.inputTokens.cacheWrite,
|
|
3557
|
-
providerUsage.outputTokens.text,
|
|
3558
|
-
providerUsage.outputTokens.reasoning,
|
|
3559
|
-
].every((value) => value !== undefined)
|
|
3560
|
-
? "complete"
|
|
3561
|
-
: "partial",
|
|
3562
|
-
pricingStatus,
|
|
3563
|
-
...(serviceTier === undefined ? {} : { serviceTier }),
|
|
3564
|
-
...(pricingVersion === undefined ? {} : { pricingVersion }),
|
|
3565
|
-
inputTokens: inputUsage,
|
|
3566
|
-
outputTokens: outputUsage,
|
|
3567
|
-
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",
|
|
3568
3543
|
});
|
|
3544
|
+
}
|
|
3569
3545
|
|
|
3570
|
-
|
|
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
|
+
});
|
|
3571
3574
|
|
|
3572
|
-
|
|
3573
|
-
context.webSearchCalls === undefined
|
|
3574
|
-
? undefined
|
|
3575
|
-
: yield* decodeProviderUsageTotal(context.webSearchCalls + webSearchCalls);
|
|
3575
|
+
const modelCalls = yield* decodeProviderUsageTotal(context.modelCalls + 1);
|
|
3576
3576
|
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3577
|
+
const cumulativeWebSearchCalls =
|
|
3578
|
+
context.webSearchCalls === undefined
|
|
3579
|
+
? undefined
|
|
3580
|
+
: yield* decodeProviderUsageTotal(context.webSearchCalls + webSearchCalls);
|
|
3580
3581
|
|
|
3581
|
-
|
|
3582
|
-
context.outputTokens + outputTokens,
|
|
3583
|
-
);
|
|
3582
|
+
const cumulativeInputTokens = yield* decodeProviderUsageTotal(context.inputTokens + inputTokens);
|
|
3584
3583
|
|
|
3585
|
-
|
|
3584
|
+
const cumulativeOutputTokens = yield* decodeProviderUsageTotal(
|
|
3585
|
+
context.outputTokens + outputTokens,
|
|
3586
|
+
);
|
|
3586
3587
|
|
|
3587
|
-
|
|
3588
|
-
return yield* AgentPolicyError.make({
|
|
3589
|
-
limit: "cost",
|
|
3590
|
-
message: "Cumulative model cost exceeds safe-integer accounting capacity",
|
|
3591
|
-
});
|
|
3592
|
-
}
|
|
3593
|
-
const firstObserved = context.modelCalls === 0 && context.unobservedModelCalls === 0;
|
|
3588
|
+
const cumulativeCostMicrousd = context.costMicrousd + costMicrousd;
|
|
3594
3589
|
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
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;
|
|
3599
3597
|
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
);
|
|
3605
|
-
context.modelCalls = modelCalls;
|
|
3606
|
-
context.webSearchCalls = cumulativeWebSearchCalls;
|
|
3607
|
-
context.inputTokens = cumulativeInputTokens;
|
|
3608
|
-
context.outputTokens = cumulativeOutputTokens;
|
|
3609
|
-
context.lastInputTokens = inputTokens;
|
|
3610
|
-
context.lastOutputTokens = outputTokens;
|
|
3611
|
-
context.costMicrousd = cumulativeCostMicrousd;
|
|
3612
|
-
context.lastCostMicrousd = costMicrousd;
|
|
3613
|
-
|
|
3614
|
-
if (options.durability !== undefined) {
|
|
3615
|
-
// Stage before enforcing hard rails: a response that spends past the
|
|
3616
|
-
// cost/token budget still becomes auditable in its canonical Turn and
|
|
3617
|
-
// terminal settlement.
|
|
3618
|
-
yield* options.durability.noteTurnUsage({ turn, usage: modelUsage });
|
|
3619
|
-
}
|
|
3598
|
+
const combineStatus = (
|
|
3599
|
+
prior: typeof UsageCompleteness.Type,
|
|
3600
|
+
next: typeof UsageCompleteness.Type,
|
|
3601
|
+
) => (firstObserved && prior === "complete" ? next : prior === next ? prior : "partial");
|
|
3620
3602
|
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
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
|
+
}
|
|
3625
3623
|
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
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",
|
|
3635
3658
|
});
|
|
3636
|
-
// Fail mode rejects before any declared application Handler starts.
|
|
3637
|
-
if (policy.onExhaustion === "fail") {
|
|
3638
|
-
return yield* breach;
|
|
3639
|
-
}
|
|
3640
|
-
// One-shot (RUN-025): the flag joins the final-answer derivation so the
|
|
3641
|
-
// grace Turn's own usage accumulates and charges without re-breaching.
|
|
3642
|
-
context.tokenExhausted = true;
|
|
3643
|
-
context.exhaustedDimension ??= "tokens";
|
|
3644
3659
|
}
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
if (costBudget !== undefined) {
|
|
3651
|
-
if (options.estimateCostMicrousd === undefined) {
|
|
3652
|
-
return yield* AgentPolicyError.make({
|
|
3653
|
-
limit: "cost",
|
|
3654
|
-
message: "Agent cost budget requires a model cost estimator",
|
|
3655
|
-
});
|
|
3656
|
-
}
|
|
3657
|
-
if (pricingStatus === "unknown") {
|
|
3658
|
-
return yield* AgentPolicyError.make({
|
|
3659
|
-
limit: "cost",
|
|
3660
|
-
message: "Agent cost budget requires a known model cost estimate",
|
|
3661
|
-
});
|
|
3662
|
-
}
|
|
3663
|
-
if (context.costMicrousd > costBudget) {
|
|
3664
|
-
// Cost is spend, not context: it never earns a grace Turn.
|
|
3665
|
-
return yield* AgentPolicyError.make({
|
|
3666
|
-
limit: "cost",
|
|
3667
|
-
message: `Agent exceeded its ${costBudget} microdollar cost budget`,
|
|
3668
|
-
});
|
|
3669
|
-
}
|
|
3660
|
+
if (pricingStatus === "unknown") {
|
|
3661
|
+
return yield* AgentPolicyError.make({
|
|
3662
|
+
limit: "cost",
|
|
3663
|
+
message: "Agent cost budget requires a known model cost estimate",
|
|
3664
|
+
});
|
|
3670
3665
|
}
|
|
3671
|
-
if (
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
toolCalls: toolCallCount,
|
|
3678
|
-
costMicrousd,
|
|
3679
|
-
usage,
|
|
3680
|
-
modelUsage,
|
|
3681
|
-
};
|
|
3682
|
-
|
|
3683
|
-
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
|
+
});
|
|
3684
3672
|
}
|
|
3685
|
-
|
|
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
|
+
};
|
|
3686
3685
|
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
nearingLimit(consumedTokens, tokenBudget)
|
|
3691
|
-
) {
|
|
3692
|
-
context.warnedLimits.add("tokens");
|
|
3693
|
-
warnings?.push(
|
|
3694
|
-
BudgetWarning.make({
|
|
3695
|
-
...(yield* eventBase(context)),
|
|
3696
|
-
limit: "tokens",
|
|
3697
|
-
consumed: consumedTokens,
|
|
3698
|
-
limitValue: tokenBudget,
|
|
3699
|
-
}),
|
|
3700
|
-
);
|
|
3701
|
-
}
|
|
3686
|
+
yield* options.budget.consume(delta);
|
|
3687
|
+
}
|
|
3688
|
+
const warnings: Array<RunEvent> | undefined = context.publish === undefined ? undefined : [];
|
|
3702
3689
|
|
|
3703
|
-
|
|
3704
|
-
|
|
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
|
+
});
|
|
3705
3708
|
|
|
3706
3709
|
// `Effect.fnUntraced`: this helper runs for every streamed Response Part, so a
|
|
3707
3710
|
// named span here would emit one span per TextDelta/ReasoningDelta. Spans stay
|
|
@@ -5774,13 +5777,6 @@ const makeTurn = <
|
|
|
5774
5777
|
|
|
5775
5778
|
const started = Effect.gen(function* () {
|
|
5776
5779
|
yield* Metric.update(modelCounter, 1);
|
|
5777
|
-
yield* Effect.logDebug("agent model call started").pipe(
|
|
5778
|
-
Effect.annotateLogs({
|
|
5779
|
-
agentId: context.agentId,
|
|
5780
|
-
runId: context.runId,
|
|
5781
|
-
turnId,
|
|
5782
|
-
}),
|
|
5783
|
-
);
|
|
5784
5780
|
|
|
5785
5781
|
if (context.publish === undefined) return;
|
|
5786
5782
|
|
|
@@ -5800,7 +5796,7 @@ const makeTurn = <
|
|
|
5800
5796
|
]);
|
|
5801
5797
|
|
|
5802
5798
|
return;
|
|
5803
|
-
})
|
|
5799
|
+
});
|
|
5804
5800
|
|
|
5805
5801
|
// Final-answer mode (RUN-018/RUN-019, extended to tokens by RUN-025):
|
|
5806
5802
|
// once the Turn, Tool Call, or token budget is
|
|
@@ -6557,8 +6553,8 @@ const makeTurn = <
|
|
|
6557
6553
|
activeProvisionalText = provisionalText;
|
|
6558
6554
|
}),
|
|
6559
6555
|
),
|
|
6560
|
-
Stream.runForEachArray(
|
|
6561
|
-
Effect.
|
|
6556
|
+
Stream.runForEachArray(
|
|
6557
|
+
Effect.fnUntracedEager(function* (parts) {
|
|
6562
6558
|
for (const part of parts) {
|
|
6563
6559
|
const owned = yield* ownModelResponsePart(
|
|
6564
6560
|
part,
|
|
@@ -8876,13 +8872,10 @@ function executeWithCompletion<
|
|
|
8876
8872
|
|
|
8877
8873
|
const started = Effect.gen(function* () {
|
|
8878
8874
|
yield* Metric.update(runCounter, 1);
|
|
8879
|
-
yield* Effect.logDebug("agent run started").pipe(
|
|
8880
|
-
Effect.annotateLogs({ agentId: context.agentId, runId: context.runId }),
|
|
8881
|
-
);
|
|
8882
8875
|
yield* publishEvent(context, () =>
|
|
8883
8876
|
eventBase(context).pipe(Effect.map((base) => RunStarted.make(base))),
|
|
8884
8877
|
);
|
|
8885
|
-
})
|
|
8878
|
+
});
|
|
8886
8879
|
|
|
8887
8880
|
const execution = Effect.flatten(
|
|
8888
8881
|
Effect.gen(function* () {
|
|
@@ -9193,6 +9186,7 @@ function executeWithCompletion<
|
|
|
9193
9186
|
history,
|
|
9194
9187
|
preparation,
|
|
9195
9188
|
agent.definition.policy,
|
|
9189
|
+
DateTime.makeUnsafe(context.durationDeadlineMillis),
|
|
9196
9190
|
options.subagentGrant,
|
|
9197
9191
|
options.subagentBudget,
|
|
9198
9192
|
options.subagentBudgetScope,
|
|
@@ -11128,6 +11122,8 @@ const spawnWithParent = (
|
|
|
11128
11122
|
export interface AgentSpawnerService {
|
|
11129
11123
|
/** Resolved defaults for this parent Run; never includes its tools or handlers. */
|
|
11130
11124
|
readonly policy: AgentPolicy;
|
|
11125
|
+
/** Effective deadline of this active parent execution, including RunOptions.durationDeadline. */
|
|
11126
|
+
readonly durationDeadline: DateTime.Utc;
|
|
11131
11127
|
readonly depth: number;
|
|
11132
11128
|
readonly grant?: SubagentGrant;
|
|
11133
11129
|
readonly budget?: SubagentBudgetReservation;
|
|
@@ -11236,6 +11232,7 @@ const makeAgentSpawner = (
|
|
|
11236
11232
|
history: ThreadHistory["Service"],
|
|
11237
11233
|
preparation: RunContextPreparation["Service"],
|
|
11238
11234
|
policy: AgentPolicy,
|
|
11235
|
+
durationDeadline: DateTime.Utc,
|
|
11239
11236
|
grant?: SubagentGrant,
|
|
11240
11237
|
budget?: SubagentBudgetReservation,
|
|
11241
11238
|
budgetScope?: WorkerBudgetScope,
|
|
@@ -11245,6 +11242,7 @@ const makeAgentSpawner = (
|
|
|
11245
11242
|
...(budget === undefined ? {} : { budget }),
|
|
11246
11243
|
...(budgetScope === undefined ? {} : { budgetScope }),
|
|
11247
11244
|
policy,
|
|
11245
|
+
durationDeadline,
|
|
11248
11246
|
depth,
|
|
11249
11247
|
parent,
|
|
11250
11248
|
spawn: spawnWithParent(parent, depth, history, preparation, onChild),
|