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/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/index.js +549 -65
- package/dist/index.js.map +1 -1
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
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" &&
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
51997
|
+
if (!isObj2(item) || !Array.isArray(item.content)) continue;
|
|
51570
51998
|
for (const part of item.content) {
|
|
51571
|
-
if (!
|
|
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 (!
|
|
52009
|
+
if (!isObj2(m2) || !Array.isArray(m2.content)) continue;
|
|
51582
52010
|
for (const part of m2.content) {
|
|
51583
|
-
if (!
|
|
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 (
|
|
51592
|
-
else if (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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 = () =>
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
59824
|
-
const
|
|
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([...
|
|
60319
|
+
return repairResponsesAssistantOrdering(stripKernelSummaries([...viewed, ...records], turn.state), prepared.originalMessages);
|
|
59836
60320
|
};
|
|
59837
60321
|
const loop = runCompressLoop(
|
|
59838
60322
|
streamToRead,
|