billion-context 0.1.32 → 0.1.34
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 +481 -1505
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -43736,7 +43736,7 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
43736
43736
|
iterationThreshold: 15,
|
|
43737
43737
|
force: "soft",
|
|
43738
43738
|
growthRatio: 0.05,
|
|
43739
|
-
growthFloor:
|
|
43739
|
+
growthFloor: 5e4,
|
|
43740
43740
|
growthCap: 5e4,
|
|
43741
43741
|
minGrowthFloor: 2e4,
|
|
43742
43742
|
minGrowthRatio: 0.45,
|
|
@@ -43930,14 +43930,49 @@ function truncateLargeToolOutputs(messages, tokenCount, config, countTokens, opt
|
|
|
43930
43930
|
return { messages: updated, truncatedCount, savedTokens };
|
|
43931
43931
|
}
|
|
43932
43932
|
var KEEP_LAST_ORPHANED = 0;
|
|
43933
|
+
function rangeKey(startRef, endRef) {
|
|
43934
|
+
return `${startRef}::${endRef}`;
|
|
43935
|
+
}
|
|
43936
|
+
function rewriteCompressText(text, liveKeys) {
|
|
43937
|
+
let parsed;
|
|
43938
|
+
try {
|
|
43939
|
+
parsed = JSON.parse(text ?? "");
|
|
43940
|
+
} catch {
|
|
43941
|
+
return null;
|
|
43942
|
+
}
|
|
43943
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
43944
|
+
const obj = parsed;
|
|
43945
|
+
const content = obj.content;
|
|
43946
|
+
if (!Array.isArray(content) || content.length === 0) return null;
|
|
43947
|
+
const kept = content.filter((entry) => {
|
|
43948
|
+
if (!entry || typeof entry !== "object") return false;
|
|
43949
|
+
const s3 = typeof entry.startId === "string" ? entry.startId : typeof entry.messageId === "string" ? entry.messageId : "";
|
|
43950
|
+
const e = typeof entry.endId === "string" ? entry.endId : typeof entry.messageId === "string" ? entry.messageId : "";
|
|
43951
|
+
return liveKeys.has(rangeKey(s3, e));
|
|
43952
|
+
});
|
|
43953
|
+
if (kept.length === content.length || kept.length === 0) return null;
|
|
43954
|
+
return JSON.stringify({ ...obj, content: kept });
|
|
43955
|
+
}
|
|
43933
43956
|
function hideConsumedCompressCalls(state, messages) {
|
|
43934
|
-
const activeCallIds = /* @__PURE__ */ new Set();
|
|
43935
43957
|
const allBlockCallIds = /* @__PURE__ */ new Set();
|
|
43958
|
+
const activeCallIds = /* @__PURE__ */ new Set();
|
|
43959
|
+
const liveRangeKeysByCallId = /* @__PURE__ */ new Map();
|
|
43960
|
+
const legacyLiveByCallId = /* @__PURE__ */ new Set();
|
|
43936
43961
|
for (const block of state.blocks) {
|
|
43937
|
-
if (block.compressCallId)
|
|
43938
|
-
|
|
43939
|
-
|
|
43962
|
+
if (!block.compressCallId) continue;
|
|
43963
|
+
allBlockCallIds.add(block.compressCallId);
|
|
43964
|
+
if (!block.active) continue;
|
|
43965
|
+
activeCallIds.add(block.compressCallId);
|
|
43966
|
+
if (block.startRef === void 0 || block.endRef === void 0) {
|
|
43967
|
+
legacyLiveByCallId.add(block.compressCallId);
|
|
43968
|
+
continue;
|
|
43940
43969
|
}
|
|
43970
|
+
let keys = liveRangeKeysByCallId.get(block.compressCallId);
|
|
43971
|
+
if (!keys) {
|
|
43972
|
+
keys = /* @__PURE__ */ new Set();
|
|
43973
|
+
liveRangeKeysByCallId.set(block.compressCallId, keys);
|
|
43974
|
+
}
|
|
43975
|
+
keys.add(rangeKey(block.startRef, block.endRef));
|
|
43941
43976
|
}
|
|
43942
43977
|
const lastOrphanedCallIds = [];
|
|
43943
43978
|
for (let i = messages.length - 1; i >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; i--) {
|
|
@@ -43966,6 +44001,16 @@ function hideConsumedCompressCalls(state, messages) {
|
|
|
43966
44001
|
hidden++;
|
|
43967
44002
|
continue;
|
|
43968
44003
|
}
|
|
44004
|
+
if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
|
|
44005
|
+
const liveKeys = liveRangeKeysByCallId.get(message.toolCallId);
|
|
44006
|
+
if (liveKeys && liveKeys.size > 0 && !legacyLiveByCallId.has(message.toolCallId)) {
|
|
44007
|
+
const rewritten = rewriteCompressText(message.text, liveKeys);
|
|
44008
|
+
if (rewritten !== null) {
|
|
44009
|
+
result.push({ ...message, text: rewritten });
|
|
44010
|
+
continue;
|
|
44011
|
+
}
|
|
44012
|
+
}
|
|
44013
|
+
}
|
|
43969
44014
|
result.push(message);
|
|
43970
44015
|
}
|
|
43971
44016
|
return { messages: result, hidden };
|
|
@@ -44199,8 +44244,8 @@ function refNum(ref) {
|
|
|
44199
44244
|
const n = parseInt(ref.slice(1), 10);
|
|
44200
44245
|
return Number.isNaN(n) ? -1 : n;
|
|
44201
44246
|
}
|
|
44202
|
-
function
|
|
44203
|
-
return Math.ceil(
|
|
44247
|
+
function estimateTextTokens(text) {
|
|
44248
|
+
return Math.ceil(text.length / 4);
|
|
44204
44249
|
}
|
|
44205
44250
|
function isToolMessage(message) {
|
|
44206
44251
|
return message.contentType === "tool-call" || message.contentType === "tool-result";
|
|
@@ -44212,7 +44257,7 @@ function isSyntheticOrPruned(message, state) {
|
|
|
44212
44257
|
}
|
|
44213
44258
|
return false;
|
|
44214
44259
|
}
|
|
44215
|
-
function computeProtectedRefs(messages, state, config) {
|
|
44260
|
+
function computeProtectedRefs(messages, state, config, countTokens = estimateTextTokens) {
|
|
44216
44261
|
const preserveN = config.preserveRecentMessages;
|
|
44217
44262
|
const preserveTokens = config.preserveRecentTokens;
|
|
44218
44263
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -44222,7 +44267,7 @@ function computeProtectedRefs(messages, state, config) {
|
|
|
44222
44267
|
if (isNeverPreserveRecent(msg2)) continue;
|
|
44223
44268
|
const ref = state.messageRefs.byRaw[msg2.id];
|
|
44224
44269
|
if (!ref || ref === "BLOCKED") continue;
|
|
44225
|
-
visible.push({ ref, tokens:
|
|
44270
|
+
visible.push({ ref, tokens: countTokens(msg2.text ?? "") });
|
|
44226
44271
|
}
|
|
44227
44272
|
if (preserveN > 0) {
|
|
44228
44273
|
for (const m2 of visible.slice(-preserveN)) {
|
|
@@ -44247,7 +44292,7 @@ function computeProtectedRefs(messages, state, config) {
|
|
|
44247
44292
|
}
|
|
44248
44293
|
return result;
|
|
44249
44294
|
}
|
|
44250
|
-
function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
|
|
44295
|
+
function buildCompressibleRanges(messages, state, config, protectedZoneRefs, countTokens = estimateTextTokens) {
|
|
44251
44296
|
const compressibleMsgs = [];
|
|
44252
44297
|
const protectedMsgs = [];
|
|
44253
44298
|
const protectedCallIds = collectProtectedToolCallIds(messages, config);
|
|
@@ -44260,7 +44305,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
|
|
|
44260
44305
|
protectedMsgs.push({
|
|
44261
44306
|
ref,
|
|
44262
44307
|
refNum: rn2,
|
|
44263
|
-
tokens:
|
|
44308
|
+
tokens: countTokens(msg2.text ?? ""),
|
|
44264
44309
|
tools: msg2.toolName ? [msg2.toolName] : []
|
|
44265
44310
|
});
|
|
44266
44311
|
continue;
|
|
@@ -44271,7 +44316,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
|
|
|
44271
44316
|
compressibleMsgs.push({
|
|
44272
44317
|
ref,
|
|
44273
44318
|
refNum: rn2,
|
|
44274
|
-
tokens:
|
|
44319
|
+
tokens: countTokens(msg2.text ?? ""),
|
|
44275
44320
|
isTool: isToolMessage(msg2),
|
|
44276
44321
|
isUser: msg2.role === "user"
|
|
44277
44322
|
});
|
|
@@ -44358,7 +44403,7 @@ function createCore(ports = {}) {
|
|
|
44358
44403
|
let tokensCompressed = 0;
|
|
44359
44404
|
const errors = [];
|
|
44360
44405
|
const warnings = [];
|
|
44361
|
-
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config);
|
|
44406
|
+
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config, countTokens);
|
|
44362
44407
|
const preExistingCoverage = collectCoverage(state);
|
|
44363
44408
|
const rangeIndexSets = [];
|
|
44364
44409
|
for (const spec of input.ranges) {
|
|
@@ -44383,29 +44428,25 @@ function createCore(ports = {}) {
|
|
|
44383
44428
|
const bMin = b2.indices.length > 0 ? Math.min(...b2.indices) : Infinity;
|
|
44384
44429
|
return aMin - bMin;
|
|
44385
44430
|
});
|
|
44386
|
-
|
|
44387
|
-
|
|
44388
|
-
|
|
44389
|
-
const
|
|
44390
|
-
const
|
|
44391
|
-
if (
|
|
44392
|
-
|
|
44393
|
-
|
|
44394
|
-
|
|
44395
|
-
|
|
44396
|
-
|
|
44397
|
-
errors: [
|
|
44398
|
-
`content: range (${prev.spec.startRef}..${prev.spec.endRef}) overlaps (${curr.spec.startRef}..${curr.spec.endRef}). Overlapping ranges cannot be compressed in the same batch.`
|
|
44399
|
-
],
|
|
44400
|
-
warnings: []
|
|
44401
|
-
}
|
|
44402
|
-
};
|
|
44431
|
+
const skipSpecs = /* @__PURE__ */ new Set();
|
|
44432
|
+
let acceptedMaxIndex = -1;
|
|
44433
|
+
for (const entry of sortedRanges) {
|
|
44434
|
+
const entryMax = entry.indices.length > 0 ? Math.max(...entry.indices) : -1;
|
|
44435
|
+
const entryMin = entry.indices.length > 0 ? Math.min(...entry.indices) : -1;
|
|
44436
|
+
if (entryMin >= 0 && entryMin <= acceptedMaxIndex) {
|
|
44437
|
+
skipSpecs.add(entry.spec);
|
|
44438
|
+
warnings.push(
|
|
44439
|
+
`Skipped range (${entry.spec.startRef}..${entry.spec.endRef}) \u2014 overlaps an earlier range in the batch; the earlier range takes precedence. Keep ranges disjoint.`
|
|
44440
|
+
);
|
|
44441
|
+
continue;
|
|
44403
44442
|
}
|
|
44443
|
+
if (entryMax > acceptedMaxIndex) acceptedMaxIndex = entryMax;
|
|
44404
44444
|
}
|
|
44405
44445
|
if (input.config.compress.minCompressRange > 0 && input.ranges.length > 0) {
|
|
44406
44446
|
let totalRangeChars = 0;
|
|
44407
44447
|
let hasBlockBoundaryRange = false;
|
|
44408
44448
|
for (const spec of input.ranges) {
|
|
44449
|
+
if (skipSpecs.has(spec)) continue;
|
|
44409
44450
|
let resolved;
|
|
44410
44451
|
try {
|
|
44411
44452
|
resolved = resolveBoundaries({
|
|
@@ -44441,6 +44482,7 @@ function createCore(ports = {}) {
|
|
|
44441
44482
|
}
|
|
44442
44483
|
}
|
|
44443
44484
|
for (const spec of input.ranges) {
|
|
44485
|
+
if (skipSpecs.has(spec)) continue;
|
|
44444
44486
|
try {
|
|
44445
44487
|
const outcome = applySingleRange({
|
|
44446
44488
|
spec,
|
|
@@ -44581,13 +44623,15 @@ var recommendNode = {
|
|
|
44581
44623
|
const protectedRefs = computeProtectedRefs(
|
|
44582
44624
|
io2.messages,
|
|
44583
44625
|
io2.state,
|
|
44584
|
-
ctx.config
|
|
44626
|
+
ctx.config,
|
|
44627
|
+
ctx.countTokens
|
|
44585
44628
|
);
|
|
44586
44629
|
const contextRanges = buildCompressibleRanges(
|
|
44587
44630
|
io2.messages,
|
|
44588
44631
|
io2.state,
|
|
44589
44632
|
ctx.config,
|
|
44590
|
-
protectedRefs
|
|
44633
|
+
protectedRefs,
|
|
44634
|
+
ctx.countTokens
|
|
44591
44635
|
);
|
|
44592
44636
|
const nothingToCompress = contextRanges.compressible.length === 0;
|
|
44593
44637
|
const recommendation = {
|
|
@@ -44765,7 +44809,9 @@ function applySingleRange(input) {
|
|
|
44765
44809
|
survivedCount: 0,
|
|
44766
44810
|
generation: "young",
|
|
44767
44811
|
active: true,
|
|
44768
|
-
compressCallId: input.spec.compressCallId
|
|
44812
|
+
compressCallId: input.spec.compressCallId,
|
|
44813
|
+
startRef: input.spec.startRef,
|
|
44814
|
+
endRef: input.spec.endRef
|
|
44769
44815
|
};
|
|
44770
44816
|
input.state.blocks.push(block);
|
|
44771
44817
|
for (const consumedId of consumedBlockIds) {
|
|
@@ -45256,7 +45302,7 @@ function renderNudgeText(decision) {
|
|
|
45256
45302
|
breakdownStr,
|
|
45257
45303
|
"",
|
|
45258
45304
|
`[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`,
|
|
45259
|
-
isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries.`,
|
|
45305
|
+
isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
|
|
45260
45306
|
blockList,
|
|
45261
45307
|
`Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
|
|
45262
45308
|
"",
|
|
@@ -46045,7 +46091,7 @@ function resolveProxyDecision(routes, globalProxy, upstreamUrl, fallback = {}) {
|
|
|
46045
46091
|
return { proxy: parsed.url, source: "provider" };
|
|
46046
46092
|
}
|
|
46047
46093
|
}
|
|
46048
|
-
if (globalProxy === "") return { source: "direct" };
|
|
46094
|
+
if (globalProxy === "" && fallback.explicitDirect) return { source: "direct" };
|
|
46049
46095
|
const explicit = parseHttpProxy(globalProxy, fallback.biliPort)?.url;
|
|
46050
46096
|
if (explicit) return { proxy: explicit, source: fallback.globalSource ?? "global" };
|
|
46051
46097
|
if (target && matchesNoProxy(target, fallback.noProxy)) return { source: "no-proxy" };
|
|
@@ -46283,34 +46329,19 @@ function loadRoutes(env = process.env) {
|
|
|
46283
46329
|
function loadOptions(env = process.env) {
|
|
46284
46330
|
const fileConfig = loadConfigFile();
|
|
46285
46331
|
const port = parseInt(env.ACP_PORT ?? env.PORT ?? `${fileConfig.port ?? 8787}`, 10);
|
|
46332
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
46333
|
+
throw new Error(`Invalid port ${Number.isNaN(port) ? "(not a number)" : port}; must be 1-65535`);
|
|
46334
|
+
}
|
|
46286
46335
|
const host = env.ACP_HOST ?? fileConfig.host ?? "127.0.0.1";
|
|
46287
46336
|
const upstream = (env.ACP_UPSTREAM ?? fileConfig.upstream ?? "https://api.anthropic.com").replace(/\/$/, "");
|
|
46288
|
-
|
|
46289
|
-
const routesPath = env.ACP_PROVIDERS ?? fileConfig.providersPath ?? "";
|
|
46290
|
-
if (routesPath) {
|
|
46291
|
-
const parsed = safeReadJson(routesPath);
|
|
46292
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
46293
|
-
for (const [k2, v2] of Object.entries(parsed)) {
|
|
46294
|
-
rejectLegacyRoute(k2, v2);
|
|
46295
|
-
const route = parseRouteEntry(v2);
|
|
46296
|
-
if (route) routes[normalizeUrlKey(k2)] = route;
|
|
46297
|
-
}
|
|
46298
|
-
}
|
|
46299
|
-
}
|
|
46300
|
-
if (fileConfig.providers) {
|
|
46301
|
-
for (const [k2, v2] of Object.entries(fileConfig.providers)) {
|
|
46302
|
-
rejectLegacyRoute(k2, v2);
|
|
46303
|
-
const route = parseRouteEntry(v2);
|
|
46304
|
-
if (route && !routes[normalizeUrlKey(k2)]) routes[normalizeUrlKey(k2)] = route;
|
|
46305
|
-
}
|
|
46306
|
-
}
|
|
46337
|
+
const routes = loadRoutes(env);
|
|
46307
46338
|
const modelContextLimit = parseInt(env.ACP_MODEL_CONTEXT_LIMIT ?? `${fileConfig.modelContextLimit ?? 2e5}`, 10);
|
|
46308
46339
|
const biliProxy = nonEmpty(env.BILI_UPSTREAM_PROXY);
|
|
46309
46340
|
const webProxy = nonEmpty(fileConfig.upstreamProxy);
|
|
46310
46341
|
const configProxy = nonEmpty(fileConfig.proxy);
|
|
46311
|
-
const
|
|
46312
|
-
|
|
46313
|
-
|
|
46342
|
+
const rawProxyMode = env.BILI_UPSTREAM_PROXY_MODE ?? fileConfig.upstreamProxyMode ?? (webProxy ? "manual" : void 0);
|
|
46343
|
+
const proxyMode = parseUpstreamProxyMode(rawProxyMode);
|
|
46344
|
+
const explicitDirect = proxyMode === "direct" && rawProxyMode === "direct";
|
|
46314
46345
|
const proxy = biliProxy ?? (proxyMode === "direct" ? "" : proxyMode === "manual" ? webProxy ?? configProxy : configProxy);
|
|
46315
46346
|
const proxySource = biliProxy ? "bili-env" : proxyMode === "direct" ? "direct" : proxyMode === "manual" && webProxy ? "web-manual" : configProxy ? "config" : "auto";
|
|
46316
46347
|
const httpProxy = nonEmpty(env.HTTP_PROXY ?? env.http_proxy);
|
|
@@ -46322,8 +46353,9 @@ function loadOptions(env = process.env) {
|
|
|
46322
46353
|
...httpsProxy ? { httpsProxy } : {},
|
|
46323
46354
|
...allProxy ? { allProxy } : {},
|
|
46324
46355
|
...noProxy ? { noProxy } : {},
|
|
46325
|
-
biliPort:
|
|
46326
|
-
globalSource: proxySource
|
|
46356
|
+
biliPort: port,
|
|
46357
|
+
globalSource: proxySource,
|
|
46358
|
+
explicitDirect
|
|
46327
46359
|
};
|
|
46328
46360
|
validateHttpProxy(proxy, proxyFallback.biliPort);
|
|
46329
46361
|
for (const [url, route] of Object.entries(routes)) {
|
|
@@ -46334,7 +46366,7 @@ function loadOptions(env = process.env) {
|
|
|
46334
46366
|
}
|
|
46335
46367
|
}
|
|
46336
46368
|
return {
|
|
46337
|
-
port
|
|
46369
|
+
port,
|
|
46338
46370
|
host,
|
|
46339
46371
|
upstream,
|
|
46340
46372
|
routes,
|
|
@@ -46544,15 +46576,30 @@ async function contextFromRegistry(model, host) {
|
|
|
46544
46576
|
// src/fetch-util.ts
|
|
46545
46577
|
var MAX_REQUEST_BYTES = 100 * 1024 * 1024;
|
|
46546
46578
|
var UPSTREAM_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
46547
|
-
async function fetchWithTimeout(url, opts, timeoutMs = UPSTREAM_TIMEOUT_MS) {
|
|
46579
|
+
async function fetchWithTimeout(url, opts, timeoutMs = UPSTREAM_TIMEOUT_MS, externalSignal) {
|
|
46548
46580
|
const controller = new AbortController();
|
|
46549
46581
|
const timer2 = setTimeout(() => controller.abort(), timeoutMs);
|
|
46582
|
+
let onExternalAbort = null;
|
|
46583
|
+
if (externalSignal) {
|
|
46584
|
+
if (externalSignal.aborted) controller.abort();
|
|
46585
|
+
else {
|
|
46586
|
+
onExternalAbort = () => controller.abort();
|
|
46587
|
+
externalSignal.addEventListener("abort", onExternalAbort, { once: true });
|
|
46588
|
+
}
|
|
46589
|
+
}
|
|
46550
46590
|
try {
|
|
46551
46591
|
const finalOpts = { ...opts, signal: controller.signal };
|
|
46552
46592
|
const response = await fetch(url, finalOpts);
|
|
46553
|
-
return {
|
|
46593
|
+
return {
|
|
46594
|
+
response,
|
|
46595
|
+
clearTimer: () => {
|
|
46596
|
+
clearTimeout(timer2);
|
|
46597
|
+
if (onExternalAbort && externalSignal) externalSignal.removeEventListener("abort", onExternalAbort);
|
|
46598
|
+
}
|
|
46599
|
+
};
|
|
46554
46600
|
} catch (e) {
|
|
46555
46601
|
clearTimeout(timer2);
|
|
46602
|
+
if (onExternalAbort && externalSignal) externalSignal.removeEventListener("abort", onExternalAbort);
|
|
46556
46603
|
throw e;
|
|
46557
46604
|
}
|
|
46558
46605
|
}
|
|
@@ -47562,7 +47609,7 @@ function getStore() {
|
|
|
47562
47609
|
|
|
47563
47610
|
// src/session.ts
|
|
47564
47611
|
var sessions = /* @__PURE__ */ new Map();
|
|
47565
|
-
var MAX_SESSIONS = Number.parseInt(process.env.BILI_MAX_SESSIONS ?? "256", 10) || 256;
|
|
47612
|
+
var MAX_SESSIONS = Math.max(1, Number.parseInt(process.env.BILI_MAX_SESSIONS ?? "256", 10) || 256);
|
|
47566
47613
|
var initialized = false;
|
|
47567
47614
|
async function initSessions() {
|
|
47568
47615
|
if (initialized) return;
|
|
@@ -47597,7 +47644,12 @@ function getSession(id, meta) {
|
|
|
47597
47644
|
sessions.set(id, reloaded);
|
|
47598
47645
|
return reloaded;
|
|
47599
47646
|
}
|
|
47600
|
-
if (sessions.size >= MAX_SESSIONS)
|
|
47647
|
+
if (sessions.size >= MAX_SESSIONS) {
|
|
47648
|
+
const evicted = evictOldest();
|
|
47649
|
+
if (!evicted) {
|
|
47650
|
+
throw new Error(`session pool exhausted (MAX_SESSIONS=${MAX_SESSIONS}; all in-flight)`);
|
|
47651
|
+
}
|
|
47652
|
+
}
|
|
47601
47653
|
const session = {
|
|
47602
47654
|
id,
|
|
47603
47655
|
meta: { protocol: meta?.protocol, upstreamOrigin: meta?.upstreamOrigin, label: meta?.label },
|
|
@@ -47681,13 +47733,14 @@ function evictOldest() {
|
|
|
47681
47733
|
oldestId = id;
|
|
47682
47734
|
}
|
|
47683
47735
|
}
|
|
47684
|
-
if (!oldestId) return;
|
|
47736
|
+
if (!oldestId) return false;
|
|
47685
47737
|
const s3 = sessions.get(oldestId);
|
|
47686
47738
|
const ok = getStore().flushSync(s3);
|
|
47687
47739
|
if (!ok && !s3.persisted) {
|
|
47688
|
-
return;
|
|
47740
|
+
return false;
|
|
47689
47741
|
}
|
|
47690
47742
|
sessions.delete(oldestId);
|
|
47743
|
+
return true;
|
|
47691
47744
|
}
|
|
47692
47745
|
async function flushAllSessions() {
|
|
47693
47746
|
await getStore().flushAll(sessions.values());
|
|
@@ -47968,15 +48021,38 @@ var MUTATING_PROXY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
47968
48021
|
COMPRESS_TOOL_NAME,
|
|
47969
48022
|
DECOMPRESS_TOOL_NAME
|
|
47970
48023
|
]);
|
|
47971
|
-
var READONLY_PROXY_TOOLS = /* @__PURE__ */ new Set([
|
|
47972
|
-
SEARCH_CONTEXT_TOOL_NAME,
|
|
47973
|
-
ACP_STATUS_TOOL_NAME
|
|
47974
|
-
]);
|
|
47975
48024
|
|
|
47976
48025
|
// src/decompress-shared.ts
|
|
47977
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
48026
|
+
import { mkdirSync as mkdirSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
47978
48027
|
import { dirname as dirname3, join as join2 } from "path";
|
|
47979
48028
|
import { tmpdir } from "os";
|
|
48029
|
+
var trackedTempFiles = [];
|
|
48030
|
+
function getDecompressTmpCap() {
|
|
48031
|
+
const raw = process.env.BILI_DECOMPRESS_TMP_CAP;
|
|
48032
|
+
const parsed = raw ? Number.parseInt(raw, 10) : NaN;
|
|
48033
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 50;
|
|
48034
|
+
}
|
|
48035
|
+
function reapTempFiles() {
|
|
48036
|
+
const cap = getDecompressTmpCap();
|
|
48037
|
+
while (trackedTempFiles.length > cap) {
|
|
48038
|
+
trackedTempFiles.sort((a, b2) => a.mtimeMs - b2.mtimeMs);
|
|
48039
|
+
const oldest = trackedTempFiles.shift();
|
|
48040
|
+
if (!oldest) break;
|
|
48041
|
+
try {
|
|
48042
|
+
unlinkSync2(oldest.path);
|
|
48043
|
+
} catch {
|
|
48044
|
+
}
|
|
48045
|
+
}
|
|
48046
|
+
}
|
|
48047
|
+
process.on("beforeExit", () => {
|
|
48048
|
+
for (const f2 of trackedTempFiles) {
|
|
48049
|
+
try {
|
|
48050
|
+
unlinkSync2(f2.path);
|
|
48051
|
+
} catch {
|
|
48052
|
+
}
|
|
48053
|
+
}
|
|
48054
|
+
trackedTempFiles.length = 0;
|
|
48055
|
+
});
|
|
47980
48056
|
function resolveDecompress(args, ctx) {
|
|
47981
48057
|
const rawBlockId = args.blockId;
|
|
47982
48058
|
if (typeof rawBlockId !== "string" || rawBlockId.length === 0) {
|
|
@@ -48009,6 +48085,8 @@ function resolveDecompress(args, ctx) {
|
|
|
48009
48085
|
try {
|
|
48010
48086
|
mkdirSync4(dirname3(outPath), { recursive: true });
|
|
48011
48087
|
writeFileSync3(outPath, body, "utf8");
|
|
48088
|
+
trackedTempFiles.push({ path: outPath, mtimeMs: Date.now() });
|
|
48089
|
+
reapTempFiles();
|
|
48012
48090
|
return `${header}
|
|
48013
48091
|
Content (${body.length} chars) written to: ${outPath}
|
|
48014
48092
|
Use the read tool to access it.`;
|
|
@@ -48022,12 +48100,6 @@ ${body.slice(0, 4e3)}...`;
|
|
|
48022
48100
|
${body}`;
|
|
48023
48101
|
}
|
|
48024
48102
|
|
|
48025
|
-
// src/sse-util.ts
|
|
48026
|
-
function normalizeSseLineEndings(buf) {
|
|
48027
|
-
if (buf.indexOf("\r") === -1) return buf;
|
|
48028
|
-
return buf.replace(/\r\n|\r/g, "\n");
|
|
48029
|
-
}
|
|
48030
|
-
|
|
48031
48103
|
// src/stream.ts
|
|
48032
48104
|
function executeAnthropicProxyTool(toolName, args, ctx) {
|
|
48033
48105
|
if (toolName === COMPRESS_TOOL_NAME) {
|
|
@@ -48191,7 +48263,7 @@ function renderPage(origin, version2) {
|
|
|
48191
48263
|
}
|
|
48192
48264
|
|
|
48193
48265
|
// src/web/api.ts
|
|
48194
|
-
import { closeSync, fsyncSync, mkdirSync as mkdirSync5, openSync, renameSync as renameSync3, unlinkSync as
|
|
48266
|
+
import { closeSync, fsyncSync, mkdirSync as mkdirSync5, openSync, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
48195
48267
|
import { dirname as dirname4 } from "path";
|
|
48196
48268
|
import { randomUUID } from "crypto";
|
|
48197
48269
|
function readConfig() {
|
|
@@ -48224,7 +48296,7 @@ function atomicWriteConfig(config) {
|
|
|
48224
48296
|
} catch (error) {
|
|
48225
48297
|
if (descriptor !== void 0) closeSync(descriptor);
|
|
48226
48298
|
try {
|
|
48227
|
-
|
|
48299
|
+
unlinkSync3(tempPath);
|
|
48228
48300
|
} catch {
|
|
48229
48301
|
}
|
|
48230
48302
|
throw error;
|
|
@@ -48372,122 +48444,6 @@ function reapOrphanBlocks(session, visible, deactivate) {
|
|
|
48372
48444
|
}
|
|
48373
48445
|
|
|
48374
48446
|
// src/compress-loop.ts
|
|
48375
|
-
function executeProxyTool(toolName, args, ctx) {
|
|
48376
|
-
if (toolName === "compress") {
|
|
48377
|
-
return applyRanges(parseCompressInput(args), ctx);
|
|
48378
|
-
}
|
|
48379
|
-
if (toolName === "decompress") {
|
|
48380
|
-
return resolveDecompress(args, ctx);
|
|
48381
|
-
}
|
|
48382
|
-
if (toolName === "search_context") {
|
|
48383
|
-
const query = typeof args.query === "string" ? args.query : "";
|
|
48384
|
-
if (query.length === 0) return "[search_context FAILED: query is required]";
|
|
48385
|
-
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 5;
|
|
48386
|
-
const blocks = ctx.core.search(query, ctx.session.state).slice(0, limit);
|
|
48387
|
-
if (blocks.length === 0) return `[No blocks matched "${query}"]`;
|
|
48388
|
-
const lines = blocks.map((b2) => {
|
|
48389
|
-
const topic = b2.topic ?? "(no topic)";
|
|
48390
|
-
const preview = b2.summary.length > 200 ? b2.summary.slice(0, 200) + "..." : b2.summary;
|
|
48391
|
-
return `${b2.blockId} (T${b2.tier}) "${topic}"
|
|
48392
|
-
${preview}`;
|
|
48393
|
-
});
|
|
48394
|
-
return `Found ${blocks.length} block(s) for "${query}":
|
|
48395
|
-
|
|
48396
|
-
${lines.join("\n\n")}`;
|
|
48397
|
-
}
|
|
48398
|
-
if (toolName === "acp_status") {
|
|
48399
|
-
return buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast);
|
|
48400
|
-
}
|
|
48401
|
-
return `[Unknown proxy tool: ${toolName}]`;
|
|
48402
|
-
}
|
|
48403
|
-
function classifySseEvent(eventStr) {
|
|
48404
|
-
const dataLine = eventStr.split("\n").find((l) => l.startsWith("data:"));
|
|
48405
|
-
if (!dataLine) return {};
|
|
48406
|
-
const jsonStr = dataLine.slice(5).trim();
|
|
48407
|
-
if (jsonStr === "[DONE]") return { done: true };
|
|
48408
|
-
let parsed;
|
|
48409
|
-
try {
|
|
48410
|
-
parsed = JSON.parse(jsonStr);
|
|
48411
|
-
} catch {
|
|
48412
|
-
return {};
|
|
48413
|
-
}
|
|
48414
|
-
const choices = parsed.choices;
|
|
48415
|
-
const choice = choices?.[0];
|
|
48416
|
-
if (!choice) return {};
|
|
48417
|
-
const delta = choice.delta;
|
|
48418
|
-
const finishReason = choice.finish_reason;
|
|
48419
|
-
const out = {};
|
|
48420
|
-
if (finishReason) {
|
|
48421
|
-
out.finishReason = finishReason;
|
|
48422
|
-
out.usage = parsed.usage ?? null;
|
|
48423
|
-
}
|
|
48424
|
-
if (!delta) return out;
|
|
48425
|
-
if (delta.tool_calls) {
|
|
48426
|
-
const tcs = delta.tool_calls;
|
|
48427
|
-
const toolCalls = [];
|
|
48428
|
-
for (const tc of tcs) {
|
|
48429
|
-
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
48430
|
-
const fn = tc.function;
|
|
48431
|
-
const name = typeof fn?.name === "string" ? fn.name : "";
|
|
48432
|
-
const id = typeof tc.id === "string" ? tc.id : "";
|
|
48433
|
-
const args = typeof fn?.arguments === "string" ? fn.arguments : "";
|
|
48434
|
-
toolCalls.push({ index: idx, id, name, arguments: args });
|
|
48435
|
-
}
|
|
48436
|
-
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
48437
|
-
out.contentDelta = delta.content;
|
|
48438
|
-
}
|
|
48439
|
-
out.toolCalls = toolCalls;
|
|
48440
|
-
return out;
|
|
48441
|
-
}
|
|
48442
|
-
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
48443
|
-
out.contentDelta = delta.content;
|
|
48444
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
48445
|
-
return out;
|
|
48446
|
-
}
|
|
48447
|
-
if (delta.role || Object.keys(delta).length === 0 && !finishReason) {
|
|
48448
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
48449
|
-
}
|
|
48450
|
-
return out;
|
|
48451
|
-
}
|
|
48452
|
-
function buildToolCallSse(base, tc) {
|
|
48453
|
-
return `data: ${JSON.stringify({
|
|
48454
|
-
...base,
|
|
48455
|
-
choices: [{
|
|
48456
|
-
index: 0,
|
|
48457
|
-
delta: {
|
|
48458
|
-
tool_calls: [{
|
|
48459
|
-
index: tc.index,
|
|
48460
|
-
id: tc.id,
|
|
48461
|
-
type: "function",
|
|
48462
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
48463
|
-
}]
|
|
48464
|
-
},
|
|
48465
|
-
finish_reason: null
|
|
48466
|
-
}]
|
|
48467
|
-
})}
|
|
48468
|
-
|
|
48469
|
-
`;
|
|
48470
|
-
}
|
|
48471
|
-
function buildFinishSse(base, finishReason, usage) {
|
|
48472
|
-
return `data: ${JSON.stringify({
|
|
48473
|
-
...base,
|
|
48474
|
-
choices: [{ index: 0, delta: {}, finish_reason: finishReason }],
|
|
48475
|
-
...usage ? { usage } : {}
|
|
48476
|
-
})}
|
|
48477
|
-
|
|
48478
|
-
`;
|
|
48479
|
-
}
|
|
48480
|
-
function buildContentSse(id, model, content) {
|
|
48481
|
-
return `data: ${JSON.stringify({
|
|
48482
|
-
id,
|
|
48483
|
-
object: "chat.completion.chunk",
|
|
48484
|
-
created: Date.now(),
|
|
48485
|
-
model,
|
|
48486
|
-
choices: [{ index: 0, delta: { content }, finish_reason: null }]
|
|
48487
|
-
})}
|
|
48488
|
-
|
|
48489
|
-
`;
|
|
48490
|
-
}
|
|
48491
48447
|
function buildVisibilityMarker(toolName, result) {
|
|
48492
48448
|
const lines = result.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
48493
48449
|
const failed = lines.some(
|
|
@@ -48500,10 +48456,10 @@ function buildVisibilityMarker(toolName, result) {
|
|
|
48500
48456
|
acp_status: "\u{1F4CA}"
|
|
48501
48457
|
};
|
|
48502
48458
|
const icon = failed ? "\u274C" : icons[toolName] ?? "\u{1F4E6}";
|
|
48503
|
-
if (toolName === "acp_status"
|
|
48504
|
-
const dataLine = lines.slice(0, 3).join(" | ").replace(/\s+/g, " ");
|
|
48459
|
+
if (toolName === "acp_status") {
|
|
48505
48460
|
return `
|
|
48506
|
-
${icon} [ACP]
|
|
48461
|
+
${icon} [ACP] acp_status result:
|
|
48462
|
+
${result.trim()}
|
|
48507
48463
|
`;
|
|
48508
48464
|
}
|
|
48509
48465
|
const inner = (lines[0] ?? "").replace(/^\[/, "").replace(/\]$/, "").trim();
|
|
@@ -48511,250 +48467,12 @@ ${icon} [ACP] ${dataLine}
|
|
|
48511
48467
|
${icon} [ACP] ${inner}
|
|
48512
48468
|
`;
|
|
48513
48469
|
}
|
|
48514
|
-
async function* compressLoopStream(initialUpstream, ctx, requestBody, requestOptions) {
|
|
48515
|
-
let upstream = initialUpstream;
|
|
48516
|
-
let activeClearTimer = null;
|
|
48517
|
-
try {
|
|
48518
|
-
const model = requestBody.model ?? "unknown";
|
|
48519
|
-
let responseId = `chatcmpl-proxy-${Date.now()}`;
|
|
48520
|
-
const makeBase = () => ({
|
|
48521
|
-
id: responseId,
|
|
48522
|
-
object: "chat.completion.chunk",
|
|
48523
|
-
created: Date.now(),
|
|
48524
|
-
model
|
|
48525
|
-
});
|
|
48526
|
-
let loopCount = 0;
|
|
48527
|
-
for (; ; ) {
|
|
48528
|
-
loopCount++;
|
|
48529
|
-
if (loopCount > 10) {
|
|
48530
|
-
ctx.log("[acp-proxy: compress loop limit (10) reached, forwarding as-is]");
|
|
48531
|
-
yield Buffer.from(buildFinishSse(makeBase(), "stop", null), "utf8");
|
|
48532
|
-
yield Buffer.from("data: [DONE]\n\n", "utf8");
|
|
48533
|
-
return;
|
|
48534
|
-
}
|
|
48535
|
-
const toolCallByIndex = /* @__PURE__ */ new Map();
|
|
48536
|
-
let contentText = "";
|
|
48537
|
-
let finishReason = null;
|
|
48538
|
-
let usage = null;
|
|
48539
|
-
const isFirstRound = loopCount === 1;
|
|
48540
|
-
const reader = upstream.getReader();
|
|
48541
|
-
const decoder = new TextDecoder("utf-8");
|
|
48542
|
-
let sseBuffer = "";
|
|
48543
|
-
try {
|
|
48544
|
-
for (; ; ) {
|
|
48545
|
-
const { done, value } = await reader.read();
|
|
48546
|
-
if (done) break;
|
|
48547
|
-
sseBuffer += decoder.decode(value, { stream: true });
|
|
48548
|
-
sseBuffer = normalizeSseLineEndings(sseBuffer);
|
|
48549
|
-
let sep;
|
|
48550
|
-
while ((sep = sseBuffer.indexOf("\n\n")) >= 0) {
|
|
48551
|
-
const eventStr = sseBuffer.slice(0, sep);
|
|
48552
|
-
sseBuffer = sseBuffer.slice(sep + 2);
|
|
48553
|
-
if (!eventStr.trim()) continue;
|
|
48554
|
-
const d = classifySseEvent(eventStr);
|
|
48555
|
-
if (d.done) {
|
|
48556
|
-
continue;
|
|
48557
|
-
}
|
|
48558
|
-
if (isFirstRound) {
|
|
48559
|
-
if (d.yieldChunk) {
|
|
48560
|
-
if (!responseId) {
|
|
48561
|
-
const dataLine = eventStr.split("\n").find((l) => l.startsWith("data:"));
|
|
48562
|
-
if (dataLine) {
|
|
48563
|
-
try {
|
|
48564
|
-
const p2 = JSON.parse(dataLine.slice(5).trim());
|
|
48565
|
-
if (typeof p2.id === "string") responseId = p2.id;
|
|
48566
|
-
} catch {
|
|
48567
|
-
}
|
|
48568
|
-
}
|
|
48569
|
-
}
|
|
48570
|
-
yield d.yieldChunk;
|
|
48571
|
-
}
|
|
48572
|
-
} else {
|
|
48573
|
-
if (d.contentDelta) {
|
|
48574
|
-
yield Buffer.from(buildContentSse(responseId, model, d.contentDelta), "utf8");
|
|
48575
|
-
}
|
|
48576
|
-
}
|
|
48577
|
-
if (d.contentDelta) contentText += d.contentDelta;
|
|
48578
|
-
if (d.finishReason) finishReason = d.finishReason;
|
|
48579
|
-
if (d.usage !== void 0) usage = d.usage;
|
|
48580
|
-
if (d.toolCalls) {
|
|
48581
|
-
for (const tc of d.toolCalls) {
|
|
48582
|
-
const existing = toolCallByIndex.get(tc.index);
|
|
48583
|
-
if (existing) {
|
|
48584
|
-
if (tc.name) existing.name = tc.name;
|
|
48585
|
-
if (tc.id) existing.id = tc.id;
|
|
48586
|
-
existing.arguments += tc.arguments;
|
|
48587
|
-
} else {
|
|
48588
|
-
toolCallByIndex.set(tc.index, tc);
|
|
48589
|
-
}
|
|
48590
|
-
}
|
|
48591
|
-
}
|
|
48592
|
-
}
|
|
48593
|
-
}
|
|
48594
|
-
sseBuffer += decoder.decode();
|
|
48595
|
-
sseBuffer = normalizeSseLineEndings(sseBuffer);
|
|
48596
|
-
let resSep;
|
|
48597
|
-
while ((resSep = sseBuffer.indexOf("\n\n")) >= 0) {
|
|
48598
|
-
const eventStr = sseBuffer.slice(0, resSep);
|
|
48599
|
-
sseBuffer = sseBuffer.slice(resSep + 2);
|
|
48600
|
-
if (!eventStr.trim()) continue;
|
|
48601
|
-
const d = classifySseEvent(eventStr);
|
|
48602
|
-
if (d.done) continue;
|
|
48603
|
-
if (isFirstRound) {
|
|
48604
|
-
if (d.yieldChunk) yield d.yieldChunk;
|
|
48605
|
-
} else {
|
|
48606
|
-
if (d.contentDelta) yield Buffer.from(buildContentSse(responseId, model, d.contentDelta), "utf8");
|
|
48607
|
-
}
|
|
48608
|
-
if (d.contentDelta) contentText += d.contentDelta;
|
|
48609
|
-
if (d.finishReason) finishReason = d.finishReason;
|
|
48610
|
-
if (d.usage !== void 0) usage = d.usage;
|
|
48611
|
-
if (d.toolCalls) {
|
|
48612
|
-
for (const tc of d.toolCalls) {
|
|
48613
|
-
const existing = toolCallByIndex.get(tc.index);
|
|
48614
|
-
if (existing) {
|
|
48615
|
-
if (tc.name) existing.name = tc.name;
|
|
48616
|
-
if (tc.id) existing.id = tc.id;
|
|
48617
|
-
existing.arguments += tc.arguments;
|
|
48618
|
-
} else {
|
|
48619
|
-
toolCallByIndex.set(tc.index, tc);
|
|
48620
|
-
}
|
|
48621
|
-
}
|
|
48622
|
-
}
|
|
48623
|
-
}
|
|
48624
|
-
} finally {
|
|
48625
|
-
reader.releaseLock();
|
|
48626
|
-
}
|
|
48627
|
-
const sortedIndices = [...toolCallByIndex.keys()].sort((a, b2) => a - b2);
|
|
48628
|
-
const toolCalls = sortedIndices.map((i) => {
|
|
48629
|
-
const tc = toolCallByIndex.get(i);
|
|
48630
|
-
return { ...tc, id: tc.id || `call_${tc.index}` };
|
|
48631
|
-
}).filter((tc) => tc.name.length > 0);
|
|
48632
|
-
const proxyCalls = toolCalls.filter((tc) => PROXY_TOOL_NAMES.has(tc.name));
|
|
48633
|
-
const realCalls = toolCalls.filter((tc) => !PROXY_TOOL_NAMES.has(tc.name));
|
|
48634
|
-
const mutatingProxy = proxyCalls.filter((tc) => MUTATING_PROXY_TOOLS.has(tc.name));
|
|
48635
|
-
const readonlyProxy = proxyCalls.filter((tc) => READONLY_PROXY_TOOLS.has(tc.name));
|
|
48636
|
-
const hasMutatingOnly = mutatingProxy.length > 0 && realCalls.length === 0;
|
|
48637
|
-
if (usage) {
|
|
48638
|
-
const prompt = usage.prompt_tokens ?? usage.input_tokens;
|
|
48639
|
-
const det = usage.prompt_tokens_details ?? usage.prompt_cache_hit_tokens;
|
|
48640
|
-
const cached = det?.cached_tokens ?? usage.prompt_cache_hit_tokens;
|
|
48641
|
-
const out = usage.completion_tokens ?? usage.output_tokens;
|
|
48642
|
-
if (typeof prompt === "number") {
|
|
48643
|
-
const ch = typeof cached === "number" ? cached : 0;
|
|
48644
|
-
log("info", `[acp-usage] round ${loopCount} input=${prompt} cached=${typeof cached === "number" ? cached : "?"} output=${out ?? "?"}${ch > 0 ? ` (cache hit ${Math.round(ch / prompt * 100)}%)` : ""}`);
|
|
48645
|
-
ctx.session.stats.inputTokens += prompt;
|
|
48646
|
-
ctx.session.stats.lastInputTokens = prompt + (typeof cached === "number" ? cached : 0);
|
|
48647
|
-
if (typeof cached === "number") ctx.session.stats.cachedTokens += cached;
|
|
48648
|
-
if (typeof out === "number") ctx.session.stats.outputTokens += out;
|
|
48649
|
-
ctx.session.stats.cacheSamples += 1;
|
|
48650
|
-
}
|
|
48651
|
-
}
|
|
48652
|
-
if (!hasMutatingOnly) {
|
|
48653
|
-
for (const tc of readonlyProxy) {
|
|
48654
|
-
let args = {};
|
|
48655
|
-
try {
|
|
48656
|
-
args = JSON.parse(tc.arguments);
|
|
48657
|
-
} catch {
|
|
48658
|
-
args = {};
|
|
48659
|
-
}
|
|
48660
|
-
let result;
|
|
48661
|
-
try {
|
|
48662
|
-
result = executeProxyTool(tc.name, args, ctx);
|
|
48663
|
-
} catch (e) {
|
|
48664
|
-
result = `[${tc.name} FAILED: ${e instanceof Error ? e.message : String(e)}]`;
|
|
48665
|
-
}
|
|
48666
|
-
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
48667
|
-
ctx.log(`[acp-proxy: ${tc.name} (${tc.id}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
48668
|
-
yield Buffer.from(
|
|
48669
|
-
buildContentSse(responseId, model, buildVisibilityMarker(tc.name, result)),
|
|
48670
|
-
"utf8"
|
|
48671
|
-
);
|
|
48672
|
-
}
|
|
48673
|
-
for (const tc of realCalls) {
|
|
48674
|
-
yield Buffer.from(buildToolCallSse(makeBase(), tc), "utf8");
|
|
48675
|
-
}
|
|
48676
|
-
const fr2 = realCalls.length > 0 ? "tool_calls" : finishReason ?? "stop";
|
|
48677
|
-
yield Buffer.from(buildFinishSse(makeBase(), fr2, usage), "utf8");
|
|
48678
|
-
yield Buffer.from("data: [DONE]\n\n", "utf8");
|
|
48679
|
-
return;
|
|
48680
|
-
}
|
|
48681
|
-
const names = proxyCalls.map((c) => c.name).join(", ");
|
|
48682
|
-
ctx.log(`[acp-proxy: round ${loopCount} \u2014 ${proxyCalls.length} proxy call(s): ${names}]`);
|
|
48683
|
-
const messages = requestBody.messages ?? [];
|
|
48684
|
-
messages.push({
|
|
48685
|
-
role: "assistant",
|
|
48686
|
-
content: contentText || null,
|
|
48687
|
-
tool_calls: proxyCalls.map((tc) => ({
|
|
48688
|
-
id: tc.id,
|
|
48689
|
-
type: "function",
|
|
48690
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
48691
|
-
}))
|
|
48692
|
-
});
|
|
48693
|
-
for (const tc of proxyCalls) {
|
|
48694
|
-
let args = {};
|
|
48695
|
-
try {
|
|
48696
|
-
args = JSON.parse(tc.arguments);
|
|
48697
|
-
} catch {
|
|
48698
|
-
args = {};
|
|
48699
|
-
}
|
|
48700
|
-
const result = executeProxyTool(tc.name, args, ctx);
|
|
48701
|
-
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
48702
|
-
ctx.log(`[acp-proxy: ${tc.name} (${tc.id}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
48703
|
-
yield Buffer.from(
|
|
48704
|
-
buildContentSse(responseId, model, buildVisibilityMarker(tc.name, result)),
|
|
48705
|
-
"utf8"
|
|
48706
|
-
);
|
|
48707
|
-
messages.push({
|
|
48708
|
-
role: "tool",
|
|
48709
|
-
tool_call_id: tc.id,
|
|
48710
|
-
content: result
|
|
48711
|
-
});
|
|
48712
|
-
}
|
|
48713
|
-
requestBody.messages = messages;
|
|
48714
|
-
const { response: resp, clearTimer } = await fetchWithTimeout(requestOptions.url, {
|
|
48715
|
-
method: "POST",
|
|
48716
|
-
headers: requestOptions.headers,
|
|
48717
|
-
body: JSON.stringify(requestBody),
|
|
48718
|
-
...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
|
|
48719
|
-
});
|
|
48720
|
-
if (!resp.ok || !resp.body) {
|
|
48721
|
-
const errText = await resp.text().catch(() => "upstream error");
|
|
48722
|
-
ctx.log(`[acp-proxy: compress loop upstream error ${resp.status}: ${errText.slice(0, 200)}]`);
|
|
48723
|
-
yield Buffer.from(
|
|
48724
|
-
`data: ${JSON.stringify({
|
|
48725
|
-
...makeBase(),
|
|
48726
|
-
choices: [{
|
|
48727
|
-
index: 0,
|
|
48728
|
-
delta: { content: `
|
|
48729
|
-
[acp-proxy: upstream error ${resp.status}: ${errText.slice(0, 200)}]
|
|
48730
|
-
` },
|
|
48731
|
-
finish_reason: null
|
|
48732
|
-
}]
|
|
48733
|
-
})}
|
|
48734
|
-
|
|
48735
|
-
`,
|
|
48736
|
-
"utf8"
|
|
48737
|
-
);
|
|
48738
|
-
yield Buffer.from(buildFinishSse(makeBase(), "stop", null), "utf8");
|
|
48739
|
-
yield Buffer.from("data: [DONE]\n\n", "utf8");
|
|
48740
|
-
return;
|
|
48741
|
-
}
|
|
48742
|
-
upstream = resp.body;
|
|
48743
|
-
if (activeClearTimer) activeClearTimer();
|
|
48744
|
-
activeClearTimer = clearTimer;
|
|
48745
|
-
}
|
|
48746
|
-
} finally {
|
|
48747
|
-
if (activeClearTimer) {
|
|
48748
|
-
activeClearTimer();
|
|
48749
|
-
activeClearTimer = null;
|
|
48750
|
-
}
|
|
48751
|
-
}
|
|
48752
|
-
}
|
|
48753
48470
|
|
|
48754
|
-
// src/
|
|
48755
|
-
|
|
48471
|
+
// src/loop/core.ts
|
|
48472
|
+
var MAX_LOOP_ROUNDS = 10;
|
|
48473
|
+
function executeProxyTool(toolName, args, ctx, callId) {
|
|
48756
48474
|
if (toolName === "compress") {
|
|
48757
|
-
return applyRanges(parseCompressInput(args), ctx);
|
|
48475
|
+
return applyRanges(parseCompressInput(args, callId), ctx);
|
|
48758
48476
|
}
|
|
48759
48477
|
if (toolName === "decompress") {
|
|
48760
48478
|
return resolveDecompress(args, ctx);
|
|
@@ -48776,941 +48494,62 @@ function executeProxyTool2(toolName, args, ctx) {
|
|
|
48776
48494
|
${lines.join("\n\n")}`;
|
|
48777
48495
|
}
|
|
48778
48496
|
if (toolName === "acp_status") {
|
|
48779
|
-
return
|
|
48497
|
+
return handleAcpStatus(args, ctx);
|
|
48780
48498
|
}
|
|
48781
48499
|
return `[Unknown proxy tool: ${toolName}]`;
|
|
48782
48500
|
}
|
|
48783
|
-
function
|
|
48784
|
-
const
|
|
48785
|
-
|
|
48786
|
-
const
|
|
48787
|
-
|
|
48788
|
-
|
|
48789
|
-
|
|
48790
|
-
|
|
48791
|
-
|
|
48792
|
-
|
|
48793
|
-
|
|
48794
|
-
|
|
48795
|
-
|
|
48796
|
-
|
|
48797
|
-
|
|
48798
|
-
|
|
48799
|
-
|
|
48800
|
-
|
|
48501
|
+
function handleAcpStatus(args, ctx) {
|
|
48502
|
+
const scope = typeof args.scope === "string" ? args.scope : void 0;
|
|
48503
|
+
const view = typeof args.view === "string" ? args.view : void 0;
|
|
48504
|
+
const tool = typeof args.tool === "string" ? args.tool : void 0;
|
|
48505
|
+
const sort = typeof args.sort === "string" ? args.sort : void 0;
|
|
48506
|
+
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
48507
|
+
const base = buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast, { scope, view, tool, sort, limit });
|
|
48508
|
+
if (scope) return base;
|
|
48509
|
+
const nudge = ctx.nudge;
|
|
48510
|
+
const ranges = nudge?.compressibleRanges ?? [];
|
|
48511
|
+
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
48512
|
+
const extra = [];
|
|
48513
|
+
if (nudge) {
|
|
48514
|
+
extra.push("");
|
|
48515
|
+
extra.push(nudge.shouldInject ? `Nudge: ACTIVE \u2014 ${nudge.reason}` : `Nudge: idle \u2014 ${nudge.reason}`);
|
|
48516
|
+
}
|
|
48517
|
+
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
48518
|
+
extra.push("");
|
|
48519
|
+
extra.push(formatRanges(ranges, protectedRanges));
|
|
48520
|
+
}
|
|
48521
|
+
return extra.length > 0 ? `${base}
|
|
48522
|
+
${extra.join("\n")}` : base;
|
|
48523
|
+
}
|
|
48524
|
+
function recordUsage(ctx, usage, round) {
|
|
48525
|
+
const prompt = usage.inputTokens;
|
|
48526
|
+
const cached = usage.cachedTokens;
|
|
48527
|
+
const out = usage.outputTokens;
|
|
48528
|
+
if (typeof prompt === "number") ctx.session.stats.inputTokens += prompt;
|
|
48529
|
+
ctx.session.stats.lastInputTokens = (typeof prompt === "number" ? prompt : 0) + (typeof cached === "number" ? cached : 0);
|
|
48530
|
+
if (typeof cached === "number") {
|
|
48531
|
+
ctx.session.stats.cachedTokens += cached;
|
|
48532
|
+
ctx.session.stats.cacheSamples += 1;
|
|
48801
48533
|
}
|
|
48534
|
+
if (typeof out === "number") ctx.session.stats.outputTokens += out;
|
|
48535
|
+
const hitPct = typeof prompt === "number" && typeof cached === "number" && prompt + cached > 0 ? Math.round(cached / (prompt + cached) * 100) : 0;
|
|
48536
|
+
ctx.log(
|
|
48537
|
+
`[acp-usage] round ${round} input=${ctx.session.stats.lastInputTokens} cached=${cached ?? 0} (cache hit ${hitPct}%)`
|
|
48538
|
+
);
|
|
48802
48539
|
}
|
|
48803
|
-
function
|
|
48804
|
-
return `event: content_block_start
|
|
48805
|
-
data: ${JSON.stringify({ type: "content_block_start", index, content_block: { type: "text", text: "" } })}
|
|
48806
|
-
|
|
48807
|
-
event: content_block_delta
|
|
48808
|
-
data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: "text_delta", text } })}
|
|
48809
|
-
|
|
48810
|
-
event: content_block_stop
|
|
48811
|
-
data: ${JSON.stringify({ type: "content_block_stop", index })}
|
|
48812
|
-
|
|
48813
|
-
`;
|
|
48814
|
-
}
|
|
48815
|
-
function buildTerminalSse(stopReason, outputTokens, inputTokens, cachedTokens, messageId, model) {
|
|
48816
|
-
const usage = {
|
|
48817
|
-
input_tokens: inputTokens,
|
|
48818
|
-
output_tokens: outputTokens,
|
|
48819
|
-
cache_read_input_tokens: cachedTokens
|
|
48820
|
-
};
|
|
48821
|
-
const extra = {};
|
|
48822
|
-
if (messageId) extra.id = messageId;
|
|
48823
|
-
if (model) extra.model = model;
|
|
48824
|
-
return `event: message_delta
|
|
48825
|
-
data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: stopReason, stop_sequence: null }, usage, ...extra })}
|
|
48826
|
-
|
|
48827
|
-
event: message_stop
|
|
48828
|
-
data: ${JSON.stringify({ type: "message_stop" })}
|
|
48829
|
-
|
|
48830
|
-
`;
|
|
48831
|
-
}
|
|
48832
|
-
function remapIndex(json, oldIndex, newIndex) {
|
|
48833
|
-
return json.replaceAll(`"index":${oldIndex}`, `"index":${newIndex}`).replaceAll(`"index": ${oldIndex}`, `"index": ${newIndex}`);
|
|
48834
|
-
}
|
|
48835
|
-
function safeParse2(s3) {
|
|
48836
|
-
try {
|
|
48837
|
-
const v2 = JSON.parse(s3);
|
|
48838
|
-
return typeof v2 === "object" && v2 ? v2 : {};
|
|
48839
|
-
} catch {
|
|
48840
|
-
return {};
|
|
48841
|
-
}
|
|
48842
|
-
}
|
|
48843
|
-
async function* compressLoopAnthropicStream(initialUpstream, ctx, requestBody, requestOptions) {
|
|
48844
|
-
let upstream = initialUpstream;
|
|
48845
|
-
let activeClearTimer = null;
|
|
48846
|
-
try {
|
|
48847
|
-
const model = requestBody.model ?? void 0;
|
|
48848
|
-
let messageId;
|
|
48849
|
-
let clientIndex = 0;
|
|
48850
|
-
let totalOutputTokens = 0;
|
|
48851
|
-
let totalInputTokens = 0;
|
|
48852
|
-
let totalCachedTokens = 0;
|
|
48853
|
-
for (let loopCount = 1; ; loopCount++) {
|
|
48854
|
-
if (loopCount > 10) {
|
|
48855
|
-
ctx.log("[acp-proxy: anthropic compress loop limit (10) reached, finishing]");
|
|
48856
|
-
yield Buffer.from(buildTerminalSse("end_turn", totalOutputTokens, totalInputTokens, totalCachedTokens, messageId, model), "utf8");
|
|
48857
|
-
return;
|
|
48858
|
-
}
|
|
48859
|
-
const isFirstRound = loopCount === 1;
|
|
48860
|
-
const state = { clientIndex, toolBlocks: /* @__PURE__ */ new Map(), indexMap: /* @__PURE__ */ new Map() };
|
|
48861
|
-
let hasRealToolUse = false;
|
|
48862
|
-
let roundText = "";
|
|
48863
|
-
let roundStopReason;
|
|
48864
|
-
const reader = upstream.getReader();
|
|
48865
|
-
const decoder = new TextDecoder("utf-8");
|
|
48866
|
-
let sseBuffer = "";
|
|
48867
|
-
const cbs = {
|
|
48868
|
-
onRealToolUse: () => {
|
|
48869
|
-
hasRealToolUse = true;
|
|
48870
|
-
},
|
|
48871
|
-
onText: (t) => {
|
|
48872
|
-
roundText += t;
|
|
48873
|
-
},
|
|
48874
|
-
onOutputTokens: (n) => {
|
|
48875
|
-
totalOutputTokens += n;
|
|
48876
|
-
},
|
|
48877
|
-
onMessageId: (id) => {
|
|
48878
|
-
if (!messageId) messageId = id;
|
|
48879
|
-
},
|
|
48880
|
-
onStopReason: (r) => {
|
|
48881
|
-
roundStopReason = r;
|
|
48882
|
-
},
|
|
48883
|
-
onCacheUsage: (input, cached) => {
|
|
48884
|
-
if (typeof input === "number") {
|
|
48885
|
-
ctx.session.stats.inputTokens += input;
|
|
48886
|
-
ctx.session.stats.lastInputTokens = input + (typeof cached === "number" ? cached : 0);
|
|
48887
|
-
totalInputTokens += input;
|
|
48888
|
-
}
|
|
48889
|
-
if (typeof cached === "number") {
|
|
48890
|
-
ctx.session.stats.cachedTokens += cached;
|
|
48891
|
-
ctx.session.stats.cacheSamples += 1;
|
|
48892
|
-
totalCachedTokens += cached;
|
|
48893
|
-
}
|
|
48894
|
-
}
|
|
48895
|
-
};
|
|
48896
|
-
try {
|
|
48897
|
-
for (; ; ) {
|
|
48898
|
-
const { done, value } = await reader.read();
|
|
48899
|
-
if (done) break;
|
|
48900
|
-
sseBuffer += decoder.decode(value, { stream: true });
|
|
48901
|
-
sseBuffer = normalizeSseLineEndings(sseBuffer);
|
|
48902
|
-
let sep;
|
|
48903
|
-
while ((sep = sseBuffer.indexOf("\n\n")) >= 0) {
|
|
48904
|
-
const eventStr = sseBuffer.slice(0, sep);
|
|
48905
|
-
sseBuffer = sseBuffer.slice(sep + 2);
|
|
48906
|
-
if (!eventStr.trim()) continue;
|
|
48907
|
-
for (const b2 of routeAnthropicEvent(eventStr, isFirstRound, state, cbs)) {
|
|
48908
|
-
yield b2;
|
|
48909
|
-
}
|
|
48910
|
-
}
|
|
48911
|
-
}
|
|
48912
|
-
sseBuffer += decoder.decode();
|
|
48913
|
-
sseBuffer = normalizeSseLineEndings(sseBuffer);
|
|
48914
|
-
let resSep;
|
|
48915
|
-
while ((resSep = sseBuffer.indexOf("\n\n")) >= 0) {
|
|
48916
|
-
const eventStr = sseBuffer.slice(0, resSep);
|
|
48917
|
-
sseBuffer = sseBuffer.slice(resSep + 2);
|
|
48918
|
-
if (!eventStr.trim()) continue;
|
|
48919
|
-
for (const b2 of routeAnthropicEvent(eventStr, isFirstRound, state, cbs)) {
|
|
48920
|
-
yield b2;
|
|
48921
|
-
}
|
|
48922
|
-
}
|
|
48923
|
-
} finally {
|
|
48924
|
-
reader.releaseLock();
|
|
48925
|
-
}
|
|
48926
|
-
clientIndex = state.clientIndex;
|
|
48927
|
-
const proxyCalls = [...state.toolBlocks.values()].filter((b2) => PROXY_TOOL_NAMES.has(b2.name));
|
|
48928
|
-
const mutatingProxy = proxyCalls.filter((b2) => MUTATING_PROXY_TOOLS.has(b2.name));
|
|
48929
|
-
const readonlyProxy = proxyCalls.filter((b2) => READONLY_PROXY_TOOLS.has(b2.name));
|
|
48930
|
-
const hasMutatingOnly = mutatingProxy.length > 0 && !hasRealToolUse;
|
|
48931
|
-
if (!hasMutatingOnly) {
|
|
48932
|
-
for (const tc of readonlyProxy) {
|
|
48933
|
-
const args = safeParse2(tc.json);
|
|
48934
|
-
let result;
|
|
48935
|
-
try {
|
|
48936
|
-
result = executeProxyTool2(tc.name, args, ctx);
|
|
48937
|
-
} catch (e) {
|
|
48938
|
-
result = `[${tc.name} FAILED: ${e instanceof Error ? e.message : String(e)}]`;
|
|
48939
|
-
}
|
|
48940
|
-
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
48941
|
-
ctx.log(`[acp-proxy: ${tc.name} (${tc.id}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
48942
|
-
yield Buffer.from(buildTextBlockSse(clientIndex, buildVisibilityMarker(tc.name, result)), "utf8");
|
|
48943
|
-
clientIndex++;
|
|
48944
|
-
}
|
|
48945
|
-
const stop = hasRealToolUse ? "tool_use" : roundStopReason ?? "end_turn";
|
|
48946
|
-
yield Buffer.from(buildTerminalSse(stop, totalOutputTokens, totalInputTokens, totalCachedTokens, messageId, model), "utf8");
|
|
48947
|
-
return;
|
|
48948
|
-
}
|
|
48949
|
-
const names = proxyCalls.map((c) => c.name).join(", ");
|
|
48950
|
-
ctx.log(`[acp-proxy: anthropic round ${loopCount} \u2014 ${proxyCalls.length} proxy call(s): ${names}]`);
|
|
48951
|
-
const messages = requestBody.messages ?? [];
|
|
48952
|
-
const assistantContent = [];
|
|
48953
|
-
if (roundText.length > 0) {
|
|
48954
|
-
assistantContent.push({ type: "text", text: roundText });
|
|
48955
|
-
}
|
|
48956
|
-
for (const tc of proxyCalls) {
|
|
48957
|
-
assistantContent.push({ type: "tool_use", id: tc.id, name: tc.name, input: safeParse2(tc.json) });
|
|
48958
|
-
}
|
|
48959
|
-
messages.push({ role: "assistant", content: assistantContent });
|
|
48960
|
-
for (const tc of proxyCalls) {
|
|
48961
|
-
const args = safeParse2(tc.json);
|
|
48962
|
-
const result = executeProxyTool2(tc.name, args, ctx);
|
|
48963
|
-
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
48964
|
-
ctx.log(`[acp-proxy: ${tc.name} (${tc.id}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
48965
|
-
yield Buffer.from(buildTextBlockSse(clientIndex, buildVisibilityMarker(tc.name, result)), "utf8");
|
|
48966
|
-
clientIndex++;
|
|
48967
|
-
messages.push({
|
|
48968
|
-
role: "user",
|
|
48969
|
-
content: [{ type: "tool_result", tool_use_id: tc.id, content: result }]
|
|
48970
|
-
});
|
|
48971
|
-
}
|
|
48972
|
-
requestBody.messages = messages;
|
|
48973
|
-
const { response: resp, clearTimer } = await fetchWithTimeout(requestOptions.url, {
|
|
48974
|
-
method: "POST",
|
|
48975
|
-
headers: requestOptions.headers,
|
|
48976
|
-
body: JSON.stringify(requestBody),
|
|
48977
|
-
...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
|
|
48978
|
-
});
|
|
48979
|
-
if (!resp.ok || !resp.body) {
|
|
48980
|
-
const errText = await resp.text().catch(() => "upstream error");
|
|
48981
|
-
ctx.log(`[acp-proxy: anthropic compress loop upstream error ${resp.status}: ${errText.slice(0, 200)}]`);
|
|
48982
|
-
yield Buffer.from(buildTextBlockSse(clientIndex, `
|
|
48983
|
-
[acp-proxy: upstream error ${resp.status}: ${errText.slice(0, 200)}]
|
|
48984
|
-
`), "utf8");
|
|
48985
|
-
yield Buffer.from(buildTerminalSse("end_turn", totalOutputTokens, totalInputTokens, totalCachedTokens, messageId, model), "utf8");
|
|
48986
|
-
return;
|
|
48987
|
-
}
|
|
48988
|
-
upstream = resp.body;
|
|
48989
|
-
if (activeClearTimer) activeClearTimer();
|
|
48990
|
-
activeClearTimer = clearTimer;
|
|
48991
|
-
}
|
|
48992
|
-
} finally {
|
|
48993
|
-
if (activeClearTimer) {
|
|
48994
|
-
activeClearTimer();
|
|
48995
|
-
activeClearTimer = null;
|
|
48996
|
-
}
|
|
48997
|
-
}
|
|
48998
|
-
}
|
|
48999
|
-
function routeAnthropicEvent(eventStr, isFirstRound, state, cb) {
|
|
49000
|
-
const parsed = parseAnthropicSse(eventStr);
|
|
49001
|
-
if (!parsed) return [];
|
|
49002
|
-
const { type, data } = parsed;
|
|
49003
|
-
if (type === "message_start") {
|
|
49004
|
-
const msg2 = data.message ?? {};
|
|
49005
|
-
if (typeof msg2.id === "string") cb.onMessageId(msg2.id);
|
|
49006
|
-
const u2 = msg2.usage ?? {};
|
|
49007
|
-
cb.onCacheUsage(u2.input_tokens, u2.cache_read_input_tokens);
|
|
49008
|
-
return isFirstRound ? [Buffer.from(eventStr + "\n\n", "utf8")] : [];
|
|
49009
|
-
}
|
|
49010
|
-
if (type === "ping") {
|
|
49011
|
-
return [Buffer.from(eventStr + "\n\n", "utf8")];
|
|
49012
|
-
}
|
|
49013
|
-
if (type === "content_block_start") {
|
|
49014
|
-
const upstreamIndex = data.index ?? 0;
|
|
49015
|
-
const block = data.content_block ?? {};
|
|
49016
|
-
if (block.type === "tool_use") {
|
|
49017
|
-
const name = typeof block.name === "string" ? block.name : "";
|
|
49018
|
-
const id = typeof block.id === "string" ? block.id : `toolu_${upstreamIndex}`;
|
|
49019
|
-
if (PROXY_TOOL_NAMES.has(name)) {
|
|
49020
|
-
state.toolBlocks.set(upstreamIndex, { id, name, json: "" });
|
|
49021
|
-
return [];
|
|
49022
|
-
}
|
|
49023
|
-
cb.onRealToolUse();
|
|
49024
|
-
}
|
|
49025
|
-
const ci2 = state.clientIndex++;
|
|
49026
|
-
state.indexMap.set(upstreamIndex, ci2);
|
|
49027
|
-
if (isFirstRound) return [Buffer.from(eventStr + "\n\n", "utf8")];
|
|
49028
|
-
return [Buffer.from(remapIndex(eventStr + "\n\n", upstreamIndex, ci2), "utf8")];
|
|
49029
|
-
}
|
|
49030
|
-
if (type === "content_block_delta") {
|
|
49031
|
-
const upstreamIndex = data.index ?? 0;
|
|
49032
|
-
const delta = data.delta ?? {};
|
|
49033
|
-
if (state.toolBlocks.has(upstreamIndex)) {
|
|
49034
|
-
if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
49035
|
-
state.toolBlocks.get(upstreamIndex).json += delta.partial_json;
|
|
49036
|
-
}
|
|
49037
|
-
return [];
|
|
49038
|
-
}
|
|
49039
|
-
if (delta.type === "text_delta" && typeof delta.text === "string") cb.onText(delta.text);
|
|
49040
|
-
if (isFirstRound) return [Buffer.from(eventStr + "\n\n", "utf8")];
|
|
49041
|
-
const ci2 = state.indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
49042
|
-
return [Buffer.from(remapIndex(eventStr + "\n\n", upstreamIndex, ci2), "utf8")];
|
|
49043
|
-
}
|
|
49044
|
-
if (type === "content_block_stop") {
|
|
49045
|
-
const upstreamIndex = data.index ?? 0;
|
|
49046
|
-
if (state.toolBlocks.has(upstreamIndex)) return [];
|
|
49047
|
-
if (isFirstRound) return [Buffer.from(eventStr + "\n\n", "utf8")];
|
|
49048
|
-
const ci2 = state.indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
49049
|
-
return [Buffer.from(remapIndex(eventStr + "\n\n", upstreamIndex, ci2), "utf8")];
|
|
49050
|
-
}
|
|
49051
|
-
if (type === "message_delta") {
|
|
49052
|
-
const u2 = data.usage ?? {};
|
|
49053
|
-
const out = u2.output_tokens;
|
|
49054
|
-
if (typeof out === "number") cb.onOutputTokens(out);
|
|
49055
|
-
cb.onCacheUsage(
|
|
49056
|
-
u2.input_tokens,
|
|
49057
|
-
u2.cache_read_input_tokens
|
|
49058
|
-
);
|
|
49059
|
-
const d = data.delta ?? {};
|
|
49060
|
-
if (typeof d.stop_reason === "string") cb.onStopReason(d.stop_reason);
|
|
49061
|
-
return [];
|
|
49062
|
-
}
|
|
49063
|
-
if (type === "message_stop") {
|
|
49064
|
-
return [];
|
|
49065
|
-
}
|
|
49066
|
-
return isFirstRound ? [Buffer.from(eventStr + "\n\n", "utf8")] : [];
|
|
49067
|
-
}
|
|
49068
|
-
|
|
49069
|
-
// src/compress-loop-responses.ts
|
|
49070
|
-
var TEXT_PROTOCOL = process.env.ACP_COMPRESS_PROTOCOL === "text";
|
|
49071
|
-
function extractTextTriggers(text) {
|
|
49072
|
-
const calls = [];
|
|
49073
|
-
let clean = "";
|
|
49074
|
-
let i = 0;
|
|
49075
|
-
let n = 0;
|
|
49076
|
-
while (i < text.length) {
|
|
49077
|
-
const open = text.indexOf(ACP_TEXT_OPEN, i);
|
|
49078
|
-
if (open === -1) {
|
|
49079
|
-
clean += text.slice(i);
|
|
49080
|
-
break;
|
|
49081
|
-
}
|
|
49082
|
-
clean += text.slice(i, open);
|
|
49083
|
-
const after = open + ACP_TEXT_OPEN.length;
|
|
49084
|
-
const close = text.indexOf(ACP_TEXT_CLOSE, after);
|
|
49085
|
-
if (close === -1) {
|
|
49086
|
-
clean += text.slice(open);
|
|
49087
|
-
break;
|
|
49088
|
-
}
|
|
49089
|
-
const payload = text.slice(after, close).trim();
|
|
49090
|
-
if (payload) {
|
|
49091
|
-
const stamp = `${Date.now()}_${n++}`;
|
|
49092
|
-
calls.push({
|
|
49093
|
-
itemId: `fc_text_${stamp}`,
|
|
49094
|
-
callId: `call_text_${stamp}`,
|
|
49095
|
-
name: COMPRESS_TOOL_NAME,
|
|
49096
|
-
arguments: payload
|
|
49097
|
-
});
|
|
49098
|
-
}
|
|
49099
|
-
i = close + ACP_TEXT_CLOSE.length;
|
|
49100
|
-
}
|
|
49101
|
-
return { clean, calls };
|
|
49102
|
-
}
|
|
49103
|
-
function executeProxyTool3(toolName, args, ctx) {
|
|
49104
|
-
if (toolName === "compress") {
|
|
49105
|
-
return applyRanges(parseCompressInput(args), ctx);
|
|
49106
|
-
}
|
|
49107
|
-
if (toolName === "decompress") {
|
|
49108
|
-
return resolveDecompress(args, ctx);
|
|
49109
|
-
}
|
|
49110
|
-
if (toolName === "search_context") {
|
|
49111
|
-
const query = typeof args.query === "string" ? args.query : "";
|
|
49112
|
-
if (query.length === 0) return "[search_context FAILED: query is required]";
|
|
49113
|
-
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 5;
|
|
49114
|
-
const blocks = ctx.core.search(query, ctx.session.state).slice(0, limit);
|
|
49115
|
-
if (blocks.length === 0) return `[No blocks matched "${query}"]`;
|
|
49116
|
-
const lines = blocks.map((b2) => {
|
|
49117
|
-
const topic = b2.topic ?? "(no topic)";
|
|
49118
|
-
const preview = b2.summary.length > 200 ? b2.summary.slice(0, 200) + "..." : b2.summary;
|
|
49119
|
-
return `${b2.blockId} (T${b2.tier}) "${topic}"
|
|
49120
|
-
${preview}`;
|
|
49121
|
-
});
|
|
49122
|
-
return `Found ${blocks.length} block(s) for "${query}":
|
|
49123
|
-
|
|
49124
|
-
${lines.join("\n\n")}`;
|
|
49125
|
-
}
|
|
49126
|
-
if (toolName === "acp_status") {
|
|
49127
|
-
return buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast);
|
|
49128
|
-
}
|
|
49129
|
-
return `[Unknown proxy tool: ${toolName}]`;
|
|
49130
|
-
}
|
|
49131
|
-
function extractEventType(rawEvent) {
|
|
49132
|
-
for (const l of rawEvent.split("\n")) {
|
|
49133
|
-
if (l.startsWith("event:")) return l.slice(6).trim();
|
|
49134
|
-
}
|
|
49135
|
-
return null;
|
|
49136
|
-
}
|
|
49137
|
-
function extractDataLine(rawEvent) {
|
|
49138
|
-
const parts = [];
|
|
49139
|
-
for (const l of rawEvent.split("\n")) {
|
|
49140
|
-
if (l.startsWith("data:")) {
|
|
49141
|
-
let v2 = l.slice(5);
|
|
49142
|
-
if (v2.startsWith(" ")) v2 = v2.slice(1);
|
|
49143
|
-
parts.push(v2);
|
|
49144
|
-
}
|
|
49145
|
-
}
|
|
49146
|
-
return parts.length ? parts.join("\n") : null;
|
|
49147
|
-
}
|
|
49148
|
-
function classifyResponsesSseEvent(eventStr) {
|
|
49149
|
-
const type = extractEventType(eventStr);
|
|
49150
|
-
const dataLine = extractDataLine(eventStr);
|
|
49151
|
-
if (!type || !dataLine) return {};
|
|
49152
|
-
let obj;
|
|
49153
|
-
try {
|
|
49154
|
-
obj = JSON.parse(dataLine);
|
|
49155
|
-
} catch {
|
|
49156
|
-
return {};
|
|
49157
|
-
}
|
|
49158
|
-
const out = {};
|
|
49159
|
-
switch (type) {
|
|
49160
|
-
case "response.created":
|
|
49161
|
-
case "response.in_progress":
|
|
49162
|
-
out.isMeta = true;
|
|
49163
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
49164
|
-
return out;
|
|
49165
|
-
case "response.output_item.added": {
|
|
49166
|
-
const item = obj.item;
|
|
49167
|
-
if (item?.type === "function_call") {
|
|
49168
|
-
const name = typeof item.name === "string" ? item.name : "";
|
|
49169
|
-
out.fcStart = {
|
|
49170
|
-
itemId: typeof item.id === "string" ? item.id : "",
|
|
49171
|
-
callId: typeof item.call_id === "string" ? item.call_id : "",
|
|
49172
|
-
name
|
|
49173
|
-
};
|
|
49174
|
-
return out;
|
|
49175
|
-
}
|
|
49176
|
-
if (item?.type === "custom_tool_call") out.noBuffer = true;
|
|
49177
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
49178
|
-
return out;
|
|
49179
|
-
}
|
|
49180
|
-
case "response.content_part.added":
|
|
49181
|
-
case "response.content_part.done":
|
|
49182
|
-
case "response.output_text.done":
|
|
49183
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
49184
|
-
return out;
|
|
49185
|
-
case "response.output_text.delta": {
|
|
49186
|
-
const delta = typeof obj.delta === "string" ? obj.delta : "";
|
|
49187
|
-
if (delta) {
|
|
49188
|
-
out.contentDelta = delta;
|
|
49189
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
49190
|
-
}
|
|
49191
|
-
return out;
|
|
49192
|
-
}
|
|
49193
|
-
case "response.function_call_arguments.delta": {
|
|
49194
|
-
const itemId = typeof obj.item_id === "string" ? obj.item_id : "";
|
|
49195
|
-
const delta = typeof obj.delta === "string" ? obj.delta : "";
|
|
49196
|
-
out.fcArgs = { itemId, delta };
|
|
49197
|
-
return out;
|
|
49198
|
-
}
|
|
49199
|
-
case "response.output_item.done": {
|
|
49200
|
-
const item = obj.item;
|
|
49201
|
-
if (item?.type === "function_call") {
|
|
49202
|
-
out.fcDone = { itemId: typeof item.id === "string" ? item.id : "" };
|
|
49203
|
-
return out;
|
|
49204
|
-
}
|
|
49205
|
-
if (item?.type === "custom_tool_call") {
|
|
49206
|
-
out.noBuffer = true;
|
|
49207
|
-
out.customToolCallDone = true;
|
|
49208
|
-
}
|
|
49209
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
49210
|
-
return out;
|
|
49211
|
-
}
|
|
49212
|
-
case "response.completed":
|
|
49213
|
-
out.isMeta = true;
|
|
49214
|
-
out.terminal = true;
|
|
49215
|
-
out.terminalKind = "completed";
|
|
49216
|
-
out.responseObj = obj.response ?? null;
|
|
49217
|
-
return out;
|
|
49218
|
-
case "response.incomplete":
|
|
49219
|
-
out.isMeta = true;
|
|
49220
|
-
out.terminal = true;
|
|
49221
|
-
out.terminalKind = "incomplete";
|
|
49222
|
-
out.terminalRaw = eventStr;
|
|
49223
|
-
return out;
|
|
49224
|
-
case "response.failed":
|
|
49225
|
-
case "response.error":
|
|
49226
|
-
out.isMeta = true;
|
|
49227
|
-
out.terminal = true;
|
|
49228
|
-
out.terminalKind = "failed";
|
|
49229
|
-
out.terminalRaw = eventStr;
|
|
49230
|
-
return out;
|
|
49231
|
-
default:
|
|
49232
|
-
if (type.startsWith("response.custom_tool_call.")) out.noBuffer = true;
|
|
49233
|
-
out.yieldChunk = Buffer.from(eventStr + "\n\n", "utf8");
|
|
49234
|
-
return out;
|
|
49235
|
-
}
|
|
49236
|
-
}
|
|
49237
|
-
function buildMessageItemSequence(itemId, outputIndex, text) {
|
|
49238
|
-
const item = { type: "message", id: itemId, role: "assistant", content: [] };
|
|
49239
|
-
const part = { type: "output_text", text: "" };
|
|
49240
|
-
const doneItem = { type: "message", id: itemId, role: "assistant", content: [{ type: "output_text", text }] };
|
|
49241
|
-
return [
|
|
49242
|
-
`event: response.output_item.added
|
|
49243
|
-
data: ${JSON.stringify({ type: "response.output_item.added", output_index: outputIndex, item })}
|
|
49244
|
-
|
|
49245
|
-
`,
|
|
49246
|
-
`event: response.content_part.added
|
|
49247
|
-
data: ${JSON.stringify({ type: "response.content_part.added", item_id: itemId, output_index: outputIndex, part })}
|
|
49248
|
-
|
|
49249
|
-
`,
|
|
49250
|
-
`event: response.output_text.delta
|
|
49251
|
-
data: ${JSON.stringify({ type: "response.output_text.delta", item_id: itemId, output_index: outputIndex, delta: text })}
|
|
49252
|
-
|
|
49253
|
-
`,
|
|
49254
|
-
`event: response.output_text.done
|
|
49255
|
-
data: ${JSON.stringify({ type: "response.output_text.done", item_id: itemId, output_index: outputIndex, text })}
|
|
49256
|
-
|
|
49257
|
-
`,
|
|
49258
|
-
`event: response.content_part.done
|
|
49259
|
-
data: ${JSON.stringify({ type: "response.content_part.done", item_id: itemId, output_index: outputIndex, part: { type: "output_text", text } })}
|
|
49260
|
-
|
|
49261
|
-
`,
|
|
49262
|
-
`event: response.output_item.done
|
|
49263
|
-
data: ${JSON.stringify({ type: "response.output_item.done", output_index: outputIndex, item: doneItem })}
|
|
49264
|
-
|
|
49265
|
-
`
|
|
49266
|
-
].join("");
|
|
49267
|
-
}
|
|
49268
|
-
function buildFunctionCallEvents(fc, outputIndex) {
|
|
49269
|
-
return [
|
|
49270
|
-
`event: response.output_item.added
|
|
49271
|
-
data: ${JSON.stringify({
|
|
49272
|
-
type: "response.output_item.added",
|
|
49273
|
-
output_index: outputIndex,
|
|
49274
|
-
item: { type: "function_call", id: fc.itemId, call_id: fc.callId, name: fc.name, arguments: "" }
|
|
49275
|
-
})}
|
|
49276
|
-
|
|
49277
|
-
`,
|
|
49278
|
-
`event: response.function_call_arguments.delta
|
|
49279
|
-
data: ${JSON.stringify({
|
|
49280
|
-
type: "response.function_call_arguments.delta",
|
|
49281
|
-
item_id: fc.itemId,
|
|
49282
|
-
delta: fc.arguments
|
|
49283
|
-
})}
|
|
49284
|
-
|
|
49285
|
-
`,
|
|
49286
|
-
`event: response.function_call_arguments.done
|
|
49287
|
-
data: ${JSON.stringify({
|
|
49288
|
-
type: "response.function_call_arguments.done",
|
|
49289
|
-
item_id: fc.itemId,
|
|
49290
|
-
arguments: fc.arguments
|
|
49291
|
-
})}
|
|
49292
|
-
|
|
49293
|
-
`,
|
|
49294
|
-
`event: response.output_item.done
|
|
49295
|
-
data: ${JSON.stringify({
|
|
49296
|
-
type: "response.output_item.done",
|
|
49297
|
-
output_index: outputIndex,
|
|
49298
|
-
item: { type: "function_call", id: fc.itemId, call_id: fc.callId, name: fc.name, arguments: fc.arguments }
|
|
49299
|
-
})}
|
|
49300
|
-
|
|
49301
|
-
`
|
|
49302
|
-
].join("");
|
|
49303
|
-
}
|
|
49304
|
-
function buildCompleted(responseObj) {
|
|
49305
|
-
const resp = responseObj ?? { id: `resp-proxy-${Date.now()}`, status: "completed", output: [] };
|
|
49306
|
-
return `event: response.completed
|
|
49307
|
-
data: ${JSON.stringify({
|
|
49308
|
-
type: "response.completed",
|
|
49309
|
-
response: resp
|
|
49310
|
-
})}
|
|
49311
|
-
|
|
49312
|
-
`;
|
|
49313
|
-
}
|
|
49314
|
-
function buildFailed(responseObj) {
|
|
49315
|
-
const id = responseObj?.id ?? `resp-failed-${Date.now()}`;
|
|
49316
|
-
const resp = { ...responseObj ?? {}, id, status: "failed", error: { code: "server_error", message: "upstream returned empty response" } };
|
|
49317
|
-
return `event: response.failed
|
|
49318
|
-
data: ${JSON.stringify({
|
|
49319
|
-
type: "response.failed",
|
|
49320
|
-
response: resp
|
|
49321
|
-
})}
|
|
49322
|
-
|
|
49323
|
-
`;
|
|
49324
|
-
}
|
|
49325
|
-
function responsesJsonOutput(response) {
|
|
49326
|
-
const textParts = [];
|
|
49327
|
-
const calls = [];
|
|
49328
|
-
for (const item of Array.isArray(response.output) ? response.output : []) {
|
|
49329
|
-
if (!item || typeof item !== "object") continue;
|
|
49330
|
-
const value = item;
|
|
49331
|
-
if (value.type === "message") {
|
|
49332
|
-
for (const part of Array.isArray(value.content) ? value.content : []) {
|
|
49333
|
-
if (part && typeof part === "object" && part.type === "output_text") {
|
|
49334
|
-
textParts.push(part);
|
|
49335
|
-
}
|
|
49336
|
-
}
|
|
49337
|
-
} else if (value.type === "function_call") {
|
|
49338
|
-
calls.push({
|
|
49339
|
-
itemId: typeof value.id === "string" ? value.id : "",
|
|
49340
|
-
callId: typeof value.call_id === "string" ? value.call_id : "",
|
|
49341
|
-
name: typeof value.name === "string" ? value.name : "",
|
|
49342
|
-
arguments: typeof value.arguments === "string" ? value.arguments : ""
|
|
49343
|
-
});
|
|
49344
|
-
}
|
|
49345
|
-
}
|
|
49346
|
-
return {
|
|
49347
|
-
text: textParts.map((part) => typeof part.text === "string" ? part.text : "").join(""),
|
|
49348
|
-
textParts,
|
|
49349
|
-
calls
|
|
49350
|
-
};
|
|
49351
|
-
}
|
|
49352
|
-
function replaceResponsesJsonText(parts, text) {
|
|
49353
|
-
parts.forEach((part, index) => {
|
|
49354
|
-
part.text = index === 0 ? text : "";
|
|
49355
|
-
});
|
|
49356
|
-
}
|
|
49357
|
-
function surfaceReadonlyJson(current, proxyCalls, ctx) {
|
|
49358
|
-
const markers = [];
|
|
49359
|
-
for (const call of proxyCalls) {
|
|
49360
|
-
if (MUTATING_PROXY_TOOLS.has(call.name)) continue;
|
|
49361
|
-
let args = {};
|
|
49362
|
-
try {
|
|
49363
|
-
args = JSON.parse(call.arguments);
|
|
49364
|
-
} catch {
|
|
49365
|
-
args = {};
|
|
49366
|
-
}
|
|
49367
|
-
let result;
|
|
49368
|
-
try {
|
|
49369
|
-
result = executeProxyTool3(call.name, args, ctx);
|
|
49370
|
-
ctx.log(`[acp-proxy: responses JSON ${call.name} (read-only) \u2192 ${result.slice(0, 120).replace(/\n/g, " ")}]`);
|
|
49371
|
-
} catch (e) {
|
|
49372
|
-
result = `\u274C [ACP] ${call.name} FAILED: ${String(e)}`;
|
|
49373
|
-
ctx.log(`[acp-proxy: responses JSON ${call.name} (read-only) FAILED: ${String(e)}]`);
|
|
49374
|
-
}
|
|
49375
|
-
markers.push(buildVisibilityMarker(call.name, result));
|
|
49376
|
-
}
|
|
49377
|
-
if (markers.length === 0) return current;
|
|
49378
|
-
const out = Array.isArray(current.output) ? [...current.output] : [];
|
|
49379
|
-
out.push({ type: "message", id: `msg_acp_ro_${Date.now()}_${markers.length}`, role: "assistant", content: [{ type: "output_text", text: markers.join("\n") }] });
|
|
49380
|
-
return { ...current, output: out };
|
|
49381
|
-
}
|
|
49382
|
-
async function compressLoopResponsesJson(initialResponse, ctx, requestBody, requestOptions) {
|
|
49383
|
-
let current = initialResponse;
|
|
49384
|
-
for (let loopCount = 1; loopCount <= 5; loopCount++) {
|
|
49385
|
-
const output = responsesJsonOutput(current);
|
|
49386
|
-
const extracted = extractTextTriggers(output.text);
|
|
49387
|
-
const allCalls = [...output.calls, ...extracted.calls].filter((call) => call.name.length > 0);
|
|
49388
|
-
const proxyCalls = allCalls.filter((call) => PROXY_TOOL_NAMES.has(call.name));
|
|
49389
|
-
const realCalls = allCalls.filter((call) => !PROXY_TOOL_NAMES.has(call.name));
|
|
49390
|
-
const mutatingProxy = proxyCalls.filter((call) => MUTATING_PROXY_TOOLS.has(call.name));
|
|
49391
|
-
if (mutatingProxy.length === 0 || realCalls.length > 0) {
|
|
49392
|
-
if (proxyCalls.length > 0) {
|
|
49393
|
-
replaceResponsesJsonText(output.textParts, extracted.clean);
|
|
49394
|
-
current = surfaceReadonlyJson(current, proxyCalls, ctx);
|
|
49395
|
-
}
|
|
49396
|
-
return current;
|
|
49397
|
-
}
|
|
49398
|
-
const inputItems = Array.isArray(requestBody.input) ? [...requestBody.input] : [];
|
|
49399
|
-
if (extracted.clean.trim()) {
|
|
49400
|
-
inputItems.push({ type: "message", role: "assistant", content: [{ type: "output_text", text: extracted.clean }] });
|
|
49401
|
-
}
|
|
49402
|
-
for (const call of proxyCalls) {
|
|
49403
|
-
let args = {};
|
|
49404
|
-
try {
|
|
49405
|
-
args = JSON.parse(call.arguments);
|
|
49406
|
-
} catch (error) {
|
|
49407
|
-
log("warn", `[acp-compress-args] ${call.name} JSON.parse failed: ${String(error)}`);
|
|
49408
|
-
}
|
|
49409
|
-
const result = executeProxyTool3(call.name, args, ctx);
|
|
49410
|
-
ctx.log(`[acp-proxy: responses JSON ${call.name} \u2192 ${result.slice(0, 120).replace(/\n/g, " ")}]`);
|
|
49411
|
-
inputItems.push({ type: "message", role: "user", content: buildVisibilityMarker(call.name, result) });
|
|
49412
|
-
}
|
|
49413
|
-
requestBody.input = inputItems;
|
|
49414
|
-
const { response, clearTimer } = await fetchWithTimeout(requestOptions.url, {
|
|
49415
|
-
method: "POST",
|
|
49416
|
-
headers: requestOptions.headers,
|
|
49417
|
-
body: JSON.stringify(requestBody),
|
|
49418
|
-
...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
|
|
49419
|
-
});
|
|
49420
|
-
try {
|
|
49421
|
-
if (!response.ok) {
|
|
49422
|
-
const detail = await response.text().catch(() => "upstream error");
|
|
49423
|
-
throw new Error(`responses compress loop upstream error ${response.status}: ${detail.slice(0, 200)}`);
|
|
49424
|
-
}
|
|
49425
|
-
current = await response.json();
|
|
49426
|
-
} finally {
|
|
49427
|
-
clearTimer();
|
|
49428
|
-
}
|
|
49429
|
-
}
|
|
49430
|
-
ctx.log("[acp-proxy: responses JSON compress loop limit (5) reached]");
|
|
49431
|
-
return current;
|
|
49432
|
-
}
|
|
49433
|
-
async function* compressLoopResponsesStream(initialUpstream, ctx, requestBody, requestOptions) {
|
|
49434
|
-
const textProtocol = ctx.textProtocol ?? TEXT_PROTOCOL;
|
|
49435
|
-
let upstream = initialUpstream;
|
|
49436
|
-
let loopCount = 0;
|
|
49437
|
-
let responseObj = null;
|
|
49438
|
-
let activeClearTimer = null;
|
|
49439
|
-
let nextOutputIndex = 0;
|
|
49440
|
-
for (; ; ) {
|
|
49441
|
-
loopCount++;
|
|
49442
|
-
if (loopCount > 5) {
|
|
49443
|
-
ctx.log("[acp-proxy: responses compress loop limit (5) reached, forwarding completion as-is]");
|
|
49444
|
-
const limItemId = `msg_acp_limit_${Date.now()}`;
|
|
49445
|
-
yield Buffer.from(buildMessageItemSequence(limItemId, nextOutputIndex++, "\n[acp-proxy: compress loop limit reached]\n"), "utf8");
|
|
49446
|
-
yield Buffer.from(buildCompleted(responseObj), "utf8");
|
|
49447
|
-
return;
|
|
49448
|
-
}
|
|
49449
|
-
const fcByItemId = /* @__PURE__ */ new Map();
|
|
49450
|
-
let contentText = "";
|
|
49451
|
-
let customToolCalls = 0;
|
|
49452
|
-
let completed = false;
|
|
49453
|
-
let terminalKind = null;
|
|
49454
|
-
let terminalRaw = null;
|
|
49455
|
-
const isFirstRound = loopCount === 1;
|
|
49456
|
-
const reader = upstream.getReader();
|
|
49457
|
-
const decoder = new TextDecoder("utf-8");
|
|
49458
|
-
let sseBuffer = "";
|
|
49459
|
-
try {
|
|
49460
|
-
for (; ; ) {
|
|
49461
|
-
const { done, value } = await reader.read();
|
|
49462
|
-
if (done) break;
|
|
49463
|
-
sseBuffer += decoder.decode(value, { stream: true });
|
|
49464
|
-
if (sseBuffer.indexOf("\r") !== -1) sseBuffer = sseBuffer.replace(/\r\n|\r/g, "\n");
|
|
49465
|
-
let sep;
|
|
49466
|
-
while ((sep = sseBuffer.indexOf("\n\n")) >= 0) {
|
|
49467
|
-
const eventStr = sseBuffer.slice(0, sep);
|
|
49468
|
-
sseBuffer = sseBuffer.slice(sep + 2);
|
|
49469
|
-
if (!eventStr.trim()) continue;
|
|
49470
|
-
const d = classifyResponsesSseEvent(eventStr);
|
|
49471
|
-
if (d.yieldChunk && (isFirstRound || !d.isMeta) && !(textProtocol && !d.isMeta && !d.noBuffer)) {
|
|
49472
|
-
yield d.yieldChunk;
|
|
49473
|
-
}
|
|
49474
|
-
if (d.contentDelta) contentText += d.contentDelta;
|
|
49475
|
-
if (d.fcStart) {
|
|
49476
|
-
fcByItemId.set(d.fcStart.itemId, {
|
|
49477
|
-
itemId: d.fcStart.itemId,
|
|
49478
|
-
callId: d.fcStart.callId,
|
|
49479
|
-
name: d.fcStart.name,
|
|
49480
|
-
arguments: ""
|
|
49481
|
-
});
|
|
49482
|
-
}
|
|
49483
|
-
if (d.fcArgs) {
|
|
49484
|
-
const existing = fcByItemId.get(d.fcArgs.itemId);
|
|
49485
|
-
if (existing) existing.arguments += d.fcArgs.delta;
|
|
49486
|
-
}
|
|
49487
|
-
if (d.fcDone) {
|
|
49488
|
-
const existing = fcByItemId.get(d.fcDone.itemId);
|
|
49489
|
-
if (existing && !existing.arguments) {
|
|
49490
|
-
const item = JSON.parse(extractDataLine(eventStr) ?? "{}").item;
|
|
49491
|
-
const args = typeof item?.arguments === "string" ? item.arguments : "";
|
|
49492
|
-
existing.arguments = args;
|
|
49493
|
-
}
|
|
49494
|
-
}
|
|
49495
|
-
if (d.customToolCallDone) customToolCalls++;
|
|
49496
|
-
if (d.terminal) {
|
|
49497
|
-
completed = true;
|
|
49498
|
-
terminalKind = d.terminalKind ?? null;
|
|
49499
|
-
terminalRaw = d.terminalRaw ?? null;
|
|
49500
|
-
responseObj = d.responseObj ?? responseObj;
|
|
49501
|
-
const resp2 = d.responseObj ?? {};
|
|
49502
|
-
const usage = resp2.usage;
|
|
49503
|
-
if (usage && d.terminalKind === "completed") {
|
|
49504
|
-
const prompt = usage.input_tokens ?? usage.prompt_tokens ?? "?";
|
|
49505
|
-
const inDet = usage.input_tokens_details;
|
|
49506
|
-
const prDet = usage.prompt_tokens_details;
|
|
49507
|
-
const cached = inDet?.cached_tokens ?? prDet?.cached_tokens ?? "?";
|
|
49508
|
-
const out = usage.output_tokens ?? "?";
|
|
49509
|
-
log("info", `[acp-usage] round ${loopCount} input=${prompt} cached=${cached} output=${out}${cached !== "?" && cached !== 0 && prompt !== "?" ? ` (cache hit ${Math.round(Number(cached) / Number(prompt) * 100)}%)` : ""}`);
|
|
49510
|
-
if (typeof prompt === "number") {
|
|
49511
|
-
ctx.session.stats.inputTokens += prompt;
|
|
49512
|
-
ctx.session.stats.lastInputTokens = prompt + (typeof cached === "number" ? cached : 0);
|
|
49513
|
-
if (typeof cached === "number") ctx.session.stats.cachedTokens += cached;
|
|
49514
|
-
if (typeof out === "number") ctx.session.stats.outputTokens += out;
|
|
49515
|
-
ctx.session.stats.cacheSamples += 1;
|
|
49516
|
-
}
|
|
49517
|
-
}
|
|
49518
|
-
}
|
|
49519
|
-
}
|
|
49520
|
-
}
|
|
49521
|
-
} finally {
|
|
49522
|
-
reader.releaseLock();
|
|
49523
|
-
if (activeClearTimer) {
|
|
49524
|
-
activeClearTimer();
|
|
49525
|
-
activeClearTimer = null;
|
|
49526
|
-
}
|
|
49527
|
-
}
|
|
49528
|
-
if (textProtocol) {
|
|
49529
|
-
const extracted = extractTextTriggers(contentText);
|
|
49530
|
-
contentText = extracted.clean;
|
|
49531
|
-
for (const c of extracted.calls) {
|
|
49532
|
-
fcByItemId.set(c.itemId, c);
|
|
49533
|
-
}
|
|
49534
|
-
if (contentText.trim()) {
|
|
49535
|
-
const textItemId = `msg_acp_text_r${loopCount}_${Date.now()}`;
|
|
49536
|
-
yield Buffer.from(buildMessageItemSequence(textItemId, nextOutputIndex++, contentText), "utf8");
|
|
49537
|
-
}
|
|
49538
|
-
}
|
|
49539
|
-
const allCalls = [...fcByItemId.values()].filter((c) => c.name.length > 0);
|
|
49540
|
-
const proxyCalls = allCalls.filter((c) => PROXY_TOOL_NAMES.has(c.name));
|
|
49541
|
-
const realCalls = allCalls.filter((c) => !PROXY_TOOL_NAMES.has(c.name));
|
|
49542
|
-
const readonlyProxy = proxyCalls.filter((c) => READONLY_PROXY_TOOLS.has(c.name));
|
|
49543
|
-
log("debug", `[acp-diag] round ${loopCount} allCalls=[${allCalls.map((c) => c.name).join(",")}] realCalls=[${realCalls.map((c) => c.name).join(",")}] customToolCalls=${customToolCalls} text=${JSON.stringify(contentText.slice(0, 120))}`);
|
|
49544
|
-
const hasMutatingOnly = proxyCalls.some((c) => MUTATING_PROXY_TOOLS.has(c.name)) && realCalls.length === 0;
|
|
49545
|
-
if (!hasMutatingOnly) {
|
|
49546
|
-
for (const fc of readonlyProxy) {
|
|
49547
|
-
let args = {};
|
|
49548
|
-
try {
|
|
49549
|
-
args = JSON.parse(fc.arguments);
|
|
49550
|
-
} catch (e) {
|
|
49551
|
-
log("warn", `[acp-compress-args] ${fc.name} JSON.parse failed: ${String(e)}`);
|
|
49552
|
-
args = {};
|
|
49553
|
-
}
|
|
49554
|
-
let result;
|
|
49555
|
-
try {
|
|
49556
|
-
result = executeProxyTool3(fc.name, args, ctx);
|
|
49557
|
-
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
49558
|
-
ctx.log(`[acp-proxy: responses ${fc.name} (read-only) (${fc.callId}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
49559
|
-
} catch (e) {
|
|
49560
|
-
result = `\u274C [ACP] ${fc.name} FAILED: ${String(e)}`;
|
|
49561
|
-
ctx.log(`[acp-proxy: responses ${fc.name} (read-only) (${fc.callId}) FAILED: ${String(e)}]`);
|
|
49562
|
-
}
|
|
49563
|
-
const markerItemId = `msg_acp_ro_${Date.now()}_${nextOutputIndex}`;
|
|
49564
|
-
yield Buffer.from(buildMessageItemSequence(markerItemId, nextOutputIndex++, buildVisibilityMarker(fc.name, result)), "utf8");
|
|
49565
|
-
}
|
|
49566
|
-
let oi2 = nextOutputIndex;
|
|
49567
|
-
for (const fc of realCalls) {
|
|
49568
|
-
yield Buffer.from(buildFunctionCallEvents(fc, oi2), "utf8");
|
|
49569
|
-
oi2++;
|
|
49570
|
-
}
|
|
49571
|
-
nextOutputIndex = oi2;
|
|
49572
|
-
if (terminalKind && terminalKind !== "completed" && terminalRaw) {
|
|
49573
|
-
yield Buffer.from(terminalRaw + "\n\n", "utf8");
|
|
49574
|
-
return;
|
|
49575
|
-
}
|
|
49576
|
-
const hasUsage = !!responseObj?.usage;
|
|
49577
|
-
const emittedReadonly = readonlyProxy.length > 0;
|
|
49578
|
-
if (contentText.length === 0 && realCalls.length === 0 && customToolCalls === 0 && !emittedReadonly && !hasUsage) {
|
|
49579
|
-
ctx.log("[acp-proxy: empty upstream response (no content/usage) \u2014 injecting response.failed for client retry]");
|
|
49580
|
-
yield Buffer.from(buildFailed(responseObj), "utf8");
|
|
49581
|
-
return;
|
|
49582
|
-
}
|
|
49583
|
-
if (!completed) {
|
|
49584
|
-
ctx.log("[acp-proxy: responses stream ended without completion]");
|
|
49585
|
-
}
|
|
49586
|
-
yield Buffer.from(buildCompleted(responseObj), "utf8");
|
|
49587
|
-
return;
|
|
49588
|
-
}
|
|
49589
|
-
const names = proxyCalls.map((c) => c.name).join(", ");
|
|
49590
|
-
ctx.log(`[acp-proxy: responses round ${loopCount} \u2014 ${proxyCalls.length} proxy call(s): ${names}]`);
|
|
49591
|
-
const inputItems = Array.isArray(requestBody.input) ? [...requestBody.input] : [];
|
|
49592
|
-
if (contentText) {
|
|
49593
|
-
inputItems.push({
|
|
49594
|
-
type: "message",
|
|
49595
|
-
role: "assistant",
|
|
49596
|
-
content: [{ type: "output_text", text: contentText }]
|
|
49597
|
-
});
|
|
49598
|
-
}
|
|
49599
|
-
if (!textProtocol) {
|
|
49600
|
-
for (const fc of proxyCalls) {
|
|
49601
|
-
inputItems.push({
|
|
49602
|
-
type: "function_call",
|
|
49603
|
-
id: fc.itemId || `fc_${Date.now()}`,
|
|
49604
|
-
call_id: fc.callId || `call_${Date.now()}`,
|
|
49605
|
-
name: fc.name,
|
|
49606
|
-
arguments: fc.arguments
|
|
49607
|
-
});
|
|
49608
|
-
}
|
|
49609
|
-
}
|
|
49610
|
-
for (const fc of proxyCalls) {
|
|
49611
|
-
let args = {};
|
|
49612
|
-
try {
|
|
49613
|
-
args = JSON.parse(fc.arguments);
|
|
49614
|
-
} catch (e) {
|
|
49615
|
-
log("warn", `[acp-compress-args] ${fc.name} JSON.parse failed: ${String(e)}. raw arguments (len=${fc.arguments.length}): ${fc.arguments.slice(0, 300)}`);
|
|
49616
|
-
args = {};
|
|
49617
|
-
}
|
|
49618
|
-
if (fc.name === "compress") {
|
|
49619
|
-
log("debug", `[acp-compress-args] compress args parsed: ${JSON.stringify(args).slice(0, 400)}`);
|
|
49620
|
-
}
|
|
49621
|
-
const result = executeProxyTool3(fc.name, args, ctx);
|
|
49622
|
-
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
49623
|
-
ctx.log(`[acp-proxy: responses ${fc.name} (${fc.callId}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
49624
|
-
const markerItemId = `msg_acp_${Date.now()}_${nextOutputIndex}`;
|
|
49625
|
-
yield Buffer.from(
|
|
49626
|
-
buildMessageItemSequence(markerItemId, nextOutputIndex++, buildVisibilityMarker(fc.name, result)),
|
|
49627
|
-
"utf8"
|
|
49628
|
-
);
|
|
49629
|
-
inputItems.push(textProtocol ? { type: "message", role: "user", content: buildVisibilityMarker(fc.name, result) } : { type: "function_call_output", call_id: fc.callId || `call_${Date.now()}`, output: result });
|
|
49630
|
-
}
|
|
49631
|
-
requestBody.input = inputItems;
|
|
49632
|
-
if (!("stream" in requestBody)) requestBody.stream = true;
|
|
49633
|
-
const { response: resp, clearTimer } = await fetchWithTimeout(requestOptions.url, {
|
|
49634
|
-
method: "POST",
|
|
49635
|
-
headers: requestOptions.headers,
|
|
49636
|
-
body: JSON.stringify(requestBody),
|
|
49637
|
-
...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
|
|
49638
|
-
});
|
|
49639
|
-
if (!resp.ok || !resp.body) {
|
|
49640
|
-
clearTimer();
|
|
49641
|
-
const errText = await resp.text().catch(() => "upstream error");
|
|
49642
|
-
ctx.log(`[acp-proxy: responses compress loop upstream error ${resp.status}: ${errText.slice(0, 200)}]`);
|
|
49643
|
-
const errItemId = `msg_acp_err_${Date.now()}`;
|
|
49644
|
-
yield Buffer.from(
|
|
49645
|
-
buildMessageItemSequence(errItemId, nextOutputIndex++, `
|
|
49646
|
-
[acp-proxy: upstream error ${resp.status}: ${errText.slice(0, 200)}]
|
|
49647
|
-
`),
|
|
49648
|
-
"utf8"
|
|
49649
|
-
);
|
|
49650
|
-
yield Buffer.from(buildCompleted(responseObj), "utf8");
|
|
49651
|
-
return;
|
|
49652
|
-
}
|
|
49653
|
-
upstream = resp.body;
|
|
49654
|
-
if (activeClearTimer) activeClearTimer();
|
|
49655
|
-
activeClearTimer = clearTimer;
|
|
49656
|
-
}
|
|
49657
|
-
}
|
|
49658
|
-
|
|
49659
|
-
// src/loop/core.ts
|
|
49660
|
-
var MAX_LOOP_ROUNDS = 10;
|
|
49661
|
-
function executeProxyTool4(toolName, args, ctx, callId) {
|
|
49662
|
-
if (toolName === "compress") {
|
|
49663
|
-
return applyRanges(parseCompressInput(args, callId), ctx);
|
|
49664
|
-
}
|
|
49665
|
-
if (toolName === "decompress") {
|
|
49666
|
-
return resolveDecompress(args, ctx);
|
|
49667
|
-
}
|
|
49668
|
-
if (toolName === "search_context") {
|
|
49669
|
-
const query = typeof args.query === "string" ? args.query : "";
|
|
49670
|
-
if (query.length === 0) return "[search_context FAILED: query is required]";
|
|
49671
|
-
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 5;
|
|
49672
|
-
const blocks = ctx.core.search(query, ctx.session.state).slice(0, limit);
|
|
49673
|
-
if (blocks.length === 0) return `[No blocks matched "${query}"]`;
|
|
49674
|
-
const lines = blocks.map((b2) => {
|
|
49675
|
-
const topic = b2.topic ?? "(no topic)";
|
|
49676
|
-
const preview = b2.summary.length > 200 ? b2.summary.slice(0, 200) + "..." : b2.summary;
|
|
49677
|
-
return `${b2.blockId} (T${b2.tier}) "${topic}"
|
|
49678
|
-
${preview}`;
|
|
49679
|
-
});
|
|
49680
|
-
return `Found ${blocks.length} block(s) for "${query}":
|
|
49681
|
-
|
|
49682
|
-
${lines.join("\n\n")}`;
|
|
49683
|
-
}
|
|
49684
|
-
if (toolName === "acp_status") {
|
|
49685
|
-
return buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast);
|
|
49686
|
-
}
|
|
49687
|
-
return `[Unknown proxy tool: ${toolName}]`;
|
|
49688
|
-
}
|
|
49689
|
-
function recordUsage(ctx, usage, round) {
|
|
49690
|
-
const prompt = usage.inputTokens;
|
|
49691
|
-
const cached = usage.cachedTokens;
|
|
49692
|
-
const out = usage.outputTokens;
|
|
49693
|
-
if (typeof prompt === "number") ctx.session.stats.inputTokens += prompt;
|
|
49694
|
-
ctx.session.stats.lastInputTokens = (typeof prompt === "number" ? prompt : 0) + (typeof cached === "number" ? cached : 0);
|
|
49695
|
-
if (typeof cached === "number") ctx.session.stats.cachedTokens += cached;
|
|
49696
|
-
if (typeof out === "number") ctx.session.stats.outputTokens += out;
|
|
49697
|
-
ctx.session.stats.cacheSamples += 1;
|
|
49698
|
-
const hitPct = typeof prompt === "number" && typeof cached === "number" && prompt + cached > 0 ? Math.round(cached / (prompt + cached) * 100) : 0;
|
|
49699
|
-
ctx.log(
|
|
49700
|
-
`[acp-usage] round ${round} input=${ctx.session.stats.lastInputTokens} cached=${cached ?? 0} (cache hit ${hitPct}%)`
|
|
49701
|
-
);
|
|
49702
|
-
}
|
|
49703
|
-
async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adapter, systemPrompt) {
|
|
48540
|
+
async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adapter, systemPrompt, signal) {
|
|
49704
48541
|
let activeClearTimer = null;
|
|
49705
48542
|
let currentUpstream = upstream;
|
|
49706
48543
|
const coreMessages = [...ctx.messages];
|
|
49707
48544
|
try {
|
|
49708
48545
|
for (let round = 1; round <= MAX_LOOP_ROUNDS; round++) {
|
|
48546
|
+
if (signal?.aborted) break;
|
|
49709
48547
|
let assistantText = "";
|
|
49710
48548
|
const calls = [];
|
|
49711
48549
|
let usage = {};
|
|
49712
48550
|
let finishReason;
|
|
49713
48551
|
for await (const ev of adapter.parseStream(currentUpstream, round)) {
|
|
48552
|
+
if (signal?.aborted) break;
|
|
49714
48553
|
if (ev.kind === "text") {
|
|
49715
48554
|
assistantText += ev.delta;
|
|
49716
48555
|
if (!ctx.textProtocol && round === 1 && ev.raw) {
|
|
@@ -49758,7 +48597,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
|
|
|
49758
48597
|
} catch {
|
|
49759
48598
|
parsedArgs = {};
|
|
49760
48599
|
}
|
|
49761
|
-
const result =
|
|
48600
|
+
const result = executeProxyTool(call.name, parsedArgs, ctx, call.callId);
|
|
49762
48601
|
proxyResults.push({ name: call.name, callId: call.callId, result, arguments: call.arguments });
|
|
49763
48602
|
yield adapter.emitMarker(call.name, result);
|
|
49764
48603
|
} else {
|
|
@@ -49791,7 +48630,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
|
|
|
49791
48630
|
if (ctx.textProtocol) {
|
|
49792
48631
|
coreMessages.push({
|
|
49793
48632
|
id: `acp_loop_r${round}_marker_${pr2.callId}`,
|
|
49794
|
-
role: "
|
|
48633
|
+
role: "system",
|
|
49795
48634
|
contentType: "text",
|
|
49796
48635
|
text: buildVisibilityMarker(pr2.name, pr2.result)
|
|
49797
48636
|
});
|
|
@@ -49837,6 +48676,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
|
|
|
49837
48676
|
return;
|
|
49838
48677
|
}
|
|
49839
48678
|
ctx.log(`[acp-loop] round ${round} saw mutating proxy tool; re-requesting`);
|
|
48679
|
+
if (signal?.aborted) break;
|
|
49840
48680
|
const newBody = adapter.buildRequest(coreMessages, systemPrompt, requestBody);
|
|
49841
48681
|
if (process.env.ACP_DUMP_REQ !== "0" && ctx.debug) {
|
|
49842
48682
|
try {
|
|
@@ -49848,12 +48688,17 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
|
|
|
49848
48688
|
} catch {
|
|
49849
48689
|
}
|
|
49850
48690
|
}
|
|
49851
|
-
const { response: resp, clearTimer } = await fetchWithTimeout(
|
|
49852
|
-
|
|
49853
|
-
|
|
49854
|
-
|
|
49855
|
-
|
|
49856
|
-
|
|
48691
|
+
const { response: resp, clearTimer } = await fetchWithTimeout(
|
|
48692
|
+
requestOptions.url,
|
|
48693
|
+
{
|
|
48694
|
+
method: "POST",
|
|
48695
|
+
headers: requestOptions.headers,
|
|
48696
|
+
body: JSON.stringify(newBody),
|
|
48697
|
+
...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
|
|
48698
|
+
},
|
|
48699
|
+
void 0,
|
|
48700
|
+
signal
|
|
48701
|
+
);
|
|
49857
48702
|
if (!resp.ok || !resp.body) {
|
|
49858
48703
|
clearTimer();
|
|
49859
48704
|
const errText = await resp.text().catch(() => "upstream error");
|
|
@@ -49902,13 +48747,13 @@ async function* iterSseEvents(stream2) {
|
|
|
49902
48747
|
reader.releaseLock();
|
|
49903
48748
|
}
|
|
49904
48749
|
}
|
|
49905
|
-
function
|
|
48750
|
+
function extractEventType(rawEvent) {
|
|
49906
48751
|
for (const l of rawEvent.split("\n")) {
|
|
49907
48752
|
if (l.startsWith("event:")) return l.slice(6).trim();
|
|
49908
48753
|
}
|
|
49909
48754
|
return null;
|
|
49910
48755
|
}
|
|
49911
|
-
function
|
|
48756
|
+
function extractDataLine(rawEvent) {
|
|
49912
48757
|
const parts = [];
|
|
49913
48758
|
for (const l of rawEvent.split("\n")) {
|
|
49914
48759
|
if (l.startsWith("data:")) {
|
|
@@ -49919,7 +48764,7 @@ function extractDataLine2(rawEvent) {
|
|
|
49919
48764
|
}
|
|
49920
48765
|
return parts.length ? parts.join("\n") : null;
|
|
49921
48766
|
}
|
|
49922
|
-
function
|
|
48767
|
+
function buildMessageItemSequence(itemId, outputIndex, text) {
|
|
49923
48768
|
const item = { type: "message", id: itemId, role: "assistant", content: [] };
|
|
49924
48769
|
const part = { type: "output_text", text: "" };
|
|
49925
48770
|
const doneItem = {
|
|
@@ -49958,7 +48803,7 @@ data: ${JSON.stringify({ type: "response.output_item.done", output_index: output
|
|
|
49958
48803
|
"utf8"
|
|
49959
48804
|
);
|
|
49960
48805
|
}
|
|
49961
|
-
function
|
|
48806
|
+
function buildFunctionCallEvents(fc, itemId, outputIndex) {
|
|
49962
48807
|
return Buffer.from(
|
|
49963
48808
|
[
|
|
49964
48809
|
`event: response.output_item.added
|
|
@@ -49997,7 +48842,7 @@ data: ${JSON.stringify({
|
|
|
49997
48842
|
"utf8"
|
|
49998
48843
|
);
|
|
49999
48844
|
}
|
|
50000
|
-
function
|
|
48845
|
+
function buildCompleted(responseObj) {
|
|
50001
48846
|
const resp = responseObj ?? { id: `resp-proxy-${Date.now()}`, status: "completed", output: [] };
|
|
50002
48847
|
return Buffer.from(
|
|
50003
48848
|
`event: response.completed
|
|
@@ -50041,8 +48886,8 @@ function createResponsesAdapter(textProtocol, projection) {
|
|
|
50041
48886
|
async *parseStream(upstream, round) {
|
|
50042
48887
|
const pending = /* @__PURE__ */ new Map();
|
|
50043
48888
|
for await (const eventStr of iterSseEvents(upstream)) {
|
|
50044
|
-
const type =
|
|
50045
|
-
const dataLine =
|
|
48889
|
+
const type = extractEventType(eventStr);
|
|
48890
|
+
const dataLine = extractDataLine(eventStr);
|
|
50046
48891
|
if (!type || !dataLine) continue;
|
|
50047
48892
|
let obj;
|
|
50048
48893
|
try {
|
|
@@ -50140,15 +48985,15 @@ function createResponsesAdapter(textProtocol, projection) {
|
|
|
50140
48985
|
}
|
|
50141
48986
|
},
|
|
50142
48987
|
emitText(delta) {
|
|
50143
|
-
return
|
|
48988
|
+
return buildMessageItemSequence(`msg-proxy-${Date.now()}-${outputIndex}`, outputIndex++, delta);
|
|
50144
48989
|
},
|
|
50145
48990
|
emitToolCall(call) {
|
|
50146
|
-
const buf =
|
|
48991
|
+
const buf = buildFunctionCallEvents(call, `fc-proxy-${Date.now()}-${outputIndex}`, outputIndex);
|
|
50147
48992
|
outputIndex += 1;
|
|
50148
48993
|
return buf;
|
|
50149
48994
|
},
|
|
50150
48995
|
emitMarker(toolName, result) {
|
|
50151
|
-
return
|
|
48996
|
+
return buildMessageItemSequence(
|
|
50152
48997
|
`marker-${Date.now()}-${outputIndex}`,
|
|
50153
48998
|
outputIndex++,
|
|
50154
48999
|
buildVisibilityMarker(toolName, result)
|
|
@@ -50187,7 +49032,7 @@ data: ${JSON.stringify({ type: "response.failed", response: failed })}
|
|
|
50187
49032
|
}
|
|
50188
49033
|
resp = { ...resp, usage };
|
|
50189
49034
|
}
|
|
50190
|
-
return
|
|
49035
|
+
return buildCompleted(resp);
|
|
50191
49036
|
},
|
|
50192
49037
|
emitError(message) {
|
|
50193
49038
|
const resp = {
|
|
@@ -50482,7 +49327,7 @@ async function* iterSseEvents2(stream2) {
|
|
|
50482
49327
|
reader.releaseLock();
|
|
50483
49328
|
}
|
|
50484
49329
|
}
|
|
50485
|
-
function
|
|
49330
|
+
function parseAnthropicSse(eventStr) {
|
|
50486
49331
|
const lines = eventStr.split("\n");
|
|
50487
49332
|
let type = "";
|
|
50488
49333
|
const dataLines = [];
|
|
@@ -50592,7 +49437,7 @@ ${systemPrompt}` : systemPrompt;
|
|
|
50592
49437
|
let usageYielded = false;
|
|
50593
49438
|
const indexMap = /* @__PURE__ */ new Map();
|
|
50594
49439
|
for await (const eventStr of iterSseEvents2(upstream)) {
|
|
50595
|
-
const parsed =
|
|
49440
|
+
const parsed = parseAnthropicSse(eventStr);
|
|
50596
49441
|
if (!parsed) continue;
|
|
50597
49442
|
const { type, data } = parsed;
|
|
50598
49443
|
const rawBuf = Buffer.from(eventStr + "\n\n", "utf8");
|
|
@@ -50720,6 +49565,184 @@ function pickAdapter(protocol, requestBody, textProtocol, responsesProjection, a
|
|
|
50720
49565
|
throw new Error(`[acp-loop] unknown protocol: ${protocol}`);
|
|
50721
49566
|
}
|
|
50722
49567
|
|
|
49568
|
+
// src/compress-loop-responses.ts
|
|
49569
|
+
function extractTextTriggers(text) {
|
|
49570
|
+
const calls = [];
|
|
49571
|
+
let clean = "";
|
|
49572
|
+
let i = 0;
|
|
49573
|
+
let n = 0;
|
|
49574
|
+
while (i < text.length) {
|
|
49575
|
+
const open = text.indexOf(ACP_TEXT_OPEN, i);
|
|
49576
|
+
if (open === -1) {
|
|
49577
|
+
clean += text.slice(i);
|
|
49578
|
+
break;
|
|
49579
|
+
}
|
|
49580
|
+
clean += text.slice(i, open);
|
|
49581
|
+
const after = open + ACP_TEXT_OPEN.length;
|
|
49582
|
+
const close = text.indexOf(ACP_TEXT_CLOSE, after);
|
|
49583
|
+
if (close === -1) {
|
|
49584
|
+
clean += text.slice(open);
|
|
49585
|
+
break;
|
|
49586
|
+
}
|
|
49587
|
+
const payload = text.slice(after, close).trim();
|
|
49588
|
+
if (payload) {
|
|
49589
|
+
const stamp = `${Date.now()}_${n++}`;
|
|
49590
|
+
calls.push({
|
|
49591
|
+
itemId: `fc_text_${stamp}`,
|
|
49592
|
+
callId: `call_text_${stamp}`,
|
|
49593
|
+
name: COMPRESS_TOOL_NAME,
|
|
49594
|
+
arguments: payload
|
|
49595
|
+
});
|
|
49596
|
+
}
|
|
49597
|
+
i = close + ACP_TEXT_CLOSE.length;
|
|
49598
|
+
}
|
|
49599
|
+
return { clean, calls };
|
|
49600
|
+
}
|
|
49601
|
+
function executeProxyTool2(toolName, args, ctx) {
|
|
49602
|
+
if (toolName === "compress") {
|
|
49603
|
+
return applyRanges(parseCompressInput(args), ctx);
|
|
49604
|
+
}
|
|
49605
|
+
if (toolName === "decompress") {
|
|
49606
|
+
return resolveDecompress(args, ctx);
|
|
49607
|
+
}
|
|
49608
|
+
if (toolName === "search_context") {
|
|
49609
|
+
const query = typeof args.query === "string" ? args.query : "";
|
|
49610
|
+
if (query.length === 0) return "[search_context FAILED: query is required]";
|
|
49611
|
+
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 5;
|
|
49612
|
+
const blocks = ctx.core.search(query, ctx.session.state).slice(0, limit);
|
|
49613
|
+
if (blocks.length === 0) return `[No blocks matched "${query}"]`;
|
|
49614
|
+
const lines = blocks.map((b2) => {
|
|
49615
|
+
const topic = b2.topic ?? "(no topic)";
|
|
49616
|
+
const preview = b2.summary.length > 200 ? b2.summary.slice(0, 200) + "..." : b2.summary;
|
|
49617
|
+
return `${b2.blockId} (T${b2.tier}) "${topic}"
|
|
49618
|
+
${preview}`;
|
|
49619
|
+
});
|
|
49620
|
+
return `Found ${blocks.length} block(s) for "${query}":
|
|
49621
|
+
|
|
49622
|
+
${lines.join("\n\n")}`;
|
|
49623
|
+
}
|
|
49624
|
+
if (toolName === "acp_status") {
|
|
49625
|
+
return buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast);
|
|
49626
|
+
}
|
|
49627
|
+
return `[Unknown proxy tool: ${toolName}]`;
|
|
49628
|
+
}
|
|
49629
|
+
function responsesJsonOutput(response) {
|
|
49630
|
+
const textParts = [];
|
|
49631
|
+
const calls = [];
|
|
49632
|
+
for (const item of Array.isArray(response.output) ? response.output : []) {
|
|
49633
|
+
if (!item || typeof item !== "object") continue;
|
|
49634
|
+
const value = item;
|
|
49635
|
+
if (value.type === "message") {
|
|
49636
|
+
for (const part of Array.isArray(value.content) ? value.content : []) {
|
|
49637
|
+
if (part && typeof part === "object" && part.type === "output_text") {
|
|
49638
|
+
textParts.push(part);
|
|
49639
|
+
}
|
|
49640
|
+
}
|
|
49641
|
+
} else if (value.type === "function_call") {
|
|
49642
|
+
calls.push({
|
|
49643
|
+
itemId: typeof value.id === "string" ? value.id : "",
|
|
49644
|
+
callId: typeof value.call_id === "string" ? value.call_id : "",
|
|
49645
|
+
name: typeof value.name === "string" ? value.name : "",
|
|
49646
|
+
arguments: typeof value.arguments === "string" ? value.arguments : ""
|
|
49647
|
+
});
|
|
49648
|
+
}
|
|
49649
|
+
}
|
|
49650
|
+
return {
|
|
49651
|
+
text: textParts.map((part) => typeof part.text === "string" ? part.text : "").join(""),
|
|
49652
|
+
textParts,
|
|
49653
|
+
calls
|
|
49654
|
+
};
|
|
49655
|
+
}
|
|
49656
|
+
function replaceResponsesJsonText(parts, text) {
|
|
49657
|
+
parts.forEach((part, index) => {
|
|
49658
|
+
part.text = index === 0 ? text : "";
|
|
49659
|
+
});
|
|
49660
|
+
}
|
|
49661
|
+
function surfaceReadonlyJson(current, proxyCalls, ctx) {
|
|
49662
|
+
const markers = [];
|
|
49663
|
+
for (const call of proxyCalls) {
|
|
49664
|
+
if (MUTATING_PROXY_TOOLS.has(call.name)) continue;
|
|
49665
|
+
let args = {};
|
|
49666
|
+
try {
|
|
49667
|
+
args = JSON.parse(call.arguments);
|
|
49668
|
+
} catch {
|
|
49669
|
+
args = {};
|
|
49670
|
+
}
|
|
49671
|
+
let result;
|
|
49672
|
+
try {
|
|
49673
|
+
result = executeProxyTool2(call.name, args, ctx);
|
|
49674
|
+
ctx.log(`[acp-proxy: responses JSON ${call.name} (read-only) \u2192 ${result.slice(0, 120).replace(/\n/g, " ")}]`);
|
|
49675
|
+
} catch (e) {
|
|
49676
|
+
result = `\u274C [ACP] ${call.name} FAILED: ${String(e)}`;
|
|
49677
|
+
ctx.log(`[acp-proxy: responses JSON ${call.name} (read-only) FAILED: ${String(e)}]`);
|
|
49678
|
+
}
|
|
49679
|
+
markers.push(buildVisibilityMarker(call.name, result));
|
|
49680
|
+
}
|
|
49681
|
+
if (markers.length === 0) return current;
|
|
49682
|
+
const out = Array.isArray(current.output) ? [...current.output] : [];
|
|
49683
|
+
out.push({ type: "message", id: `msg_acp_ro_${Date.now()}_${markers.length}`, role: "assistant", content: [{ type: "output_text", text: markers.join("\n") }] });
|
|
49684
|
+
return { ...current, output: out };
|
|
49685
|
+
}
|
|
49686
|
+
async function compressLoopResponsesJson(initialResponse, ctx, requestBody, requestOptions) {
|
|
49687
|
+
let current = initialResponse;
|
|
49688
|
+
for (let loopCount = 1; loopCount <= MAX_LOOP_ROUNDS; loopCount++) {
|
|
49689
|
+
const output = responsesJsonOutput(current);
|
|
49690
|
+
const extracted = extractTextTriggers(output.text);
|
|
49691
|
+
const allCalls = [...output.calls, ...extracted.calls].filter((call) => call.name.length > 0);
|
|
49692
|
+
const proxyCalls = allCalls.filter((call) => PROXY_TOOL_NAMES.has(call.name));
|
|
49693
|
+
const realCalls = allCalls.filter((call) => !PROXY_TOOL_NAMES.has(call.name));
|
|
49694
|
+
const mutatingProxy = proxyCalls.filter((call) => MUTATING_PROXY_TOOLS.has(call.name));
|
|
49695
|
+
if (mutatingProxy.length === 0 || realCalls.length > 0) {
|
|
49696
|
+
if (proxyCalls.length > 0) {
|
|
49697
|
+
replaceResponsesJsonText(output.textParts, extracted.clean);
|
|
49698
|
+
current = surfaceReadonlyJson(current, proxyCalls, ctx);
|
|
49699
|
+
}
|
|
49700
|
+
return current;
|
|
49701
|
+
}
|
|
49702
|
+
const inputItems = Array.isArray(requestBody.input) ? [...requestBody.input] : [];
|
|
49703
|
+
if (extracted.clean.trim()) {
|
|
49704
|
+
inputItems.push({ type: "message", role: "assistant", content: [{ type: "output_text", text: extracted.clean }] });
|
|
49705
|
+
}
|
|
49706
|
+
let mutatedThisTurn = false;
|
|
49707
|
+
for (const call of proxyCalls) {
|
|
49708
|
+
let args = {};
|
|
49709
|
+
try {
|
|
49710
|
+
args = JSON.parse(call.arguments);
|
|
49711
|
+
} catch (error) {
|
|
49712
|
+
log("warn", `[acp-compress-args] ${call.name} JSON.parse failed: ${String(error)}`);
|
|
49713
|
+
}
|
|
49714
|
+
let result;
|
|
49715
|
+
if (MUTATING_PROXY_TOOLS.has(call.name) && mutatedThisTurn) {
|
|
49716
|
+
result = `Already ${call.name}ed once this turn. Do not ${call.name} again; generate your normal response now.`;
|
|
49717
|
+
ctx.log(`[acp-proxy: responses JSON ${call.name} skipped (state already mutated this turn)]`);
|
|
49718
|
+
} else {
|
|
49719
|
+
result = executeProxyTool2(call.name, args, ctx);
|
|
49720
|
+
if (MUTATING_PROXY_TOOLS.has(call.name)) mutatedThisTurn = true;
|
|
49721
|
+
ctx.log(`[acp-proxy: responses JSON ${call.name} \u2192 ${result.slice(0, 120).replace(/\n/g, " ")}]`);
|
|
49722
|
+
}
|
|
49723
|
+
inputItems.push({ type: "message", role: "developer", content: buildVisibilityMarker(call.name, result) });
|
|
49724
|
+
}
|
|
49725
|
+
requestBody.input = inputItems;
|
|
49726
|
+
const { response, clearTimer } = await fetchWithTimeout(requestOptions.url, {
|
|
49727
|
+
method: "POST",
|
|
49728
|
+
headers: requestOptions.headers,
|
|
49729
|
+
body: JSON.stringify(requestBody),
|
|
49730
|
+
...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
|
|
49731
|
+
});
|
|
49732
|
+
try {
|
|
49733
|
+
if (!response.ok) {
|
|
49734
|
+
const detail = await response.text().catch(() => "upstream error");
|
|
49735
|
+
throw new Error(`responses compress loop upstream error ${response.status}: ${detail.slice(0, 200)}`);
|
|
49736
|
+
}
|
|
49737
|
+
current = await response.json();
|
|
49738
|
+
} finally {
|
|
49739
|
+
clearTimer();
|
|
49740
|
+
}
|
|
49741
|
+
}
|
|
49742
|
+
ctx.log(`[acp-proxy: responses JSON compress loop limit (${MAX_LOOP_ROUNDS}) reached]`);
|
|
49743
|
+
return current;
|
|
49744
|
+
}
|
|
49745
|
+
|
|
50723
49746
|
// src/stream-openai.ts
|
|
50724
49747
|
function rewriteOpenaiJsonResponse(body, ctx) {
|
|
50725
49748
|
if (!body || typeof body !== "object") return body;
|
|
@@ -50763,7 +49786,7 @@ ${note}` : note;
|
|
|
50763
49786
|
}
|
|
50764
49787
|
|
|
50765
49788
|
// src/stream-responses.ts
|
|
50766
|
-
var
|
|
49789
|
+
var TEXT_PROTOCOL = process.env.ACP_COMPRESS_PROTOCOL === "text";
|
|
50767
49790
|
function rewriteResponsesJsonResponse(body, ctx) {
|
|
50768
49791
|
if (!body || typeof body !== "object") return body;
|
|
50769
49792
|
const b2 = body;
|
|
@@ -51795,6 +50818,15 @@ var UPSTREAM_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
|
51795
50818
|
// streamed from fetch, otherwise clients try to decompress plain bytes.
|
|
51796
50819
|
"content-encoding"
|
|
51797
50820
|
]);
|
|
50821
|
+
function buildForwardHeaders(headers) {
|
|
50822
|
+
const out = {};
|
|
50823
|
+
for (const [k2, v2] of Object.entries(headers)) {
|
|
50824
|
+
if (k2.toLowerCase() === "content-length" || k2.toLowerCase() === "host") continue;
|
|
50825
|
+
out[k2] = v2;
|
|
50826
|
+
}
|
|
50827
|
+
out["content-type"] = "application/json";
|
|
50828
|
+
return out;
|
|
50829
|
+
}
|
|
51798
50830
|
function resolveUpstream(_opts, reqUrl, req) {
|
|
51799
50831
|
const mitmUpstream = readMitmUpstream(req?.socket);
|
|
51800
50832
|
if (mitmUpstream) {
|
|
@@ -52018,11 +51050,24 @@ async function handle(req, res, opts, core, config, log2) {
|
|
|
52018
51050
|
return;
|
|
52019
51051
|
}
|
|
52020
51052
|
let bodyBuffer;
|
|
51053
|
+
let urlPath;
|
|
51054
|
+
let responsesCompact;
|
|
51055
|
+
let route;
|
|
51056
|
+
let upstreamOrigin;
|
|
51057
|
+
let protocol;
|
|
52021
51058
|
try {
|
|
52022
51059
|
bodyBuffer = await readBody(req);
|
|
52023
|
-
const
|
|
52024
|
-
|
|
52025
|
-
|
|
51060
|
+
const url = req.url ?? "";
|
|
51061
|
+
urlPath = url.split("?", 2)[0];
|
|
51062
|
+
responsesCompact = urlPath.endsWith("/responses/compact");
|
|
51063
|
+
route = resolveUpstream(opts, req.url ?? "", req);
|
|
51064
|
+
upstreamOrigin = route ? route.upstream : opts.upstream;
|
|
51065
|
+
protocol = route?.explicitProtocol ?? (req.method === "POST" && bodyBuffer.length > 0 ? urlPath.endsWith("/chat/completions") ? "openai" : urlPath.endsWith("/v1/messages") || urlPath.endsWith("/messages") ? "anthropic" : urlPath.endsWith("/responses") || responsesCompact ? "responses" : null : null);
|
|
51066
|
+
if (protocol !== null && bodyBuffer.length > 0) {
|
|
51067
|
+
const decoded = await decodeRequestBody(headerValue(req, "content-encoding"), bodyBuffer, MAX_REQUEST_BYTES);
|
|
51068
|
+
bodyBuffer = decoded.body;
|
|
51069
|
+
if (decoded.decoded) delete req.headers["content-encoding"];
|
|
51070
|
+
}
|
|
52026
51071
|
} catch (err2) {
|
|
52027
51072
|
if (err2 instanceof BodyTooLargeError) {
|
|
52028
51073
|
log2("warn", `413: request body exceeds ${err2.limit} bytes`);
|
|
@@ -52035,13 +51080,7 @@ async function handle(req, res, opts, core, config, log2) {
|
|
|
52035
51080
|
res.end(JSON.stringify({ error: { type: "invalid_request", message: String(err2) } }));
|
|
52036
51081
|
return;
|
|
52037
51082
|
}
|
|
52038
|
-
const url = req.url ?? "";
|
|
52039
|
-
const urlPath = url.split("?", 2)[0];
|
|
52040
|
-
const responsesCompact = urlPath.endsWith("/responses/compact");
|
|
52041
51083
|
const countTokens = isCountTokensRequest(req.method ?? "GET", urlPath, bodyBuffer.length > 0);
|
|
52042
|
-
const route = resolveUpstream(opts, req.url ?? "", req);
|
|
52043
|
-
const upstreamOrigin = route ? route.upstream : opts.upstream;
|
|
52044
|
-
const protocol = route?.explicitProtocol ?? (req.method === "POST" && bodyBuffer.length > 0 ? urlPath.endsWith("/chat/completions") ? "openai" : urlPath.endsWith("/v1/messages") || urlPath.endsWith("/messages") ? "anthropic" : urlPath.endsWith("/responses") || responsesCompact ? "responses" : null : null);
|
|
52045
51084
|
let parsed = null;
|
|
52046
51085
|
if (protocol && bodyBuffer.length > 0) {
|
|
52047
51086
|
try {
|
|
@@ -52086,19 +51125,19 @@ async function handle(req, res, opts, core, config, log2) {
|
|
|
52086
51125
|
});
|
|
52087
51126
|
const clientLabel = responsesIdentity?.clientProvided ? responsesIdentity.value : clientConversationHeader(req.headers);
|
|
52088
51127
|
const session = getSession(sessionId, { protocol, upstreamOrigin, label: clientLabel ?? void 0 });
|
|
52089
|
-
|
|
52090
|
-
|
|
52091
|
-
|
|
52092
|
-
|
|
51128
|
+
acquireInFlight(session);
|
|
51129
|
+
try {
|
|
51130
|
+
await withSessionLock(session, async () => {
|
|
51131
|
+
prepared = countTokens ? prepareCountTokens(parsed, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(parsed, req, opts, core, reqConfig, log2, session) : protocol === "openai" ? prepareOpenai(parsed, req, opts, core, reqConfig, log2, session) : responsesCompact ? prepareResponsesCompact(bodyBuffer, parsed, session) : prepareResponses(parsed, req, opts, core, reqConfig, log2, session, responsesIdentity);
|
|
52093
51132
|
await forward(req, res, opts, prepared.body, prepared, core, reqConfig, log2, route, affinity);
|
|
52094
|
-
}
|
|
52095
|
-
|
|
52096
|
-
|
|
52097
|
-
}
|
|
51133
|
+
});
|
|
51134
|
+
} finally {
|
|
51135
|
+
releaseInFlight(session);
|
|
51136
|
+
}
|
|
52098
51137
|
}
|
|
52099
51138
|
if (!prepared) {
|
|
52100
51139
|
if (protocol === null && !opts.passthrough) {
|
|
52101
|
-
log2("warn", `unrecognized path ${url} \u2014 not a known protocol (/chat/completions, /v1/messages, /responses, /responses/compact); forwarding unchanged`);
|
|
51140
|
+
log2("warn", `unrecognized path ${req.url ?? ""} \u2014 not a known protocol (/chat/completions, /v1/messages, /responses, /responses/compact); forwarding unchanged`);
|
|
52102
51141
|
}
|
|
52103
51142
|
await forward(req, res, opts, bodyBuffer, null, core, reqConfig, log2, route, void 0);
|
|
52104
51143
|
}
|
|
@@ -52134,6 +51173,7 @@ function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
|
|
|
52134
51173
|
++session.stats.requests;
|
|
52135
51174
|
let processedMessages = [];
|
|
52136
51175
|
let originalMessages = [];
|
|
51176
|
+
let nudge;
|
|
52137
51177
|
let rebuiltMessages = parsed.messages;
|
|
52138
51178
|
let systemOut = parsed.system;
|
|
52139
51179
|
let toolsOut = parsed.tools;
|
|
@@ -52144,6 +51184,7 @@ function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
|
|
|
52144
51184
|
const tokenCount = session.stats.lastInputTokens;
|
|
52145
51185
|
const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
|
|
52146
51186
|
session.state = turn.state;
|
|
51187
|
+
nudge = turn.nudge;
|
|
52147
51188
|
session.stats.contextTokens = tokenCount;
|
|
52148
51189
|
if (!session.meta.title) {
|
|
52149
51190
|
const t = deriveTitle(msgs);
|
|
@@ -52173,7 +51214,7 @@ function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
|
|
|
52173
51214
|
}
|
|
52174
51215
|
const rebuilt = { ...parsed, messages: rebuiltMessages, system: systemOut, tools: toolsOut };
|
|
52175
51216
|
markDirty(session);
|
|
52176
|
-
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: opts.compress.injectTool };
|
|
51217
|
+
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: opts.compress.injectTool, nudge };
|
|
52177
51218
|
}
|
|
52178
51219
|
function prepareOpenai(parsed, req, opts, core, config, log2, session) {
|
|
52179
51220
|
const sessionId = session.id;
|
|
@@ -52181,6 +51222,7 @@ function prepareOpenai(parsed, req, opts, core, config, log2, session) {
|
|
|
52181
51222
|
++session.stats.requests;
|
|
52182
51223
|
let processedMessages = [];
|
|
52183
51224
|
let originalMessages = [];
|
|
51225
|
+
let nudge;
|
|
52184
51226
|
let rebuiltMessages = parsed.messages;
|
|
52185
51227
|
let toolsOut = parsed.tools;
|
|
52186
51228
|
const maxTokens = typeof parsed.max_tokens === "number" ? parsed.max_tokens : 8192;
|
|
@@ -52192,6 +51234,7 @@ function prepareOpenai(parsed, req, opts, core, config, log2, session) {
|
|
|
52192
51234
|
const tokenCount = session.stats.lastInputTokens;
|
|
52193
51235
|
const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
|
|
52194
51236
|
session.state = turn.state;
|
|
51237
|
+
nudge = turn.nudge;
|
|
52195
51238
|
session.stats.contextTokens = tokenCount;
|
|
52196
51239
|
if (!session.meta.title) {
|
|
52197
51240
|
const t = deriveTitle(msgs);
|
|
@@ -52226,7 +51269,7 @@ function prepareOpenai(parsed, req, opts, core, config, log2, session) {
|
|
|
52226
51269
|
rebuilt.stream_options = { include_usage: true };
|
|
52227
51270
|
}
|
|
52228
51271
|
markDirty(session);
|
|
52229
|
-
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, protocol: "openai", stream: stream2, compressInjected: shouldInject };
|
|
51272
|
+
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, protocol: "openai", stream: stream2, compressInjected: shouldInject, nudge };
|
|
52230
51273
|
}
|
|
52231
51274
|
function prepareResponses(parsed, req, opts, core, config, log2, session, identity) {
|
|
52232
51275
|
const sessionId = session.id;
|
|
@@ -52237,6 +51280,7 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
|
|
|
52237
51280
|
}
|
|
52238
51281
|
let processedMessages = [];
|
|
52239
51282
|
let originalMessages = [];
|
|
51283
|
+
let nudge;
|
|
52240
51284
|
let responsesProjection;
|
|
52241
51285
|
let rebuiltInput = parsed.input;
|
|
52242
51286
|
let toolsOut = parsed.tools;
|
|
@@ -52253,6 +51297,7 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
|
|
|
52253
51297
|
const tokenCount = session.stats.lastInputTokens;
|
|
52254
51298
|
const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: process.env.ACP_RENDER_NONE ? "none" : "text-only" });
|
|
52255
51299
|
session.state = turn.state;
|
|
51300
|
+
nudge = turn.nudge;
|
|
52256
51301
|
session.stats.contextTokens = tokenCount;
|
|
52257
51302
|
if (!session.meta.title) {
|
|
52258
51303
|
const t = deriveTitle(msgs);
|
|
@@ -52314,7 +51359,8 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
|
|
|
52314
51359
|
protocol: "responses",
|
|
52315
51360
|
stream: stream2,
|
|
52316
51361
|
compressInjected: shouldInject,
|
|
52317
|
-
responsesTextProtocol
|
|
51362
|
+
responsesTextProtocol,
|
|
51363
|
+
nudge
|
|
52318
51364
|
};
|
|
52319
51365
|
}
|
|
52320
51366
|
function isCountTokensRequest(method, urlPath, hasBody) {
|
|
@@ -52557,105 +51603,39 @@ async function forward(req, res, opts, body, prepared, core, config, log2, route
|
|
|
52557
51603
|
dumpRaw = dumpStreamToFile(b2, opts.dumpSse, `${Date.now()}-${prepared.session.id}-raw.sse`);
|
|
52558
51604
|
}
|
|
52559
51605
|
try {
|
|
52560
|
-
|
|
52561
|
-
|
|
52562
|
-
|
|
52563
|
-
|
|
52564
|
-
|
|
52565
|
-
|
|
52566
|
-
|
|
52567
|
-
|
|
52568
|
-
|
|
52569
|
-
|
|
52570
|
-
|
|
52571
|
-
|
|
52572
|
-
|
|
52573
|
-
|
|
52574
|
-
|
|
52575
|
-
|
|
52576
|
-
|
|
52577
|
-
|
|
52578
|
-
|
|
52579
|
-
|
|
52580
|
-
|
|
52581
|
-
|
|
52582
|
-
|
|
52583
|
-
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains <acp tag`);
|
|
52584
|
-
}
|
|
52585
|
-
}
|
|
52586
|
-
res.write(chunk);
|
|
52587
|
-
if (res.writableNeedDrain) await new Promise((r) => res.once("drain", () => r()));
|
|
52588
|
-
}
|
|
52589
|
-
res.end();
|
|
52590
|
-
} else if (prepared.protocol === "openai") {
|
|
52591
|
-
const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
52592
|
-
const reqHeaders = {};
|
|
52593
|
-
for (const [k2, v2] of Object.entries(headers)) {
|
|
52594
|
-
if (k2.toLowerCase() === "content-length" || k2.toLowerCase() === "host") continue;
|
|
52595
|
-
reqHeaders[k2] = v2;
|
|
52596
|
-
}
|
|
52597
|
-
reqHeaders["content-type"] = "application/json";
|
|
52598
|
-
const loop = compressLoopStream(
|
|
52599
|
-
streamToRead,
|
|
52600
|
-
{ core, config, messages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl },
|
|
52601
|
-
parsedReq,
|
|
52602
|
-
{ url: upstreamUrl, headers: reqHeaders }
|
|
52603
|
-
);
|
|
52604
|
-
for await (const chunk of loop) {
|
|
52605
|
-
{
|
|
52606
|
-
const s3 = chunk.toString("utf8");
|
|
52607
|
-
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
52608
|
-
log2("warn", `[${prepared.session.id}] tag echo: openai response stream contains <acp tag`);
|
|
52609
|
-
}
|
|
52610
|
-
}
|
|
52611
|
-
if (!res.write(chunk)) await new Promise((r) => res.once("drain", () => r()));
|
|
52612
|
-
}
|
|
52613
|
-
} else if (prepared.protocol === "responses") {
|
|
52614
|
-
const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
52615
|
-
const reqHeaders = {};
|
|
52616
|
-
for (const [k2, v2] of Object.entries(headers)) {
|
|
52617
|
-
if (k2.toLowerCase() === "content-length" || k2.toLowerCase() === "host") continue;
|
|
52618
|
-
reqHeaders[k2] = v2;
|
|
52619
|
-
}
|
|
52620
|
-
reqHeaders["content-type"] = "application/json";
|
|
52621
|
-
const loop = compressLoopResponsesStream(
|
|
52622
|
-
streamToRead,
|
|
52623
|
-
{ core, config, messages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, textProtocol: prepared.responsesTextProtocol },
|
|
52624
|
-
parsedReq,
|
|
52625
|
-
{ url: upstreamUrl, headers: reqHeaders }
|
|
52626
|
-
);
|
|
52627
|
-
for await (const chunk of loop) {
|
|
52628
|
-
{
|
|
52629
|
-
const s3 = chunk.toString("utf8");
|
|
52630
|
-
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
52631
|
-
log2("warn", `[${prepared.session.id}] tag echo: responses response stream contains <acp tag`);
|
|
52632
|
-
}
|
|
51606
|
+
const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
51607
|
+
const reqHeaders = buildForwardHeaders(headers);
|
|
51608
|
+
const textProtocol = prepared.protocol === "responses" && !!prepared.responsesTextProtocol;
|
|
51609
|
+
const systemPrompt = textProtocol ? buildCompressTextSystemPrompt() : buildCompressSystemPrompt();
|
|
51610
|
+
const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem);
|
|
51611
|
+
const abortCtrl = new AbortController();
|
|
51612
|
+
req.on("close", () => {
|
|
51613
|
+
if (!res.writableEnded) abortCtrl.abort();
|
|
51614
|
+
});
|
|
51615
|
+
const loop = runCompressLoop(
|
|
51616
|
+
streamToRead,
|
|
51617
|
+
{ core, config, messages: prepared.processedMessages.length > 0 ? prepared.processedMessages : prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, textProtocol, debug: opts.debug, nudge: prepared.nudge },
|
|
51618
|
+
parsedReq,
|
|
51619
|
+
{ url: upstreamUrl, headers: reqHeaders },
|
|
51620
|
+
adapter,
|
|
51621
|
+
systemPrompt,
|
|
51622
|
+
abortCtrl.signal
|
|
51623
|
+
);
|
|
51624
|
+
for await (const chunk of loop) {
|
|
51625
|
+
{
|
|
51626
|
+
const s3 = chunk.toString("utf8");
|
|
51627
|
+
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
51628
|
+
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains <acp tag`);
|
|
52633
51629
|
}
|
|
52634
|
-
if (!res.write(chunk)) await new Promise((r) => res.once("drain", () => r()));
|
|
52635
51630
|
}
|
|
52636
|
-
|
|
52637
|
-
|
|
52638
|
-
|
|
52639
|
-
|
|
52640
|
-
|
|
52641
|
-
|
|
52642
|
-
}
|
|
52643
|
-
reqHeaders["content-type"] = "application/json";
|
|
52644
|
-
const loop = compressLoopAnthropicStream(
|
|
52645
|
-
streamToRead,
|
|
52646
|
-
{ core, config, messages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl },
|
|
52647
|
-
parsedReq,
|
|
52648
|
-
{ url: upstreamUrl, headers: reqHeaders }
|
|
52649
|
-
);
|
|
52650
|
-
for await (const chunk of loop) {
|
|
52651
|
-
{
|
|
52652
|
-
const s3 = chunk.toString("utf8");
|
|
52653
|
-
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
52654
|
-
log2("warn", `[${prepared.session.id}] tag echo: anthropic response stream contains <acp tag`);
|
|
52655
|
-
}
|
|
52656
|
-
}
|
|
52657
|
-
if (!res.write(chunk)) await new Promise((r) => res.once("drain", () => r()));
|
|
51631
|
+
res.write(chunk);
|
|
51632
|
+
if (res.writableNeedDrain) {
|
|
51633
|
+
await Promise.race([
|
|
51634
|
+
new Promise((r) => res.once("drain", () => r())),
|
|
51635
|
+
new Promise((r) => res.once("close", () => r()))
|
|
51636
|
+
]);
|
|
52658
51637
|
}
|
|
51638
|
+
if (res.destroyed || res.writableEnded) break;
|
|
52659
51639
|
}
|
|
52660
51640
|
res.end();
|
|
52661
51641
|
} catch (e) {
|
|
@@ -52672,12 +51652,7 @@ async function forward(req, res, opts, body, prepared, core, config, log2, route
|
|
|
52672
51652
|
let json = JSON.parse(text);
|
|
52673
51653
|
if (prepared.protocol === "responses" && prepared.responsesTextProtocol) {
|
|
52674
51654
|
const requestBody = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
52675
|
-
const requestHeaders =
|
|
52676
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
52677
|
-
if (key.toLowerCase() === "content-length" || key.toLowerCase() === "host") continue;
|
|
52678
|
-
requestHeaders[key] = value;
|
|
52679
|
-
}
|
|
52680
|
-
requestHeaders["content-type"] = "application/json";
|
|
51655
|
+
const requestHeaders = buildForwardHeaders(headers);
|
|
52681
51656
|
json = await compressLoopResponsesJson(
|
|
52682
51657
|
json,
|
|
52683
51658
|
{ core, config, messages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, textProtocol: true },
|
|
@@ -52814,6 +51789,7 @@ function readBody(req) {
|
|
|
52814
51789
|
size += c.length;
|
|
52815
51790
|
if (size > MAX_REQUEST_BYTES) {
|
|
52816
51791
|
aborted = true;
|
|
51792
|
+
req.destroy();
|
|
52817
51793
|
reject(new BodyTooLargeError(MAX_REQUEST_BYTES));
|
|
52818
51794
|
return;
|
|
52819
51795
|
}
|