@axlsdk/axl 0.24.3 → 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.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);
@@ -193,7 +210,7 @@ var TimeoutError = class extends AxlError {
193
210
  constructor(operation, timeoutMs, breakdown, agent2) {
194
211
  super(
195
212
  "TIMEOUT",
196
- `${operation} exceeded timeout of ${timeoutMs}ms` + (breakdown ? ` (elapsed ${breakdown.elapsedMs}ms: queued ${breakdown.queuedMs}ms, retries ${breakdown.retryMs}ms, wire ${breakdown.wireMs}ms, other ${breakdown.otherMs}ms)` : "") + (agent2 ? ` for agent '${agent2}'` : "")
213
+ `${operation} exceeded timeout of ${timeoutMs}ms` + (breakdown ? ` (elapsed ${breakdown.elapsedMs}ms: queued ${breakdown.queuedMs}ms, retries ${breakdown.retryMs}ms, wire ${breakdown.wireMs}ms, other ${breakdown.otherMs}ms, charged ${breakdown.chargedMs}ms)` : "") + (agent2 ? ` for agent '${agent2}'` : "")
197
214
  );
198
215
  this.name = "TimeoutError";
199
216
  if (breakdown) this.breakdown = breakdown;
@@ -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.24.3" : "unknown"
351
+ version: true ? "0.25.1" : "unknown"
335
352
  };
336
353
  function warnCrossCopy(other, joined) {
337
354
  if (other === thisCopy) return;
@@ -1315,6 +1332,41 @@ var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
1315
1332
  var ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
1316
1333
  var GEMINI_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
1317
1334
 
1335
+ // src/ask-clock.ts
1336
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
1337
+ var askClockStorage = new AsyncLocalStorage2();
1338
+ function runWithAskClock(fn) {
1339
+ const tracker = { pausedMs: 0, activePauses: 0 };
1340
+ const enclosing = askClockStorage.getStore() ?? [];
1341
+ return askClockStorage.run([...enclosing, tracker], fn);
1342
+ }
1343
+ function currentAskClock() {
1344
+ return askClockStorage.getStore()?.at(-1);
1345
+ }
1346
+ function pausedAskClockMs(tracker, now = Date.now()) {
1347
+ return tracker.pausedMs + (tracker.activePauses > 0 && tracker.pauseStartedAt !== void 0 ? now - tracker.pauseStartedAt : 0);
1348
+ }
1349
+ async function pauseAskClocks(wait) {
1350
+ const trackers = askClockStorage.getStore();
1351
+ if (!trackers || trackers.length === 0) return wait();
1352
+ const startedAt = Date.now();
1353
+ for (const tracker of trackers) {
1354
+ if (tracker.activePauses++ === 0) tracker.pauseStartedAt = startedAt;
1355
+ }
1356
+ try {
1357
+ return await wait();
1358
+ } finally {
1359
+ const finishedAt = Date.now();
1360
+ for (const tracker of trackers) {
1361
+ tracker.activePauses--;
1362
+ if (tracker.activePauses === 0 && tracker.pauseStartedAt !== void 0) {
1363
+ tracker.pausedMs += finishedAt - tracker.pauseStartedAt;
1364
+ tracker.pauseStartedAt = void 0;
1365
+ }
1366
+ }
1367
+ }
1368
+ }
1369
+
1318
1370
  // src/providers/rate-limiter.ts
1319
1371
  function sanitizeRateLimitConfig(config) {
1320
1372
  const limits = {};
@@ -2377,7 +2429,7 @@ async function fetchWithRetry(input, init, opts) {
2377
2429
  let headersAt = 0;
2378
2430
  const waitSelfImposed = async (wait) => {
2379
2431
  const start = Date.now();
2380
- await wait();
2432
+ await pauseAskClocks(wait);
2381
2433
  const waited = Date.now() - start;
2382
2434
  queuedMs += waited;
2383
2435
  if (dispatches > 0) queuedAfterFirstDispatchMs += waited;
@@ -3169,8 +3221,12 @@ var OpenAICompatibleProvider = class {
3169
3221
  for (const key of forbidden) {
3170
3222
  if (!userKeys.includes(key)) delete body[key];
3171
3223
  }
3224
+ this.validateFinalRequestBody(body);
3172
3225
  return body;
3173
3226
  }
3227
+ /** Native subclasses may reject model-invalid fields after providerOptions merge. */
3228
+ validateFinalRequestBody(_body) {
3229
+ }
3174
3230
  formatMessage(msg, model) {
3175
3231
  const modalities = this.inputModalities(model);
3176
3232
  const out = {
@@ -3578,6 +3634,24 @@ var withLongContext = (short) => ({
3578
3634
  output: short.output * 1.5
3579
3635
  });
3580
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
+ },
3581
3655
  {
3582
3656
  aliases: ["gpt-5.6", "gpt-5.6-sol"],
3583
3657
  snapshotBase: "gpt-5.6-sol",
@@ -3908,7 +3982,7 @@ function estimateDirectOpenAICost(model, usage, context) {
3908
3982
  const audioOutput = audio_output_tokens ?? 0;
3909
3983
  if (![prompt_tokens, completion_tokens, cached, cacheWrite, audioInput, audioOutput].every(
3910
3984
  (count) => Number.isSafeInteger(count) && count >= 0
3911
- ) || 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) {
3912
3986
  return void 0;
3913
3987
  }
3914
3988
  if (audioInput > 0 && entry.audio === void 0 || audioOutput > 0 && entry.audio?.output === void 0) {
@@ -3933,57 +4007,206 @@ function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens)
3933
4007
  cached_tokens: cachedTokens
3934
4008
  });
3935
4009
  }
3936
- function isOSeriesModel(model) {
3937
- return /^(o1|o3|o4-mini)/.test(model);
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 };
3938
4021
  }
3939
- function supportsReasoningEffort(model) {
3940
- return isOSeriesModel(model) || /^gpt-5/.test(model);
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
+ };
3941
4037
  }
3942
- var GPT_56_REASONING_MODELS = /* @__PURE__ */ new Set([
3943
- "gpt-5.6",
3944
- "gpt-5.6-sol",
3945
- "gpt-5.6-terra",
3946
- "gpt-5.6-luna"
3947
- ]);
3948
- function supportsMaxReasoningEffort(model) {
3949
- return GPT_56_REASONING_MODELS.has(model);
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
+ };
3950
4051
  }
3951
- function supportsReasoningNone(model) {
3952
- return /^gpt-5\.[1-9]/.test(model);
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
+ };
3953
4115
  }
3954
- function supportsXhigh(model) {
3955
- return /^gpt-5\.([2-9]|\d{2,})/.test(model);
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;
3956
4139
  }
3957
- function clampReasoningEffort(model, effort) {
3958
- if (model.startsWith("gpt-5-pro")) return "high";
3959
- if (effort === "none" && !supportsReasoningNone(model)) return "minimal";
3960
- if (effort === "max" && !supportsMaxReasoningEffort(model)) {
3961
- return clampReasoningEffort(model, "xhigh");
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";
3962
4150
  }
3963
- if (effort === "xhigh" && !supportsXhigh(model)) return "high";
3964
- return effort;
3965
4151
  }
3966
- function effortToReasoningEffort(effort, model) {
3967
- return effort === "max" && model !== void 0 && supportsMaxReasoningEffort(model) ? "max" : effort === "max" ? "xhigh" : effort;
4152
+ function isOSeriesModel(model) {
4153
+ return resolveOpenAIModelDescriptor(model).family === "o-series";
3968
4154
  }
3969
4155
  function budgetToReasoningEffort(budget) {
3970
4156
  if (budget <= 1024) return "low";
3971
4157
  if (budget <= 8192) return "medium";
3972
4158
  return "high";
3973
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
+ }
3974
4166
  function resolveOpenAIReasoningEffort(model, resolved) {
3975
- if (!supportsReasoningEffort(model)) return void 0;
3976
- if (resolved.hasBudgetOverride) {
3977
- return clampReasoningEffort(model, budgetToReasoningEffort(resolved.thinkingBudget));
3978
- }
3979
- if (!resolved.thinkingDisabled && resolved.activeEffort) {
3980
- return clampReasoningEffort(model, effortToReasoningEffort(resolved.activeEffort, model));
3981
- }
3982
- return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
4167
+ return resolveEndpointReasoningEffort(model, "responses", resolved);
3983
4168
  }
3984
4169
  function resolveOpenAIChatReasoningEffort(model, resolved) {
3985
- const effort = resolveOpenAIReasoningEffort(model, resolved);
3986
- return effort === "max" ? "xhigh" : effort;
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
+ }
3987
4210
  }
3988
4211
  function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
3989
4212
  const resolved = resolveThinkingOptions(options);
@@ -3999,11 +4222,15 @@ function resolveOpenAIEffortResolution(options, resolveWireEffort, endpoint) {
3999
4222
  };
4000
4223
  }
4001
4224
  var openaiReasoningEmit = (body, resolved, model) => {
4002
- const oSeries = isOSeriesModel(model);
4003
- const reasoningCapable = supportsReasoningEffort(model);
4004
4225
  const wireEffort = resolveOpenAIChatReasoningEffort(model, resolved);
4005
4226
  if (wireEffort) body.reasoning_effort = wireEffort;
4006
- return { stripTemperature: oSeries || reasoningCapable && wireEffort !== void 0 };
4227
+ return {
4228
+ stripTemperature: stripsPortableSampling(
4229
+ resolveOpenAIModelDescriptor(model),
4230
+ "chat",
4231
+ wireEffort
4232
+ )
4233
+ };
4007
4234
  };
4008
4235
  var OPENAI_PROFILE = {
4009
4236
  name: "openai",
@@ -4038,6 +4265,9 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
4038
4265
  "OpenAI Chat Completions"
4039
4266
  );
4040
4267
  }
4268
+ validateFinalRequestBody(body) {
4269
+ validateOpenAIFinalRequestBody(body, "chat");
4270
+ }
4041
4271
  computeCost(model, usage, _reportedCost, context) {
4042
4272
  return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
4043
4273
  }
@@ -4310,12 +4540,12 @@ var OpenAIResponsesProvider = class {
4310
4540
  // ---------------------------------------------------------------------------
4311
4541
  buildRequestBody(messages, options, stream) {
4312
4542
  const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
4313
- const oSeries = isOSeriesModel(effectiveModel);
4314
- const reasoningCapable = supportsReasoningEffort(effectiveModel);
4543
+ const descriptor = resolveOpenAIModelDescriptor(effectiveModel);
4544
+ const reasoningCapable = descriptor.reasoning;
4315
4545
  const resolved = resolveThinkingOptions(options);
4316
4546
  const { includeThoughts } = resolved;
4317
4547
  const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
4318
- const stripTemp = oSeries || supportsMaxReasoningEffort(effectiveModel) || reasoningCapable && wireEffort !== void 0;
4548
+ const stripTemp = stripsPortableSampling(descriptor, "responses", wireEffort);
4319
4549
  const systemMessages = messages.filter((m) => m.role === "system");
4320
4550
  const nonSystemMessages = messages.filter((m) => m.role !== "system");
4321
4551
  const body = {
@@ -4364,6 +4594,7 @@ var OpenAIResponsesProvider = class {
4364
4594
  if (options.providerOptions) {
4365
4595
  Object.assign(body, options.providerOptions);
4366
4596
  }
4597
+ validateOpenAIFinalRequestBody(body, "responses");
4367
4598
  return body;
4368
4599
  }
4369
4600
  requestModel(body, fallback) {
@@ -4678,6 +4909,36 @@ function anthropicImageBlocks(parts, model) {
4678
4909
  }
4679
4910
  var ANTHROPIC_API_VERSION = "2023-06-01";
4680
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
+ }
4681
4942
  function hasAnthropicProviderFileImage(messages) {
4682
4943
  return messages.some(
4683
4944
  (message) => Array.isArray(message.content) && message.content.some(
@@ -4686,11 +4947,19 @@ function hasAnthropicProviderFileImage(messages) {
4686
4947
  );
4687
4948
  }
4688
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
+ },
4689
4957
  "claude-fable-5-1": {
4690
4958
  thinking: "adaptive-always-on",
4691
4959
  effortLevels: ["low", "medium", "high", "xhigh", "max"],
4692
4960
  manualBudget: false,
4693
- stripTemperature: true
4961
+ stripTemperature: true,
4962
+ thinkingBinding: true
4694
4963
  },
4695
4964
  "claude-fable-5": {
4696
4965
  thinking: "adaptive-always-on",
@@ -4784,6 +5053,7 @@ function resolveClaudeCapability(model) {
4784
5053
  }
4785
5054
  var DEFAULT_CACHE_READ_MULTIPLIER = 0.1;
4786
5055
  var ANTHROPIC_RATES = {
5056
+ "claude-opus-5-5": { input: 4e-6, output: 2e-5, cacheReadMultiplier: 0.05 },
4787
5057
  "claude-fable-5-1": { input: 1e-5, output: 5e-5, cacheReadMultiplier: 0.025 },
4788
5058
  "claude-fable-5": { input: 1e-5, output: 5e-5 },
4789
5059
  // Limited availability (Glasswing). Priced from the public pricing table; no
@@ -5218,11 +5488,12 @@ var AnthropicProvider = class {
5218
5488
  // chat - non-streaming completion
5219
5489
  // ---------------------------------------------------------------------------
5220
5490
  async chat(messages, options) {
5491
+ const body = this.buildRequestBody(messages, options, false);
5221
5492
  const headers = this.buildHeaders(
5222
5493
  await this.resolveKey(),
5223
- hasAnthropicProviderFileImage(messages)
5494
+ hasAnthropicProviderFileImage(messages) || hasFinalFileImage(body),
5495
+ this.requiresBindingBeta(body)
5224
5496
  );
5225
- const body = this.buildRequestBody(messages, options, false);
5226
5497
  const pricingContext = pricingContextFromBody(body);
5227
5498
  const recorder = new CallTimingRecorder(options.requestLifecycle);
5228
5499
  const governor = this.governorFor(pricingContext.model ?? options.model);
@@ -5260,11 +5531,12 @@ var AnthropicProvider = class {
5260
5531
  // stream - SSE streaming completion
5261
5532
  // ---------------------------------------------------------------------------
5262
5533
  async *stream(messages, options) {
5534
+ const body = this.buildRequestBody(messages, options, true);
5263
5535
  const headers = this.buildHeaders(
5264
5536
  await this.resolveKey(),
5265
- hasAnthropicProviderFileImage(messages)
5537
+ hasAnthropicProviderFileImage(messages) || hasFinalFileImage(body),
5538
+ this.requiresBindingBeta(body)
5266
5539
  );
5267
- const body = this.buildRequestBody(messages, options, true);
5268
5540
  const pricingContext = pricingContextFromBody(body);
5269
5541
  const recorder = new CallTimingRecorder(options.requestLifecycle);
5270
5542
  const governor = this.governorFor(pricingContext.model ?? options.model);
@@ -5306,12 +5578,61 @@ var AnthropicProvider = class {
5306
5578
  // ---------------------------------------------------------------------------
5307
5579
  // Internal: request building
5308
5580
  // ---------------------------------------------------------------------------
5309
- 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
+ ];
5310
5586
  return {
5311
5587
  "Content-Type": "application/json",
5312
5588
  "x-api-key": apiKey,
5313
5589
  "anthropic-version": ANTHROPIC_API_VERSION,
5314
- ...requiresFilesBeta ? { "anthropic-beta": ANTHROPIC_FILES_BETA } : {}
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" }
5315
5636
  };
5316
5637
  }
5317
5638
  /** Extract a human-readable message from an API error response body. */
@@ -5390,6 +5711,7 @@ ${jsonInstruction}` : jsonInstruction;
5390
5711
  if (options.providerOptions) {
5391
5712
  Object.assign(body, options.providerOptions);
5392
5713
  }
5714
+ this.validateAndBindFinalBody(body);
5393
5715
  return body;
5394
5716
  }
5395
5717
  /**
@@ -5546,6 +5868,7 @@ ${jsonInstruction}` : jsonInstruction;
5546
5868
  }
5547
5869
  }
5548
5870
  const normalized = json.usage ? normalizeAnthropicUsage(json.usage) : void 0;
5871
+ const reasoningContextReset = normalizeThinkingDrops(json.input_transformations);
5549
5872
  const effectiveModel = typeof json.model === "string" ? json.model : pricingContext.model;
5550
5873
  if (hasFallbackBoundary) {
5551
5874
  throw new Error(
@@ -5560,7 +5883,8 @@ ${jsonInstruction}` : jsonInstruction;
5560
5883
  usage: normalized?.usage,
5561
5884
  cost,
5562
5885
  costProvenance: tableEstimate(cost),
5563
- providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
5886
+ providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0,
5887
+ ...reasoningContextReset ? { diagnostics: { reasoningContextReset } } : {}
5564
5888
  };
5565
5889
  }
5566
5890
  // ---------------------------------------------------------------------------
@@ -5582,6 +5906,7 @@ ${jsonInstruction}` : jsonInstruction;
5582
5906
  let hasFallbackBoundary = false;
5583
5907
  let hasFallbackIterationSignal = false;
5584
5908
  let refused = false;
5909
+ let reasoningContextReset;
5585
5910
  const finalizeUsage = () => {
5586
5911
  const normalized = rawUsage ? normalizeAnthropicUsage(rawUsage) : void 0;
5587
5912
  usage = normalized?.usage;
@@ -5594,7 +5919,8 @@ ${jsonInstruction}` : jsonInstruction;
5594
5919
  usage,
5595
5920
  cost,
5596
5921
  costProvenance: tableEstimate(cost),
5597
- 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 } } : {}
5598
5924
  };
5599
5925
  };
5600
5926
  try {
@@ -5687,6 +6013,7 @@ ${jsonInstruction}` : jsonInstruction;
5687
6013
  rawUsage = mergeAnthropicUsage(rawUsage, event.message.usage);
5688
6014
  }
5689
6015
  if (typeof event.message?.model === "string") effectiveModel = event.message.model;
6016
+ reasoningContextReset = normalizeThinkingDrops(event.message?.input_transformations);
5690
6017
  const messageFallback = hasFallbackIteration(event.message?.usage?.iterations);
5691
6018
  if (isAnthropicResponseModifier(
5692
6019
  "inference_geo",
@@ -5703,6 +6030,10 @@ ${jsonInstruction}` : jsonInstruction;
5703
6030
  break;
5704
6031
  }
5705
6032
  case "message_delta": {
6033
+ const finalTransformations = event.input_transformations ?? event.delta?.input_transformations;
6034
+ if (finalTransformations !== void 0) {
6035
+ reasoningContextReset = normalizeThinkingDrops(finalTransformations);
6036
+ }
5706
6037
  if (event.usage) {
5707
6038
  rawUsage = mergeAnthropicUsage(rawUsage, event.usage);
5708
6039
  finalizeUsage();
@@ -5931,7 +6262,31 @@ var GEMINI_PRICING = {
5931
6262
  },
5932
6263
  "gemini-3.8-flash": { input: 75e-8, cached: 75e-9, output: 375e-8 }
5933
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
+ }
5934
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
+ }
5935
6290
  function isValidTokenCount2(value) {
5936
6291
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5937
6292
  }
@@ -6012,8 +6367,8 @@ function geminiInteractionErrorStatus(code) {
6012
6367
  function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
6013
6368
  return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
6014
6369
  }
6015
- function estimateGeminiCost(model, usage) {
6016
- const pricing = GEMINI_PRICING[model];
6370
+ function estimateGeminiCost(model, usage, rates) {
6371
+ const pricing = rates && Object.hasOwn(rates, model) ? rates[model] : void 0;
6017
6372
  const cached = usage.cachedTokens ?? 0;
6018
6373
  if (!pricing || !isValidTokenCount2(usage.inputTokens) || !isValidTokenCount2(usage.outputTokens) || !isValidTokenCount2(cached) || cached > usage.inputTokens) {
6019
6374
  return void 0;
@@ -6341,7 +6696,7 @@ var GeminiProvider = class {
6341
6696
  {
6342
6697
  governor: this.governorFor(pricingContext.model),
6343
6698
  provider: this.name,
6344
- timing: recorder.observer,
6699
+ timing: geminiPricingTimingObserver(pricingContext, recorder),
6345
6700
  admission: options.dispatchAdmission
6346
6701
  }
6347
6702
  );
@@ -6384,7 +6739,7 @@ var GeminiProvider = class {
6384
6739
  {
6385
6740
  governor: this.governorFor(pricingContext.model),
6386
6741
  provider: this.name,
6387
- timing: recorder.observer,
6742
+ timing: geminiPricingTimingObserver(pricingContext, recorder),
6388
6743
  admission: options.dispatchAdmission
6389
6744
  }
6390
6745
  );
@@ -6422,7 +6777,7 @@ var GeminiProvider = class {
6422
6777
  {
6423
6778
  governor: this.governorFor(pricingContext.model),
6424
6779
  provider: this.name,
6425
- timing: recorder.observer,
6780
+ timing: geminiPricingTimingObserver(pricingContext, recorder),
6426
6781
  admission: options.dispatchAdmission
6427
6782
  }
6428
6783
  );
@@ -6458,7 +6813,7 @@ var GeminiProvider = class {
6458
6813
  {
6459
6814
  governor: this.governorFor(pricingContext.model),
6460
6815
  provider: this.name,
6461
- timing: recorder.observer,
6816
+ timing: geminiPricingTimingObserver(pricingContext, recorder),
6462
6817
  admission: options.dispatchAdmission
6463
6818
  }
6464
6819
  );
@@ -6677,7 +7032,7 @@ var GeminiProvider = class {
6677
7032
  )) {
6678
7033
  return void 0;
6679
7034
  }
6680
- return estimateGeminiCost(effectiveModel, normalized.pricingUsage);
7035
+ return estimateGeminiCost(effectiveModel, normalized.pricingUsage, pricingContext.rates);
6681
7036
  }
6682
7037
  /**
6683
7038
  * Whether every model-produced step is text. A non-text reply part (image,
@@ -7262,7 +7617,7 @@ var GeminiProvider = class {
7262
7617
  pricingContext,
7263
7618
  isDefinitiveStandardGeminiResponseTier(json.usageMetadata?.serviceTier),
7264
7619
  json.usageMetadata?.serviceTier !== void 0 && !isDefinitiveStandardGeminiResponseTier(json.usageMetadata.serviceTier)
7265
- ) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage) : void 0;
7620
+ ) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage, pricingContext.rates) : void 0;
7266
7621
  const rawParts = candidate?.content?.parts;
7267
7622
  const providerMetadata = rawParts ? { geminiParts: rawParts } : void 0;
7268
7623
  return {
@@ -7353,7 +7708,7 @@ var GeminiProvider = class {
7353
7708
  pricingContext,
7354
7709
  hasDefinitiveStandardResponseTier,
7355
7710
  hasInvalidResponseTier
7356
- ) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage) : void 0;
7711
+ ) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage, pricingContext.rates) : void 0;
7357
7712
  yield {
7358
7713
  type: "done",
7359
7714
  usage: normalizedUsage?.usage,
@@ -9002,7 +9357,7 @@ var TranscriptionProviderRegistry = class {
9002
9357
  };
9003
9358
 
9004
9359
  // src/context.ts
9005
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
9360
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
9006
9361
  import { randomUUID } from "crypto";
9007
9362
  import { z, ZodError as ZodError2 } from "zod";
9008
9363
 
@@ -9266,6 +9621,95 @@ function sessionHistoryForAsk(history, normalizedInput) {
9266
9621
  return snapshot;
9267
9622
  }
9268
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
+
9269
9713
  // src/partial-json.ts
9270
9714
  var MAX_DEPTH = 256;
9271
9715
  function parsePartialJson(input) {
@@ -10902,9 +11346,9 @@ var LEGACY_REDACTION_RULES = {
10902
11346
  // which is user content or PII (field paths are schema-shape, preserved like
10903
11347
  // `string_delta.path`). Passthrough, as an explicit reviewed decision.
10904
11348
  schema_diagnostic: passthrough,
10905
- // `provider_diagnostic` carries only provider capability metadata — a provider
10906
- // name, a model id, and two effort level strings. No application data or user
10907
- // content ever reaches it. Passthrough, as an explicit reviewed decision.
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.
10908
11352
  provider_diagnostic: passthrough,
10909
11353
  log: (e) => {
10910
11354
  if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
@@ -12534,9 +12978,8 @@ function recordToolSpanOutcome(span, outcome) {
12534
12978
  }
12535
12979
 
12536
12980
  // src/context.ts
12537
- var signalStorage = new AsyncLocalStorage2();
12538
- var hardSignalStorage = new AsyncLocalStorage2();
12539
- var timeoutTrackerStorage = new AsyncLocalStorage2();
12981
+ var signalStorage = new AsyncLocalStorage3();
12982
+ var hardSignalStorage = new AsyncLocalStorage3();
12540
12983
  var MAX_TIMER_DELAY_MS = 2147483647;
12541
12984
  function composeAbortSignals(...signals) {
12542
12985
  const active = signals.filter((signal) => signal !== void 0);
@@ -12568,9 +13011,6 @@ function raceWithAbortSignal(promise, signal) {
12568
13011
  );
12569
13012
  });
12570
13013
  }
12571
- function pausedHumanWaitMs(tracker, now = Date.now()) {
12572
- return tracker.humanWaitMs + (tracker.activeHumanWaits > 0 && tracker.humanWaitStartedAt !== void 0 ? now - tracker.humanWaitStartedAt : 0);
12573
- }
12574
13014
  var DEFAULT_BRANCH_DRAIN_TIMEOUT_MS = 5e3;
12575
13015
  function resolveBranchDrainTimeoutMs(value) {
12576
13016
  const resolved = value === void 0 ? DEFAULT_BRANCH_DRAIN_TIMEOUT_MS : value;
@@ -12641,7 +13081,7 @@ function parseHumanDecision(value) {
12641
13081
  }
12642
13082
  return invalid3();
12643
13083
  }
12644
- var askStorage = new AsyncLocalStorage2();
13084
+ var askStorage = new AsyncLocalStorage3();
12645
13085
  var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
12646
13086
  function zodToJsonSchema(schema) {
12647
13087
  const cached = inlineSchemaCache.get(schema);
@@ -12913,7 +13353,14 @@ var WorkflowContext = class _WorkflowContext {
12913
13353
  * suppresses late branch events after bounded finalization. */
12914
13354
  workflowLifecycleState;
12915
13355
  signal;
12916
- summaryCache;
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();
12917
13364
  /** Consecutive provider calls with promptCache on that wrote to the cache
12918
13365
  * without reading from it (prefix changes per call), and consecutive calls
12919
13366
  * with no cache activity at all (prefix below the model's minimum, which
@@ -13089,7 +13536,7 @@ var WorkflowContext = class _WorkflowContext {
13089
13536
  endEmitted: false
13090
13537
  };
13091
13538
  if (init.metadata?.summaryCache) {
13092
- this.summaryCache = init.metadata.summaryCache;
13539
+ this.sessionSummary = init.metadata.summaryCache;
13093
13540
  }
13094
13541
  }
13095
13542
  /**
@@ -13578,10 +14025,7 @@ var WorkflowContext = class _WorkflowContext {
13578
14025
  return hardAskSignal ? hardSignalStorage.run(hardAskSignal, runWithAskSignal) : runWithAskSignal();
13579
14026
  }
13580
14027
  async executeAgentCall(agent2, input, options, handoffMessages, usageCapture, sessionHistory = normalizeSessionHistory(this.sessionHistory)) {
13581
- const tracker = { humanWaitMs: 0, activeHumanWaits: 0 };
13582
- const enclosing = timeoutTrackerStorage.getStore() ?? [];
13583
- return timeoutTrackerStorage.run(
13584
- [...enclosing, tracker],
14028
+ return runWithAskClock(
13585
14029
  () => this.executeAgentCallImpl(
13586
14030
  agent2,
13587
14031
  input,
@@ -13717,6 +14161,7 @@ var WorkflowContext = class _WorkflowContext {
13717
14161
  if (systemPrompt) {
13718
14162
  messages.push({ role: "system", content: systemPrompt });
13719
14163
  }
14164
+ const cachedSummaryMessage = this.sessionSummary ? summaryContextMessage(this.sessionSummary) : void 0;
13720
14165
  const maxContext = agent2._config.maxContext;
13721
14166
  if (maxContext && sessionHistory.length > 0) {
13722
14167
  const reserveTokens = this.config.contextManagement?.reserveTokens ?? 2e3;
@@ -13741,22 +14186,44 @@ var WorkflowContext = class _WorkflowContext {
13741
14186
  }
13742
14187
  });
13743
14188
  }
13744
- if (historyEstimate.tokens > availableForHistory) {
13745
- const summarizedMessages = await this.summarizeHistory(
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,
13746
14194
  provider,
13747
14195
  model,
13748
14196
  sessionHistory,
13749
14197
  availableForHistory
13750
14198
  );
13751
- for (const msg of summarizedMessages) {
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) {
13752
14217
  messages.push(msg);
13753
14218
  }
13754
14219
  } else {
14220
+ if (cachedSummaryMessage) messages.push(cachedSummaryMessage);
13755
14221
  for (const msg of sessionHistory) {
13756
14222
  messages.push(msg);
13757
14223
  }
13758
14224
  }
13759
14225
  } else {
14226
+ if (cachedSummaryMessage) messages.push(cachedSummaryMessage);
13760
14227
  for (const msg of sessionHistory) {
13761
14228
  messages.push(msg);
13762
14229
  }
@@ -13856,7 +14323,8 @@ ${appendedSchemaText}`;
13856
14323
  );
13857
14324
  }
13858
14325
  const startTime = Date.now();
13859
- const timeoutTracker = timeoutTrackerStorage.getStore()?.at(-1);
14326
+ const askClock = currentAskClock();
14327
+ const pausedAtStartMs = askClock ? pausedAskClockMs(askClock, startTime) : 0;
13860
14328
  const timingTotals = { turns: 0, queuedMs: 0, retryMs: 0, wireMs: 0 };
13861
14329
  const currentMessages = [...messages];
13862
14330
  let turns = 0;
@@ -13873,13 +14341,16 @@ ${appendedSchemaText}`;
13873
14341
  while (turns < maxTurns) {
13874
14342
  this.currentSignal?.throwIfAborted();
13875
14343
  const now = Date.now();
13876
- const elapsedMs = now - startTime - (timeoutTracker ? pausedHumanWaitMs(timeoutTracker, now) : 0);
13877
- if (elapsedMs > timeoutMs) {
14344
+ const elapsedMs = now - startTime;
14345
+ const pausedMs = askClock ? Math.max(0, pausedAskClockMs(askClock, now) - pausedAtStartMs) : 0;
14346
+ const chargedMs = Math.max(0, elapsedMs - Math.min(pausedMs, Math.max(0, elapsedMs)));
14347
+ if (chargedMs > timeoutMs) {
13878
14348
  throw new TimeoutError(
13879
14349
  "ctx.ask()",
13880
14350
  timeoutMs,
13881
14351
  timingTotals.turns > 0 ? {
13882
14352
  elapsedMs,
14353
+ chargedMs,
13883
14354
  queuedMs: timingTotals.queuedMs,
13884
14355
  retryMs: timingTotals.retryMs,
13885
14356
  wireMs: timingTotals.wireMs,
@@ -14126,6 +14597,7 @@ ${appendedSchemaText}`;
14126
14597
  usage: chunk.usage,
14127
14598
  cost: chunk.cost,
14128
14599
  providerMetadata: chunk.providerMetadata,
14600
+ diagnostics: chunk.diagnostics,
14129
14601
  timing: chunk.timing
14130
14602
  };
14131
14603
  }
@@ -14243,6 +14715,34 @@ ${appendedSchemaText}`;
14243
14715
  timingTotals.retryMs += response.timing.retryMs;
14244
14716
  timingTotals.wireMs += response.timing.wireMs;
14245
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
+ }
14246
14746
  this.emitEvent({
14247
14747
  type: "agent_call_end",
14248
14748
  agent: agent2._name,
@@ -14985,31 +15485,65 @@ ${appendedSchemaText}`;
14985
15485
  }
14986
15486
  return defs;
14987
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
+ }
14988
15523
  /**
14989
- * Summarize old messages to fit within context window.
14990
- * Keeps recent messages intact, summarizes older ones.
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.
14991
15530
  */
14992
- async summarizeHistory(provider, model, history, availableTokens) {
14993
- if (this.summaryCache) {
14994
- const summaryMsg2 = {
14995
- role: "system",
14996
- origin: "runtime",
14997
- content: `Summary of earlier conversation:
14998
- ${this.summaryCache}`
14999
- };
15000
- const summaryTokens = estimateTokens(summarizeModelInput(summaryMsg2.content)) + 4;
15001
- const remaining = availableTokens - summaryTokens;
15002
- let recentTokens2 = 0;
15003
- let splitIdx2 = history.length;
15004
- for (let i = history.length - 1; i >= 0; i--) {
15005
- const msgTokens = estimateTokens(summarizeModelInput(history[i].content)) + 4;
15006
- if (recentTokens2 + msgTokens > remaining) break;
15007
- recentTokens2 += msgTokens;
15008
- splitIdx2 = i;
15009
- }
15010
- splitIdx2 = nextUserTurn(history, splitIdx2) ?? history.length;
15011
- if (splitIdx2 < history.length) {
15012
- 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 };
15013
15547
  }
15014
15548
  }
15015
15549
  let recentTokens = 0;
@@ -15034,42 +15568,103 @@ ${this.summaryCache}`
15034
15568
  } else {
15035
15569
  splitIdx = history.length;
15036
15570
  }
15037
- if (splitIdx === 0) return history;
15571
+ if (splitIdx === 0 && this.sessionSummary) splitIdx = history.length;
15572
+ if (splitIdx === 0) return { messages: history, removedThinkingBlocks: 0 };
15038
15573
  const oldMessages = history.slice(0, splitIdx);
15039
- const summaryModelUri = this.config.contextManagement?.summaryModel;
15574
+ const configuredSummaryModelUri = this.config.contextManagement?.summaryModel;
15040
15575
  let summaryProvider;
15041
15576
  let summaryModel;
15042
- if (summaryModelUri) {
15043
- const resolved = this.resolveProviderUri(summaryModelUri);
15577
+ if (configuredSummaryModelUri) {
15578
+ const resolved = this.resolveProviderUri(configuredSummaryModelUri);
15044
15579
  summaryProvider = resolved.provider;
15045
15580
  summaryModel = resolved.model;
15046
15581
  } else {
15047
15582
  summaryProvider = provider;
15048
15583
  summaryModel = model;
15049
15584
  }
15050
- const oldContent = oldMessages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`).join("\n");
15051
- const summaryResponse = await summaryProvider.chat(
15052
- [
15053
- {
15054
- role: "system",
15055
- content: "Summarize the following conversation concisely, preserving key facts, decisions, and context needed for continuing the conversation."
15056
- },
15057
- { role: "user", content: oldContent }
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)
15058
15662
  ],
15059
- { model: summaryModel, maxTokens: 1024, signal: this.currentSignal }
15060
- );
15061
- this.summaryCache = summaryResponse.content;
15062
- const sessionId = this.metadata?.sessionId;
15063
- if (sessionId && this.stateStore) {
15064
- await this.stateStore.saveSessionMeta(sessionId, "summaryCache", this.summaryCache);
15065
- }
15066
- const summaryMsg = {
15067
- role: "system",
15068
- origin: "runtime",
15069
- content: `Summary of earlier conversation:
15070
- ${summaryResponse.content}`
15663
+ removedThinkingBlocks: tail.reduce(
15664
+ (count, message) => count + anthropicThinkingBlockCount(message),
15665
+ 0
15666
+ )
15071
15667
  };
15072
- return [summaryMsg, ...history.slice(splitIdx)];
15073
15668
  }
15074
15669
  // ── ctx.checkpoint() ────────────────────────────────────────────────
15075
15670
  /**
@@ -15749,12 +16344,7 @@ ${summaryResponse.content}`
15749
16344
  }
15750
16345
  // ── ctx.awaitHuman() ──────────────────────────────────────────────────
15751
16346
  async awaitHuman(options) {
15752
- const trackers = timeoutTrackerStorage.getStore();
15753
- const startedAt = Date.now();
15754
- for (const tracker of trackers ?? []) {
15755
- if (tracker.activeHumanWaits++ === 0) tracker.humanWaitStartedAt = startedAt;
15756
- }
15757
- try {
16347
+ return pauseAskClocks(async () => {
15758
16348
  if (this.spanManager) {
15759
16349
  return await this.spanManager.withSpanAsync(
15760
16350
  "axl.ctx.awaitHuman",
@@ -15771,16 +16361,7 @@ ${summaryResponse.content}`
15771
16361
  );
15772
16362
  }
15773
16363
  return await this._awaitHumanImpl(options);
15774
- } finally {
15775
- const finishedAt = Date.now();
15776
- for (const tracker of trackers ?? []) {
15777
- tracker.activeHumanWaits--;
15778
- if (tracker.activeHumanWaits === 0 && tracker.humanWaitStartedAt !== void 0) {
15779
- tracker.humanWaitMs += finishedAt - tracker.humanWaitStartedAt;
15780
- tracker.humanWaitStartedAt = void 0;
15781
- }
15782
- }
15783
- }
16364
+ });
15784
16365
  }
15785
16366
  async _awaitHumanImpl(options) {
15786
16367
  const branchSignal = this.currentSignal;
@@ -16777,7 +17358,7 @@ function workflow(config) {
16777
17358
  // src/runtime.ts
16778
17359
  import { EventEmitter as EventEmitter2 } from "events";
16779
17360
  import { randomUUID as randomUUID4 } from "crypto";
16780
- import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
17361
+ import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
16781
17362
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
16782
17363
  import { dirname } from "path";
16783
17364
 
@@ -17492,17 +18073,17 @@ var Session = class _Session {
17492
18073
  throw new Error("SessionOptions.history.summaryModel is required when summarize is true");
17493
18074
  }
17494
18075
  const messagesToDrop = history.slice(0, history.length - maxMessages);
17495
- const toSummarize = cachedSummary ? [
17496
- { role: "system", content: `Previous conversation summary: ${cachedSummary}` },
17497
- ...messagesToDrop
17498
- ] : messagesToDrop;
17499
- const summary = await this.runtime.summarizeMessages(toSummarize, summaryModel);
18076
+ const summary = await this.runtime.summarizeMessages(
18077
+ messagesToDrop,
18078
+ summaryModel,
18079
+ cachedSummary ? { previousSummary: cachedSummary } : void 0
18080
+ );
17500
18081
  await this.store.saveSessionMeta(this.sessionId, "summaryCache", summary);
17501
18082
  cachedSummary = summary;
17502
18083
  }
17503
18084
  const trimmed = history.slice(-maxMessages);
17504
18085
  history.length = 0;
17505
- history.push(...trimmed);
18086
+ history.push(...trimmed.map(withoutAnthropicThinking));
17506
18087
  }
17507
18088
  const userMessage = { role: "user", content: preparedInput.content };
17508
18089
  history.push(userMessage);
@@ -17512,7 +18093,10 @@ var Session = class _Session {
17512
18093
  const metadata = {
17513
18094
  sessionId: this.sessionId,
17514
18095
  sessionHistory: history,
17515
- ...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 } : {}
17516
18100
  };
17517
18101
  return { history, metadata };
17518
18102
  }
@@ -18966,7 +19550,7 @@ function assertNoRemovedObservationCallbacks(options) {
18966
19550
  `runtime.createContext() no longer accepts ${removed.join(", ")}. Observe ctx.events or use runtime.stream() instead. See docs/migration/stream-first-observation.md.`
18967
19551
  );
18968
19552
  }
18969
- var costScopeStorage = new AsyncLocalStorage3();
19553
+ var costScopeStorage = new AsyncLocalStorage4();
18970
19554
  function forwardAbortSignal(external, internal) {
18971
19555
  if (!external) return () => {
18972
19556
  };
@@ -20594,22 +21178,17 @@ var AxlRuntime = class extends EventEmitter2 {
20594
21178
  /**
20595
21179
  * Summarize a list of chat messages into a concise summary string.
20596
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.
20597
21185
  */
20598
- async summarizeMessages(messages, modelUri) {
21186
+ async summarizeMessages(messages, modelUri, options) {
20599
21187
  const { provider, model } = this.resolveProvider(modelUri);
20600
- const response = await provider.chat(
20601
- [
20602
- {
20603
- role: "system",
20604
- content: "Summarize the following conversation concisely, preserving key facts, decisions, and context needed for continuing the conversation."
20605
- },
20606
- {
20607
- role: "user",
20608
- content: messages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`).join("\n")
20609
- }
20610
- ],
20611
- { model, maxTokens: 1024 }
20612
- );
21188
+ const response = await requestSummary(provider, {
21189
+ model,
21190
+ prompt: buildSummaryPrompt(messages, options?.previousSummary)
21191
+ });
20613
21192
  return response.content;
20614
21193
  }
20615
21194
  /** Get the state store (for testing and advanced use cases). */
@@ -22039,6 +22618,7 @@ export {
22039
22618
  TranscriptionProviderRegistry,
22040
22619
  UnsupportedEventSchemaVersionError,
22041
22620
  UnsupportedModelInputError,
22621
+ UnsupportedModelOptionError,
22042
22622
  UnsupportedTranscriptionInputError,
22043
22623
  VLLM_PROFILE,
22044
22624
  ValidationError,