@axlsdk/axl 0.25.0 → 0.25.1
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/index.cjs +717 -153
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -19
- package/dist/index.d.ts +94 -19
- package/dist/index.js +702 -139
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -40,6 +40,23 @@ var UnsupportedModelInputError = class extends AxlError {
|
|
|
40
40
|
this.source = options.source;
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
+
var UnsupportedModelOptionError = class extends AxlError {
|
|
44
|
+
provider;
|
|
45
|
+
model;
|
|
46
|
+
option;
|
|
47
|
+
remediation;
|
|
48
|
+
constructor(options) {
|
|
49
|
+
super(
|
|
50
|
+
"UNSUPPORTED_MODEL_OPTION",
|
|
51
|
+
`Provider '${options.provider}' model '${options.model}' does not support ${options.option}. ${options.remediation}`
|
|
52
|
+
);
|
|
53
|
+
this.name = "UnsupportedModelOptionError";
|
|
54
|
+
this.provider = options.provider;
|
|
55
|
+
this.model = options.model;
|
|
56
|
+
this.option = options.option;
|
|
57
|
+
this.remediation = options.remediation;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
43
60
|
var InvalidTranscriptionInputError = class extends AxlError {
|
|
44
61
|
constructor(message) {
|
|
45
62
|
super("INVALID_TRANSCRIPTION_INPUT", message);
|
|
@@ -331,7 +348,7 @@ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null
|
|
|
331
348
|
var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
|
|
332
349
|
var thisCopy = {
|
|
333
350
|
path: buildPath.startsWith("file:") ? fileURLToPath(buildPath) : buildPath,
|
|
334
|
-
version: true ? "0.25.
|
|
351
|
+
version: true ? "0.25.1" : "unknown"
|
|
335
352
|
};
|
|
336
353
|
function warnCrossCopy(other, joined) {
|
|
337
354
|
if (other === thisCopy) return;
|
|
@@ -3204,8 +3221,12 @@ var OpenAICompatibleProvider = class {
|
|
|
3204
3221
|
for (const key of forbidden) {
|
|
3205
3222
|
if (!userKeys.includes(key)) delete body[key];
|
|
3206
3223
|
}
|
|
3224
|
+
this.validateFinalRequestBody(body);
|
|
3207
3225
|
return body;
|
|
3208
3226
|
}
|
|
3227
|
+
/** Native subclasses may reject model-invalid fields after providerOptions merge. */
|
|
3228
|
+
validateFinalRequestBody(_body) {
|
|
3229
|
+
}
|
|
3209
3230
|
formatMessage(msg, model) {
|
|
3210
3231
|
const modalities = this.inputModalities(model);
|
|
3211
3232
|
const out = {
|
|
@@ -3613,6 +3634,24 @@ var withLongContext = (short) => ({
|
|
|
3613
3634
|
output: short.output * 1.5
|
|
3614
3635
|
});
|
|
3615
3636
|
var DIRECT_OPENAI_CATALOG = [
|
|
3637
|
+
{
|
|
3638
|
+
aliases: ["gpt-6-astra"],
|
|
3639
|
+
short: { input: 10 / M, cachedInput: 1 / M, cacheWrite: 12.5 / M, output: 50 / M },
|
|
3640
|
+
long: { input: 20 / M, cachedInput: 2 / M, cacheWrite: 25 / M, output: 75 / M },
|
|
3641
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
3642
|
+
},
|
|
3643
|
+
{
|
|
3644
|
+
aliases: ["gpt-6-sol"],
|
|
3645
|
+
short: { input: 2 / M, cachedInput: 0.2 / M, cacheWrite: 2.5 / M, output: 10 / M },
|
|
3646
|
+
long: { input: 4 / M, cachedInput: 0.4 / M, cacheWrite: 5 / M, output: 15 / M },
|
|
3647
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
3648
|
+
},
|
|
3649
|
+
{
|
|
3650
|
+
aliases: ["gpt-6-luna"],
|
|
3651
|
+
short: { input: 0.1 / M, cachedInput: 0.01 / M, cacheWrite: 0.125 / M, output: 0.5 / M },
|
|
3652
|
+
long: { input: 0.2 / M, cachedInput: 0.02 / M, cacheWrite: 0.25 / M, output: 0.75 / M },
|
|
3653
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
3654
|
+
},
|
|
3616
3655
|
{
|
|
3617
3656
|
aliases: ["gpt-5.6", "gpt-5.6-sol"],
|
|
3618
3657
|
snapshotBase: "gpt-5.6-sol",
|
|
@@ -3943,7 +3982,7 @@ function estimateDirectOpenAICost(model, usage, context) {
|
|
|
3943
3982
|
const audioOutput = audio_output_tokens ?? 0;
|
|
3944
3983
|
if (![prompt_tokens, completion_tokens, cached, cacheWrite, audioInput, audioOutput].every(
|
|
3945
3984
|
(count) => Number.isSafeInteger(count) && count >= 0
|
|
3946
|
-
) || cached + cacheWrite + audioInput > prompt_tokens || audioOutput > completion_tokens) {
|
|
3985
|
+
) || resolveOpenAIModelDescriptor(model).strictUsageTotals && (!Number.isSafeInteger(usage.total_tokens) || usage.total_tokens !== prompt_tokens + completion_tokens) || cached + cacheWrite + audioInput > prompt_tokens || audioOutput > completion_tokens) {
|
|
3947
3986
|
return void 0;
|
|
3948
3987
|
}
|
|
3949
3988
|
if (audioInput > 0 && entry.audio === void 0 || audioOutput > 0 && entry.audio?.output === void 0) {
|
|
@@ -3968,57 +4007,206 @@ function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens)
|
|
|
3968
4007
|
cached_tokens: cachedTokens
|
|
3969
4008
|
});
|
|
3970
4009
|
}
|
|
3971
|
-
|
|
3972
|
-
|
|
4010
|
+
var EFFORT_FALLBACK = {
|
|
4011
|
+
none: "minimal",
|
|
4012
|
+
minimal: "low",
|
|
4013
|
+
low: "medium",
|
|
4014
|
+
medium: "high",
|
|
4015
|
+
max: "xhigh",
|
|
4016
|
+
xhigh: "high"
|
|
4017
|
+
};
|
|
4018
|
+
var BASE_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
4019
|
+
function bothEndpoints(value) {
|
|
4020
|
+
return { chat: value, responses: value };
|
|
3973
4021
|
}
|
|
3974
|
-
function
|
|
3975
|
-
return
|
|
4022
|
+
function gpt6(id, efforts, chatTools) {
|
|
4023
|
+
return {
|
|
4024
|
+
id,
|
|
4025
|
+
family: "gpt-6",
|
|
4026
|
+
reasoning: true,
|
|
4027
|
+
efforts: bothEndpoints(efforts),
|
|
4028
|
+
defaultEffort: "medium",
|
|
4029
|
+
// Same policy as GPT-5.x: a portable `temperature` is dropped while
|
|
4030
|
+
// reasoning is active (including the active default). A raw
|
|
4031
|
+
// `providerOptions` sampling field is rejected by the final-body check.
|
|
4032
|
+
sampling: bothEndpoints("unless-effort-none"),
|
|
4033
|
+
chatTools,
|
|
4034
|
+
validatesFinalBody: true,
|
|
4035
|
+
strictUsageTotals: true
|
|
4036
|
+
};
|
|
3976
4037
|
}
|
|
3977
|
-
|
|
3978
|
-
"
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
4038
|
+
function gpt56(id) {
|
|
4039
|
+
const chat = ["none", ...BASE_EFFORTS, "xhigh"];
|
|
4040
|
+
return {
|
|
4041
|
+
id,
|
|
4042
|
+
family: "gpt-5.6",
|
|
4043
|
+
reasoning: true,
|
|
4044
|
+
efforts: { chat, responses: [...chat, "max"] },
|
|
4045
|
+
// Responses rejects temperature even at the provider-default effort.
|
|
4046
|
+
sampling: { chat: "when-effort-sent", responses: "always" },
|
|
4047
|
+
chatTools: "allowed",
|
|
4048
|
+
validatesFinalBody: false,
|
|
4049
|
+
strictUsageTotals: false
|
|
4050
|
+
};
|
|
3985
4051
|
}
|
|
3986
|
-
|
|
3987
|
-
|
|
4052
|
+
var OPENAI_EXACT_MODELS = new Map(
|
|
4053
|
+
[
|
|
4054
|
+
gpt6("gpt-6-astra", ["low", "medium", "high", "xhigh", "max"], "never"),
|
|
4055
|
+
gpt6("gpt-6-sol", ["none", "low", "medium", "high", "xhigh", "max"], "effort-none-only"),
|
|
4056
|
+
gpt6("gpt-6-luna", ["none", "low", "medium", "high", "xhigh", "max"], "effort-none-only"),
|
|
4057
|
+
gpt56("gpt-5.6"),
|
|
4058
|
+
gpt56("gpt-5.6-sol"),
|
|
4059
|
+
gpt56("gpt-5.6-terra"),
|
|
4060
|
+
gpt56("gpt-5.6-luna")
|
|
4061
|
+
].map((descriptor) => [descriptor.id, descriptor])
|
|
4062
|
+
);
|
|
4063
|
+
var O_SERIES_DESCRIPTOR = {
|
|
4064
|
+
id: "o-series",
|
|
4065
|
+
family: "o-series",
|
|
4066
|
+
reasoning: true,
|
|
4067
|
+
efforts: bothEndpoints(BASE_EFFORTS),
|
|
4068
|
+
defaultEffort: "medium",
|
|
4069
|
+
sampling: bothEndpoints("always"),
|
|
4070
|
+
chatTools: "allowed",
|
|
4071
|
+
validatesFinalBody: false,
|
|
4072
|
+
strictUsageTotals: false
|
|
4073
|
+
};
|
|
4074
|
+
var GPT_5_PRO_DESCRIPTOR = {
|
|
4075
|
+
id: "gpt-5-pro",
|
|
4076
|
+
family: "gpt-5-pro",
|
|
4077
|
+
reasoning: true,
|
|
4078
|
+
efforts: bothEndpoints(["high"]),
|
|
4079
|
+
defaultEffort: "high",
|
|
4080
|
+
sampling: bothEndpoints("when-effort-sent"),
|
|
4081
|
+
chatTools: "allowed",
|
|
4082
|
+
validatesFinalBody: false,
|
|
4083
|
+
strictUsageTotals: false
|
|
4084
|
+
};
|
|
4085
|
+
var DEFAULT_DESCRIPTOR = {
|
|
4086
|
+
id: "default",
|
|
4087
|
+
family: "default",
|
|
4088
|
+
reasoning: false,
|
|
4089
|
+
// Never emitted (the model is not reasoning-capable); keeps the clamp total.
|
|
4090
|
+
efforts: bothEndpoints(BASE_EFFORTS),
|
|
4091
|
+
sampling: bothEndpoints("never"),
|
|
4092
|
+
chatTools: "allowed",
|
|
4093
|
+
validatesFinalBody: false,
|
|
4094
|
+
strictUsageTotals: false
|
|
4095
|
+
};
|
|
4096
|
+
function gpt5FamilyDescriptor(model) {
|
|
4097
|
+
const none = /^gpt-5\.[1-9]/.test(model);
|
|
4098
|
+
const xhigh = /^gpt-5\.([2-9]|\d{2,})/.test(model);
|
|
4099
|
+
const efforts = [
|
|
4100
|
+
...none ? ["none"] : [],
|
|
4101
|
+
...BASE_EFFORTS,
|
|
4102
|
+
...xhigh ? ["xhigh"] : []
|
|
4103
|
+
];
|
|
4104
|
+
return {
|
|
4105
|
+
id: "gpt-5",
|
|
4106
|
+
family: "gpt-5",
|
|
4107
|
+
reasoning: true,
|
|
4108
|
+
efforts: bothEndpoints(efforts),
|
|
4109
|
+
...none ? {} : { defaultEffort: "medium" },
|
|
4110
|
+
sampling: bothEndpoints("when-effort-sent"),
|
|
4111
|
+
chatTools: "allowed",
|
|
4112
|
+
validatesFinalBody: false,
|
|
4113
|
+
strictUsageTotals: false
|
|
4114
|
+
};
|
|
3988
4115
|
}
|
|
3989
|
-
|
|
3990
|
-
|
|
4116
|
+
var OPENAI_FAMILY_FALLBACKS = [
|
|
4117
|
+
{ match: /^(o1|o3|o4-mini)/, describe: () => O_SERIES_DESCRIPTOR },
|
|
4118
|
+
{ match: /^gpt-5-pro/, describe: () => GPT_5_PRO_DESCRIPTOR },
|
|
4119
|
+
{ match: /^gpt-5/, describe: gpt5FamilyDescriptor }
|
|
4120
|
+
];
|
|
4121
|
+
function resolveOpenAIModelDescriptor(model) {
|
|
4122
|
+
const exact = OPENAI_EXACT_MODELS.get(model);
|
|
4123
|
+
if (exact) return exact;
|
|
4124
|
+
const family = OPENAI_FAMILY_FALLBACKS.find((entry) => entry.match.test(model));
|
|
4125
|
+
return family ? family.describe(model) : DEFAULT_DESCRIPTOR;
|
|
4126
|
+
}
|
|
4127
|
+
function clampToDescriptor(descriptor, endpoint, effort) {
|
|
4128
|
+
const accepted = descriptor.efforts[endpoint];
|
|
4129
|
+
let candidate = effort;
|
|
4130
|
+
while (candidate !== void 0 && !accepted.includes(candidate)) {
|
|
4131
|
+
candidate = EFFORT_FALLBACK[candidate];
|
|
4132
|
+
}
|
|
4133
|
+
if (candidate === void 0) {
|
|
4134
|
+
throw new Error(
|
|
4135
|
+
`OpenAI descriptor '${descriptor.id}' accepts no ${endpoint} effort reachable from '${effort}'`
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4138
|
+
return candidate;
|
|
3991
4139
|
}
|
|
3992
|
-
function
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4140
|
+
function stripsPortableSampling(descriptor, endpoint, wireEffort) {
|
|
4141
|
+
switch (descriptor.sampling[endpoint]) {
|
|
4142
|
+
case "never":
|
|
4143
|
+
return false;
|
|
4144
|
+
case "always":
|
|
4145
|
+
return true;
|
|
4146
|
+
case "when-effort-sent":
|
|
4147
|
+
return wireEffort !== void 0;
|
|
4148
|
+
case "unless-effort-none":
|
|
4149
|
+
return (wireEffort ?? descriptor.defaultEffort) !== "none";
|
|
3997
4150
|
}
|
|
3998
|
-
if (effort === "xhigh" && !supportsXhigh(model)) return "high";
|
|
3999
|
-
return effort;
|
|
4000
4151
|
}
|
|
4001
|
-
function
|
|
4002
|
-
return
|
|
4152
|
+
function isOSeriesModel(model) {
|
|
4153
|
+
return resolveOpenAIModelDescriptor(model).family === "o-series";
|
|
4003
4154
|
}
|
|
4004
4155
|
function budgetToReasoningEffort(budget) {
|
|
4005
4156
|
if (budget <= 1024) return "low";
|
|
4006
4157
|
if (budget <= 8192) return "medium";
|
|
4007
4158
|
return "high";
|
|
4008
4159
|
}
|
|
4160
|
+
function resolveEndpointReasoningEffort(model, endpoint, resolved) {
|
|
4161
|
+
const descriptor = resolveOpenAIModelDescriptor(model);
|
|
4162
|
+
if (!descriptor.reasoning) return void 0;
|
|
4163
|
+
const requested = resolved.hasBudgetOverride ? budgetToReasoningEffort(resolved.thinkingBudget) : !resolved.thinkingDisabled && resolved.activeEffort ? resolved.activeEffort : resolved.thinkingDisabled ? "none" : void 0;
|
|
4164
|
+
return requested === void 0 ? void 0 : clampToDescriptor(descriptor, endpoint, requested);
|
|
4165
|
+
}
|
|
4009
4166
|
function resolveOpenAIReasoningEffort(model, resolved) {
|
|
4010
|
-
|
|
4011
|
-
if (resolved.hasBudgetOverride) {
|
|
4012
|
-
return clampReasoningEffort(model, budgetToReasoningEffort(resolved.thinkingBudget));
|
|
4013
|
-
}
|
|
4014
|
-
if (!resolved.thinkingDisabled && resolved.activeEffort) {
|
|
4015
|
-
return clampReasoningEffort(model, effortToReasoningEffort(resolved.activeEffort, model));
|
|
4016
|
-
}
|
|
4017
|
-
return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
|
|
4167
|
+
return resolveEndpointReasoningEffort(model, "responses", resolved);
|
|
4018
4168
|
}
|
|
4019
4169
|
function resolveOpenAIChatReasoningEffort(model, resolved) {
|
|
4020
|
-
|
|
4021
|
-
|
|
4170
|
+
return resolveEndpointReasoningEffort(model, "chat", resolved);
|
|
4171
|
+
}
|
|
4172
|
+
function validateOpenAIFinalRequestBody(body, endpoint) {
|
|
4173
|
+
const model = body.model;
|
|
4174
|
+
if (typeof model !== "string") return;
|
|
4175
|
+
const descriptor = resolveOpenAIModelDescriptor(model);
|
|
4176
|
+
if (!descriptor.validatesFinalBody) return;
|
|
4177
|
+
const provider = endpoint === "chat" ? "openai" : "openai-responses";
|
|
4178
|
+
const fail = (option, remediation) => {
|
|
4179
|
+
throw new UnsupportedModelOptionError({ provider, model, option, remediation });
|
|
4180
|
+
};
|
|
4181
|
+
const rawEffort = endpoint === "chat" ? body.reasoning_effort : body.reasoning !== null && typeof body.reasoning === "object" ? body.reasoning.effort : void 0;
|
|
4182
|
+
const accepted = descriptor.efforts[endpoint];
|
|
4183
|
+
if (rawEffort !== void 0 && !accepted.includes(rawEffort)) {
|
|
4184
|
+
fail("reasoning effort", `Use one of: ${accepted.join(", ")}.`);
|
|
4185
|
+
}
|
|
4186
|
+
const activeReasoning = (rawEffort ?? descriptor.defaultEffort) !== "none";
|
|
4187
|
+
if (endpoint === "chat" && descriptor.chatTools !== "allowed") {
|
|
4188
|
+
const toolChoice = body.tool_choice;
|
|
4189
|
+
const toolIntent = Array.isArray(body.tools) && body.tools.length > 0 || toolChoice !== void 0 && toolChoice !== null && toolChoice !== "none" && toolChoice !== "auto";
|
|
4190
|
+
if (toolIntent && (descriptor.chatTools === "never" || activeReasoning)) {
|
|
4191
|
+
fail(
|
|
4192
|
+
"Chat Completions tool calling",
|
|
4193
|
+
descriptor.chatTools === "never" ? `Use openai-responses:${model} for tools.` : `Use openai-responses:${model} for reasoning with tools, or set effective reasoning_effort to 'none'.`
|
|
4194
|
+
);
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
if (activeReasoning) {
|
|
4198
|
+
const forbidden = endpoint === "chat" ? ["temperature", "top_p", "top_logprobs", "logprobs"] : ["temperature", "top_p", "top_logprobs"];
|
|
4199
|
+
for (const key of forbidden) {
|
|
4200
|
+
if (body[key] !== void 0)
|
|
4201
|
+
fail(key, `Remove ${key} or set reasoning effort to 'none' on Sol or Luna.`);
|
|
4202
|
+
}
|
|
4203
|
+
if (endpoint === "responses" && Array.isArray(body.include) && body.include.includes("message.output_text.logprobs")) {
|
|
4204
|
+
fail(
|
|
4205
|
+
"message.output_text.logprobs",
|
|
4206
|
+
"Remove it from include or set reasoning effort to none on Sol or Luna."
|
|
4207
|
+
);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4022
4210
|
}
|
|
4023
4211
|
function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
|
|
4024
4212
|
const resolved = resolveThinkingOptions(options);
|
|
@@ -4034,11 +4222,15 @@ function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
|
|
|
4034
4222
|
};
|
|
4035
4223
|
}
|
|
4036
4224
|
var openaiReasoningEmit = (body, resolved, model) => {
|
|
4037
|
-
const oSeries = isOSeriesModel(model);
|
|
4038
|
-
const reasoningCapable = supportsReasoningEffort(model);
|
|
4039
4225
|
const wireEffort = resolveOpenAIChatReasoningEffort(model, resolved);
|
|
4040
4226
|
if (wireEffort) body.reasoning_effort = wireEffort;
|
|
4041
|
-
return {
|
|
4227
|
+
return {
|
|
4228
|
+
stripTemperature: stripsPortableSampling(
|
|
4229
|
+
resolveOpenAIModelDescriptor(model),
|
|
4230
|
+
"chat",
|
|
4231
|
+
wireEffort
|
|
4232
|
+
)
|
|
4233
|
+
};
|
|
4042
4234
|
};
|
|
4043
4235
|
var OPENAI_PROFILE = {
|
|
4044
4236
|
name: "openai",
|
|
@@ -4073,6 +4265,9 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
4073
4265
|
"OpenAI Chat Completions"
|
|
4074
4266
|
);
|
|
4075
4267
|
}
|
|
4268
|
+
validateFinalRequestBody(body) {
|
|
4269
|
+
validateOpenAIFinalRequestBody(body, "chat");
|
|
4270
|
+
}
|
|
4076
4271
|
computeCost(model, usage, _reportedCost, context) {
|
|
4077
4272
|
return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
|
|
4078
4273
|
}
|
|
@@ -4345,12 +4540,12 @@ var OpenAIResponsesProvider = class {
|
|
|
4345
4540
|
// ---------------------------------------------------------------------------
|
|
4346
4541
|
buildRequestBody(messages, options, stream) {
|
|
4347
4542
|
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
4348
|
-
const
|
|
4349
|
-
const reasoningCapable =
|
|
4543
|
+
const descriptor = resolveOpenAIModelDescriptor(effectiveModel);
|
|
4544
|
+
const reasoningCapable = descriptor.reasoning;
|
|
4350
4545
|
const resolved = resolveThinkingOptions(options);
|
|
4351
4546
|
const { includeThoughts } = resolved;
|
|
4352
4547
|
const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
|
|
4353
|
-
const stripTemp =
|
|
4548
|
+
const stripTemp = stripsPortableSampling(descriptor, "responses", wireEffort);
|
|
4354
4549
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
4355
4550
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
4356
4551
|
const body = {
|
|
@@ -4399,6 +4594,7 @@ var OpenAIResponsesProvider = class {
|
|
|
4399
4594
|
if (options.providerOptions) {
|
|
4400
4595
|
Object.assign(body, options.providerOptions);
|
|
4401
4596
|
}
|
|
4597
|
+
validateOpenAIFinalRequestBody(body, "responses");
|
|
4402
4598
|
return body;
|
|
4403
4599
|
}
|
|
4404
4600
|
requestModel(body, fallback) {
|
|
@@ -4713,6 +4909,36 @@ function anthropicImageBlocks(parts, model) {
|
|
|
4713
4909
|
}
|
|
4714
4910
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
4715
4911
|
var ANTHROPIC_FILES_BETA = "files-api-2025-04-14";
|
|
4912
|
+
var ANTHROPIC_THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
|
|
4913
|
+
function isRecord(value) {
|
|
4914
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4915
|
+
}
|
|
4916
|
+
function hasReplayedThinking(body) {
|
|
4917
|
+
return Array.isArray(body.messages) && body.messages.some(
|
|
4918
|
+
(message) => isRecord(message) && Array.isArray(message.content) && message.content.some(
|
|
4919
|
+
(block) => isRecord(block) && (block.type === "thinking" || block.type === "redacted_thinking")
|
|
4920
|
+
)
|
|
4921
|
+
);
|
|
4922
|
+
}
|
|
4923
|
+
function hasFinalFileImage(body) {
|
|
4924
|
+
return Array.isArray(body.messages) && body.messages.some(
|
|
4925
|
+
(message) => isRecord(message) && Array.isArray(message.content) && message.content.some(
|
|
4926
|
+
(block) => isRecord(block) && block.type === "image" && isRecord(block.source) && block.source.type === "file"
|
|
4927
|
+
)
|
|
4928
|
+
);
|
|
4929
|
+
}
|
|
4930
|
+
function normalizeThinkingDrops(value) {
|
|
4931
|
+
if (!Array.isArray(value)) return void 0;
|
|
4932
|
+
const reasons = {};
|
|
4933
|
+
let droppedBlocks = 0;
|
|
4934
|
+
for (const entry of value) {
|
|
4935
|
+
if (!isRecord(entry) || entry.type !== "thinking_dropped") continue;
|
|
4936
|
+
const reason = entry.reason === "prefix_binding_mismatch" || entry.reason === "model_binding_mismatch" || entry.reason === "organization_binding_mismatch" || entry.reason === "end_user_binding_mismatch" ? entry.reason : "other";
|
|
4937
|
+
reasons[reason] = (reasons[reason] ?? 0) + 1;
|
|
4938
|
+
droppedBlocks++;
|
|
4939
|
+
}
|
|
4940
|
+
return droppedBlocks > 0 ? { droppedBlocks, reasons } : void 0;
|
|
4941
|
+
}
|
|
4716
4942
|
function hasAnthropicProviderFileImage(messages) {
|
|
4717
4943
|
return messages.some(
|
|
4718
4944
|
(message) => Array.isArray(message.content) && message.content.some(
|
|
@@ -4721,11 +4947,19 @@ function hasAnthropicProviderFileImage(messages) {
|
|
|
4721
4947
|
);
|
|
4722
4948
|
}
|
|
4723
4949
|
var CLAUDE_CAPABILITIES = {
|
|
4950
|
+
"claude-opus-5-5": {
|
|
4951
|
+
thinking: "adaptive-always-on",
|
|
4952
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
4953
|
+
manualBudget: false,
|
|
4954
|
+
stripTemperature: true,
|
|
4955
|
+
thinkingBinding: true
|
|
4956
|
+
},
|
|
4724
4957
|
"claude-fable-5-1": {
|
|
4725
4958
|
thinking: "adaptive-always-on",
|
|
4726
4959
|
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
4727
4960
|
manualBudget: false,
|
|
4728
|
-
stripTemperature: true
|
|
4961
|
+
stripTemperature: true,
|
|
4962
|
+
thinkingBinding: true
|
|
4729
4963
|
},
|
|
4730
4964
|
"claude-fable-5": {
|
|
4731
4965
|
thinking: "adaptive-always-on",
|
|
@@ -4819,6 +5053,7 @@ function resolveClaudeCapability(model) {
|
|
|
4819
5053
|
}
|
|
4820
5054
|
var DEFAULT_CACHE_READ_MULTIPLIER = 0.1;
|
|
4821
5055
|
var ANTHROPIC_RATES = {
|
|
5056
|
+
"claude-opus-5-5": { input: 4e-6, output: 2e-5, cacheReadMultiplier: 0.05 },
|
|
4822
5057
|
"claude-fable-5-1": { input: 1e-5, output: 5e-5, cacheReadMultiplier: 0.025 },
|
|
4823
5058
|
"claude-fable-5": { input: 1e-5, output: 5e-5 },
|
|
4824
5059
|
// Limited availability (Glasswing). Priced from the public pricing table; no
|
|
@@ -5253,11 +5488,12 @@ var AnthropicProvider = class {
|
|
|
5253
5488
|
// chat - non-streaming completion
|
|
5254
5489
|
// ---------------------------------------------------------------------------
|
|
5255
5490
|
async chat(messages, options) {
|
|
5491
|
+
const body = this.buildRequestBody(messages, options, false);
|
|
5256
5492
|
const headers = this.buildHeaders(
|
|
5257
5493
|
await this.resolveKey(),
|
|
5258
|
-
hasAnthropicProviderFileImage(messages)
|
|
5494
|
+
hasAnthropicProviderFileImage(messages) || hasFinalFileImage(body),
|
|
5495
|
+
this.requiresBindingBeta(body)
|
|
5259
5496
|
);
|
|
5260
|
-
const body = this.buildRequestBody(messages, options, false);
|
|
5261
5497
|
const pricingContext = pricingContextFromBody(body);
|
|
5262
5498
|
const recorder = new CallTimingRecorder(options.requestLifecycle);
|
|
5263
5499
|
const governor = this.governorFor(pricingContext.model ?? options.model);
|
|
@@ -5295,11 +5531,12 @@ var AnthropicProvider = class {
|
|
|
5295
5531
|
// stream - SSE streaming completion
|
|
5296
5532
|
// ---------------------------------------------------------------------------
|
|
5297
5533
|
async *stream(messages, options) {
|
|
5534
|
+
const body = this.buildRequestBody(messages, options, true);
|
|
5298
5535
|
const headers = this.buildHeaders(
|
|
5299
5536
|
await this.resolveKey(),
|
|
5300
|
-
hasAnthropicProviderFileImage(messages)
|
|
5537
|
+
hasAnthropicProviderFileImage(messages) || hasFinalFileImage(body),
|
|
5538
|
+
this.requiresBindingBeta(body)
|
|
5301
5539
|
);
|
|
5302
|
-
const body = this.buildRequestBody(messages, options, true);
|
|
5303
5540
|
const pricingContext = pricingContextFromBody(body);
|
|
5304
5541
|
const recorder = new CallTimingRecorder(options.requestLifecycle);
|
|
5305
5542
|
const governor = this.governorFor(pricingContext.model ?? options.model);
|
|
@@ -5341,12 +5578,61 @@ var AnthropicProvider = class {
|
|
|
5341
5578
|
// ---------------------------------------------------------------------------
|
|
5342
5579
|
// Internal: request building
|
|
5343
5580
|
// ---------------------------------------------------------------------------
|
|
5344
|
-
buildHeaders(apiKey, requiresFilesBeta = false) {
|
|
5581
|
+
buildHeaders(apiKey, requiresFilesBeta = false, requiresBindingBeta = false) {
|
|
5582
|
+
const betas = [
|
|
5583
|
+
...requiresFilesBeta ? [ANTHROPIC_FILES_BETA] : [],
|
|
5584
|
+
...requiresBindingBeta ? [ANTHROPIC_THINKING_BINDING_BETA] : []
|
|
5585
|
+
];
|
|
5345
5586
|
return {
|
|
5346
5587
|
"Content-Type": "application/json",
|
|
5347
5588
|
"x-api-key": apiKey,
|
|
5348
5589
|
"anthropic-version": ANTHROPIC_API_VERSION,
|
|
5349
|
-
...
|
|
5590
|
+
...betas.length > 0 ? { "anthropic-beta": betas.join(",") } : {}
|
|
5591
|
+
};
|
|
5592
|
+
}
|
|
5593
|
+
requiresBindingBeta(body) {
|
|
5594
|
+
const thinking = body.thinking;
|
|
5595
|
+
return resolveClaudeCapability(String(body.model))?.thinkingBinding === true && (hasReplayedThinking(body) || isRecord(thinking) && thinking.block_binding !== void 0);
|
|
5596
|
+
}
|
|
5597
|
+
validateAndBindFinalBody(body) {
|
|
5598
|
+
const model = typeof body.model === "string" ? body.model : "";
|
|
5599
|
+
if (resolveClaudeCapability(model)?.thinkingBinding !== true) return;
|
|
5600
|
+
const reject = (option, remediation) => {
|
|
5601
|
+
throw new UnsupportedModelOptionError({ provider: this.name, model, option, remediation });
|
|
5602
|
+
};
|
|
5603
|
+
const toolChoice = body.tool_choice;
|
|
5604
|
+
if (isRecord(toolChoice) && (toolChoice.type === "any" || toolChoice.type === "tool")) {
|
|
5605
|
+
reject("forced tool choice", "Use toolChoice: 'auto' or 'none' on this model.");
|
|
5606
|
+
}
|
|
5607
|
+
if (body.thinking !== void 0 && (!isRecord(body.thinking) || body.thinking.type !== "adaptive" || body.thinking.budget_tokens !== void 0)) {
|
|
5608
|
+
reject(
|
|
5609
|
+
"manual or disabled thinking",
|
|
5610
|
+
"Use thinking: { type: 'adaptive' } and output_config.effort."
|
|
5611
|
+
);
|
|
5612
|
+
}
|
|
5613
|
+
const outputConfig = body.output_config;
|
|
5614
|
+
if (outputConfig !== void 0 && (!isRecord(outputConfig) || outputConfig.effort !== void 0 && !["low", "medium", "high", "xhigh", "max"].includes(String(outputConfig.effort)))) {
|
|
5615
|
+
reject("output_config.effort", "Use low, medium, high, xhigh, or max effort.");
|
|
5616
|
+
}
|
|
5617
|
+
if (body.temperature !== void 0 && body.temperature !== 1) {
|
|
5618
|
+
reject("temperature", "Omit temperature or use the default value 1.");
|
|
5619
|
+
}
|
|
5620
|
+
if (body.top_p !== void 0 && (typeof body.top_p !== "number" || body.top_p < 0.99 || body.top_p > 1)) {
|
|
5621
|
+
reject("top_p", "Omit top_p or use a value from 0.99 to 1.");
|
|
5622
|
+
}
|
|
5623
|
+
if (body.top_k !== void 0) {
|
|
5624
|
+
reject("top_k", "Omit top_k on this model.");
|
|
5625
|
+
}
|
|
5626
|
+
const replayed = hasReplayedThinking(body);
|
|
5627
|
+
if (!replayed) return;
|
|
5628
|
+
const thinking = isRecord(body.thinking) ? body.thinking : { type: "adaptive" };
|
|
5629
|
+
const binding = thinking.block_binding;
|
|
5630
|
+
if (binding !== void 0 && (!isRecord(binding) || binding.prefix_mismatch_behavior !== "error" && binding.prefix_mismatch_behavior !== "drop_block")) {
|
|
5631
|
+
reject("thinking.block_binding", "Use prefix_mismatch_behavior: 'error' or 'drop_block'.");
|
|
5632
|
+
}
|
|
5633
|
+
body.thinking = {
|
|
5634
|
+
...thinking,
|
|
5635
|
+
block_binding: binding ?? { prefix_mismatch_behavior: "drop_block" }
|
|
5350
5636
|
};
|
|
5351
5637
|
}
|
|
5352
5638
|
/** Extract a human-readable message from an API error response body. */
|
|
@@ -5425,6 +5711,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5425
5711
|
if (options.providerOptions) {
|
|
5426
5712
|
Object.assign(body, options.providerOptions);
|
|
5427
5713
|
}
|
|
5714
|
+
this.validateAndBindFinalBody(body);
|
|
5428
5715
|
return body;
|
|
5429
5716
|
}
|
|
5430
5717
|
/**
|
|
@@ -5581,6 +5868,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5581
5868
|
}
|
|
5582
5869
|
}
|
|
5583
5870
|
const normalized = json.usage ? normalizeAnthropicUsage(json.usage) : void 0;
|
|
5871
|
+
const reasoningContextReset = normalizeThinkingDrops(json.input_transformations);
|
|
5584
5872
|
const effectiveModel = typeof json.model === "string" ? json.model : pricingContext.model;
|
|
5585
5873
|
if (hasFallbackBoundary) {
|
|
5586
5874
|
throw new Error(
|
|
@@ -5595,7 +5883,8 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5595
5883
|
usage: normalized?.usage,
|
|
5596
5884
|
cost,
|
|
5597
5885
|
costProvenance: tableEstimate(cost),
|
|
5598
|
-
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
5886
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0,
|
|
5887
|
+
...reasoningContextReset ? { diagnostics: { reasoningContextReset } } : {}
|
|
5599
5888
|
};
|
|
5600
5889
|
}
|
|
5601
5890
|
// ---------------------------------------------------------------------------
|
|
@@ -5617,6 +5906,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5617
5906
|
let hasFallbackBoundary = false;
|
|
5618
5907
|
let hasFallbackIterationSignal = false;
|
|
5619
5908
|
let refused = false;
|
|
5909
|
+
let reasoningContextReset;
|
|
5620
5910
|
const finalizeUsage = () => {
|
|
5621
5911
|
const normalized = rawUsage ? normalizeAnthropicUsage(rawUsage) : void 0;
|
|
5622
5912
|
usage = normalized?.usage;
|
|
@@ -5629,7 +5919,8 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5629
5919
|
usage,
|
|
5630
5920
|
cost,
|
|
5631
5921
|
costProvenance: tableEstimate(cost),
|
|
5632
|
-
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
5922
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0,
|
|
5923
|
+
...reasoningContextReset ? { diagnostics: { reasoningContextReset } } : {}
|
|
5633
5924
|
};
|
|
5634
5925
|
};
|
|
5635
5926
|
try {
|
|
@@ -5722,6 +6013,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5722
6013
|
rawUsage = mergeAnthropicUsage(rawUsage, event.message.usage);
|
|
5723
6014
|
}
|
|
5724
6015
|
if (typeof event.message?.model === "string") effectiveModel = event.message.model;
|
|
6016
|
+
reasoningContextReset = normalizeThinkingDrops(event.message?.input_transformations);
|
|
5725
6017
|
const messageFallback = hasFallbackIteration(event.message?.usage?.iterations);
|
|
5726
6018
|
if (isAnthropicResponseModifier(
|
|
5727
6019
|
"inference_geo",
|
|
@@ -5738,6 +6030,10 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
5738
6030
|
break;
|
|
5739
6031
|
}
|
|
5740
6032
|
case "message_delta": {
|
|
6033
|
+
const finalTransformations = event.input_transformations ?? event.delta?.input_transformations;
|
|
6034
|
+
if (finalTransformations !== void 0) {
|
|
6035
|
+
reasoningContextReset = normalizeThinkingDrops(finalTransformations);
|
|
6036
|
+
}
|
|
5741
6037
|
if (event.usage) {
|
|
5742
6038
|
rawUsage = mergeAnthropicUsage(rawUsage, event.usage);
|
|
5743
6039
|
finalizeUsage();
|
|
@@ -5966,7 +6262,31 @@ var GEMINI_PRICING = {
|
|
|
5966
6262
|
},
|
|
5967
6263
|
"gemini-3.8-flash": { input: 75e-8, cached: 75e-9, output: 375e-8 }
|
|
5968
6264
|
};
|
|
6265
|
+
var GEMINI_FLASH_SUCCESSOR_PRICING = {
|
|
6266
|
+
...GEMINI_PRICING,
|
|
6267
|
+
"gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 },
|
|
6268
|
+
"gemini-3.7-flash": {
|
|
6269
|
+
input: 15e-7,
|
|
6270
|
+
cached: 15e-8,
|
|
6271
|
+
output: 75e-7,
|
|
6272
|
+
audioInput: 15e-7
|
|
6273
|
+
},
|
|
6274
|
+
"gemini-3.8-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
|
|
6275
|
+
};
|
|
6276
|
+
var GEMINI_FLASH_SUCCESSOR_START_MS = Date.UTC(2027, 0, 1);
|
|
6277
|
+
function geminiRatesAtDispatch(at) {
|
|
6278
|
+
return at < GEMINI_FLASH_SUCCESSOR_START_MS ? GEMINI_PRICING : GEMINI_FLASH_SUCCESSOR_PRICING;
|
|
6279
|
+
}
|
|
5969
6280
|
var CANONICAL_GEMINI_BASE_URL = GEMINI_DEFAULT_BASE_URL;
|
|
6281
|
+
function geminiPricingTimingObserver(context, recorder) {
|
|
6282
|
+
return {
|
|
6283
|
+
...recorder.observer,
|
|
6284
|
+
onDispatch: (attempt, at) => {
|
|
6285
|
+
context.rates = geminiRatesAtDispatch(at);
|
|
6286
|
+
recorder.observer.onDispatch?.(attempt, at);
|
|
6287
|
+
}
|
|
6288
|
+
};
|
|
6289
|
+
}
|
|
5970
6290
|
function isValidTokenCount2(value) {
|
|
5971
6291
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
5972
6292
|
}
|
|
@@ -6047,8 +6367,8 @@ function geminiInteractionErrorStatus(code) {
|
|
|
6047
6367
|
function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
|
|
6048
6368
|
return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
|
|
6049
6369
|
}
|
|
6050
|
-
function estimateGeminiCost(model, usage) {
|
|
6051
|
-
const pricing =
|
|
6370
|
+
function estimateGeminiCost(model, usage, rates) {
|
|
6371
|
+
const pricing = rates && Object.hasOwn(rates, model) ? rates[model] : void 0;
|
|
6052
6372
|
const cached = usage.cachedTokens ?? 0;
|
|
6053
6373
|
if (!pricing || !isValidTokenCount2(usage.inputTokens) || !isValidTokenCount2(usage.outputTokens) || !isValidTokenCount2(cached) || cached > usage.inputTokens) {
|
|
6054
6374
|
return void 0;
|
|
@@ -6376,7 +6696,7 @@ var GeminiProvider = class {
|
|
|
6376
6696
|
{
|
|
6377
6697
|
governor: this.governorFor(pricingContext.model),
|
|
6378
6698
|
provider: this.name,
|
|
6379
|
-
timing: recorder
|
|
6699
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6380
6700
|
admission: options.dispatchAdmission
|
|
6381
6701
|
}
|
|
6382
6702
|
);
|
|
@@ -6419,7 +6739,7 @@ var GeminiProvider = class {
|
|
|
6419
6739
|
{
|
|
6420
6740
|
governor: this.governorFor(pricingContext.model),
|
|
6421
6741
|
provider: this.name,
|
|
6422
|
-
timing: recorder
|
|
6742
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6423
6743
|
admission: options.dispatchAdmission
|
|
6424
6744
|
}
|
|
6425
6745
|
);
|
|
@@ -6457,7 +6777,7 @@ var GeminiProvider = class {
|
|
|
6457
6777
|
{
|
|
6458
6778
|
governor: this.governorFor(pricingContext.model),
|
|
6459
6779
|
provider: this.name,
|
|
6460
|
-
timing: recorder
|
|
6780
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6461
6781
|
admission: options.dispatchAdmission
|
|
6462
6782
|
}
|
|
6463
6783
|
);
|
|
@@ -6493,7 +6813,7 @@ var GeminiProvider = class {
|
|
|
6493
6813
|
{
|
|
6494
6814
|
governor: this.governorFor(pricingContext.model),
|
|
6495
6815
|
provider: this.name,
|
|
6496
|
-
timing: recorder
|
|
6816
|
+
timing: geminiPricingTimingObserver(pricingContext, recorder),
|
|
6497
6817
|
admission: options.dispatchAdmission
|
|
6498
6818
|
}
|
|
6499
6819
|
);
|
|
@@ -6712,7 +7032,7 @@ var GeminiProvider = class {
|
|
|
6712
7032
|
)) {
|
|
6713
7033
|
return void 0;
|
|
6714
7034
|
}
|
|
6715
|
-
return estimateGeminiCost(effectiveModel, normalized.pricingUsage);
|
|
7035
|
+
return estimateGeminiCost(effectiveModel, normalized.pricingUsage, pricingContext.rates);
|
|
6716
7036
|
}
|
|
6717
7037
|
/**
|
|
6718
7038
|
* Whether every model-produced step is text. A non-text reply part (image,
|
|
@@ -7297,7 +7617,7 @@ var GeminiProvider = class {
|
|
|
7297
7617
|
pricingContext,
|
|
7298
7618
|
isDefinitiveStandardGeminiResponseTier(json.usageMetadata?.serviceTier),
|
|
7299
7619
|
json.usageMetadata?.serviceTier !== void 0 && !isDefinitiveStandardGeminiResponseTier(json.usageMetadata.serviceTier)
|
|
7300
|
-
) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
7620
|
+
) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage, pricingContext.rates) : void 0;
|
|
7301
7621
|
const rawParts = candidate?.content?.parts;
|
|
7302
7622
|
const providerMetadata = rawParts ? { geminiParts: rawParts } : void 0;
|
|
7303
7623
|
return {
|
|
@@ -7388,7 +7708,7 @@ var GeminiProvider = class {
|
|
|
7388
7708
|
pricingContext,
|
|
7389
7709
|
hasDefinitiveStandardResponseTier,
|
|
7390
7710
|
hasInvalidResponseTier
|
|
7391
|
-
) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage) : void 0;
|
|
7711
|
+
) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage, pricingContext.rates) : void 0;
|
|
7392
7712
|
yield {
|
|
7393
7713
|
type: "done",
|
|
7394
7714
|
usage: normalizedUsage?.usage,
|
|
@@ -9301,6 +9621,95 @@ function sessionHistoryForAsk(history, normalizedInput) {
|
|
|
9301
9621
|
return snapshot;
|
|
9302
9622
|
}
|
|
9303
9623
|
|
|
9624
|
+
// src/compaction.ts
|
|
9625
|
+
import { createHash } from "crypto";
|
|
9626
|
+
var SUMMARY_SYSTEM_PROMPT = "Summarize the following conversation concisely, preserving key facts, decisions, and context needed for continuing the conversation.";
|
|
9627
|
+
var SUMMARY_MAX_TOKENS = 1024;
|
|
9628
|
+
function buildSummaryPrompt(messages, previousSummary) {
|
|
9629
|
+
return [
|
|
9630
|
+
...previousSummary ? [`Previous conversation summary: ${previousSummary}`] : [],
|
|
9631
|
+
...messages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`)
|
|
9632
|
+
].join("\n");
|
|
9633
|
+
}
|
|
9634
|
+
function requestSummary(provider, options) {
|
|
9635
|
+
return provider.chat(
|
|
9636
|
+
[
|
|
9637
|
+
{ role: "system", content: SUMMARY_SYSTEM_PROMPT },
|
|
9638
|
+
{ role: "user", content: options.prompt }
|
|
9639
|
+
],
|
|
9640
|
+
{
|
|
9641
|
+
model: options.model,
|
|
9642
|
+
maxTokens: SUMMARY_MAX_TOKENS,
|
|
9643
|
+
...options.signal ? { signal: options.signal } : {}
|
|
9644
|
+
}
|
|
9645
|
+
);
|
|
9646
|
+
}
|
|
9647
|
+
function summaryContextMessage(summary) {
|
|
9648
|
+
return {
|
|
9649
|
+
role: "system",
|
|
9650
|
+
origin: "runtime",
|
|
9651
|
+
content: `Summary of earlier conversation:
|
|
9652
|
+
${summary}`
|
|
9653
|
+
};
|
|
9654
|
+
}
|
|
9655
|
+
function withoutAnthropicThinking(message) {
|
|
9656
|
+
const metadata = message.providerMetadata;
|
|
9657
|
+
if (!metadata || !Object.hasOwn(metadata, "anthropicThinkingBlocks")) return message;
|
|
9658
|
+
const retainedMetadata = { ...metadata };
|
|
9659
|
+
delete retainedMetadata.anthropicThinkingBlocks;
|
|
9660
|
+
return {
|
|
9661
|
+
...message,
|
|
9662
|
+
...Object.keys(retainedMetadata).length > 0 ? { providerMetadata: retainedMetadata } : { providerMetadata: void 0 }
|
|
9663
|
+
};
|
|
9664
|
+
}
|
|
9665
|
+
function anthropicThinkingBlockCount(message) {
|
|
9666
|
+
const blocks = message.providerMetadata?.anthropicThinkingBlocks;
|
|
9667
|
+
if (!Array.isArray(blocks)) return 0;
|
|
9668
|
+
return blocks.filter(
|
|
9669
|
+
(block) => block !== null && typeof block === "object" && (block.type === "thinking" || block.type === "redacted_thinking")
|
|
9670
|
+
).length;
|
|
9671
|
+
}
|
|
9672
|
+
function askSummaryMetaKey(agentName) {
|
|
9673
|
+
return `askSummary:${agentName}`;
|
|
9674
|
+
}
|
|
9675
|
+
function isCount(value) {
|
|
9676
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
9677
|
+
}
|
|
9678
|
+
function parseAskSummaryRecord(value) {
|
|
9679
|
+
if (!value || typeof value !== "object") return void 0;
|
|
9680
|
+
const record = value;
|
|
9681
|
+
if (typeof record.summary !== "string" || !isCount(record.coveredCount) || typeof record.prefixHash !== "string" || typeof record.summaryModelUri !== "string" || !isCount(record.invalidatedThinkingThrough) || record.sourceSummary !== void 0 && typeof record.sourceSummary !== "string") {
|
|
9682
|
+
return void 0;
|
|
9683
|
+
}
|
|
9684
|
+
return {
|
|
9685
|
+
summary: record.summary,
|
|
9686
|
+
coveredCount: record.coveredCount,
|
|
9687
|
+
prefixHash: record.prefixHash,
|
|
9688
|
+
...record.sourceSummary !== void 0 ? { sourceSummary: record.sourceSummary } : {},
|
|
9689
|
+
summaryModelUri: record.summaryModelUri,
|
|
9690
|
+
invalidatedThinkingThrough: record.invalidatedThinkingThrough
|
|
9691
|
+
};
|
|
9692
|
+
}
|
|
9693
|
+
function isPlainObject(value) {
|
|
9694
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
9695
|
+
const proto = Object.getPrototypeOf(value);
|
|
9696
|
+
return proto === Object.prototype || proto === null;
|
|
9697
|
+
}
|
|
9698
|
+
function summaryPrefixHash(history, coveredCount) {
|
|
9699
|
+
let canonical;
|
|
9700
|
+
try {
|
|
9701
|
+
canonical = JSON.stringify(
|
|
9702
|
+
history.slice(0, coveredCount),
|
|
9703
|
+
(_key, value) => isPlainObject(value) ? Object.fromEntries(
|
|
9704
|
+
Object.keys(value).sort().map((key) => [key, value[key]])
|
|
9705
|
+
) : value
|
|
9706
|
+
);
|
|
9707
|
+
} catch {
|
|
9708
|
+
return void 0;
|
|
9709
|
+
}
|
|
9710
|
+
return createHash("sha256").update(canonical).digest("hex");
|
|
9711
|
+
}
|
|
9712
|
+
|
|
9304
9713
|
// src/partial-json.ts
|
|
9305
9714
|
var MAX_DEPTH = 256;
|
|
9306
9715
|
function parsePartialJson(input) {
|
|
@@ -10937,9 +11346,9 @@ var LEGACY_REDACTION_RULES = {
|
|
|
10937
11346
|
// which is user content or PII (field paths are schema-shape, preserved like
|
|
10938
11347
|
// `string_delta.path`). Passthrough, as an explicit reviewed decision.
|
|
10939
11348
|
schema_diagnostic: passthrough,
|
|
10940
|
-
// `provider_diagnostic` carries only provider
|
|
10941
|
-
//
|
|
10942
|
-
//
|
|
11349
|
+
// `provider_diagnostic` carries only provider/model metadata, effort levels,
|
|
11350
|
+
// and bounded dropped-thinking reason counts. Raw transformation paths and
|
|
11351
|
+
// signed thinking never enter this event. Passthrough is reviewed here.
|
|
10943
11352
|
provider_diagnostic: passthrough,
|
|
10944
11353
|
log: (e) => {
|
|
10945
11354
|
if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
|
|
@@ -12944,7 +13353,14 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
12944
13353
|
* suppresses late branch events after bounded finalization. */
|
|
12945
13354
|
workflowLifecycleState;
|
|
12946
13355
|
signal;
|
|
12947
|
-
|
|
13356
|
+
/** Durable summary of messages removed by Session.history.maxMessages. */
|
|
13357
|
+
sessionSummary;
|
|
13358
|
+
/** Per-agent `maxContext` summaries, keyed by agent name. `null` records a
|
|
13359
|
+
* store lookup that found nothing. Mirrored to session metadata
|
|
13360
|
+
* (`askSummary:<agent>`) when this execution belongs to a session, so a
|
|
13361
|
+
* later execution can reuse the same exact-prefix boundary. Never the
|
|
13362
|
+
* durable `sessionSummary`. */
|
|
13363
|
+
askSummaries = /* @__PURE__ */ new Map();
|
|
12948
13364
|
/** Consecutive provider calls with promptCache on that wrote to the cache
|
|
12949
13365
|
* without reading from it (prefix changes per call), and consecutive calls
|
|
12950
13366
|
* with no cache activity at all (prefix below the model's minimum, which
|
|
@@ -13120,7 +13536,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
13120
13536
|
endEmitted: false
|
|
13121
13537
|
};
|
|
13122
13538
|
if (init.metadata?.summaryCache) {
|
|
13123
|
-
this.
|
|
13539
|
+
this.sessionSummary = init.metadata.summaryCache;
|
|
13124
13540
|
}
|
|
13125
13541
|
}
|
|
13126
13542
|
/**
|
|
@@ -13745,6 +14161,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
13745
14161
|
if (systemPrompt) {
|
|
13746
14162
|
messages.push({ role: "system", content: systemPrompt });
|
|
13747
14163
|
}
|
|
14164
|
+
const cachedSummaryMessage = this.sessionSummary ? summaryContextMessage(this.sessionSummary) : void 0;
|
|
13748
14165
|
const maxContext = agent2._config.maxContext;
|
|
13749
14166
|
if (maxContext && sessionHistory.length > 0) {
|
|
13750
14167
|
const reserveTokens = this.config.contextManagement?.reserveTokens ?? 2e3;
|
|
@@ -13769,22 +14186,44 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
13769
14186
|
}
|
|
13770
14187
|
});
|
|
13771
14188
|
}
|
|
13772
|
-
|
|
13773
|
-
|
|
14189
|
+
const summaryTokens = cachedSummaryMessage ? estimateMessagesTokens([cachedSummaryMessage]).tokens : 0;
|
|
14190
|
+
if (historyEstimate.tokens + summaryTokens > availableForHistory) {
|
|
14191
|
+
const projection = await this.summarizeHistory(
|
|
14192
|
+
agent2._name,
|
|
14193
|
+
modelUri,
|
|
13774
14194
|
provider,
|
|
13775
14195
|
model,
|
|
13776
14196
|
sessionHistory,
|
|
13777
14197
|
availableForHistory
|
|
13778
14198
|
);
|
|
13779
|
-
|
|
14199
|
+
if (this.budgetContext?.exceeded && this.budgetContext.policy !== "warn") {
|
|
14200
|
+
const { limit, totalCost: spent, policy } = this.budgetContext;
|
|
14201
|
+
throw new BudgetExceededError(limit, spent, policy);
|
|
14202
|
+
}
|
|
14203
|
+
if (projection.removedThinkingBlocks > 0) {
|
|
14204
|
+
this.emitEvent({
|
|
14205
|
+
type: "provider_diagnostic",
|
|
14206
|
+
agent: agent2._name,
|
|
14207
|
+
data: {
|
|
14208
|
+
kind: "reasoning_context_reset",
|
|
14209
|
+
...provider.name ? { provider: provider.name } : {},
|
|
14210
|
+
model: typeof providerOptions?.model === "string" ? providerOptions.model : model,
|
|
14211
|
+
droppedBlocks: projection.removedThinkingBlocks,
|
|
14212
|
+
reasons: { client_prefix_rewrite: projection.removedThinkingBlocks }
|
|
14213
|
+
}
|
|
14214
|
+
});
|
|
14215
|
+
}
|
|
14216
|
+
for (const msg of projection.messages) {
|
|
13780
14217
|
messages.push(msg);
|
|
13781
14218
|
}
|
|
13782
14219
|
} else {
|
|
14220
|
+
if (cachedSummaryMessage) messages.push(cachedSummaryMessage);
|
|
13783
14221
|
for (const msg of sessionHistory) {
|
|
13784
14222
|
messages.push(msg);
|
|
13785
14223
|
}
|
|
13786
14224
|
}
|
|
13787
14225
|
} else {
|
|
14226
|
+
if (cachedSummaryMessage) messages.push(cachedSummaryMessage);
|
|
13788
14227
|
for (const msg of sessionHistory) {
|
|
13789
14228
|
messages.push(msg);
|
|
13790
14229
|
}
|
|
@@ -13885,6 +14324,7 @@ ${appendedSchemaText}`;
|
|
|
13885
14324
|
}
|
|
13886
14325
|
const startTime = Date.now();
|
|
13887
14326
|
const askClock = currentAskClock();
|
|
14327
|
+
const pausedAtStartMs = askClock ? pausedAskClockMs(askClock, startTime) : 0;
|
|
13888
14328
|
const timingTotals = { turns: 0, queuedMs: 0, retryMs: 0, wireMs: 0 };
|
|
13889
14329
|
const currentMessages = [...messages];
|
|
13890
14330
|
let turns = 0;
|
|
@@ -13902,7 +14342,7 @@ ${appendedSchemaText}`;
|
|
|
13902
14342
|
this.currentSignal?.throwIfAborted();
|
|
13903
14343
|
const now = Date.now();
|
|
13904
14344
|
const elapsedMs = now - startTime;
|
|
13905
|
-
const pausedMs = askClock ? pausedAskClockMs(askClock, now) : 0;
|
|
14345
|
+
const pausedMs = askClock ? Math.max(0, pausedAskClockMs(askClock, now) - pausedAtStartMs) : 0;
|
|
13906
14346
|
const chargedMs = Math.max(0, elapsedMs - Math.min(pausedMs, Math.max(0, elapsedMs)));
|
|
13907
14347
|
if (chargedMs > timeoutMs) {
|
|
13908
14348
|
throw new TimeoutError(
|
|
@@ -14157,6 +14597,7 @@ ${appendedSchemaText}`;
|
|
|
14157
14597
|
usage: chunk.usage,
|
|
14158
14598
|
cost: chunk.cost,
|
|
14159
14599
|
providerMetadata: chunk.providerMetadata,
|
|
14600
|
+
diagnostics: chunk.diagnostics,
|
|
14160
14601
|
timing: chunk.timing
|
|
14161
14602
|
};
|
|
14162
14603
|
}
|
|
@@ -14274,6 +14715,34 @@ ${appendedSchemaText}`;
|
|
|
14274
14715
|
timingTotals.retryMs += response.timing.retryMs;
|
|
14275
14716
|
timingTotals.wireMs += response.timing.wireMs;
|
|
14276
14717
|
}
|
|
14718
|
+
const reasoningReset = response.diagnostics?.reasoningContextReset;
|
|
14719
|
+
if (reasoningReset && Number.isSafeInteger(reasoningReset.droppedBlocks) && reasoningReset.droppedBlocks > 0) {
|
|
14720
|
+
const reasons = {};
|
|
14721
|
+
for (const reason of [
|
|
14722
|
+
"prefix_binding_mismatch",
|
|
14723
|
+
"model_binding_mismatch",
|
|
14724
|
+
"organization_binding_mismatch",
|
|
14725
|
+
"end_user_binding_mismatch",
|
|
14726
|
+
"client_prefix_rewrite",
|
|
14727
|
+
"other"
|
|
14728
|
+
]) {
|
|
14729
|
+
const count = reasoningReset.reasons?.[reason];
|
|
14730
|
+
if (typeof count === "number" && Number.isSafeInteger(count) && count > 0) {
|
|
14731
|
+
reasons[reason] = count;
|
|
14732
|
+
}
|
|
14733
|
+
}
|
|
14734
|
+
this.emitEvent({
|
|
14735
|
+
type: "provider_diagnostic",
|
|
14736
|
+
agent: agent2._name,
|
|
14737
|
+
data: {
|
|
14738
|
+
kind: "reasoning_context_reset",
|
|
14739
|
+
...provider.name ? { provider: provider.name } : {},
|
|
14740
|
+
model: typeof providerOptions?.model === "string" ? providerOptions.model : model,
|
|
14741
|
+
droppedBlocks: reasoningReset.droppedBlocks,
|
|
14742
|
+
reasons
|
|
14743
|
+
}
|
|
14744
|
+
});
|
|
14745
|
+
}
|
|
14277
14746
|
this.emitEvent({
|
|
14278
14747
|
type: "agent_call_end",
|
|
14279
14748
|
agent: agent2._name,
|
|
@@ -15016,31 +15485,65 @@ ${appendedSchemaText}`;
|
|
|
15016
15485
|
}
|
|
15017
15486
|
return defs;
|
|
15018
15487
|
}
|
|
15488
|
+
/** Session id for session-scoped ask-summary persistence, when this
|
|
15489
|
+
* execution belongs to a session and has a store to persist into. */
|
|
15490
|
+
askSummarySessionId() {
|
|
15491
|
+
const sessionId = this.metadata?.sessionId;
|
|
15492
|
+
if (this.metadata?.sessionPersist === false) return void 0;
|
|
15493
|
+
return this.stateStore && typeof sessionId === "string" && sessionId.length > 0 ? sessionId : void 0;
|
|
15494
|
+
}
|
|
15495
|
+
/** This agent's ask summary: the in-memory copy first, else the session's
|
|
15496
|
+
* persisted record (read once per execution). */
|
|
15497
|
+
async loadAskSummary(agentName) {
|
|
15498
|
+
if (this.askSummaries.has(agentName)) return this.askSummaries.get(agentName) ?? void 0;
|
|
15499
|
+
const sessionId = this.askSummarySessionId();
|
|
15500
|
+
const stored = sessionId ? parseAskSummaryRecord(
|
|
15501
|
+
await this.stateStore.getSessionMeta(sessionId, askSummaryMetaKey(agentName))
|
|
15502
|
+
) : void 0;
|
|
15503
|
+
this.askSummaries.set(agentName, stored ?? null);
|
|
15504
|
+
return stored;
|
|
15505
|
+
}
|
|
15506
|
+
async saveAskSummary(agentName, record) {
|
|
15507
|
+
this.askSummaries.set(agentName, record);
|
|
15508
|
+
const sessionId = this.askSummarySessionId();
|
|
15509
|
+
if (!sessionId) return;
|
|
15510
|
+
const key = askSummaryMetaKey(agentName);
|
|
15511
|
+
try {
|
|
15512
|
+
await this.stateStore.saveSessionMeta(sessionId, key, record);
|
|
15513
|
+
} catch (error) {
|
|
15514
|
+
this.emitEvent({
|
|
15515
|
+
type: "log",
|
|
15516
|
+
agent: agentName,
|
|
15517
|
+
data: {
|
|
15518
|
+
warning: `Could not persist ask summary '${key}' for session '${sessionId}'; later executions will regenerate it. ` + (error instanceof Error ? error.message : String(error))
|
|
15519
|
+
}
|
|
15520
|
+
});
|
|
15521
|
+
}
|
|
15522
|
+
}
|
|
15019
15523
|
/**
|
|
15020
|
-
*
|
|
15021
|
-
*
|
|
15524
|
+
* Project `history` into `availableTokens` for one agent's request: a summary
|
|
15525
|
+
* of an exact covered prefix followed by every later message. Reuses the
|
|
15526
|
+
* agent's cached boundary (in memory or persisted for the session) while the
|
|
15527
|
+
* covered prefix, durable session summary, and summary model are unchanged
|
|
15528
|
+
* and the whole tail fits; otherwise generates a new summary. Also reports how
|
|
15529
|
+
* many Anthropic thinking blocks the projection removed from carried turns.
|
|
15022
15530
|
*/
|
|
15023
|
-
async summarizeHistory(provider, model, history, availableTokens) {
|
|
15024
|
-
|
|
15025
|
-
|
|
15026
|
-
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
15030
|
-
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
splitIdx2 = i;
|
|
15040
|
-
}
|
|
15041
|
-
splitIdx2 = nextUserTurn(history, splitIdx2) ?? history.length;
|
|
15042
|
-
if (splitIdx2 < history.length) {
|
|
15043
|
-
return [summaryMsg2, ...history.slice(splitIdx2)];
|
|
15531
|
+
async summarizeHistory(agentName, modelUri, provider, model, history, availableTokens) {
|
|
15532
|
+
const summaryModelUri = this.config.contextManagement?.summaryModel ?? modelUri;
|
|
15533
|
+
const cache = await this.loadAskSummary(agentName);
|
|
15534
|
+
if (cache && cache.sourceSummary === this.sessionSummary && cache.summaryModelUri === summaryModelUri && cache.coveredCount <= history.length && cache.prefixHash === summaryPrefixHash(history, cache.coveredCount)) {
|
|
15535
|
+
const summaryMsg = summaryContextMessage(cache.summary);
|
|
15536
|
+
const summaryTokens = estimateTokens(summarizeModelInput(summaryMsg.content)) + 4;
|
|
15537
|
+
const tail2 = history.slice(cache.coveredCount);
|
|
15538
|
+
const tailTokens = estimateMessagesTokens(tail2).tokens;
|
|
15539
|
+
if (summaryTokens + tailTokens <= availableTokens && (tail2.length === 0 || tail2[0].role === "user")) {
|
|
15540
|
+
let removedThinkingBlocks = 0;
|
|
15541
|
+
const projected = tail2.map((message, index) => {
|
|
15542
|
+
if (cache.coveredCount + index >= cache.invalidatedThinkingThrough) return message;
|
|
15543
|
+
removedThinkingBlocks += anthropicThinkingBlockCount(message);
|
|
15544
|
+
return withoutAnthropicThinking(message);
|
|
15545
|
+
});
|
|
15546
|
+
return { messages: [summaryMsg, ...projected], removedThinkingBlocks };
|
|
15044
15547
|
}
|
|
15045
15548
|
}
|
|
15046
15549
|
let recentTokens = 0;
|
|
@@ -15065,42 +15568,103 @@ ${this.summaryCache}`
|
|
|
15065
15568
|
} else {
|
|
15066
15569
|
splitIdx = history.length;
|
|
15067
15570
|
}
|
|
15068
|
-
if (splitIdx === 0)
|
|
15571
|
+
if (splitIdx === 0 && this.sessionSummary) splitIdx = history.length;
|
|
15572
|
+
if (splitIdx === 0) return { messages: history, removedThinkingBlocks: 0 };
|
|
15069
15573
|
const oldMessages = history.slice(0, splitIdx);
|
|
15070
|
-
const
|
|
15574
|
+
const configuredSummaryModelUri = this.config.contextManagement?.summaryModel;
|
|
15071
15575
|
let summaryProvider;
|
|
15072
15576
|
let summaryModel;
|
|
15073
|
-
if (
|
|
15074
|
-
const resolved = this.resolveProviderUri(
|
|
15577
|
+
if (configuredSummaryModelUri) {
|
|
15578
|
+
const resolved = this.resolveProviderUri(configuredSummaryModelUri);
|
|
15075
15579
|
summaryProvider = resolved.provider;
|
|
15076
15580
|
summaryModel = resolved.model;
|
|
15077
15581
|
} else {
|
|
15078
15582
|
summaryProvider = provider;
|
|
15079
15583
|
summaryModel = model;
|
|
15080
15584
|
}
|
|
15081
|
-
const
|
|
15082
|
-
const
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15585
|
+
const summaryPrompt = buildSummaryPrompt(oldMessages, this.sessionSummary);
|
|
15586
|
+
const summaryStart = Date.now();
|
|
15587
|
+
this.emitEvent({
|
|
15588
|
+
type: "agent_call_start",
|
|
15589
|
+
agent: agentName,
|
|
15590
|
+
model: summaryModelUri,
|
|
15591
|
+
turn: 0,
|
|
15592
|
+
data: {
|
|
15593
|
+
purpose: "summary",
|
|
15594
|
+
prompt: summaryPrompt,
|
|
15595
|
+
system: SUMMARY_SYSTEM_PROMPT,
|
|
15596
|
+
params: { maxTokens: SUMMARY_MAX_TOKENS },
|
|
15597
|
+
turn: 0
|
|
15598
|
+
}
|
|
15599
|
+
});
|
|
15600
|
+
let summaryResponse;
|
|
15601
|
+
try {
|
|
15602
|
+
this.currentSignal?.throwIfAborted();
|
|
15603
|
+
summaryResponse = await requestSummary(summaryProvider, {
|
|
15604
|
+
model: summaryModel,
|
|
15605
|
+
prompt: summaryPrompt,
|
|
15606
|
+
signal: this.currentSignal
|
|
15607
|
+
});
|
|
15608
|
+
} catch (error) {
|
|
15609
|
+
const providerError = error instanceof ProviderError ? error : void 0;
|
|
15610
|
+
this.emitEvent({
|
|
15611
|
+
type: "agent_call_end",
|
|
15612
|
+
agent: agentName,
|
|
15613
|
+
model: summaryModelUri,
|
|
15614
|
+
duration: Date.now() - summaryStart,
|
|
15615
|
+
...providerError?.timing ? { timing: providerError.timing } : {},
|
|
15616
|
+
data: {
|
|
15617
|
+
purpose: "summary",
|
|
15618
|
+
response: "",
|
|
15619
|
+
turn: 0,
|
|
15620
|
+
// Provider errors may echo the summarized history, so the event
|
|
15621
|
+
// uses a fixed projection even when trace redaction is disabled.
|
|
15622
|
+
error: "Context-management summary call failed",
|
|
15623
|
+
...providerError ? { status: providerError.status, retryable: providerError.retryable } : {}
|
|
15624
|
+
}
|
|
15625
|
+
});
|
|
15626
|
+
throw error;
|
|
15627
|
+
}
|
|
15628
|
+
if (isUsableCost2(summaryResponse.cost)) this._accumulateBudgetCost(summaryResponse.cost);
|
|
15629
|
+
this.emitEvent({
|
|
15630
|
+
type: "agent_call_end",
|
|
15631
|
+
agent: agentName,
|
|
15632
|
+
model: summaryModelUri,
|
|
15633
|
+
cost: summaryResponse.cost,
|
|
15634
|
+
...!isUsableCost2(summaryResponse.cost) ? { unpriced: true } : {},
|
|
15635
|
+
tokens: summaryResponse.usage ? {
|
|
15636
|
+
input: summaryResponse.usage.prompt_tokens,
|
|
15637
|
+
output: summaryResponse.usage.completion_tokens,
|
|
15638
|
+
reasoning: summaryResponse.usage.reasoning_tokens,
|
|
15639
|
+
cached: summaryResponse.usage.cached_tokens,
|
|
15640
|
+
cacheWrite: summaryResponse.usage.cache_write_tokens
|
|
15641
|
+
} : void 0,
|
|
15642
|
+
duration: Date.now() - summaryStart,
|
|
15643
|
+
...summaryResponse.timing ? { timing: summaryResponse.timing } : {},
|
|
15644
|
+
data: { purpose: "summary", response: summaryResponse.content, turn: 0 }
|
|
15645
|
+
});
|
|
15646
|
+
const prefixHash = summaryPrefixHash(history, splitIdx);
|
|
15647
|
+
if (prefixHash) {
|
|
15648
|
+
await this.saveAskSummary(agentName, {
|
|
15649
|
+
summary: summaryResponse.content,
|
|
15650
|
+
coveredCount: splitIdx,
|
|
15651
|
+
prefixHash,
|
|
15652
|
+
...this.sessionSummary !== void 0 ? { sourceSummary: this.sessionSummary } : {},
|
|
15653
|
+
summaryModelUri,
|
|
15654
|
+
invalidatedThinkingThrough: history.length
|
|
15655
|
+
});
|
|
15656
|
+
}
|
|
15657
|
+
const tail = history.slice(splitIdx);
|
|
15658
|
+
return {
|
|
15659
|
+
messages: [
|
|
15660
|
+
summaryContextMessage(summaryResponse.content),
|
|
15661
|
+
...tail.map(withoutAnthropicThinking)
|
|
15089
15662
|
],
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
if (sessionId && this.stateStore) {
|
|
15095
|
-
await this.stateStore.saveSessionMeta(sessionId, "summaryCache", this.summaryCache);
|
|
15096
|
-
}
|
|
15097
|
-
const summaryMsg = {
|
|
15098
|
-
role: "system",
|
|
15099
|
-
origin: "runtime",
|
|
15100
|
-
content: `Summary of earlier conversation:
|
|
15101
|
-
${summaryResponse.content}`
|
|
15663
|
+
removedThinkingBlocks: tail.reduce(
|
|
15664
|
+
(count, message) => count + anthropicThinkingBlockCount(message),
|
|
15665
|
+
0
|
|
15666
|
+
)
|
|
15102
15667
|
};
|
|
15103
|
-
return [summaryMsg, ...history.slice(splitIdx)];
|
|
15104
15668
|
}
|
|
15105
15669
|
// ── ctx.checkpoint() ────────────────────────────────────────────────
|
|
15106
15670
|
/**
|
|
@@ -17509,17 +18073,17 @@ var Session = class _Session {
|
|
|
17509
18073
|
throw new Error("SessionOptions.history.summaryModel is required when summarize is true");
|
|
17510
18074
|
}
|
|
17511
18075
|
const messagesToDrop = history.slice(0, history.length - maxMessages);
|
|
17512
|
-
const
|
|
17513
|
-
|
|
17514
|
-
|
|
17515
|
-
|
|
17516
|
-
|
|
18076
|
+
const summary = await this.runtime.summarizeMessages(
|
|
18077
|
+
messagesToDrop,
|
|
18078
|
+
summaryModel,
|
|
18079
|
+
cachedSummary ? { previousSummary: cachedSummary } : void 0
|
|
18080
|
+
);
|
|
17517
18081
|
await this.store.saveSessionMeta(this.sessionId, "summaryCache", summary);
|
|
17518
18082
|
cachedSummary = summary;
|
|
17519
18083
|
}
|
|
17520
18084
|
const trimmed = history.slice(-maxMessages);
|
|
17521
18085
|
history.length = 0;
|
|
17522
|
-
history.push(...trimmed);
|
|
18086
|
+
history.push(...trimmed.map(withoutAnthropicThinking));
|
|
17523
18087
|
}
|
|
17524
18088
|
const userMessage = { role: "user", content: preparedInput.content };
|
|
17525
18089
|
history.push(userMessage);
|
|
@@ -17529,7 +18093,10 @@ var Session = class _Session {
|
|
|
17529
18093
|
const metadata = {
|
|
17530
18094
|
sessionId: this.sessionId,
|
|
17531
18095
|
sessionHistory: history,
|
|
17532
|
-
...cachedSummary ? { summaryCache: cachedSummary } : {}
|
|
18096
|
+
...cachedSummary ? { summaryCache: cachedSummary } : {},
|
|
18097
|
+
// An opted-out session must leave nothing at rest, including the
|
|
18098
|
+
// agent-scoped ask-summary records the runtime would otherwise cache.
|
|
18099
|
+
...this.options.persist === false ? { sessionPersist: false } : {}
|
|
17533
18100
|
};
|
|
17534
18101
|
return { history, metadata };
|
|
17535
18102
|
}
|
|
@@ -20611,22 +21178,17 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
20611
21178
|
/**
|
|
20612
21179
|
* Summarize a list of chat messages into a concise summary string.
|
|
20613
21180
|
* Used by Session to summarize dropped messages when history.summarize is enabled.
|
|
21181
|
+
* `previousSummary` is folded in so a rolling summary never loses what it
|
|
21182
|
+
* alone still carries. Shares its prompt and provider call with the
|
|
21183
|
+
* `AgentConfig.maxContext` summarizer (see `compaction.ts`). It runs outside
|
|
21184
|
+
* any workflow execution, so it emits no ask-scoped events.
|
|
20614
21185
|
*/
|
|
20615
|
-
async summarizeMessages(messages, modelUri) {
|
|
21186
|
+
async summarizeMessages(messages, modelUri, options) {
|
|
20616
21187
|
const { provider, model } = this.resolveProvider(modelUri);
|
|
20617
|
-
const response = await provider
|
|
20618
|
-
|
|
20619
|
-
|
|
20620
|
-
|
|
20621
|
-
content: "Summarize the following conversation concisely, preserving key facts, decisions, and context needed for continuing the conversation."
|
|
20622
|
-
},
|
|
20623
|
-
{
|
|
20624
|
-
role: "user",
|
|
20625
|
-
content: messages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`).join("\n")
|
|
20626
|
-
}
|
|
20627
|
-
],
|
|
20628
|
-
{ model, maxTokens: 1024 }
|
|
20629
|
-
);
|
|
21188
|
+
const response = await requestSummary(provider, {
|
|
21189
|
+
model,
|
|
21190
|
+
prompt: buildSummaryPrompt(messages, options?.previousSummary)
|
|
21191
|
+
});
|
|
20630
21192
|
return response.content;
|
|
20631
21193
|
}
|
|
20632
21194
|
/** Get the state store (for testing and advanced use cases). */
|
|
@@ -22056,6 +22618,7 @@ export {
|
|
|
22056
22618
|
TranscriptionProviderRegistry,
|
|
22057
22619
|
UnsupportedEventSchemaVersionError,
|
|
22058
22620
|
UnsupportedModelInputError,
|
|
22621
|
+
UnsupportedModelOptionError,
|
|
22059
22622
|
UnsupportedTranscriptionInputError,
|
|
22060
22623
|
VLLM_PROFILE,
|
|
22061
22624
|
ValidationError,
|