billion-context 0.1.91 → 0.1.92-pr.628.445

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
@@ -43869,6 +43869,9 @@ function refToIndex(ref) {
43869
43869
  function refForRaw(map, rawId) {
43870
43870
  return map.byRaw[rawId] ?? null;
43871
43871
  }
43872
+ function rawForRef(map, ref) {
43873
+ return map.byRef[ref] ?? null;
43874
+ }
43872
43875
  function assignRefs(messages, options) {
43873
43876
  const map = {
43874
43877
  byRaw: { ...options.existing.byRaw },
@@ -44822,6 +44825,34 @@ var ACP_MUTATING_TOOLS = /* @__PURE__ */ new Set([
44822
44825
  COMPRESS_TOOL_NAME,
44823
44826
  DECOMPRESS_TOOL_NAME
44824
44827
  ]);
44828
+ var ABSORB_TOOL_DESCRIPTION = "Distill a tool result into a compact summary you write. REQUIRED immediately after a tool result ends with an [ACP absorb] instruction: pass its ref and the distilled essentials (outcome, key values, paths:lines, errors, decisions). The original output is then removed from context; your summary is the durable record.";
44829
+ var ABSORB_PARAMETERS = {
44830
+ type: "object",
44831
+ properties: {
44832
+ ref: {
44833
+ type: "string",
44834
+ description: "mNNNNN ref of the tool result to absorb (from the [ACP absorb] instruction)"
44835
+ },
44836
+ summary: {
44837
+ type: "string",
44838
+ description: "Distilled essentials of the tool result \u2014 this replaces the original output in context"
44839
+ }
44840
+ },
44841
+ required: ["ref", "summary"]
44842
+ };
44843
+ var ABSORB_TOOL = {
44844
+ name: ABSORB_TOOL_NAME,
44845
+ description: ABSORB_TOOL_DESCRIPTION,
44846
+ input_schema: ABSORB_PARAMETERS
44847
+ };
44848
+ var ABSORB_TOOL_OPENAI = {
44849
+ type: "function",
44850
+ function: {
44851
+ name: ABSORB_TOOL_NAME,
44852
+ description: ABSORB_TOOL_DESCRIPTION,
44853
+ parameters: ABSORB_PARAMETERS
44854
+ }
44855
+ };
44825
44856
  var ALWAYS_PROTECTED_TOOLS = ["compress"];
44826
44857
  var NEVER_PRESERVE_RECENT_TOOLS = [
44827
44858
  "decompress",
@@ -44890,6 +44921,11 @@ function formatTokenCount(tokens) {
44890
44921
  function buildAbsorbPrompt(ref, tokens, toolName = ABSORB_TOOL_NAME) {
44891
44922
  return `${ABSORB_PROMPT_MARKER} This tool result (~${formatTokenCount(tokens)} tokens) will be REMOVED from context. Your IMMEDIATE next action: call ${toolName}({ ref: "${ref}", summary: "..." }) \u2014 summary = distilled essentials only (outcome, key values, exact paths:lines, error text verbatim, decisions). Afterwards work from your summary; do NOT re-run this tool. If the result contains nothing you need, call ${toolName} with summary "(nothing needed)".`;
44892
44923
  }
44924
+ function buildAbsorbSystemPrompt(toolName = ABSORB_TOOL_NAME) {
44925
+ return `INSTANT TOOL-RESULT ABSORPTION (${toolName})
44926
+
44927
+ Some tool results end with a ${ABSORB_PROMPT_MARKER} instruction. When you see one, your IMMEDIATE next action must be calling ${toolName}({ ref, summary }) \u2014 distill that tool result's essentials into summary: outcome, key values, exact paths:lines, error text verbatim, decisions. The original output is then removed from context; your ${toolName} summary becomes the only durable record of it, so distill carefully. Never call another tool or answer the user before absorbing a marked result. Do not re-run the original tool afterwards \u2014 work from your summary. ${toolName} calls are ordinary context: the regular compression system may fold them later like any other message.`;
44928
+ }
44893
44929
  function isAcpOrConfiguredTool(toolName, cfg) {
44894
44930
  if (!toolName) return false;
44895
44931
  if (toolName === cfg.toolName) return true;
@@ -44944,6 +44980,139 @@ function appendAbsorbPrompts(messages, state, config, tokenCount, countTokens) {
44944
44980
  });
44945
44981
  return { messages: out, promptedCount };
44946
44982
  }
44983
+ function parseAbsorbInput(input, callId, onWarn) {
44984
+ let obj = null;
44985
+ if (typeof input === "string") {
44986
+ try {
44987
+ const parsed = JSON.parse(input);
44988
+ if (parsed && typeof parsed === "object") {
44989
+ obj = parsed;
44990
+ }
44991
+ } catch {
44992
+ obj = null;
44993
+ }
44994
+ } else if (input && typeof input === "object") {
44995
+ obj = input;
44996
+ }
44997
+ if (!obj) {
44998
+ onWarn?.(`[acp-absorb-input] rejected: not an object (${typeof input})`);
44999
+ return null;
45000
+ }
45001
+ const ref = pickString(obj, "ref", "messageId", "of");
45002
+ const summary = pickString(obj, "summary", "content");
45003
+ if (typeof ref !== "string" || typeof summary !== "string") {
45004
+ onWarn?.(
45005
+ `[acp-absorb-input] rejected: need ref (string) + summary (string); keys: ${Object.keys(obj).join(",")}`
45006
+ );
45007
+ return null;
45008
+ }
45009
+ return {
45010
+ ref: ref.trim(),
45011
+ summary,
45012
+ ...callId ? { absorbCallId: callId } : {}
45013
+ };
45014
+ }
45015
+ function pickString(obj, ...keys) {
45016
+ for (const key of keys) {
45017
+ const value = obj[key];
45018
+ if (typeof value === "string") return value;
45019
+ }
45020
+ return void 0;
45021
+ }
45022
+ function applyAbsorb(input) {
45023
+ const countTokens = input.countTokens ?? ((text) => Math.ceil(text.length / 4));
45024
+ const summary = input.summary?.trim() ?? "";
45025
+ if (!summary) {
45026
+ return {
45027
+ state: input.state,
45028
+ ok: false,
45029
+ resultText: "absorb failed: summary is empty \u2014 provide the distilled key info of the tool result."
45030
+ };
45031
+ }
45032
+ const cfg = resolveAbsorbConfig(input.config);
45033
+ const rawId = rawForRef(input.state.messageRefs, input.ref.trim());
45034
+ if (!rawId) {
45035
+ return {
45036
+ state: input.state,
45037
+ ok: false,
45038
+ resultText: `absorb failed: ref ${input.ref} does not exist in this session (it may be hidden, already compressed, or stale).`
45039
+ };
45040
+ }
45041
+ const existing = (input.state.absorbed ?? []).find(
45042
+ (record2) => record2.resultMessageId === rawId
45043
+ );
45044
+ if (existing) {
45045
+ return {
45046
+ state: input.state,
45047
+ ok: true,
45048
+ resultText: `already absorbed (${input.ref}) \u2014 no change.`
45049
+ };
45050
+ }
45051
+ const target = input.messages.find((m2) => m2.id === rawId);
45052
+ if (!target) {
45053
+ return {
45054
+ state: input.state,
45055
+ ok: false,
45056
+ resultText: `absorb failed: ref ${input.ref} is not visible in this session (hidden or compressed).`
45057
+ };
45058
+ }
45059
+ if (target.contentType !== "tool-result") {
45060
+ return {
45061
+ state: input.state,
45062
+ ok: false,
45063
+ resultText: `absorb failed: ref ${input.ref} is a ${target.contentType}, not a tool result.`
45064
+ };
45065
+ }
45066
+ if (isAcpOrConfiguredTool(target.toolName, cfg)) {
45067
+ return {
45068
+ state: input.state,
45069
+ ok: false,
45070
+ resultText: `absorb failed: ${target.toolName} is an ACP-managed tool result \u2014 it is not absorbable.`
45071
+ };
45072
+ }
45073
+ if (isMessageProtected(target, input.config)) {
45074
+ return {
45075
+ state: input.state,
45076
+ ok: false,
45077
+ resultText: `absorb failed: ${target.toolName} is a protected tool \u2014 its results must stay visible.`
45078
+ };
45079
+ }
45080
+ if (!target.toolCallId) {
45081
+ return {
45082
+ state: input.state,
45083
+ ok: false,
45084
+ resultText: `absorb failed: ref ${input.ref} has no tool-call id \u2014 cannot pair it for hiding.`
45085
+ };
45086
+ }
45087
+ const call = input.messages.find(
45088
+ (m2) => m2.contentType === "tool-call" && m2.toolCallId === target.toolCallId
45089
+ );
45090
+ const tokens = countTokens(target.text ?? "");
45091
+ const summaryTokens = countTokens(summary);
45092
+ const record = {
45093
+ toolCallId: target.toolCallId,
45094
+ callMessageId: call?.id ?? "",
45095
+ resultMessageId: target.id,
45096
+ ...input.absorbCallId ? { absorbCallId: input.absorbCallId } : {},
45097
+ summary,
45098
+ tokensReclaimed: tokens,
45099
+ createdAt: Date.now()
45100
+ };
45101
+ const state = {
45102
+ ...input.state,
45103
+ absorbed: [...input.state.absorbed ?? [], record],
45104
+ stats: {
45105
+ ...input.state.stats,
45106
+ absorbedTokens: (input.state.stats.absorbedTokens ?? 0) + tokens
45107
+ }
45108
+ };
45109
+ const bloat = summaryTokens >= tokens && tokens > 0 ? ` WARNING: your summary (~${formatTokenCount(summaryTokens)} tokens) is not smaller than the original (~${formatTokenCount(tokens)} tokens) \u2014 distill harder next time.` : "";
45110
+ return {
45111
+ state,
45112
+ ok: true,
45113
+ resultText: `absorbed ${input.ref} (~${formatTokenCount(tokens)} tokens \u2192 summary ~${formatTokenCount(summaryTokens)}). The original tool output is now hidden; your summary is the durable record.${bloat}`
45114
+ };
45115
+ }
44947
45116
  var registry = /* @__PURE__ */ new Map();
44948
45117
  function listMessageFilters() {
44949
45118
  return [...registry.values()];
@@ -48218,13 +48387,17 @@ function parseCompressSettings(v2) {
48218
48387
  }
48219
48388
  out[key] = typeof obj[key] === "string" ? obj[key].trim() : obj[key];
48220
48389
  }
48221
- for (const key of ["nudgeGrowthTokens", "preserveRecentMessages", "preserveRecentTokens", "minCompressRange", "minCompressRangeChars"]) {
48390
+ for (const key of ["nudgeGrowthTokens", "preserveRecentMessages", "preserveRecentTokens", "minCompressRange", "minCompressRangeChars", "stripImagesKeepRecent"]) {
48222
48391
  takeNumber(key);
48223
48392
  }
48224
48393
  if ("tiers" in obj) {
48225
48394
  if (typeof obj.tiers !== "boolean") ok = false;
48226
48395
  else out.tiers = obj.tiers;
48227
48396
  }
48397
+ if ("stripImages" in obj) {
48398
+ if (typeof obj.stripImages !== "boolean") ok = false;
48399
+ else out.stripImages = obj.stripImages;
48400
+ }
48228
48401
  for (const key of ["injectTool", "injectNudge"]) {
48229
48402
  if (key in obj) {
48230
48403
  if (typeof obj[key] !== "boolean") ok = false;
@@ -48235,6 +48408,51 @@ function parseCompressSettings(v2) {
48235
48408
  if (typeof obj.acknowledgePromptsRisk !== "boolean") ok = false;
48236
48409
  else out.acknowledgePromptsRisk = obj.acknowledgePromptsRisk;
48237
48410
  }
48411
+ if ("absorb" in obj && obj.absorb !== void 0) {
48412
+ const a = obj.absorb;
48413
+ if (!a || typeof a !== "object" || Array.isArray(a)) {
48414
+ ok = false;
48415
+ } else {
48416
+ const ao2 = a;
48417
+ const cleaned = {};
48418
+ for (const key of ["enabled", "minToolTokens", "contextThresholdPct", "excludeTools", "toolName"]) {
48419
+ if (!(key in ao2)) continue;
48420
+ const v3 = ao2[key];
48421
+ if (key === "enabled") {
48422
+ if (typeof v3 !== "boolean") {
48423
+ ok = false;
48424
+ continue;
48425
+ }
48426
+ cleaned.enabled = v3;
48427
+ } else if (key === "minToolTokens") {
48428
+ if (typeof v3 !== "number" || !Number.isFinite(v3)) {
48429
+ ok = false;
48430
+ continue;
48431
+ }
48432
+ cleaned.minToolTokens = v3;
48433
+ } else if (key === "contextThresholdPct") {
48434
+ if (!numberOrPercent(v3)) {
48435
+ ok = false;
48436
+ continue;
48437
+ }
48438
+ cleaned.contextThresholdPct = typeof v3 === "string" ? v3.trim() : v3;
48439
+ } else if (key === "excludeTools") {
48440
+ if (!Array.isArray(v3) || v3.some((x) => typeof x !== "string")) {
48441
+ ok = false;
48442
+ continue;
48443
+ }
48444
+ cleaned.excludeTools = [...v3];
48445
+ } else {
48446
+ if (typeof v3 !== "string" || v3.trim().length === 0) {
48447
+ ok = false;
48448
+ continue;
48449
+ }
48450
+ cleaned.toolName = v3.trim();
48451
+ }
48452
+ }
48453
+ if (ok) out.absorb = cleaned;
48454
+ }
48455
+ }
48238
48456
  if ("prompts" in obj && obj.prompts !== void 0) {
48239
48457
  const prompts = obj.prompts;
48240
48458
  if (!prompts || typeof prompts !== "object" || Array.isArray(prompts)) {
@@ -48285,6 +48503,7 @@ function mergeCompress(global2, provider, model) {
48285
48503
  const pick = (k2) => model?.[k2] ?? provider?.[k2] ?? global2?.[k2];
48286
48504
  const promptLevels = [global2?.prompts, provider?.prompts, model?.prompts].filter(Boolean);
48287
48505
  const rangeOf = (s3) => s3?.minCompressRangeChars ?? s3?.minCompressRange;
48506
+ const absorbLevels = [global2?.absorb, provider?.absorb, model?.absorb].filter(Boolean);
48288
48507
  return {
48289
48508
  modelContextLimit: pick("modelContextLimit"),
48290
48509
  maxContextLimit: pick("maxContextLimit"),
@@ -48295,7 +48514,10 @@ function mergeCompress(global2, provider, model) {
48295
48514
  minCompressRangeChars: rangeOf(model) ?? rangeOf(provider) ?? rangeOf(global2),
48296
48515
  tiers: pick("tiers"),
48297
48516
  prompts: promptLevels.length > 0 ? Object.assign({}, ...promptLevels) : void 0,
48298
- acknowledgePromptsRisk: pick("acknowledgePromptsRisk")
48517
+ acknowledgePromptsRisk: pick("acknowledgePromptsRisk"),
48518
+ absorb: absorbLevels.length > 0 ? Object.assign({}, ...absorbLevels) : void 0,
48519
+ stripImages: pick("stripImages"),
48520
+ stripImagesKeepRecent: pick("stripImagesKeepRecent")
48299
48521
  };
48300
48522
  }
48301
48523
  function resolveCompress(routes, upstreamUrl, model, global2) {
@@ -48336,6 +48558,17 @@ function applyCompressSettings(base, limit, s3) {
48336
48558
  }
48337
48559
  const tiers = { ...base.tiers };
48338
48560
  if (s3.tiers !== void 0) tiers.enabled = s3.tiers;
48561
+ let absorb;
48562
+ if (s3.absorb !== void 0) {
48563
+ const d = DEFAULT_ABSORB_CONFIG;
48564
+ absorb = {
48565
+ enabled: s3.absorb.enabled === true,
48566
+ toolName: s3.absorb.toolName ?? d.toolName,
48567
+ minToolTokens: s3.absorb.minToolTokens ?? d.minToolTokens,
48568
+ contextThresholdPct: s3.absorb.contextThresholdPct !== void 0 ? parsePercent(s3.absorb.contextThresholdPct) : d.contextThresholdPct,
48569
+ excludeTools: s3.absorb.excludeTools ?? [...d.excludeTools]
48570
+ };
48571
+ }
48339
48572
  return {
48340
48573
  ...base,
48341
48574
  modelContextLimit: limit,
@@ -48347,7 +48580,8 @@ function applyCompressSettings(base, limit, s3) {
48347
48580
  compress: {
48348
48581
  ...base.compress,
48349
48582
  minCompressRange: s3.minCompressRangeChars ?? s3.minCompressRange ?? base.compress.minCompressRange
48350
- }
48583
+ },
48584
+ ...absorb !== void 0 ? { absorb } : {}
48351
48585
  };
48352
48586
  }
48353
48587
  function parsePercent(v2) {
@@ -48363,6 +48597,69 @@ function resolveRequestConfig(base, routes, embeddedUrl, model, native, globalCo
48363
48597
  return applyCompressSettings(base, limit, compress);
48364
48598
  }
48365
48599
 
48600
+ // src/strip-images.ts
48601
+ var DEFAULT_STRIP_IMAGES_KEEP_RECENT = 5;
48602
+ var IMAGE_PLACEHOLDER = "[image]";
48603
+ function isObj(v2) {
48604
+ return typeof v2 === "object" && v2 !== null;
48605
+ }
48606
+ function isImagePart(protocol, part) {
48607
+ if (!isObj(part)) return false;
48608
+ if (protocol === "responses") return part.type === "input_image";
48609
+ if (protocol === "openai") return part.type === "image_url";
48610
+ return part.type === "image";
48611
+ }
48612
+ function placeholderContent(protocol) {
48613
+ const type = protocol === "responses" ? "input_text" : "text";
48614
+ return [{ type, text: IMAGE_PLACEHOLDER }];
48615
+ }
48616
+ function stripHistoricalImages(body, protocol, keepRecent) {
48617
+ if (!protocol || !isObj(body)) return { body, removed: 0 };
48618
+ const recentCount = Math.max(0, Math.floor(keepRecent));
48619
+ if (protocol === "responses") {
48620
+ const input = body.input;
48621
+ if (!Array.isArray(input)) return { body, removed: 0 };
48622
+ const cutoff2 = input.length - recentCount;
48623
+ let removed2 = 0;
48624
+ let touched2 = false;
48625
+ const nextInput = input.map((item, i) => {
48626
+ if (i < cutoff2 && isObj(item) && Array.isArray(item.content)) {
48627
+ const content = item.content;
48628
+ const imgs = content.filter((p2) => isImagePart("responses", p2)).length;
48629
+ if (imgs > 0) {
48630
+ removed2 += imgs;
48631
+ touched2 = true;
48632
+ const kept = content.filter((p2) => !isImagePart("responses", p2));
48633
+ return { ...item, content: kept.length > 0 ? kept : placeholderContent("responses") };
48634
+ }
48635
+ }
48636
+ return item;
48637
+ });
48638
+ if (!touched2) return { body, removed: 0 };
48639
+ return { body: { ...body, input: nextInput }, removed: removed2 };
48640
+ }
48641
+ const messages = body.messages;
48642
+ if (!Array.isArray(messages)) return { body, removed: 0 };
48643
+ const cutoff = messages.length - recentCount;
48644
+ let removed = 0;
48645
+ let touched = false;
48646
+ const nextMessages = messages.map((m2, i) => {
48647
+ if (i < cutoff && isObj(m2) && Array.isArray(m2.content)) {
48648
+ const content = m2.content;
48649
+ const imgs = content.filter((p2) => isImagePart(protocol, p2)).length;
48650
+ if (imgs > 0) {
48651
+ removed += imgs;
48652
+ touched = true;
48653
+ const kept = content.filter((p2) => !isImagePart(protocol, p2));
48654
+ return { ...m2, content: kept.length > 0 ? kept : placeholderContent(protocol) };
48655
+ }
48656
+ }
48657
+ return m2;
48658
+ });
48659
+ if (!touched) return { body, removed: 0 };
48660
+ return { body: { ...body, messages: nextMessages }, removed };
48661
+ }
48662
+
48366
48663
  // src/registry.ts
48367
48664
  import { readFile, writeFile, mkdir } from "fs/promises";
48368
48665
  import { existsSync as existsSync2, statSync as statSync2 } from "fs";
@@ -50041,7 +50338,10 @@ function mergeState(parsed) {
50041
50338
  stats: { ...fresh.stats, ...parsed.stats ?? {} },
50042
50339
  nextBlockId: parsed.nextBlockId ?? fresh.nextBlockId,
50043
50340
  nextRunId: parsed.nextRunId ?? fresh.nextRunId,
50044
- tokenSnapshot: parsed.tokenSnapshot ?? fresh.tokenSnapshot
50341
+ tokenSnapshot: parsed.tokenSnapshot ?? fresh.tokenSnapshot,
50342
+ // Without this, a restart re-exposes absorbed tool outputs: state
50343
+ // resurrects with absorbed=[] and hideAbsorbedMessages has nothing to hide.
50344
+ absorbed: parsed.absorbed ?? fresh.absorbed
50045
50345
  };
50046
50346
  }
50047
50347
  function hostLabel(upstreamOrigin) {
@@ -50644,6 +50944,12 @@ async function flushAllSessions() {
50644
50944
  }
50645
50945
 
50646
50946
  // src/compress-tool.ts
50947
+ var ABSORB_TOOL_RESPONSES = {
50948
+ type: "function",
50949
+ name: ABSORB_TOOL_OPENAI.function.name,
50950
+ description: ABSORB_TOOL_OPENAI.function.description,
50951
+ parameters: ABSORB_TOOL_OPENAI.function.parameters
50952
+ };
50647
50953
  function parseCompressInput(input, callId) {
50648
50954
  const first = parseCompressArgs(input, { callId });
50649
50955
  if (first.ranges.length === 0 || first.diagnostics.invalidItems > 0) {
@@ -50743,6 +51049,72 @@ function withStagedCompressGuidance(text) {
50743
51049
  return text + STAGED_COMPRESS_GUIDANCE;
50744
51050
  }
50745
51051
 
51052
+ // src/absorb.ts
51053
+ var EFFECTIVE_ABSORB_KEY = "effectiveAbsorb";
51054
+ function absorbEnabled(config) {
51055
+ return config.absorb?.enabled === true;
51056
+ }
51057
+ function absorbToolName(config) {
51058
+ return config.absorb?.toolName ?? ABSORB_TOOL_NAME;
51059
+ }
51060
+ function effectiveAbsorbConfig(session, fallback) {
51061
+ const meta = session?.metadata[EFFECTIVE_ABSORB_KEY];
51062
+ if (meta && typeof meta === "object" && typeof meta.enabled === "boolean") {
51063
+ return meta;
51064
+ }
51065
+ return fallback.absorb;
51066
+ }
51067
+ function storeEffectiveAbsorb(session, config) {
51068
+ session.metadata[EFFECTIVE_ABSORB_KEY] = config.absorb ?? null;
51069
+ }
51070
+ function isProxyToolFor(name, session, config) {
51071
+ if (ACP_TOOL_NAMES.has(name)) return true;
51072
+ const absorb = effectiveAbsorbConfig(session, config);
51073
+ return absorb?.enabled === true && name === (absorb.toolName ?? ABSORB_TOOL_NAME);
51074
+ }
51075
+ function applyAbsorbView(messages, state, config, tokenCount) {
51076
+ const hidden = hideAbsorbedMessages(messages, state);
51077
+ const prompted = appendAbsorbPrompts(hidden, state, config, tokenCount, defaultCountTokens);
51078
+ return prompted.messages;
51079
+ }
51080
+ function executeAbsorb(args, callId, absorb, ctx) {
51081
+ const log2 = ctx.log ?? ((msg) => log("info", msg));
51082
+ const parsed = parseAbsorbInput(args, callId, (w2) => log2(`[acp-absorb] ${w2}`));
51083
+ if (!parsed) {
51084
+ return '[absorb FAILED: invalid input \u2014 expected { ref: "mNNNNN", summary: "..." }]';
51085
+ }
51086
+ const before = ctx.session.state.absorbed?.length ?? 0;
51087
+ let outcome;
51088
+ try {
51089
+ outcome = applyAbsorb({
51090
+ ref: parsed.ref,
51091
+ summary: parsed.summary,
51092
+ absorbCallId: parsed.absorbCallId,
51093
+ messages: ctx.messages,
51094
+ state: ctx.session.state,
51095
+ config: { ...ctx.config, absorb }
51096
+ });
51097
+ } catch (err2) {
51098
+ log2(`[acp-absorb] error: ${String(err2)}`);
51099
+ return `[absorb FAILED: ${String(err2)}]`;
51100
+ }
51101
+ ctx.session.state = outcome.state;
51102
+ if (!outcome.ok) {
51103
+ log2(`[acp-absorb] ${outcome.resultText}`);
51104
+ return `[absorb FAILED: ${outcome.resultText}]`;
51105
+ }
51106
+ const records = outcome.state.absorbed ?? [];
51107
+ const record = records.length > before ? records[records.length - 1] : void 0;
51108
+ if (record) {
51109
+ ctx.session.stats.compressCreditTokens = (ctx.session.stats.compressCreditTokens ?? 0) + record.tokensReclaimed;
51110
+ ctx.session.stats.lastInputTokens = Math.max(0, ctx.session.stats.lastInputTokens - record.tokensReclaimed);
51111
+ log2(`[acp-absorb] ${outcome.resultText} (credit -${record.tokensReclaimed} tok)`);
51112
+ } else {
51113
+ log2(`[acp-absorb] ${outcome.resultText}`);
51114
+ }
51115
+ return outcome.resultText;
51116
+ }
51117
+
50746
51118
  // src/acp-status.ts
50747
51119
  function handleAcpStatus(args, ctx) {
50748
51120
  const scope = typeof args.scope === "string" ? args.scope : void 0;
@@ -51110,6 +51482,10 @@ ${lines.join("\n\n")}`;
51110
51482
  if (toolName === "acp_status") {
51111
51483
  return handleAcpStatus(args, ctx);
51112
51484
  }
51485
+ const absorb = effectiveAbsorbConfig(ctx.session, ctx.config);
51486
+ if (absorb?.enabled === true && toolName === (absorb.toolName ?? ABSORB_TOOL_NAME)) {
51487
+ return executeAbsorb(args, void 0, absorb, ctx);
51488
+ }
51113
51489
  return `[Unknown proxy tool: ${toolName}]`;
51114
51490
  }
51115
51491
  function refNum2(ref) {
@@ -51188,7 +51564,7 @@ function rewriteJsonResponse(body, ctx) {
51188
51564
  const newContent = [];
51189
51565
  for (const block of b2.content) {
51190
51566
  const blk = block;
51191
- if (blk.type === "tool_use" && typeof blk.name === "string" && ACP_TOOL_NAMES.has(blk.name)) {
51567
+ if (blk.type === "tool_use" && typeof blk.name === "string" && isProxyToolFor(blk.name, ctx.session, ctx.config)) {
51192
51568
  converted = true;
51193
51569
  const args = blk.input && typeof blk.input === "object" ? blk.input : {};
51194
51570
  newContent.push({ type: "text", text: executeAnthropicProxyTool(blk.name, args, ctx) });
@@ -51296,14 +51672,51 @@ function splitChunks(messages, startIdx, endIdx, budget, minUnits, countText = d
51296
51672
  }
51297
51673
  return chunks;
51298
51674
  }
51299
- function summaryPayload(protocol, model, system, content) {
51675
+ function summaryPayload(protocol, model, system, content, stream2) {
51300
51676
  if (protocol === "anthropic") {
51301
- return { model, max_tokens: MAX_SUMMARY_OUTPUT_TOKENS, system, messages: [{ role: "user", content }], stream: false };
51677
+ return { model, max_tokens: MAX_SUMMARY_OUTPUT_TOKENS, system, messages: [{ role: "user", content }], stream: stream2 };
51302
51678
  }
51303
51679
  if (protocol === "openai") {
51304
- return { model, max_tokens: MAX_SUMMARY_OUTPUT_TOKENS, messages: [{ role: "system", content: system }, { role: "user", content }], stream: false };
51680
+ return { model, max_tokens: MAX_SUMMARY_OUTPUT_TOKENS, messages: [{ role: "system", content: system }, { role: "user", content }], stream: stream2 };
51305
51681
  }
51306
- return { model, max_output_tokens: MAX_SUMMARY_OUTPUT_TOKENS, instructions: system, input: [{ role: "user", content }], stream: false, store: false };
51682
+ return { model, max_output_tokens: MAX_SUMMARY_OUTPUT_TOKENS, instructions: system, input: [{ role: "user", content }], stream: stream2, store: false };
51683
+ }
51684
+ var STREAM_REQUIRED_RE = /\bstream\b[^\n]{0,60}\btrue\b/i;
51685
+ function extractSummaryFromSse(protocol, text) {
51686
+ let out = "";
51687
+ for (const line of text.split("\n")) {
51688
+ if (!line.startsWith("data:")) continue;
51689
+ const payload = line.slice(5).trim();
51690
+ if (!payload || payload === "[DONE]") continue;
51691
+ let obj;
51692
+ try {
51693
+ obj = JSON.parse(payload);
51694
+ } catch {
51695
+ continue;
51696
+ }
51697
+ if (!obj || typeof obj !== "object") continue;
51698
+ const o = obj;
51699
+ if (protocol === "anthropic") {
51700
+ if (o.type === "content_block_delta") {
51701
+ const d = o.delta;
51702
+ if (d && d.type === "text_delta" && typeof d.text === "string") out += d.text;
51703
+ }
51704
+ } else if (protocol === "openai") {
51705
+ const choices = o.choices;
51706
+ if (Array.isArray(choices) && choices.length > 0) {
51707
+ const delta = choices[0].delta;
51708
+ if (delta && typeof delta.content === "string") out += delta.content;
51709
+ }
51710
+ } else {
51711
+ if (o.type === "response.output_text.delta" && typeof o.delta === "string") {
51712
+ out += o.delta;
51713
+ } else if (o.type === "response.completed" && o.response && typeof o.response === "object") {
51714
+ const full = extractSummaryText(protocol, o.response);
51715
+ if (full) return full;
51716
+ }
51717
+ }
51718
+ }
51719
+ return out;
51307
51720
  }
51308
51721
  function extractSummaryText(protocol, json) {
51309
51722
  if (protocol === "anthropic") {
@@ -51332,12 +51745,25 @@ async function summarizeRange(deps, content, startRef, endRef) {
51332
51745
  const system = buildCompressSystemPrompt(deps.prompts) + `
51333
51746
 
51334
51747
  TASK: The conversation segment below (messages ${startRef}\u2013${endRef}) must be compressed because the session context exceeds the current model's window. Write a tier-1 compression summary of the segment following every rule above. Output ONLY the summary text \u2014 no preamble, no closing remarks, no tool calls.`;
51748
+ const learned = deps.session.metadata.preflightStreamSummary === true;
51749
+ try {
51750
+ return await requestSummary(deps, system, content, learned);
51751
+ } catch (err2) {
51752
+ if (err2 instanceof UpstreamHttpError && err2.status === 400 && !learned && STREAM_REQUIRED_RE.test(err2.body)) {
51753
+ deps.session.metadata.preflightStreamSummary = true;
51754
+ deps.log("info", "[preflight] upstream requires stream for summaries; retrying with SSE (learned for this session)");
51755
+ return await requestSummary(deps, system, content, true);
51756
+ }
51757
+ throw err2;
51758
+ }
51759
+ }
51760
+ async function requestSummary(deps, system, content, stream2) {
51335
51761
  const { response, clearTimer } = await fetchWithRetry(
51336
51762
  deps.url,
51337
51763
  {
51338
51764
  method: "POST",
51339
51765
  headers: { "content-type": "application/json", ...deps.headers },
51340
- body: JSON.stringify(summaryPayload(deps.protocol, deps.model, system, content)),
51766
+ body: JSON.stringify(summaryPayload(deps.protocol, deps.model, system, content, stream2)),
51341
51767
  dispatcher: proxyDispatcher(deps.proxyUrl)
51342
51768
  },
51343
51769
  void 0,
@@ -51352,11 +51778,12 @@ TASK: The conversation segment below (messages ${startRef}\u2013${endRef}) must
51352
51778
  try {
51353
51779
  json = JSON.parse(text);
51354
51780
  } catch {
51781
+ json = null;
51782
+ }
51783
+ const summary = (json && typeof json === "object" ? extractSummaryText(deps.protocol, json) : stream2 ? extractSummaryFromSse(deps.protocol, text) : "").trim();
51784
+ if (!json && !stream2) {
51355
51785
  deps.log("warn", `[preflight] summary response was not JSON: ${text.slice(0, 200)}`);
51356
- return null;
51357
51786
  }
51358
- if (!json || typeof json !== "object") return null;
51359
- const summary = extractSummaryText(deps.protocol, json).trim();
51360
51787
  if (summary.length < MIN_SUMMARY_CHARS) {
51361
51788
  deps.log("warn", `[preflight] summary too short (${summary.length} chars); skipping range`);
51362
51789
  return null;
@@ -51405,6 +51832,7 @@ async function preflightCompress(deps, messages) {
51405
51832
  renderTags: "text-only"
51406
51833
  });
51407
51834
  deps.session.state = turn.state;
51835
+ turn.messages = applyAbsorbView(turn.messages, turn.state, activeConfig, currentTokens);
51408
51836
  currentTokens = Math.max(deps.session.stats.lastInputTokens, estimateCoreMessages(turn.messages) + (deps.imageFloor ?? 0) + (deps.wireOverhead ?? 0));
51409
51837
  if (!baselineKnown) {
51410
51838
  finalUpper = estimateCoreMessagesUpper(turn.messages) + (deps.imageFloor ?? 0) + (deps.wireOverhead ?? 0);
@@ -51551,24 +51979,24 @@ function costForUrl(url) {
51551
51979
  }
51552
51980
  return applyCap(REMOTE_IMAGE_TOKENS);
51553
51981
  }
51554
- function isObj(v2) {
51982
+ function isObj2(v2) {
51555
51983
  return typeof v2 === "object" && v2 !== null;
51556
51984
  }
51557
51985
  function urlOf(v2) {
51558
51986
  if (typeof v2 === "string") return v2;
51559
- if (isObj(v2) && typeof v2.url === "string") return v2.url;
51987
+ if (isObj2(v2) && typeof v2.url === "string") return v2.url;
51560
51988
  return void 0;
51561
51989
  }
51562
51990
  function imageTokensInParsedBody(protocol, body) {
51563
- if (!isObj(body)) return 0;
51991
+ if (!isObj2(body)) return 0;
51564
51992
  let total = 0;
51565
51993
  if (protocol === "responses") {
51566
51994
  const input = body.input;
51567
51995
  if (!Array.isArray(input)) return 0;
51568
51996
  for (const item of input) {
51569
- if (!isObj(item) || !Array.isArray(item.content)) continue;
51997
+ if (!isObj2(item) || !Array.isArray(item.content)) continue;
51570
51998
  for (const part of item.content) {
51571
- if (!isObj(part) || part.type !== "input_image") continue;
51999
+ if (!isObj2(part) || part.type !== "input_image") continue;
51572
52000
  const url = urlOf(part.image_url);
51573
52001
  if (url) total += costForUrl(url);
51574
52002
  }
@@ -51578,9 +52006,9 @@ function imageTokensInParsedBody(protocol, body) {
51578
52006
  const messages = body.messages;
51579
52007
  if (!Array.isArray(messages)) return 0;
51580
52008
  for (const m2 of messages) {
51581
- if (!isObj(m2) || !Array.isArray(m2.content)) continue;
52009
+ if (!isObj2(m2) || !Array.isArray(m2.content)) continue;
51582
52010
  for (const part of m2.content) {
51583
- if (!isObj(part)) continue;
52011
+ if (!isObj2(part)) continue;
51584
52012
  if (protocol === "openai") {
51585
52013
  if (part.type !== "image_url") continue;
51586
52014
  const url = urlOf(part.image_url);
@@ -51588,8 +52016,8 @@ function imageTokensInParsedBody(protocol, body) {
51588
52016
  } else {
51589
52017
  if (part.type !== "image") continue;
51590
52018
  const src = part.source;
51591
- if (isObj(src) && src.type === "base64" && typeof src.data === "string") total += applyCap(Math.ceil(src.data.length / 4));
51592
- else if (isObj(src) && src.type === "url" && typeof src.url === "string") total += costForUrl(src.url);
52019
+ if (isObj2(src) && src.type === "base64" && typeof src.data === "string") total += applyCap(Math.ceil(src.data.length / 4));
52020
+ else if (isObj2(src) && src.type === "url" && typeof src.url === "string") total += costForUrl(src.url);
51593
52021
  }
51594
52022
  }
51595
52023
  }
@@ -51829,7 +52257,7 @@ function renderPage(origin, version2) {
51829
52257
  <div class="card"><div class="card-head"><h2>ZCode\uFF08\u7F16\u7A0B\u5957\u9910\uFF09</h2><span class="badge">MITM</span><button class="btn small copy-btn" data-copy="${caPathEsc}">\u590D\u5236 CA \u8DEF\u5F84</button></div><dl class="kv"><dt>\u65B9\u5F0F</dt><dd>MITM \u900F\u660E\u4EE3\u7406</dd><dt>\u8BBE\u7F6E</dt><dd>Settings \u2192 Network \u2192 HTTP Proxy = <span class="mono">${origin}</span></dd><dt>CA \u8BC1\u4E66</dt><dd class="mono">${caPathEsc}${caReady ? "" : "\uFF08\u5C1A\u672A\u751F\u6210\uFF0C\u542F\u52A8\u4EE3\u7406\u540E\u81EA\u52A8\u521B\u5EFA\uFF09"}</dd></dl><p class="hint">CA \u8DEF\u5F84\u662F\u672C\u673A bili \u5B9E\u9645\u751F\u6210\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u70B9"\u590D\u5236 CA \u8DEF\u5F84"\u540E\u76F4\u63A5\u7C98\u8D34\u8FDB ZCode\u3002\u26A0\uFE0F Windows \u4E0A ZCode \u4E0D\u4F1A\u5C55\u5F00 <span class="mono">~</span>\uFF0C\u8BF7\u52FF\u624B\u586B <span class="mono">~/\u2026</span> \u5F62\u5F0F\u7684\u8DEF\u5F84\u3002</p></div></section>
51830
52258
  <section id="page-upstream" class="page"><h1>\u4E0A\u6E38\u7F51\u7EDC</h1><p class="lead">\u63A7\u5236 bili \u5982\u4F55\u8BBF\u95EE\u771F\u5B9E Provider\uFF0C\u4E0D\u4F1A\u6539\u53D8\u5BA2\u6237\u7AEF\u7684\u672C\u5730\u8DEF\u7531\u5730\u5740\u3002</p><div class="card"><div class="card-head"><h2>\u5168\u5C40\u4EE3\u7406</h2></div><div class="modes"><label><input type="radio" name="proxy-mode" value="direct" checked>\u76F4\u8FDE\uFF08\u9ED8\u8BA4\uFF09</label><label><input type="radio" name="proxy-mode" value="manual">\u624B\u52A8\u4EE3\u7406</label><label><input type="radio" name="proxy-mode" value="auto">\u81EA\u52A8\uFF08\u8DDF\u968F\u7CFB\u7EDF\uFF09</label></div><div class="field"><label>HTTP / HTTPS Proxy</label><input id="proxy-url" class="mono" placeholder="http://127.0.0.1:7897"></div><dl class="kv"><dt>\u5F53\u524D\u6765\u6E90</dt><dd id="upstream-source">\u76F4\u8FDE</dd><dt>\u6709\u6548\u4EE3\u7406</dt><dd id="upstream-effective" class="mono">direct</dd><dt>\u7CFB\u7EDF PAC</dt><dd id="upstream-pac" class="mono">\u2014</dd><dt>\u72B6\u6001</dt><dd id="upstream-state" class="status">\u5C1A\u672A\u6D4B\u8BD5</dd></dl><div class="actions"><button id="save-upstream" class="btn primary">\u4FDD\u5B58\u5E76\u70ED\u66F4\u65B0</button><button id="test-upstream" class="btn">\u6D4B\u8BD5\u8FDE\u63A5</button></div></div><div class="card"><div class="card-head"><h2>\u6309 URL \u8986\u76D6</h2></div><p class="status">\u4E3A\u7279\u5B9A Provider \u5355\u72EC\u8BBE\u7F6E\u4E0A\u6E38\u4EE3\u7406\uFF0C\u8986\u76D6\u5168\u5C40\u8BBE\u7F6E\u3002\u7559\u7A7A = \u7EE7\u627F\u5168\u5C40\u3002</p><div id="upstream-overrides" class="status">\u52A0\u8F7D\u4E2D</div><div class="actions"><button id="save-overrides" class="btn primary">\u4FDD\u5B58\u8986\u76D6\u5E76\u70ED\u66F4\u65B0</button></div></div></section>
51831
52259
  <section id="page-sessions" class="page"><div class="card-head"><div><h1>\u4F1A\u8BDD</h1><p class="lead">ACP \u538B\u7F29\u72B6\u6001\u4E0E\u4E0A\u6E38\u7528\u91CF\u3002</p></div><button id="refresh-sessions" class="btn">\u5237\u65B0</button></div><div class="card"><table><thead><tr><th>\u6807\u9898</th><th>\u534F\u8BAE</th><th>\u6807\u8BC6</th><th>\u8BF7\u6C42</th><th>\u4E0A\u4E0B\u6587</th><th>\u6700\u540E\u6D3B\u52A8</th></tr></thead><tbody id="sessions-body"></tbody></table></div></section>
51832
- <section id="page-settings" class="page"><h1>\u9AD8\u7EA7\u8BBE\u7F6E</h1><p class="lead">\u76F4\u63A5\u7F16\u8F91 providers JSON\uFF08\u6DFB\u52A0\u65B0 Provider\u3001\u914D\u7F6E\u6A21\u578B\u4E0A\u4E0B\u6587\u7A97\u53E3\u3001\u6309 URL \u4EE3\u7406\u7B49\uFF09\uFF1B\u4FDD\u5B58\u540E\u7ACB\u5373\u70ED\u66F4\u65B0\u3002</p><div class="card"><div class="field"><label>providers JSON</label><textarea id="providers-json">{}</textarea></div><div class="actions"><button id="save-providers" class="btn primary">\u4FDD\u5B58\u5E76\u5E94\u7528</button></div></div><div class="card"><div class="card-head"><h2>\u538B\u7F29\u53C2\u6570\uFF08\u5168\u5C40\uFF09</h2></div><p class="status">\u5168\u5C40 compress \u914D\u7F6E\uFF08modelContextLimit / maxContextLimit / emergencyThresholdPercent / nudgeGrowthTokens / preserveRecentMessages / preserveRecentTokens / minCompressRangeChars\uFF08\u5355\u4F4D\uFF1A\u5B57\u7B26\uFF0C\u65E7\u540D minCompressRange \u517C\u5BB9\uFF09 / tiers / injectTool / injectNudge\uFF09\u3002\u4FDD\u5B58\u540E\u70ED\u66F4\u65B0\uFF0C\u65E0\u9700\u91CD\u542F\u3002\u7F6E\u7A7A = \u6062\u590D\u9ED8\u8BA4\u3002</p><div class="field"><label>compress JSON</label><textarea id="compress-json" placeholder='{"modelContextLimit": 200000}'></textarea></div><div class="actions"><button id="save-compress" class="btn primary">\u4FDD\u5B58\u5E76\u70ED\u66F4\u65B0</button></div></div></section></main></div><div id="toast" class="toast"></div><script>${WEB_CLIENT}</script></body></html>`;
52260
+ <section id="page-settings" class="page"><h1>\u9AD8\u7EA7\u8BBE\u7F6E</h1><p class="lead">\u76F4\u63A5\u7F16\u8F91 providers JSON\uFF08\u6DFB\u52A0\u65B0 Provider\u3001\u914D\u7F6E\u6A21\u578B\u4E0A\u4E0B\u6587\u7A97\u53E3\u3001\u6309 URL \u4EE3\u7406\u7B49\uFF09\uFF1B\u4FDD\u5B58\u540E\u7ACB\u5373\u70ED\u66F4\u65B0\u3002</p><div class="card"><div class="field"><label>providers JSON</label><textarea id="providers-json">{}</textarea></div><div class="actions"><button id="save-providers" class="btn primary">\u4FDD\u5B58\u5E76\u5E94\u7528</button></div></div><div class="card"><div class="card-head"><h2>\u538B\u7F29\u53C2\u6570\uFF08\u5168\u5C40\uFF09</h2></div><p class="status">\u5168\u5C40 compress \u914D\u7F6E\uFF08modelContextLimit / maxContextLimit / emergencyThresholdPercent / nudgeGrowthTokens / preserveRecentMessages / preserveRecentTokens / minCompressRangeChars\uFF08\u5355\u4F4D\uFF1A\u5B57\u7B26\uFF0C\u65E7\u540D minCompressRange \u517C\u5BB9\uFF09 / stripImages / stripImagesKeepRecent\uFF08\u5386\u53F2\u56FE\u50CF\u5265\u79BB\uFF0C\u9ED8\u8BA4\u5173\uFF09 / tiers / injectTool / injectNudge\uFF09\u3002\u4FDD\u5B58\u540E\u70ED\u66F4\u65B0\uFF0C\u65E0\u9700\u91CD\u542F\u3002\u7F6E\u7A7A = \u6062\u590D\u9ED8\u8BA4\u3002</p><div class="field"><label>compress JSON</label><textarea id="compress-json" placeholder='{"modelContextLimit": 200000}'></textarea></div><div class="actions"><button id="save-compress" class="btn primary">\u4FDD\u5B58\u5E76\u70ED\u66F4\u65B0</button></div></div></section></main></div><div id="toast" class="toast"></div><script>${WEB_CLIENT}</script></body></html>`;
51833
52261
  }
51834
52262
 
51835
52263
  // src/web/api.ts
@@ -52256,7 +52684,8 @@ function buildVisibilityMarker(toolName, result) {
52256
52684
  compress: "\u{1F4E6}",
52257
52685
  decompress: "\u{1F4E4}",
52258
52686
  search_context: "\u{1F50D}",
52259
- acp_status: "\u{1F4CA}"
52687
+ acp_status: "\u{1F4CA}",
52688
+ absorb: "\u{1FAE7}"
52260
52689
  };
52261
52690
  const icon = failed ? "\u274C" : icons[toolName] ?? "\u{1F4E6}";
52262
52691
  if (toolName === "acp_status") {
@@ -52584,6 +53013,10 @@ ${lines.join("\n\n")}`;
52584
53013
  if (toolName === "acp_status") {
52585
53014
  return handleAcpStatus(args, ctx);
52586
53015
  }
53016
+ const absorb = effectiveAbsorbConfig(ctx.session, ctx.config);
53017
+ if (absorb?.enabled === true && toolName === (absorb.toolName ?? ABSORB_TOOL_NAME)) {
53018
+ return executeAbsorb(args, callId, absorb, ctx);
53019
+ }
52587
53020
  return `[Unknown proxy tool: ${toolName}]`;
52588
53021
  }
52589
53022
  function recordUsage(ctx, usage, round) {
@@ -52751,7 +53184,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
52751
53184
  const realToolCalls = [];
52752
53185
  const proxyResults = [];
52753
53186
  for (const call of allCalls) {
52754
- if (ACP_TOOL_NAMES.has(call.name)) {
53187
+ if (isProxyToolFor(call.name, ctx.session, ctx.config)) {
52755
53188
  let parsedArgs;
52756
53189
  try {
52757
53190
  parsedArgs = call.arguments.length > 0 ? JSON.parse(call.arguments) : {};
@@ -52840,7 +53273,11 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
52840
53273
  coreMessages.push(...hidden.messages);
52841
53274
  }
52842
53275
  }
52843
- if (ctx.refreshFolded && proxyResults.some((pr2) => pr2.name === "compress" && !pr2.result.includes("FAILED"))) {
53276
+ const absorbName = (() => {
53277
+ const a = effectiveAbsorbConfig(ctx.session, ctx.config);
53278
+ return a?.enabled === true ? a.toolName ?? ABSORB_TOOL_NAME : void 0;
53279
+ })();
53280
+ if (ctx.refreshFolded && proxyResults.some((pr2) => (pr2.name === "compress" || pr2.name === absorbName) && !pr2.result.includes("FAILED"))) {
52844
53281
  try {
52845
53282
  const refreshed = ctx.refreshFolded(coreMessages);
52846
53283
  if (refreshed.length > 0) {
@@ -53182,7 +53619,7 @@ data: ${JSON.stringify({ type: "response.completed", response: resp })}
53182
53619
  "utf8"
53183
53620
  );
53184
53621
  }
53185
- function createResponsesAdapter(textProtocol, projection) {
53622
+ function createResponsesAdapter(textProtocol, projection, absorbName) {
53186
53623
  const suppressTextLifecycle = !!textProtocol;
53187
53624
  let outputIndex = 0;
53188
53625
  let responseObj = null;
@@ -53268,7 +53705,7 @@ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId
53268
53705
  const item = obj.item;
53269
53706
  if (item?.type === "function_call") {
53270
53707
  const fcName = typeof item.name === "string" ? item.name : "";
53271
- if (ACP_TOOL_NAMES.has(fcName)) {
53708
+ if (ACP_TOOL_NAMES.has(fcName) || fcName === absorbName) {
53272
53709
  const itemId = typeof item.id === "string" ? item.id : "";
53273
53710
  pending.set(itemId, {
53274
53711
  itemId,
@@ -53613,7 +54050,7 @@ function patchUsageChunk(eventStr, parsed, u2, hostCredit) {
53613
54050
  }
53614
54051
  return Buffer.from(eventStr + "\n\n", "utf8");
53615
54052
  }
53616
- function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0) {
54053
+ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0, absorbName) {
53617
54054
  const model = requestBody.model ?? "unknown";
53618
54055
  let responseId = `chatcmpl-proxy-${Date.now()}`;
53619
54056
  let toolIndex = 0;
@@ -53715,7 +54152,7 @@ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0) {
53715
54152
  const settleToolCalls = function* () {
53716
54153
  const realIndexes = /* @__PURE__ */ new Set();
53717
54154
  for (const [idx, tc] of pending) {
53718
- if (tc.name.length > 0 && !PROXY_TOOL_SET.has(tc.name)) realIndexes.add(idx);
54155
+ if (tc.name.length > 0 && !PROXY_TOOL_SET.has(tc.name) && tc.name !== absorbName) realIndexes.add(idx);
53719
54156
  }
53720
54157
  sawRealToolCall = realIndexes.size > 0;
53721
54158
  if (!sawRealToolCall) {
@@ -54307,9 +54744,9 @@ data: ${JSON.stringify({ type: "content_block_stop", index })}
54307
54744
  }
54308
54745
 
54309
54746
  // src/loop/index.ts
54310
- function pickAdapter(protocol, requestBody, textProtocol, responsesProjection, anthropicSystem, openaiSystem, hostCredit = 0) {
54311
- if (protocol === "responses") return createResponsesAdapter(textProtocol, responsesProjection);
54312
- if (protocol === "openai") return createOpenaiAdapter(requestBody, openaiSystem, hostCredit);
54747
+ function pickAdapter(protocol, requestBody, textProtocol, responsesProjection, anthropicSystem, openaiSystem, hostCredit = 0, absorbName) {
54748
+ if (protocol === "responses") return createResponsesAdapter(textProtocol, responsesProjection, absorbName);
54749
+ if (protocol === "openai") return createOpenaiAdapter(requestBody, openaiSystem, hostCredit, absorbName);
54313
54750
  if (protocol === "anthropic") return createAnthropicAdapter(requestBody, anthropicSystem, hostCredit);
54314
54751
  throw new Error(`[acp-loop] unknown protocol: ${protocol}`);
54315
54752
  }
@@ -55594,11 +56031,16 @@ function handlePluginManifest(res) {
55594
56031
  protocolVersion: PLUGIN_PROTOCOL_VERSION,
55595
56032
  proxy: "billion-context",
55596
56033
  version: VERSION,
55597
- toolNames: [...ACP_TOOL_NAMES],
56034
+ // Absorb is advertised alongside the four ACP tools. It is host-
56035
+ // registered opt-in (not in the kernel's ACP_TOOL_NAMES), so the
56036
+ // manifest must list it explicitly. Per-session enablement is enforced
56037
+ // at execution (isProxyToolFor / executeProxyTool), not here — the
56038
+ // manifest has no request context to know which route will win.
56039
+ toolNames: [...ACP_TOOL_NAMES, ABSORB_TOOL_NAME],
55598
56040
  tools: {
55599
- anthropic: ACP_TOOLS_ANTHROPIC,
55600
- openai: ACP_TOOLS_OPENAI,
55601
- responses: ACP_TOOLS_RESPONSES
56041
+ anthropic: [...ACP_TOOLS_ANTHROPIC, ABSORB_TOOL],
56042
+ openai: [...ACP_TOOLS_OPENAI, ABSORB_TOOL_OPENAI],
56043
+ responses: [...ACP_TOOLS_RESPONSES, ABSORB_TOOL_RESPONSES]
55602
56044
  },
55603
56045
  headers: { agent: PLUGIN_AGENT_HEADER, conversation: PLUGIN_CONVERSATION_HEADER, contextWindow: PLUGIN_CONTEXT_WINDOW_HEADER },
55604
56046
  toolEndpoint: "/__bili/plugin/tool",
@@ -55695,11 +56137,6 @@ async function handlePluginTool(payload, res, deps) {
55695
56137
  res.end(JSON.stringify({ ok: false, error: `conversationId is required (send the same value as the ${PLUGIN_CONVERSATION_HEADER} header)` }));
55696
56138
  return;
55697
56139
  }
55698
- if (!ACP_TOOL_NAMES.has(tool)) {
55699
- res.writeHead(400, { "content-type": "application/json" });
55700
- res.end(JSON.stringify({ ok: false, error: `unknown tool "${tool}" (expected one of: ${[...ACP_TOOL_NAMES].join(", ")})` }));
55701
- return;
55702
- }
55703
56140
  const entry = conversations.get(conversationId2);
55704
56141
  const session = entry ? peekSession(entry.sessionId) : void 0;
55705
56142
  if (!entry || !session) {
@@ -55707,6 +56144,14 @@ async function handlePluginTool(payload, res, deps) {
55707
56144
  res.end(JSON.stringify({ ok: false, error: "unknown plugin conversation (no model request has arrived with this conversation id yet)" }));
55708
56145
  return;
55709
56146
  }
56147
+ if (!isProxyToolFor(tool, session, deps.config)) {
56148
+ const allowed = [...ACP_TOOL_NAMES];
56149
+ const absorb = effectiveAbsorbConfig(session, deps.config);
56150
+ if (absorb?.enabled === true) allowed.push(absorb.toolName ?? ABSORB_TOOL_NAME);
56151
+ res.writeHead(400, { "content-type": "application/json" });
56152
+ res.end(JSON.stringify({ ok: false, error: `unknown tool "${tool}" (expected one of: ${allowed.join(", ")})` }));
56153
+ return;
56154
+ }
55710
56155
  entry.lastSeen = Date.now();
55711
56156
  const args = parsed.args && typeof parsed.args === "object" ? parsed.args : {};
55712
56157
  const callId = `plugin_${Date.now().toString(36)}`;
@@ -58533,7 +58978,16 @@ ${bodyBuffer.toString("utf8")}`);
58533
58978
  acquireInFlight(session);
58534
58979
  try {
58535
58980
  await withSessionLock(session, async () => {
58536
- const runPrepare = () => countTokens ? prepareCountTokens(parsed, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(parsed, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode) : protocol === "openai" ? prepareOpenai(parsed, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode, nativeWindow) : responsesCompact ? prepareResponsesCompact(bodyBuffer, parsed, session, req, core, reqConfig, log2) : prepareResponses(parsed, req, opts, core, reqConfig, reqPrompts, log2, session, responsesIdentity, pluginMode, upstreamOrigin, nativeWindow);
58981
+ const runPrepare = () => {
58982
+ const cs2 = resolveCompress(opts.routes, route?.rewrittenUrl, parsed.model, opts.compress);
58983
+ const keepRecent = cs2.stripImagesKeepRecent ?? DEFAULT_STRIP_IMAGES_KEEP_RECENT;
58984
+ const stripped = cs2.stripImages ? stripHistoricalImages(parsed, protocol, keepRecent) : { body: parsed, removed: 0 };
58985
+ if (opts.debug && stripped.removed > 0) {
58986
+ log2("info", `[debug] strip-images: dropped ${stripped.removed} historical image part(s), kept last ${keepRecent} (session=${session.id})`);
58987
+ }
58988
+ const work = stripped.body;
58989
+ return countTokens ? prepareCountTokens(work, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(work, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode) : protocol === "openai" ? prepareOpenai(work, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode, nativeWindow) : responsesCompact ? prepareResponsesCompact(stripped.removed > 0 ? Buffer.from(JSON.stringify(work)) : bodyBuffer, work, session, req, core, reqConfig, log2) : prepareResponses(work, req, opts, core, reqConfig, reqPrompts, log2, session, responsesIdentity, pluginMode, upstreamOrigin, nativeWindow);
58990
+ };
58537
58991
  const isCodexCompactTrigger = protocol === "responses" && !responsesCompact && isCodexClient(req.headers) && hasCompactionTrigger(parsed.input);
58538
58992
  if (isCodexCompactTrigger) {
58539
58993
  const mode = codexCompactMode();
@@ -58710,7 +59164,7 @@ function diagNudge(turn, sessionId, tokenCount, limit, model, willInject) {
58710
59164
  }
58711
59165
  function armHostUsageCredit(session, originalMessages, processedMessages, log2) {
58712
59166
  session.hostCreditTokens = 0;
58713
- if (session.metadata.pluginAgent === "pi") return;
59167
+ if (session.metadata.pluginAgent === "pi" || session.metadata.pluginAgent === "omp") return;
58714
59168
  session.hostCreditTokens = processedMessages.length > 0 ? Math.max(0, estimateCoreMessages(originalMessages) - estimateCoreMessages(processedMessages)) : 0;
58715
59169
  if (session.hostCreditTokens > 0) {
58716
59170
  log2("info", `[${session.id}] host usage backfill armed: +${session.hostCreditTokens} tok (forwarded view is folded); host usage will report the uncompressed baseline`);
@@ -58747,9 +59201,13 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
58747
59201
  extractSystem(parsed.system);
58748
59202
  const tokenCount = effectiveTokenCount(session, msgs);
58749
59203
  const activeBefore = new Set(session.state.blocks.filter((b2) => b2.active).map((b2) => b2.blockId));
58750
- const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
59204
+ const absorbActive = absorbEnabled(config) && opts.compress.injectTool;
59205
+ const loopConfig = absorbActive ? config : { ...config, absorb: void 0 };
59206
+ const turn = core.processTurn({ messages: msgs, state: session.state, config: loopConfig, tokenCount, renderTags: "text-only" });
58751
59207
  session.state = turn.state;
58752
59208
  session.stats.compressCreditTokens = 0;
59209
+ storeEffectiveAbsorb(session, loopConfig);
59210
+ turn.messages = applyAbsorbView(turn.messages, session.state, loopConfig, tokenCount);
58753
59211
  if (turn.nudge) turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
58754
59212
  nudge = turn.nudge;
58755
59213
  session.stats.contextTokens = tokenCount;
@@ -58764,9 +59222,9 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
58764
59222
  applyCompactionArchive(session, activeBefore, new Set(msgs.map((m2) => m2.id)), log2);
58765
59223
  reapOrphanBlocks(session, msgs, deactivateBlock);
58766
59224
  rebuiltMessages = coreToAnthropic(processedMessages, cacheControls);
58767
- systemOut = injectSystem(parsed, opts, prompts);
59225
+ systemOut = injectSystem(parsed, opts, prompts, loopConfig);
58768
59226
  if (injectTools) {
58769
- toolsOut = injectTool(parsed.tools);
59227
+ toolsOut = injectTool(parsed.tools, absorbActive ? ABSORB_TOOL : void 0);
58770
59228
  }
58771
59229
  if (willInjectNudge && turn.nudge) {
58772
59230
  try {
@@ -58868,9 +59326,13 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
58868
59326
  originalMessages = msgs;
58869
59327
  const tokenCount = effectiveTokenCount(session, msgs);
58870
59328
  const activeBefore = new Set(session.state.blocks.filter((b2) => b2.active).map((b2) => b2.blockId));
58871
- const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
59329
+ const absorbActive = absorbEnabled(config) && shouldInject;
59330
+ const loopConfig = absorbActive ? config : { ...config, absorb: void 0 };
59331
+ const turn = core.processTurn({ messages: msgs, state: session.state, config: loopConfig, tokenCount, renderTags: "text-only" });
58872
59332
  session.state = turn.state;
58873
59333
  session.stats.compressCreditTokens = 0;
59334
+ storeEffectiveAbsorb(session, loopConfig);
59335
+ turn.messages = applyAbsorbView(turn.messages, session.state, loopConfig, tokenCount);
58874
59336
  if (turn.nudge) turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
58875
59337
  nudge = turn.nudge;
58876
59338
  session.stats.contextTokens = tokenCount;
@@ -58888,10 +59350,11 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
58888
59350
  const sysParts = [];
58889
59351
  if (systemText) sysParts.push(systemText);
58890
59352
  if (shouldInject) sysParts.push(buildCompressSystemPrompt(prompts));
59353
+ if (absorbActive) sysParts.push(buildAbsorbSystemPrompt(absorbToolName(config)));
58891
59354
  rebuiltMessages = injectOpenaiSystem(rebuiltMessages, sysParts);
58892
59355
  openaiOutboundSystem = sysParts.join("\n\n");
58893
59356
  if (injectTools) {
58894
- toolsOut = injectOpenaiTool(parsed.tools);
59357
+ toolsOut = injectOpenaiTool(parsed.tools, absorbActive ? ABSORB_TOOL_OPENAI : void 0);
58895
59358
  }
58896
59359
  if (willInjectNudge && turn.nudge) {
58897
59360
  try {
@@ -58972,9 +59435,13 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
58972
59435
  log2("info", `[${sessionId}] input items: ${Array.isArray(parsed.input) ? parsed.input.map((i) => i.type).join(",") : "(string)"}`);
58973
59436
  }
58974
59437
  const tokenCount = effectiveTokenCount(session, msgs);
58975
- const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags });
59438
+ const absorbActive = absorbEnabled(config) && shouldInject && !isCompactionTrigger && !responsesTextProtocol;
59439
+ const loopConfig = absorbActive ? config : { ...config, absorb: void 0 };
59440
+ const turn = core.processTurn({ messages: msgs, state: session.state, config: loopConfig, tokenCount, renderTags });
58976
59441
  session.state = turn.state;
58977
59442
  session.stats.compressCreditTokens = 0;
59443
+ storeEffectiveAbsorb(session, loopConfig);
59444
+ turn.messages = applyAbsorbView(turn.messages, session.state, loopConfig, tokenCount);
58978
59445
  if (turn.nudge) turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
58979
59446
  nudge = turn.nudge;
58980
59447
  session.stats.contextTokens = tokenCount;
@@ -58991,11 +59458,13 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
58991
59458
  const forgedSummaries = echoReplaced ? [] : session.metadata.codexForgedSummaries ?? [];
58992
59459
  if (shouldInject && !isCompactionTrigger && !process.env.ACP_NO_COMPRESS_PROMPT) {
58993
59460
  const prompt = responsesTextProtocol ? buildCompressHybridSystemPrompt(prompts) : buildCompressSystemPrompt(prompts);
58994
- const devContent = [...projection.systemParts, ...forgedSummaries, prompt].join("\n\n---\n\n");
59461
+ const devParts = [...projection.systemParts, ...forgedSummaries, prompt];
59462
+ if (absorbActive) devParts.push(buildAbsorbSystemPrompt(absorbToolName(config)));
59463
+ const devContent = devParts.join("\n\n---\n\n");
58995
59464
  responsesDevContent = devContent;
58996
59465
  rebuiltInput = injectResponsesDeveloperMessage(rebuiltInput, devContent);
58997
59466
  if (!process.env.ACP_NO_INJECT_TOOL && injectTools) {
58998
- toolsOut = responsesTextProtocol ? injectResponsesTool(parsed.tools, ACP_READONLY_TOOLS_RESPONSES) : injectResponsesTool(parsed.tools);
59467
+ toolsOut = responsesTextProtocol ? injectResponsesTool(parsed.tools, ACP_READONLY_TOOLS_RESPONSES) : injectResponsesTool(parsed.tools, absorbActive ? [...ACP_TOOLS_RESPONSES, ABSORB_TOOL_RESPONSES] : ACP_TOOLS_RESPONSES);
58999
59468
  }
59000
59469
  } else if (projection.systemParts.length > 0 || forgedSummaries.length > 0) {
59001
59470
  const devContent = [...projection.systemParts, ...forgedSummaries].join("\n\n---\n\n");
@@ -59140,14 +59609,16 @@ function prepareResponsesCompact(body, parsed, session, req, core, config, log2)
59140
59609
  let transformOk = false;
59141
59610
  try {
59142
59611
  const projection = responsesToCore(forgeBody);
59143
- const turn = core.processTurn({ messages: projection.msgs, state: session.state, config, tokenCount: session.stats.lastInputTokens, renderTags: process.env.ACP_RENDER_NONE ? "none" : "text-only" });
59612
+ const compactConfig = { ...config, absorb: void 0 };
59613
+ const turn = core.processTurn({ messages: projection.msgs, state: session.state, config: compactConfig, tokenCount: session.stats.lastInputTokens, renderTags: process.env.ACP_RENDER_NONE ? "none" : "text-only" });
59144
59614
  session.state = turn.state;
59145
59615
  transformOk = true;
59146
59616
  if (!codexCompactGate(session, config.modelContextLimit, transformOk)) {
59147
59617
  session.state = prevState;
59148
59618
  return base;
59149
59619
  }
59150
- const processed = repairResponsesAssistantOrdering(stripKernelSummaries(turn.messages, turn.state), projection.msgs);
59620
+ const viewed = applyAbsorbView(turn.messages, turn.state, compactConfig, session.stats.lastInputTokens);
59621
+ const processed = repairResponsesAssistantOrdering(stripKernelSummaries(viewed, turn.state), projection.msgs);
59151
59622
  const output = patchResponsesInput(projection, processed);
59152
59623
  if (typeof output === "string") {
59153
59624
  session.state = prevState;
@@ -59183,10 +59654,11 @@ function isAutoModeClassifier(parsed) {
59183
59654
  if (!Array.isArray(stops)) return false;
59184
59655
  return stops.some((s3) => typeof s3 === "string" && AUTO_MODE_CLASSIFIER_STOPS.has(s3));
59185
59656
  }
59186
- function injectSystem(parsed, opts, prompts = defaultPrompts) {
59657
+ function injectSystem(parsed, opts, prompts = defaultPrompts, config) {
59187
59658
  const baseText = extractSystem(parsed.system);
59188
59659
  const parts = [];
59189
59660
  if (opts.compress.injectTool) parts.push(buildCompressSystemPrompt(prompts));
59661
+ if (opts.compress.injectTool && absorbEnabled(config)) parts.push(buildAbsorbSystemPrompt(absorbToolName(config)));
59190
59662
  if (parts.length === 0) return parsed.system;
59191
59663
  const full = baseText ? `${baseText}
59192
59664
 
@@ -59195,19 +59667,23 @@ function injectSystem(parsed, opts, prompts = defaultPrompts) {
59195
59667
  ${parts.join("\n\n")}` : parts.join("\n\n");
59196
59668
  return buildSystem(full, parsed.system);
59197
59669
  }
59198
- function injectTool(tools) {
59199
- if (!Array.isArray(tools)) return [...ACP_TOOLS_ANTHROPIC];
59670
+ function injectTool(tools, extra) {
59671
+ if (!Array.isArray(tools)) return extra ? [...ACP_TOOLS_ANTHROPIC, extra] : [...ACP_TOOLS_ANTHROPIC];
59200
59672
  const names = new Set(tools.map((t) => t?.name));
59201
59673
  const missing = ACP_TOOLS_ANTHROPIC.filter((t) => !names.has(t.name));
59202
- return missing.length === 0 ? tools : [...tools, ...missing];
59674
+ const extraMissing = extra && !names.has(extra.name);
59675
+ if (missing.length === 0 && !extraMissing) return tools;
59676
+ return [...tools, ...missing, ...extraMissing ? [extra] : []];
59203
59677
  }
59204
- function injectOpenaiTool(tools) {
59205
- if (!Array.isArray(tools)) return [...ACP_TOOLS_OPENAI];
59678
+ function injectOpenaiTool(tools, extra) {
59679
+ if (!Array.isArray(tools)) return extra ? [...ACP_TOOLS_OPENAI, extra] : [...ACP_TOOLS_OPENAI];
59206
59680
  const present = new Set(
59207
59681
  tools.map((t) => t?.function?.name).filter((n) => typeof n === "string")
59208
59682
  );
59209
59683
  const additions = ACP_TOOLS_OPENAI.filter((t) => !present.has(t.function.name));
59210
- return [...tools, ...additions];
59684
+ const out = [...tools, ...additions];
59685
+ if (extra && !out.some((t) => t?.function?.name === extra.function?.name)) out.push(extra);
59686
+ return out;
59211
59687
  }
59212
59688
  var FORCE_TEXT_PROTOCOL = process.env.ACP_COMPRESS_PROTOCOL === "text";
59213
59689
  function injectResponsesTool(tools, toolsToAdd = ACP_TOOLS_RESPONSES) {
@@ -59820,19 +60296,27 @@ ${hdrText}
59820
60296
  const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
59821
60297
  const reqHeaders = buildForwardHeaders(headers);
59822
60298
  const textProtocol = prepared.protocol === "responses" && !!prepared.responsesTextProtocol;
59823
- const systemPrompt = textProtocol ? buildCompressHybridSystemPrompt(prepared.prompts ?? defaultPrompts) : buildCompressSystemPrompt(prepared.prompts ?? defaultPrompts);
59824
- const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem, prepared.openaiSystemText, prepared.session.hostCreditTokens ?? 0);
60299
+ const absorbActive = absorbEnabled(config) && opts.compress.injectTool && !textProtocol;
60300
+ const loopConfig = absorbActive ? config : { ...config, absorb: void 0 };
60301
+ const absorbSection = absorbActive ? `
60302
+
60303
+ ---
60304
+
60305
+ ${buildAbsorbSystemPrompt(absorbToolName(loopConfig))}` : "";
60306
+ const systemPrompt = (textProtocol ? buildCompressHybridSystemPrompt(prepared.prompts ?? defaultPrompts) : buildCompressSystemPrompt(prepared.prompts ?? defaultPrompts)) + absorbSection;
60307
+ const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem, prepared.openaiSystemText, prepared.session.hostCreditTokens ?? 0, absorbActive ? absorbToolName(loopConfig) : void 0);
59825
60308
  const refreshFolded = (current) => {
59826
60309
  const turn = core.processTurn({
59827
60310
  messages: prepared.originalMessages,
59828
60311
  state: prepared.session.state,
59829
- config,
60312
+ config: loopConfig,
59830
60313
  tokenCount: prepared.session.stats.lastInputTokens,
59831
60314
  renderTags: prepared.renderTags ?? "text-only"
59832
60315
  });
59833
60316
  prepared.session.state = turn.state;
60317
+ const viewed = applyAbsorbView(turn.messages, turn.state, loopConfig, prepared.session.stats.lastInputTokens);
59834
60318
  const records = current.filter((m2) => typeof m2.id === "string" && m2.id.startsWith("acp_loop_"));
59835
- return repairResponsesAssistantOrdering(stripKernelSummaries([...turn.messages, ...records], turn.state), prepared.originalMessages);
60319
+ return repairResponsesAssistantOrdering(stripKernelSummaries([...viewed, ...records], turn.state), prepared.originalMessages);
59836
60320
  };
59837
60321
  const loop = runCompressLoop(
59838
60322
  streamToRead,