billion-context 0.1.65 → 0.1.66-pr.376.226
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 +213 -72
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -46551,9 +46551,9 @@ function parseObjectValue(value, callId, diag) {
|
|
|
46551
46551
|
const content = value["content"];
|
|
46552
46552
|
if (content === void 0) {
|
|
46553
46553
|
const single = validateEntry(value, callId);
|
|
46554
|
-
if (single) {
|
|
46554
|
+
if ("range" in single) {
|
|
46555
46555
|
diag.kind = "ok";
|
|
46556
|
-
return finish([single], diag);
|
|
46556
|
+
return finish([single.range], diag);
|
|
46557
46557
|
}
|
|
46558
46558
|
diag.kind = "missing-content";
|
|
46559
46559
|
return finish([], diag);
|
|
@@ -46574,7 +46574,7 @@ function parseObjectValue(value, callId, diag) {
|
|
|
46574
46574
|
diag.kind = "content-not-array";
|
|
46575
46575
|
return finish([], diag);
|
|
46576
46576
|
}
|
|
46577
|
-
const { ranges, invalid } = validateEntries(entries, callId);
|
|
46577
|
+
const { ranges, invalid, reasons } = validateEntries(entries, callId);
|
|
46578
46578
|
const topTopic = stringOr(value["topic"]);
|
|
46579
46579
|
const topMaxChars = value["summaryMaxChars"];
|
|
46580
46580
|
const hasTopMaxChars = typeof topMaxChars === "number" && Number.isFinite(topMaxChars);
|
|
@@ -46585,6 +46585,7 @@ function parseObjectValue(value, callId, diag) {
|
|
|
46585
46585
|
}
|
|
46586
46586
|
}
|
|
46587
46587
|
diag.invalidItems = invalid;
|
|
46588
|
+
if (reasons.length > 0) diag.invalidReasons = reasons;
|
|
46588
46589
|
diag.kind = salvaged ? "truncated" : ranges.length > 0 ? "ok" : "no-valid-ranges";
|
|
46589
46590
|
return finish(ranges, diag);
|
|
46590
46591
|
}
|
|
@@ -46608,36 +46609,43 @@ function finish(ranges, diag) {
|
|
|
46608
46609
|
return { ranges, diagnostics: diag };
|
|
46609
46610
|
}
|
|
46610
46611
|
function finishSalvage(entries, callId, diag, truncatedShape) {
|
|
46611
|
-
const { ranges, invalid } = validateEntries(entries, callId);
|
|
46612
|
+
const { ranges, invalid, reasons } = validateEntries(entries, callId);
|
|
46612
46613
|
diag.invalidItems = invalid;
|
|
46614
|
+
if (reasons.length > 0) diag.invalidReasons = reasons;
|
|
46613
46615
|
diag.kind = entries.length > 0 || truncatedShape ? "truncated" : "malformed-json";
|
|
46614
46616
|
return finish(ranges, diag);
|
|
46615
46617
|
}
|
|
46616
46618
|
function validateEntries(entries, callId) {
|
|
46617
46619
|
const ranges = [];
|
|
46620
|
+
const reasons = [];
|
|
46618
46621
|
let invalid = 0;
|
|
46619
|
-
for (
|
|
46620
|
-
const
|
|
46621
|
-
if (range
|
|
46622
|
-
else
|
|
46622
|
+
for (let i = 0; i < entries.length; i++) {
|
|
46623
|
+
const outcome = validateEntry(entries[i], callId);
|
|
46624
|
+
if ("range" in outcome) ranges.push(outcome.range);
|
|
46625
|
+
else {
|
|
46626
|
+
invalid++;
|
|
46627
|
+
reasons.push(`entry ${i}: ${outcome.reason}`);
|
|
46628
|
+
}
|
|
46623
46629
|
}
|
|
46624
|
-
return { ranges, invalid };
|
|
46630
|
+
return { ranges, invalid, reasons };
|
|
46625
46631
|
}
|
|
46626
46632
|
function validateEntry(entry, callId) {
|
|
46627
|
-
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return
|
|
46633
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return { reason: "not an object" };
|
|
46628
46634
|
const e = entry;
|
|
46629
46635
|
const start = stringOr(e["startRef"]) ?? stringOr(e["startId"]) ?? stringOr(e["messageId"]);
|
|
46630
46636
|
const end = stringOr(e["endRef"]) ?? stringOr(e["endId"]) ?? stringOr(e["messageId"]);
|
|
46631
|
-
if (start === void 0 || end === void 0)
|
|
46637
|
+
if (start === void 0 || end === void 0) {
|
|
46638
|
+
return { reason: "missing range bounds (need startRef/startId and endRef/endId)" };
|
|
46639
|
+
}
|
|
46632
46640
|
const summary = stringOr(e["summary"]);
|
|
46633
|
-
if (summary === void 0) return
|
|
46641
|
+
if (summary === void 0) return { reason: "missing summary" };
|
|
46634
46642
|
const range = { startRef: start, endRef: end, summary };
|
|
46635
46643
|
const topic = stringOr(e["topic"]);
|
|
46636
46644
|
if (topic !== void 0) range.topic = topic;
|
|
46637
46645
|
const maxChars = e["summaryMaxChars"];
|
|
46638
46646
|
if (typeof maxChars === "number" && Number.isFinite(maxChars)) range.summaryMaxChars = maxChars;
|
|
46639
46647
|
if (callId !== void 0) range.compressCallId = callId;
|
|
46640
|
-
return range;
|
|
46648
|
+
return { range };
|
|
46641
46649
|
}
|
|
46642
46650
|
function stringOr(value) {
|
|
46643
46651
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -49260,11 +49268,16 @@ var StateStore = class {
|
|
|
49260
49268
|
legacyFn;
|
|
49261
49269
|
relPathFn;
|
|
49262
49270
|
validateFn;
|
|
49271
|
+
retryAttempts;
|
|
49272
|
+
retryBaseMs;
|
|
49273
|
+
retryMaxMs;
|
|
49263
49274
|
timers = /* @__PURE__ */ new Map();
|
|
49264
49275
|
pending = /* @__PURE__ */ new Map();
|
|
49265
49276
|
writeChains = /* @__PURE__ */ new Map();
|
|
49266
49277
|
/** id → absolute path, populated by writes and loadAll. */
|
|
49267
49278
|
discovered = /* @__PURE__ */ new Map();
|
|
49279
|
+
/** id → cumulative write-failure count, for rate-limited alerting. */
|
|
49280
|
+
failCounts = /* @__PURE__ */ new Map();
|
|
49268
49281
|
/** Monotonic counter for unique temp filenames within a process. */
|
|
49269
49282
|
tmpSeq = 0;
|
|
49270
49283
|
constructor(opts) {
|
|
@@ -49277,6 +49290,9 @@ var StateStore = class {
|
|
|
49277
49290
|
this.relPathFn = opts.relPath;
|
|
49278
49291
|
this.legacyFn = opts.legacy;
|
|
49279
49292
|
this.validateFn = opts.validate ?? defaultValidate;
|
|
49293
|
+
this.retryAttempts = Math.max(1, opts.retryAttempts ?? 6);
|
|
49294
|
+
this.retryBaseMs = Math.max(1, opts.retryBaseMs ?? 50);
|
|
49295
|
+
this.retryMaxMs = Math.max(this.retryBaseMs, opts.retryMaxMs ?? 1600);
|
|
49280
49296
|
}
|
|
49281
49297
|
/** Debounced save. Coalesces bursts; the builder runs at write time, so
|
|
49282
49298
|
* the freshest state is always persisted. Never throws. */
|
|
@@ -49336,32 +49352,42 @@ var StateStore = class {
|
|
|
49336
49352
|
} catch (e) {
|
|
49337
49353
|
this.log("warn", `[persist] could not create dir ${path4.dirname(file)}: ${errText(e)}`);
|
|
49338
49354
|
}
|
|
49339
|
-
|
|
49340
|
-
|
|
49341
|
-
|
|
49342
|
-
|
|
49343
|
-
|
|
49355
|
+
let lastErr;
|
|
49356
|
+
for (let attempt = 0; attempt < this.retryAttempts; attempt++) {
|
|
49357
|
+
const tmp = this.tempPath(file);
|
|
49358
|
+
try {
|
|
49359
|
+
fs.writeFileSync(tmp, data, "utf8");
|
|
49360
|
+
fs.renameSync(tmp, file);
|
|
49361
|
+
lastErr = void 0;
|
|
49362
|
+
break;
|
|
49363
|
+
} catch (e) {
|
|
49364
|
+
lastErr = e;
|
|
49344
49365
|
try {
|
|
49345
|
-
fs.
|
|
49346
|
-
|
|
49347
|
-
break;
|
|
49348
|
-
} catch (e) {
|
|
49349
|
-
lastErr = e;
|
|
49350
|
-
const code = e.code;
|
|
49351
|
-
if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES") break;
|
|
49352
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20 * (attempt + 1));
|
|
49366
|
+
fs.unlinkSync(tmp);
|
|
49367
|
+
} catch {
|
|
49353
49368
|
}
|
|
49369
|
+
const code = e.code;
|
|
49370
|
+
if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES") break;
|
|
49371
|
+
if (attempt === this.retryAttempts - 1) break;
|
|
49372
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, this.backoffMs(attempt));
|
|
49354
49373
|
}
|
|
49355
|
-
|
|
49374
|
+
}
|
|
49375
|
+
if (!lastErr) {
|
|
49376
|
+
this.discovered.set(id, file);
|
|
49377
|
+
this.clearFailure(id);
|
|
49378
|
+
this.removeSpillSync(file);
|
|
49356
49379
|
return true;
|
|
49357
|
-
} catch (e) {
|
|
49358
|
-
this.log("error", `[persist] flushSync failed for ${id}: ${errText(e)}`);
|
|
49359
|
-
try {
|
|
49360
|
-
fs.unlinkSync(tmp);
|
|
49361
|
-
} catch {
|
|
49362
|
-
}
|
|
49363
|
-
return false;
|
|
49364
49380
|
}
|
|
49381
|
+
let spillPath = null;
|
|
49382
|
+
try {
|
|
49383
|
+
const spill = this.spillPathFor(file);
|
|
49384
|
+
fs.writeFileSync(spill, data, "utf8");
|
|
49385
|
+
spillPath = spill;
|
|
49386
|
+
this.discovered.set(id, spill);
|
|
49387
|
+
} catch {
|
|
49388
|
+
}
|
|
49389
|
+
this.recordFailure(id, lastErr, spillPath);
|
|
49390
|
+
return spillPath !== null;
|
|
49365
49391
|
}
|
|
49366
49392
|
/** Load one record. Checks the discovered path (from a prior
|
|
49367
49393
|
* write/loadAll), an optional relative-path hint, and the flat default
|
|
@@ -49395,13 +49421,27 @@ var StateStore = class {
|
|
|
49395
49421
|
for (const file of files) {
|
|
49396
49422
|
const envelope = this.readEnvelope(file);
|
|
49397
49423
|
if (!envelope) continue;
|
|
49398
|
-
const
|
|
49399
|
-
|
|
49424
|
+
const base = path4.basename(file);
|
|
49425
|
+
const relBase = path4.basename(this.relPathOf(envelope.id, envelope.payload));
|
|
49426
|
+
const flatBase = flatFileNameFor(envelope.id);
|
|
49427
|
+
let owner = null;
|
|
49428
|
+
if (base === relBase || base === flatBase) {
|
|
49429
|
+
owner = envelope.id;
|
|
49430
|
+
} else if (base.endsWith(".fb.json")) {
|
|
49431
|
+
const canonicalBase = `${base.slice(0, -".fb.json".length)}.json`;
|
|
49432
|
+
if (canonicalBase === relBase || canonicalBase === flatBase) {
|
|
49433
|
+
owner = envelope.id;
|
|
49434
|
+
}
|
|
49435
|
+
}
|
|
49436
|
+
if (owner === null) {
|
|
49400
49437
|
this.log("warn", `[persist] skipping ${rel(file, this.dir)}: filename does not match record id`);
|
|
49401
49438
|
continue;
|
|
49402
49439
|
}
|
|
49403
|
-
|
|
49404
|
-
|
|
49440
|
+
const existing = out.get(owner);
|
|
49441
|
+
if (!existing || envelope.savedAt >= existing.savedAt) {
|
|
49442
|
+
out.set(owner, envelope);
|
|
49443
|
+
this.discovered.set(owner, file);
|
|
49444
|
+
}
|
|
49405
49445
|
}
|
|
49406
49446
|
return out;
|
|
49407
49447
|
}
|
|
@@ -49455,14 +49495,25 @@ var StateStore = class {
|
|
|
49455
49495
|
this.log("warn", `[persist] could not create dir ${path4.dirname(file)}: ${errText(e)}`);
|
|
49456
49496
|
}
|
|
49457
49497
|
const tmp = this.tempPath(file);
|
|
49498
|
+
const env = this.envelope(id, payload);
|
|
49458
49499
|
try {
|
|
49459
|
-
await fsp.writeFile(tmp, JSON.stringify(
|
|
49460
|
-
await renameWithRetry(tmp, file);
|
|
49500
|
+
await fsp.writeFile(tmp, JSON.stringify(env), "utf8");
|
|
49501
|
+
await this.renameWithRetry(tmp, file);
|
|
49461
49502
|
this.discovered.set(id, file);
|
|
49503
|
+
this.clearFailure(id);
|
|
49504
|
+
await this.removeSpill(file);
|
|
49462
49505
|
} catch (e) {
|
|
49463
49506
|
await fsp.unlink(tmp).catch(() => {
|
|
49464
49507
|
});
|
|
49465
|
-
|
|
49508
|
+
let spillPath = null;
|
|
49509
|
+
try {
|
|
49510
|
+
const spill = this.spillPathFor(file);
|
|
49511
|
+
await fsp.writeFile(spill, JSON.stringify(env), "utf8");
|
|
49512
|
+
spillPath = spill;
|
|
49513
|
+
this.discovered.set(id, spill);
|
|
49514
|
+
} catch {
|
|
49515
|
+
}
|
|
49516
|
+
this.recordFailure(id, e, spillPath);
|
|
49466
49517
|
throw e;
|
|
49467
49518
|
}
|
|
49468
49519
|
}
|
|
@@ -49490,6 +49541,67 @@ var StateStore = class {
|
|
|
49490
49541
|
const seq = this.tmpSeq++;
|
|
49491
49542
|
return path4.join(path4.dirname(dest), `.tmp-${path4.basename(dest, ".json")}-${process.pid}-${seq}`);
|
|
49492
49543
|
}
|
|
49544
|
+
backoffMs(attempt) {
|
|
49545
|
+
return Math.min(this.retryBaseMs * 2 ** attempt, this.retryMaxMs);
|
|
49546
|
+
}
|
|
49547
|
+
/** Side file for a record whose canonical write keeps failing: the
|
|
49548
|
+
* canonical name with a `.fb` (fallback) infix, e.g. `a.json` →
|
|
49549
|
+
* `a.fb.json`. One slot per id, overwritten on each spill, so a stuck
|
|
49550
|
+
* lock never accumulates files. Ends in `.json` so loadAll discovers it. */
|
|
49551
|
+
spillPathFor(file) {
|
|
49552
|
+
const base = path4.basename(file);
|
|
49553
|
+
const dot = base.lastIndexOf(".");
|
|
49554
|
+
const stem2 = dot > 0 ? base.slice(0, dot) : base;
|
|
49555
|
+
const ext = dot > 0 ? base.slice(dot) : ".json";
|
|
49556
|
+
return path4.join(path4.dirname(file), `${stem2}.fb${ext}`);
|
|
49557
|
+
}
|
|
49558
|
+
/** Remove a stale spill after a successful canonical write (best-effort). */
|
|
49559
|
+
async removeSpill(file) {
|
|
49560
|
+
await fsp.unlink(this.spillPathFor(file)).catch(() => {
|
|
49561
|
+
});
|
|
49562
|
+
}
|
|
49563
|
+
removeSpillSync(file) {
|
|
49564
|
+
try {
|
|
49565
|
+
fs.unlinkSync(this.spillPathFor(file));
|
|
49566
|
+
} catch {
|
|
49567
|
+
}
|
|
49568
|
+
}
|
|
49569
|
+
/** Rate-limited failure alerting: log on the first failure and at each
|
|
49570
|
+
* power-of-two count (1,2,4,8,…), so a long lock yields ~log2(N) lines
|
|
49571
|
+
* instead of one per write. Includes the spill path so the operator can
|
|
49572
|
+
* see where the data landed. */
|
|
49573
|
+
recordFailure(id, err2, spillPath) {
|
|
49574
|
+
const count = (this.failCounts.get(id) ?? 0) + 1;
|
|
49575
|
+
this.failCounts.set(id, count);
|
|
49576
|
+
const alertAt = count === 1 || count >= 2 && (count & count - 1) === 0;
|
|
49577
|
+
if (!alertAt) return;
|
|
49578
|
+
const where = spillPath ? `; data spilled to ${rel(spillPath, this.dir)}` : "; SPILLOVER ALSO FAILED \u2014 data at risk";
|
|
49579
|
+
this.log(count === 1 ? "error" : "warn", `[persist] write failed for ${id} (total ${count}x): ${errText(err2)}${where}`);
|
|
49580
|
+
}
|
|
49581
|
+
clearFailure(id) {
|
|
49582
|
+
this.failCounts.delete(id);
|
|
49583
|
+
}
|
|
49584
|
+
/** fs.rename with exponential-backoff retries on Windows transient locks
|
|
49585
|
+
* (EPERM/EBUSY/EACCES from AV scan, search indexer, SMB). Other errors
|
|
49586
|
+
* are real and rethrown immediately. */
|
|
49587
|
+
async renameWithRetry(src, dest) {
|
|
49588
|
+
let lastErr;
|
|
49589
|
+
for (let attempt = 0; attempt < this.retryAttempts; attempt++) {
|
|
49590
|
+
try {
|
|
49591
|
+
await fsp.rename(src, dest);
|
|
49592
|
+
return;
|
|
49593
|
+
} catch (e) {
|
|
49594
|
+
lastErr = e;
|
|
49595
|
+
const code = e.code;
|
|
49596
|
+
if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES") throw e;
|
|
49597
|
+
if (attempt === this.retryAttempts - 1) throw e;
|
|
49598
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
49599
|
+
setTimeout(resolve, this.backoffMs(attempt));
|
|
49600
|
+
await promise;
|
|
49601
|
+
}
|
|
49602
|
+
}
|
|
49603
|
+
throw lastErr;
|
|
49604
|
+
}
|
|
49493
49605
|
/** Parse + validate one file. Corrupt or invalid records return null
|
|
49494
49606
|
* (logged) instead of throwing — load paths must never block boot. */
|
|
49495
49607
|
readEnvelope(file) {
|
|
@@ -49565,23 +49677,6 @@ function isEnvelopeLike(value) {
|
|
|
49565
49677
|
function flatFileNameFor(id) {
|
|
49566
49678
|
return createHash2("sha256").update(id, "utf8").digest("hex").slice(0, 24) + ".json";
|
|
49567
49679
|
}
|
|
49568
|
-
async function renameWithRetry(src, dest) {
|
|
49569
|
-
let lastErr;
|
|
49570
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
49571
|
-
try {
|
|
49572
|
-
await fsp.rename(src, dest);
|
|
49573
|
-
return;
|
|
49574
|
-
} catch (e) {
|
|
49575
|
-
lastErr = e;
|
|
49576
|
-
const code = e.code;
|
|
49577
|
-
if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES") throw e;
|
|
49578
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
49579
|
-
setTimeout(resolve, 20 * (attempt + 1));
|
|
49580
|
-
await promise;
|
|
49581
|
-
}
|
|
49582
|
-
}
|
|
49583
|
-
throw lastErr;
|
|
49584
|
-
}
|
|
49585
49680
|
function errText(e) {
|
|
49586
49681
|
return e instanceof Error ? e.message : String(e);
|
|
49587
49682
|
}
|
|
@@ -50005,9 +50100,9 @@ async function flushAllSessions() {
|
|
|
50005
50100
|
function parseCompressInput(input, callId) {
|
|
50006
50101
|
const { ranges, diagnostics } = parseCompressArgs(input, { callId });
|
|
50007
50102
|
if (!diagnostics.ok && diagnostics.kind !== "ok") {
|
|
50008
|
-
log("warn", `[acp-compress-input] rejected: kind=${diagnostics.kind} invalidItems=${diagnostics.invalidItems}${diagnostics.keys ? ` keys=[${diagnostics.keys.join(",")}]` : ""}${diagnostics.length !== void 0 ? ` len=${diagnostics.length}` : ""}`);
|
|
50103
|
+
log("warn", `[acp-compress-input] rejected: kind=${diagnostics.kind} invalidItems=${diagnostics.invalidItems}${diagnostics.keys ? ` keys=[${diagnostics.keys.join(",")}]` : ""}${diagnostics.length !== void 0 ? ` len=${diagnostics.length}` : ""}${diagnostics.invalidReasons && diagnostics.invalidReasons.length > 0 ? ` reasons=[${diagnostics.invalidReasons.join(" | ")}]` : ""}`);
|
|
50009
50104
|
}
|
|
50010
|
-
return ranges;
|
|
50105
|
+
return { ranges, diagnostics };
|
|
50011
50106
|
}
|
|
50012
50107
|
var STAGED_COMPRESS_GUIDANCE = "\n\n[Smooth-transition guidance: when you compress, prefer a SMALLER, TAIL-biased range \u2014 compress the most recent large content and keep the stable prefix (the earliest messages) intact. A large single rewrite changes the request shape sharply and can trip provider risk-control; smaller tail-biased folds keep the prefix cache alive and the transition gentle.]";
|
|
50013
50108
|
function withStagedCompressGuidance(text) {
|
|
@@ -50103,17 +50198,23 @@ function normalizeSseLineEndings(buf) {
|
|
|
50103
50198
|
var PAIRED = /\x3cacp\s[^<>]{0,256}>([^<>]{0,64})<\/acp>/;
|
|
50104
50199
|
var LONE_OPEN = /\x3cacp(?:\s[^<>]{0,256})?>/;
|
|
50105
50200
|
var LONE_CLOSE = /<\/acp(?=[\s>])[^<>]{0,32}>/;
|
|
50106
|
-
var PARTIAL_TAIL = /(\x3cacp\s[^<>]
|
|
50201
|
+
var PARTIAL_TAIL = /(\x3cacp\s[^<>]*|\x3c\/acp(?:\s[^<>]*)?|<\/?a?c?p?)$/;
|
|
50107
50202
|
var TRUNC_OPEN = /\x3cacp\s[^<>]*$/;
|
|
50203
|
+
var TRUNC_CLOSE = /\x3c\/acp(?:\s[^<>]*)?$/;
|
|
50108
50204
|
var CLOSE_TAG = "</acp";
|
|
50109
50205
|
var HOLD_LIMIT = 128;
|
|
50110
50206
|
var TAG_OPEN_CAP = 4096;
|
|
50111
50207
|
var SWALLOW_CAP = 80;
|
|
50112
50208
|
function stripAcpTags(text) {
|
|
50113
|
-
return text.replace(new RegExp(PAIRED.source, "g"), "").replace(new RegExp(LONE_OPEN.source, "g"), "").replace(new RegExp(LONE_CLOSE.source, "g"), "").replace(new RegExp(TRUNC_OPEN.source), "");
|
|
50209
|
+
return text.replace(new RegExp(PAIRED.source, "g"), "").replace(new RegExp(LONE_OPEN.source, "g"), "").replace(new RegExp(LONE_CLOSE.source, "g"), "").replace(new RegExp(TRUNC_OPEN.source), "").replace(new RegExp(TRUNC_CLOSE.source), "");
|
|
50114
50210
|
}
|
|
50211
|
+
var RENDER_TAG_DETECT = /\x3c\/?acp(?=[\s>])|\\u003c\/?acp(?=[\s>\\])/;
|
|
50115
50212
|
function containsRenderTagText(s3) {
|
|
50116
|
-
return
|
|
50213
|
+
return RENDER_TAG_DETECT.test(s3);
|
|
50214
|
+
}
|
|
50215
|
+
var TOOL_CALL_XML = /\x3c\/?(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b[^<>]*\x3e|\\u003c\/?(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b|\x3c\/?antml:[a-z_]+/i;
|
|
50216
|
+
function containsToolCallXmlFragment(s3) {
|
|
50217
|
+
return TOOL_CALL_XML.test(s3);
|
|
50117
50218
|
}
|
|
50118
50219
|
function createTagEchoFilter(onDrop) {
|
|
50119
50220
|
let held = "";
|
|
@@ -50162,7 +50263,7 @@ function createTagEchoFilter(onDrop) {
|
|
|
50162
50263
|
if (!m2) {
|
|
50163
50264
|
const t = PARTIAL_TAIL.exec(buf);
|
|
50164
50265
|
if (t) {
|
|
50165
|
-
const definite = /^\x3cacp\s/.test(t[0]);
|
|
50266
|
+
const definite = /^\x3cacp\s/.test(t[0]) || /^\x3c\/acp/.test(t[0]);
|
|
50166
50267
|
const cap = definite ? TAG_OPEN_CAP : HOLD_LIMIT;
|
|
50167
50268
|
if (t[0].length <= cap) {
|
|
50168
50269
|
held = t[0];
|
|
@@ -50209,6 +50310,11 @@ function createTagEchoFilter(onDrop) {
|
|
|
50209
50310
|
drop(t[0]);
|
|
50210
50311
|
return rest.slice(0, t.index);
|
|
50211
50312
|
}
|
|
50313
|
+
const tc = new RegExp(TRUNC_CLOSE.source).exec(rest);
|
|
50314
|
+
if (tc) {
|
|
50315
|
+
drop(tc[0]);
|
|
50316
|
+
return rest.slice(0, tc.index);
|
|
50317
|
+
}
|
|
50212
50318
|
return rest;
|
|
50213
50319
|
},
|
|
50214
50320
|
dropped() {
|
|
@@ -50291,10 +50397,15 @@ ${lines.join("\n\n")}`;
|
|
|
50291
50397
|
function refNum2(ref) {
|
|
50292
50398
|
return Number(ref.replace(/\D/g, "")) || 0;
|
|
50293
50399
|
}
|
|
50294
|
-
function applyRanges(
|
|
50400
|
+
function applyRanges(parsed, ctx) {
|
|
50401
|
+
const { ranges, diagnostics } = parsed;
|
|
50295
50402
|
if (ranges.length === 0) {
|
|
50296
50403
|
ctx.log("[acp-proxy: compress call had no valid ranges; nothing compressed.]");
|
|
50297
|
-
|
|
50404
|
+
const reasons = diagnostics.invalidReasons?.slice(0, 8).map((r) => r.length > 200 ? r.slice(0, 200) + "..." : r) ?? [];
|
|
50405
|
+
const why = reasons.length > 0 ? ` Rejected entries:
|
|
50406
|
+
${reasons.map((r) => `- ${r}`).join("\n")}` : "";
|
|
50407
|
+
return `[Compression FAILED: no valid ranges parsed (kind=${diagnostics.kind}, dropped=${diagnostics.invalidItems}).${why}
|
|
50408
|
+
compress requires a non-empty 'content' array of {startId, endId, summary} ranges, where startId/endId are mNNNNN message refs from the conversation. Re-issue the compress call with a valid content array.]`;
|
|
50298
50409
|
}
|
|
50299
50410
|
ctx.log(`[acp-proxy: compress requested ${ranges.length} range(s): ${ranges.map((r) => `${r.startRef}\u2013${r.endRef}`).join(", ")}]`);
|
|
50300
50411
|
ctx.log(`[acp-proxy: ctx has ${ctx.messages.length} message(s), state has ${ctx.session.state.messageRefs?.byRef?.size ?? "?"} ref(s) mapped]`);
|
|
@@ -55420,6 +55531,28 @@ async function decodeRequestBody(contentEncoding, input, maxOutputBytes) {
|
|
|
55420
55531
|
function bodyDumpEnabled() {
|
|
55421
55532
|
return process.env.ACP_DUMP_BODY === "1";
|
|
55422
55533
|
}
|
|
55534
|
+
var dumpFailCount = 0;
|
|
55535
|
+
var lastDumpFailLog = 0;
|
|
55536
|
+
function logDumpFailure(where, err2) {
|
|
55537
|
+
dumpFailCount++;
|
|
55538
|
+
const now = Date.now();
|
|
55539
|
+
if (dumpFailCount === 1 || now - lastDumpFailLog >= 6e4) {
|
|
55540
|
+
lastDumpFailLog = now;
|
|
55541
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
55542
|
+
log("warn", `[dump] ${where} failed (total ${dumpFailCount}x): ${msg}`);
|
|
55543
|
+
}
|
|
55544
|
+
}
|
|
55545
|
+
var unrecognizedPathCounts = /* @__PURE__ */ new Map();
|
|
55546
|
+
function logUnrecognizedPath(log2, url) {
|
|
55547
|
+
const key = maskUrlsInText(url.split("?")[0]);
|
|
55548
|
+
const n = (unrecognizedPathCounts.get(key) ?? 0) + 1;
|
|
55549
|
+
unrecognizedPathCounts.set(key, n);
|
|
55550
|
+
if (n <= 3) {
|
|
55551
|
+
log2("warn", `unrecognized path ${key} \u2014 not a known protocol (/chat/completions, /v1/messages, /responses, /responses/compact); forwarding unchanged`);
|
|
55552
|
+
} else if (n === 4) {
|
|
55553
|
+
log2("info", `unrecognized path ${key}: forwarding unchanged; further occurrences suppressed`);
|
|
55554
|
+
}
|
|
55555
|
+
}
|
|
55423
55556
|
var BILI_HOP_HEADER = "x-bili-hop";
|
|
55424
55557
|
function parseLauncherModelWindows(raw) {
|
|
55425
55558
|
if (!raw) return {};
|
|
@@ -55867,7 +56000,8 @@ async function handle(req, res, opts, core, config, log2, instanceId) {
|
|
|
55867
56000
|
${hdrText}
|
|
55868
56001
|
|
|
55869
56002
|
${bodyBuffer.toString("utf8")}`);
|
|
55870
|
-
} catch {
|
|
56003
|
+
} catch (err2) {
|
|
56004
|
+
logDumpFailure("INCOMING dump", err2);
|
|
55871
56005
|
}
|
|
55872
56006
|
}
|
|
55873
56007
|
let reqConfig = config;
|
|
@@ -56072,7 +56206,7 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
56072
56206
|
}
|
|
56073
56207
|
if (!prepared) {
|
|
56074
56208
|
if (protocol === null && !opts.passthrough) {
|
|
56075
|
-
log2
|
|
56209
|
+
logUnrecognizedPath(log2, req.url ?? "");
|
|
56076
56210
|
}
|
|
56077
56211
|
await forward(req, res, opts, bodyBuffer, null, core, reqConfig, log2, route, instanceId, void 0);
|
|
56078
56212
|
}
|
|
@@ -56697,7 +56831,8 @@ ${hdrText}
|
|
|
56697
56831
|
|
|
56698
56832
|
${bodyText}`);
|
|
56699
56833
|
log2("info", `[debug] RAW request dump: ${reqPath}`);
|
|
56700
|
-
} catch {
|
|
56834
|
+
} catch (err2) {
|
|
56835
|
+
logDumpFailure("REQ dump", err2);
|
|
56701
56836
|
}
|
|
56702
56837
|
}
|
|
56703
56838
|
const dispatcher = proxyDispatcher(proxyUrl);
|
|
@@ -56745,7 +56880,8 @@ ${bodyText}`);
|
|
|
56745
56880
|
${hdrText}
|
|
56746
56881
|
`);
|
|
56747
56882
|
log2("info", `[debug] RAW response dump: ${resPath}`);
|
|
56748
|
-
} catch {
|
|
56883
|
+
} catch (err2) {
|
|
56884
|
+
logDumpFailure("RES dump", err2);
|
|
56749
56885
|
}
|
|
56750
56886
|
}
|
|
56751
56887
|
if (!upstream.ok) {
|
|
@@ -56881,12 +57017,16 @@ ${hdrText}
|
|
|
56881
57017
|
systemPrompt,
|
|
56882
57018
|
clientAbort.signal
|
|
56883
57019
|
);
|
|
57020
|
+
let protocolFragmentWarned = false;
|
|
56884
57021
|
for await (const chunk of loop) {
|
|
56885
57022
|
if (res.destroyed || res.writableEnded) break;
|
|
56886
57023
|
{
|
|
56887
57024
|
const s3 = chunk.toString("utf8");
|
|
56888
57025
|
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
56889
57026
|
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains <acp tag`);
|
|
57027
|
+
} else if (!protocolFragmentWarned && containsToolCallXmlFragment(s3)) {
|
|
57028
|
+
protocolFragmentWarned = true;
|
|
57029
|
+
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains tool-call XML fragment (possible tag echo; not stripped)`);
|
|
56890
57030
|
}
|
|
56891
57031
|
}
|
|
56892
57032
|
res.write(chunk);
|
|
@@ -56994,7 +57134,7 @@ async function dumpStreamToFile(stream2, dir, name) {
|
|
|
56994
57134
|
mkdirSync6(dir, { recursive: true });
|
|
56995
57135
|
const ws2 = createWriteStream2(join4(dir, name));
|
|
56996
57136
|
ws2.on("error", (e) => {
|
|
56997
|
-
|
|
57137
|
+
logDumpFailure("SSE stream dump", e);
|
|
56998
57138
|
});
|
|
56999
57139
|
const reader = stream2.getReader();
|
|
57000
57140
|
try {
|
|
@@ -57007,7 +57147,8 @@ async function dumpStreamToFile(stream2, dir, name) {
|
|
|
57007
57147
|
reader.releaseLock();
|
|
57008
57148
|
ws2.end();
|
|
57009
57149
|
}
|
|
57010
|
-
} catch {
|
|
57150
|
+
} catch (err2) {
|
|
57151
|
+
logDumpFailure("SSE stream dump", err2);
|
|
57011
57152
|
}
|
|
57012
57153
|
}
|
|
57013
57154
|
function deriveTitle(messages) {
|