billion-context-omp 0.2.8 → 0.3.0
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/compress-tool.d.ts +1 -0
- package/dist/index.js +525 -185
- package/dist/index.js.map +1 -1
- package/dist/messages.d.ts +8 -0
- package/dist/runtime.d.ts +4 -0
- package/dist/transform-mode.d.ts +15 -0
- package/dist/update.d.ts +19 -0
- package/dist/wire-fold.d.ts +12 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -395,8 +395,13 @@ function resolveBoundaries(input) {
|
|
|
395
395
|
input.messages.forEach(
|
|
396
396
|
(message, index) => indexByRawId.set(message.id, index)
|
|
397
397
|
);
|
|
398
|
-
let
|
|
399
|
-
|
|
398
|
+
let snappedBoundaries = [];
|
|
399
|
+
const startAnchor = resolveAnchorIndex(start, input.state, indexByRawId, "start");
|
|
400
|
+
if (startAnchor.snapped) snappedBoundaries.push(startAnchor.snapped);
|
|
401
|
+
const endAnchor = resolveAnchorIndex(end, input.state, indexByRawId, "end");
|
|
402
|
+
if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
|
|
403
|
+
let startIndex = startAnchor.index;
|
|
404
|
+
let endIndex = endAnchor.index;
|
|
400
405
|
if (startIndex > endIndex) {
|
|
401
406
|
[startIndex, endIndex] = [endIndex, startIndex];
|
|
402
407
|
}
|
|
@@ -424,7 +429,8 @@ function resolveBoundaries(input) {
|
|
|
424
429
|
messageIds,
|
|
425
430
|
nestedBlockIds,
|
|
426
431
|
boundaryKind,
|
|
427
|
-
protectedGaps
|
|
432
|
+
protectedGaps,
|
|
433
|
+
snappedBoundaries
|
|
428
434
|
};
|
|
429
435
|
}
|
|
430
436
|
function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
@@ -439,14 +445,21 @@ function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
|
439
445
|
);
|
|
440
446
|
}
|
|
441
447
|
const index = indexByRawId.get(rawId);
|
|
442
|
-
if (index
|
|
443
|
-
|
|
444
|
-
"consumed",
|
|
445
|
-
endpoint,
|
|
446
|
-
`${label}="${boundary.raw}" not found in visible context (likely consumed by an existing block).`
|
|
447
|
-
);
|
|
448
|
+
if (index !== void 0) {
|
|
449
|
+
return { index, snapped: null };
|
|
448
450
|
}
|
|
449
|
-
|
|
451
|
+
const owner2 = activeOwnerAnchor(state, [rawId], indexByRawId);
|
|
452
|
+
if (owner2 !== null) {
|
|
453
|
+
return {
|
|
454
|
+
index: owner2,
|
|
455
|
+
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to that block's summary instead.`
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
throw new BoundaryNotFoundError(
|
|
459
|
+
"consumed",
|
|
460
|
+
endpoint,
|
|
461
|
+
`${label}="${boundary.raw}" not found in visible context (likely consumed by an existing block).`
|
|
462
|
+
);
|
|
450
463
|
}
|
|
451
464
|
const block = blockById(state, `b${boundary.numericId}`);
|
|
452
465
|
if (!block) {
|
|
@@ -456,6 +469,19 @@ function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
|
456
469
|
`${label}="b${boundary.numericId}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
457
470
|
);
|
|
458
471
|
}
|
|
472
|
+
if (block.active) {
|
|
473
|
+
const anchor = earliestIndexOfIds(block.effectiveMessageIds, indexByRawId);
|
|
474
|
+
if (anchor !== null) {
|
|
475
|
+
return { index: anchor, snapped: null };
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const owner = activeOwnerAnchor(state, block.effectiveMessageIds, indexByRawId);
|
|
479
|
+
if (owner !== null) {
|
|
480
|
+
return {
|
|
481
|
+
index: owner,
|
|
482
|
+
snapped: `${label}="b${boundary.numericId}" was consumed by a higher-tier block \u2014 anchored to the active block covering its content instead.`
|
|
483
|
+
};
|
|
484
|
+
}
|
|
459
485
|
if (!block.active) {
|
|
460
486
|
throw new BoundaryNotFoundError(
|
|
461
487
|
"consumed",
|
|
@@ -463,15 +489,26 @@ function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
|
463
489
|
`${label}="b${boundary.numericId}" not found in visible context (block distilled/consumed by a higher-tier block).`
|
|
464
490
|
);
|
|
465
491
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
492
|
+
throw new BoundaryNotFoundError(
|
|
493
|
+
"consumed",
|
|
494
|
+
endpoint,
|
|
495
|
+
`${label}="b${boundary.numericId}" not found in visible context (block messages consumed by a higher-tier block).`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
function activeOwnerAnchor(state, ownedIds, indexByRawId) {
|
|
499
|
+
if (ownedIds.length === 0) return null;
|
|
500
|
+
const owned = new Set(ownedIds);
|
|
501
|
+
let best = null;
|
|
502
|
+
for (const block of state.blocks) {
|
|
503
|
+
if (!block.active) continue;
|
|
504
|
+
const anchor = earliestIndexOfIds(block.effectiveMessageIds, indexByRawId);
|
|
505
|
+
if (anchor === null) continue;
|
|
506
|
+
const ownsContent = block.effectiveMessageIds.some((id) => owned.has(id));
|
|
507
|
+
if (ownsContent && (best === null || anchor < best)) {
|
|
508
|
+
best = anchor;
|
|
509
|
+
}
|
|
473
510
|
}
|
|
474
|
-
return
|
|
511
|
+
return best;
|
|
475
512
|
}
|
|
476
513
|
function formatPaddedRef(index) {
|
|
477
514
|
return `m${String(index).padStart(5, "0")}`;
|
|
@@ -1092,6 +1129,10 @@ function runPipeline(nodes, initial, ctx) {
|
|
|
1092
1129
|
function rangeError(spec, message) {
|
|
1093
1130
|
return `range ${spec.startRef}..${spec.endRef}: ${message}`;
|
|
1094
1131
|
}
|
|
1132
|
+
function numericBlockId(id) {
|
|
1133
|
+
const parsed = /^b(\d+)$/.exec(id);
|
|
1134
|
+
return parsed ? Number(parsed[1]) : 0;
|
|
1135
|
+
}
|
|
1095
1136
|
function createCore(ports = {}) {
|
|
1096
1137
|
const countTokens = ports.countTokens ?? defaultCountTokens;
|
|
1097
1138
|
function applyCompression(input) {
|
|
@@ -1137,6 +1178,12 @@ function createCore(ports = {}) {
|
|
|
1137
1178
|
}
|
|
1138
1179
|
}
|
|
1139
1180
|
}
|
|
1181
|
+
let resolvableCount = 0;
|
|
1182
|
+
let unknownCount = 0;
|
|
1183
|
+
for (const resolution of classifications.values()) {
|
|
1184
|
+
if (resolution.status === "ok") resolvableCount++;
|
|
1185
|
+
else if (resolution.status === "unknown") unknownCount++;
|
|
1186
|
+
}
|
|
1140
1187
|
const rangeIndexSets = [];
|
|
1141
1188
|
for (const [spec, resolution] of classifications) {
|
|
1142
1189
|
if (resolution.status !== "ok") continue;
|
|
@@ -1181,7 +1228,9 @@ function createCore(ports = {}) {
|
|
|
1181
1228
|
}
|
|
1182
1229
|
}
|
|
1183
1230
|
if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
1184
|
-
const
|
|
1231
|
+
const live = activeBlocks(state).map((b) => b.blockId).sort((x, y) => numericBlockId(x) - numericBlockId(y));
|
|
1232
|
+
const liveHint = live.length > 0 ? ` Current active blocks span ${live[0]}..${live[live.length - 1]} \u2014 retry with startId/endId set to active block IDs in that span.` : "";
|
|
1233
|
+
const gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref failed with "does not exist in this session". Refs recorded before an earlier compress are stale: each successful compress renumbers the remaining refs. Run acp_status, then re-issue the compress in the same turn using only the refs it reports.` : consumedRanges.length > 0 ? `Requested range(s) already compressed (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}); remaining compressible content ${totalRangeChars} chars < min ${input.config.compress.minCompressRange}. Nothing to do.${liveHint}` : `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`;
|
|
1185
1234
|
return {
|
|
1186
1235
|
state: input.state,
|
|
1187
1236
|
result: {
|
|
@@ -1207,6 +1256,7 @@ function createCore(ports = {}) {
|
|
|
1207
1256
|
errors.push(rangeError(spec, resolution.error.message));
|
|
1208
1257
|
continue;
|
|
1209
1258
|
}
|
|
1259
|
+
warnings.push(...resolution.resolved.snappedBoundaries);
|
|
1210
1260
|
try {
|
|
1211
1261
|
const outcome = applySingleRange({
|
|
1212
1262
|
spec,
|
|
@@ -2422,24 +2472,6 @@ function renderCompressedDrilldown(blocks, state, sort, limit, countTokens) {
|
|
|
2422
2472
|
}
|
|
2423
2473
|
return lines.join("\n");
|
|
2424
2474
|
}
|
|
2425
|
-
var substringAlgorithm = {
|
|
2426
|
-
name: "substring",
|
|
2427
|
-
description: "Exact substring counting (original baseline). Predictable, no normalization.",
|
|
2428
|
-
score(docs, query) {
|
|
2429
|
-
const terms = query.toLowerCase().trim().split(/\s+/).filter((t) => t.length > 0);
|
|
2430
|
-
if (terms.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
2431
|
-
return docs.map((d) => {
|
|
2432
|
-
const haystack = d.text.toLowerCase();
|
|
2433
|
-
let score = 0;
|
|
2434
|
-
for (const term of terms) score += countOccurrences2(haystack, term);
|
|
2435
|
-
return { ref: d.ref, score };
|
|
2436
|
-
});
|
|
2437
|
-
}
|
|
2438
|
-
};
|
|
2439
|
-
function countOccurrences2(haystack, needle) {
|
|
2440
|
-
if (!needle) return 0;
|
|
2441
|
-
return haystack.split(needle).length - 1;
|
|
2442
|
-
}
|
|
2443
2475
|
function stem(word) {
|
|
2444
2476
|
let w = word;
|
|
2445
2477
|
if (w.length <= 3) return w;
|
|
@@ -2458,8 +2490,17 @@ function stem(word) {
|
|
|
2458
2490
|
return w;
|
|
2459
2491
|
}
|
|
2460
2492
|
var CJK = /[\u3400-\u9fff\uf900-\ufaff\u3040-\u30ff\uac00-\ud7af]/;
|
|
2461
|
-
var CJK_RUN = new RegExp(`${CJK.source}+`, "g");
|
|
2462
2493
|
var LATIN_WORD = /[a-z][a-z0-9_]*[a-z0-9]|[a-z0-9]/g;
|
|
2494
|
+
var cjkSegmenter = new Intl.Segmenter("zh", { granularity: "word" });
|
|
2495
|
+
function cjkRunTokens(segs) {
|
|
2496
|
+
const words = segs.filter((w) => w.length >= 2);
|
|
2497
|
+
if (words.length > 0) return words;
|
|
2498
|
+
const run = segs.join("");
|
|
2499
|
+
const out = [];
|
|
2500
|
+
for (let i = 0; i < run.length - 1; i++) out.push(run.slice(i, i + 2));
|
|
2501
|
+
for (const ch of run) out.push(ch);
|
|
2502
|
+
return out;
|
|
2503
|
+
}
|
|
2463
2504
|
function tokenize(text, opts = {}) {
|
|
2464
2505
|
const lower = text.toLowerCase();
|
|
2465
2506
|
const tokens = [];
|
|
@@ -2470,15 +2511,23 @@ function tokenize(text, opts = {}) {
|
|
|
2470
2511
|
tokens.push(w);
|
|
2471
2512
|
}
|
|
2472
2513
|
}
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2514
|
+
if (!CJK.test(lower)) return tokens;
|
|
2515
|
+
const runSegs = [];
|
|
2516
|
+
let cur = null;
|
|
2517
|
+
for (const s of cjkSegmenter.segment(lower)) {
|
|
2518
|
+
const t = s.segment;
|
|
2519
|
+
if (t.length === 0) continue;
|
|
2520
|
+
if (CJK.test(t)) {
|
|
2521
|
+
(cur ??= []).push(t);
|
|
2522
|
+
} else if (cur) {
|
|
2523
|
+
runSegs.push(cur);
|
|
2524
|
+
cur = null;
|
|
2480
2525
|
}
|
|
2481
2526
|
}
|
|
2527
|
+
if (cur) runSegs.push(cur);
|
|
2528
|
+
for (const segs of runSegs) {
|
|
2529
|
+
tokens.push(...cjkRunTokens(segs));
|
|
2530
|
+
}
|
|
2482
2531
|
return tokens;
|
|
2483
2532
|
}
|
|
2484
2533
|
function charBigrams(text) {
|
|
@@ -2494,6 +2543,50 @@ function tfMap(text, stem22) {
|
|
|
2494
2543
|
for (const t of tokenize(text, { stem: stem22 })) m.set(t, (m.get(t) ?? 0) + 1);
|
|
2495
2544
|
return m;
|
|
2496
2545
|
}
|
|
2546
|
+
var DEFAULT_CAP_CHARS = 8 * 1024 * 1024;
|
|
2547
|
+
var capChars = DEFAULT_CAP_CHARS;
|
|
2548
|
+
var cache = /* @__PURE__ */ new Map();
|
|
2549
|
+
var cachedChars = 0;
|
|
2550
|
+
function build(text) {
|
|
2551
|
+
const tf = tfMap(text, true);
|
|
2552
|
+
let len = 0;
|
|
2553
|
+
for (const v of tf.values()) len += v;
|
|
2554
|
+
const lower = text.toLowerCase();
|
|
2555
|
+
return { tf, len, lower, grams: new Set(charBigrams(lower)) };
|
|
2556
|
+
}
|
|
2557
|
+
function docFeatures(text) {
|
|
2558
|
+
const hit = cache.get(text);
|
|
2559
|
+
if (hit) return hit;
|
|
2560
|
+
const f = build(text);
|
|
2561
|
+
if (text.length > 0 && text.length <= capChars) {
|
|
2562
|
+
while (cachedChars + text.length > capChars && cache.size > 0) {
|
|
2563
|
+
const k = cache.keys().next().value;
|
|
2564
|
+
cachedChars -= k.length;
|
|
2565
|
+
cache.delete(k);
|
|
2566
|
+
}
|
|
2567
|
+
cache.set(text, f);
|
|
2568
|
+
cachedChars += text.length;
|
|
2569
|
+
}
|
|
2570
|
+
return f;
|
|
2571
|
+
}
|
|
2572
|
+
var substringAlgorithm = {
|
|
2573
|
+
name: "substring",
|
|
2574
|
+
description: "Exact substring counting (original baseline). Predictable, no normalization.",
|
|
2575
|
+
score(docs, query) {
|
|
2576
|
+
const terms = query.toLowerCase().trim().split(/\s+/).filter((t) => t.length > 0);
|
|
2577
|
+
if (terms.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
2578
|
+
return docs.map((d) => {
|
|
2579
|
+
const haystack = docFeatures(d.text).lower;
|
|
2580
|
+
let score = 0;
|
|
2581
|
+
for (const term of terms) score += countOccurrences2(haystack, term);
|
|
2582
|
+
return { ref: d.ref, score };
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
};
|
|
2586
|
+
function countOccurrences2(haystack, needle) {
|
|
2587
|
+
if (!needle) return 0;
|
|
2588
|
+
return haystack.split(needle).length - 1;
|
|
2589
|
+
}
|
|
2497
2590
|
var bm25Algorithm = {
|
|
2498
2591
|
name: "bm25",
|
|
2499
2592
|
description: "BM25 with stemming + CJK bigram tokenization. IR-standard relevance ranking.",
|
|
@@ -2502,11 +2595,8 @@ var bm25Algorithm = {
|
|
|
2502
2595
|
const k1 = 1.2;
|
|
2503
2596
|
const b = 0.75;
|
|
2504
2597
|
const parsed = docs.map((d) => {
|
|
2505
|
-
const
|
|
2506
|
-
|
|
2507
|
-
let len = 0;
|
|
2508
|
-
for (const v of tf.values()) len += v;
|
|
2509
|
-
return { id: d.ref, tf, len };
|
|
2598
|
+
const f = docFeatures(d.text);
|
|
2599
|
+
return { id: d.ref, tf: f.tf, len: f.len };
|
|
2510
2600
|
});
|
|
2511
2601
|
const avgdl = parsed.reduce((s, d) => s + d.len, 0) / (N || 1);
|
|
2512
2602
|
const qTerms = tokenize(query, { stem: true });
|
|
@@ -2533,14 +2623,13 @@ var fuzzyAlgorithm = {
|
|
|
2533
2623
|
name: "fuzzy",
|
|
2534
2624
|
description: "Character bigram overlap. Typo-tolerant, script-agnostic, high recall.",
|
|
2535
2625
|
score(docs, query) {
|
|
2536
|
-
const qTokens = query.toLowerCase().split(/[\s,]+/).filter((t) => t.length >= 4);
|
|
2626
|
+
const qTokens = query.toLowerCase().split(/[\s,]+/).filter((t) => t.length >= 4 || t.length >= 2 && CJK.test(t));
|
|
2537
2627
|
if (qTokens.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
2538
2628
|
const qGrams = /* @__PURE__ */ new Set();
|
|
2539
2629
|
for (const t of qTokens) for (const g of charBigrams(t)) qGrams.add(g);
|
|
2540
2630
|
if (qGrams.size === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
2541
2631
|
return docs.map((d) => {
|
|
2542
|
-
const
|
|
2543
|
-
const docGrams = new Set(charBigrams(haystack));
|
|
2632
|
+
const docGrams = docFeatures(d.text).grams;
|
|
2544
2633
|
let hits = 0;
|
|
2545
2634
|
for (const g of qGrams) if (docGrams.has(g)) hits++;
|
|
2546
2635
|
return { ref: d.ref, score: hits / qGrams.size };
|
|
@@ -2916,6 +3005,27 @@ function resolveTransformMode(adapter, model, hostVersion = VERSION) {
|
|
|
2916
3005
|
if (api === "openai-completions" && hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) return "provider";
|
|
2917
3006
|
return "context";
|
|
2918
3007
|
}
|
|
3008
|
+
function providerDeliveryWarning(adapter, model, hostVersion = VERSION) {
|
|
3009
|
+
if (adapter.transformMode !== "provider") return void 0;
|
|
3010
|
+
const api = model?.api;
|
|
3011
|
+
const dropWarning = (target) => ({
|
|
3012
|
+
key: `drop:${target}`,
|
|
3013
|
+
reason: `host < 17.3.8 drops the before_provider_request replacement on ${target} (fixed upstream pi-ai 17.3.8, can1357/oh-my-pi#8717)`,
|
|
3014
|
+
message: `\u26A0 billion-context-omp: transformMode "provider" is set, but this host (pi-ai < 17.3.8) discards the rewritten payload on ${target} \u2014 compression is NOT applied. Upgrade the host (omp update) or remove the transformMode override.`
|
|
3015
|
+
});
|
|
3016
|
+
if (api === "openai-completions" && !hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) {
|
|
3017
|
+
return dropWarning(api);
|
|
3018
|
+
}
|
|
3019
|
+
if (api === "amazon-bedrock" || api === "cursor") {
|
|
3020
|
+
if (!hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) return dropWarning(api);
|
|
3021
|
+
return {
|
|
3022
|
+
key: `nocodec:${api}`,
|
|
3023
|
+
reason: `${api} honors the replacement from 17.3.8 but its wire body has no codec path yet (issue #83)`,
|
|
3024
|
+
message: `\u26A0 billion-context-omp: transformMode "provider" is set, but the ${api} wire body has no codec path yet (#83) \u2014 compression is NOT applied. Remove the transformMode override to use context mode.`
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
return void 0;
|
|
3028
|
+
}
|
|
2919
3029
|
|
|
2920
3030
|
// node_modules/acp-kernel/dist/wire/index.js
|
|
2921
3031
|
import { createHash } from "crypto";
|
|
@@ -3099,14 +3209,17 @@ function openaiToCore(body) {
|
|
|
3099
3209
|
}
|
|
3100
3210
|
case "user": {
|
|
3101
3211
|
const text = stringContent(m.content);
|
|
3102
|
-
const
|
|
3212
|
+
const imgs = allImageParts(m.content);
|
|
3213
|
+
const firstImg = imgs[0];
|
|
3214
|
+
const firstUrl = firstImg ? firstImg.image_url.url : void 0;
|
|
3215
|
+
const firstParsed = firstUrl ? parseDataUrl(firstUrl) : void 0;
|
|
3103
3216
|
const base = deriveMessageId("user", "text", text);
|
|
3104
3217
|
msgs.push({
|
|
3105
3218
|
id: clusters.next(base),
|
|
3106
3219
|
role: "user",
|
|
3107
3220
|
contentType: "text",
|
|
3108
3221
|
text,
|
|
3109
|
-
...
|
|
3222
|
+
...imgs.length === 1 && firstParsed ? { rawOpenaiContent: imgs[0], imageMediaType: firstParsed.mediaType, imageBase64: firstParsed.base64 } : imgs.length > 1 ? { rawOpenaiContentParts: imgs } : {}
|
|
3110
3223
|
});
|
|
3111
3224
|
break;
|
|
3112
3225
|
}
|
|
@@ -3201,10 +3314,12 @@ function coreToOpenai(messages) {
|
|
|
3201
3314
|
if (m.role === "system") {
|
|
3202
3315
|
out.push({ role: m.originalRole === "developer" ? "developer" : "system", content: m.text ?? "" });
|
|
3203
3316
|
} else if (m.role === "user") {
|
|
3204
|
-
if (m.rawOpenaiContent || m.imageBase64) {
|
|
3317
|
+
if (m.rawOpenaiContent || m.imageBase64 || m.rawOpenaiContentParts) {
|
|
3205
3318
|
const parts = [];
|
|
3206
3319
|
if (m.text) parts.push({ type: "text", text: m.text });
|
|
3207
|
-
if (m.
|
|
3320
|
+
if (m.rawOpenaiContentParts && m.rawOpenaiContentParts.length > 0) {
|
|
3321
|
+
for (const part of m.rawOpenaiContentParts) parts.push(part);
|
|
3322
|
+
} else if (m.rawOpenaiContent) {
|
|
3208
3323
|
parts.push(m.rawOpenaiContent);
|
|
3209
3324
|
} else if (m.imageBase64 && m.imageMediaType) {
|
|
3210
3325
|
parts.push({ type: "image_url", image_url: { url: `data:${m.imageMediaType};base64,${m.imageBase64}` } });
|
|
@@ -3229,19 +3344,17 @@ function stringContent(content) {
|
|
|
3229
3344
|
}
|
|
3230
3345
|
return "";
|
|
3231
3346
|
}
|
|
3232
|
-
function
|
|
3233
|
-
if (!Array.isArray(content)) return
|
|
3347
|
+
function allImageParts(content) {
|
|
3348
|
+
if (!Array.isArray(content)) return [];
|
|
3349
|
+
const out = [];
|
|
3234
3350
|
for (const p of content) {
|
|
3235
|
-
if (
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
if (parsed) return { part: p, mediaType: parsed.mediaType, base64: parsed.base64 };
|
|
3241
|
-
}
|
|
3242
|
-
}
|
|
3351
|
+
if (typeof p !== "object" || p === null) continue;
|
|
3352
|
+
if (!("type" in p) || p.type !== "image_url" || !("image_url" in p)) continue;
|
|
3353
|
+
const imagePart = p;
|
|
3354
|
+
const url = imagePart.image_url.url;
|
|
3355
|
+
if (typeof url === "string" && parseDataUrl(url)) out.push(p);
|
|
3243
3356
|
}
|
|
3244
|
-
return
|
|
3357
|
+
return out;
|
|
3245
3358
|
}
|
|
3246
3359
|
function createSubagentNamespaces() {
|
|
3247
3360
|
const anchors = /* @__PURE__ */ new Map();
|
|
@@ -3307,6 +3420,22 @@ function toolResultTexts(stream) {
|
|
|
3307
3420
|
}
|
|
3308
3421
|
return results;
|
|
3309
3422
|
}
|
|
3423
|
+
function lastRejectedCompressPair(stream) {
|
|
3424
|
+
for (let i = stream.length - 1; i >= 0; i--) {
|
|
3425
|
+
const m = stream[i];
|
|
3426
|
+
if (m.role !== "toolResult" || m.toolName !== "compress" || !m.toolCallId) continue;
|
|
3427
|
+
if (!extractText(m.content).includes("No changes applied")) continue;
|
|
3428
|
+
for (let j = stream.length - 1; j >= 0; j--) {
|
|
3429
|
+
if (j === i) continue;
|
|
3430
|
+
const c = stream[j];
|
|
3431
|
+
if (c.role !== "assistant") continue;
|
|
3432
|
+
if (!allToolCalls(c.content).some((call) => call.id === m.toolCallId && compressToolArgs(call))) continue;
|
|
3433
|
+
return [stream[j], stream[i]];
|
|
3434
|
+
}
|
|
3435
|
+
return null;
|
|
3436
|
+
}
|
|
3437
|
+
return null;
|
|
3438
|
+
}
|
|
3310
3439
|
function findCompressCalls(message) {
|
|
3311
3440
|
const out = [];
|
|
3312
3441
|
for (const call of allToolCalls(message.content)) {
|
|
@@ -3725,10 +3854,16 @@ function unrepresentableOpenaiMessage(message) {
|
|
|
3725
3854
|
if (typeof role !== "string" || !OPENAI_CODEC_ROLES.has(role)) {
|
|
3726
3855
|
return `openai role ${JSON.stringify(role) ?? "missing"}`;
|
|
3727
3856
|
}
|
|
3857
|
+
for (const field of ["function_call", "audio", "annotations"]) {
|
|
3858
|
+
if (message[field] !== void 0) {
|
|
3859
|
+
return `openai ${field} field is dropped by the rebuild`;
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
const refusal = message.refusal;
|
|
3863
|
+
if (refusal !== null && refusal !== void 0) return "openai refusal content is dropped by the rebuild";
|
|
3728
3864
|
const content = message.content;
|
|
3729
3865
|
if (content == null || typeof content === "string") return null;
|
|
3730
3866
|
if (!Array.isArray(content)) return "content neither string nor part array";
|
|
3731
|
-
let dataImages = 0;
|
|
3732
3867
|
for (const part of content) {
|
|
3733
3868
|
if (typeof part === "string") continue;
|
|
3734
3869
|
const type5 = part?.type;
|
|
@@ -3736,13 +3871,45 @@ function unrepresentableOpenaiMessage(message) {
|
|
|
3736
3871
|
if (type5 === "image_url" && role === "user") {
|
|
3737
3872
|
const url = part?.image_url?.url;
|
|
3738
3873
|
if (typeof url !== "string" || !url.startsWith("data:")) return "image_url without a data: URL is dropped";
|
|
3739
|
-
if (++dataImages > 1) return "second image_url in one message is dropped";
|
|
3740
3874
|
continue;
|
|
3741
3875
|
}
|
|
3742
3876
|
return `openai content part type ${JSON.stringify(type5) ?? "missing"}`;
|
|
3743
3877
|
}
|
|
3744
3878
|
return null;
|
|
3745
3879
|
}
|
|
3880
|
+
function restoreOpenaiWireFidelity(originalMessages, rebuilt) {
|
|
3881
|
+
const detailsByCall = /* @__PURE__ */ new Map();
|
|
3882
|
+
for (const message of originalMessages) {
|
|
3883
|
+
if (message === null || typeof message !== "object") continue;
|
|
3884
|
+
const calls = message.tool_calls;
|
|
3885
|
+
const details = message.reasoning_details;
|
|
3886
|
+
if (!Array.isArray(calls) || !Array.isArray(details) || details.length === 0) continue;
|
|
3887
|
+
for (const call of calls) {
|
|
3888
|
+
const id = call?.id;
|
|
3889
|
+
if (typeof id === "string" && !detailsByCall.has(id)) detailsByCall.set(id, details);
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
return rebuilt.map((message) => {
|
|
3893
|
+
if (message === null || typeof message !== "object") return message;
|
|
3894
|
+
const m = message;
|
|
3895
|
+
if (m.role !== "assistant") return message;
|
|
3896
|
+
const calls = Array.isArray(m.tool_calls) ? m.tool_calls : [];
|
|
3897
|
+
const attached = [];
|
|
3898
|
+
for (const call of calls) {
|
|
3899
|
+
const id = call?.id;
|
|
3900
|
+
if (typeof id !== "string") continue;
|
|
3901
|
+
const d = detailsByCall.get(id);
|
|
3902
|
+
if (d) attached.push(...d);
|
|
3903
|
+
}
|
|
3904
|
+
const hasReasoningField = m.reasoning_content !== void 0 || m.reasoning !== void 0 || m.reasoning_text !== void 0;
|
|
3905
|
+
const emptyContent = m.content === null && (calls.length > 0 || hasReasoningField);
|
|
3906
|
+
if (attached.length === 0 && !emptyContent) return message;
|
|
3907
|
+
const out = { ...m };
|
|
3908
|
+
if (emptyContent) out.content = "";
|
|
3909
|
+
if (attached.length > 0) out.reasoning_details = attached;
|
|
3910
|
+
return out;
|
|
3911
|
+
});
|
|
3912
|
+
}
|
|
3746
3913
|
var renderRefsAll = createRenderRefsNode("all");
|
|
3747
3914
|
function applyWireTagContract(msgs, state, scope) {
|
|
3748
3915
|
const stripAssistantTags = (m) => m.contentType === "text" && m.role === "assistant" ? { ...m, text: stripRefTag(m.text ?? "") } : m;
|
|
@@ -3783,6 +3950,18 @@ function toolResultTextsCore(msgs) {
|
|
|
3783
3950
|
}
|
|
3784
3951
|
return results;
|
|
3785
3952
|
}
|
|
3953
|
+
function lastRejectedPairCore(msgs) {
|
|
3954
|
+
const names = toolCallNames(msgs);
|
|
3955
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
3956
|
+
const m = msgs[i];
|
|
3957
|
+
if (m.contentType !== "tool-result" || !m.toolCallId) continue;
|
|
3958
|
+
if (!(m.text ?? "").includes("No changes applied")) continue;
|
|
3959
|
+
if (names.get(m.toolCallId) !== "compress") continue;
|
|
3960
|
+
const call = msgs.find((c) => c.contentType === "tool-call" && c.toolCallId === m.toolCallId);
|
|
3961
|
+
return call ? [call, m] : null;
|
|
3962
|
+
}
|
|
3963
|
+
return null;
|
|
3964
|
+
}
|
|
3786
3965
|
function findCompressCallsCore(msg) {
|
|
3787
3966
|
if (msg.contentType !== "tool-call" || !msg.toolName) return [];
|
|
3788
3967
|
const args = compressToolArgs({ name: msg.toolName, arguments: msg.text });
|
|
@@ -3901,22 +4080,23 @@ function viewToCoreStream(view, systemText) {
|
|
|
3901
4080
|
if (text) messages.push({ role: "user", content: text });
|
|
3902
4081
|
} else if (m.role === "assistant") {
|
|
3903
4082
|
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
3904
|
-
const
|
|
3905
|
-
|
|
3906
|
-
);
|
|
4083
|
+
const typed = blocks;
|
|
4084
|
+
const calls = typed.filter((b) => b !== null && typeof b === "object" && b.type === "toolCall");
|
|
4085
|
+
const reasoning = typed.filter((b) => b !== null && typeof b === "object" && b.type === "thinking" && typeof b.thinking === "string" && b.thinking.trim().length > 0).map((b) => b.thinking).join("\n");
|
|
3907
4086
|
const text = extractViewText(m.content);
|
|
3908
4087
|
if (calls.length > 0) {
|
|
3909
4088
|
messages.push({
|
|
3910
4089
|
role: "assistant",
|
|
3911
4090
|
content: text,
|
|
4091
|
+
...reasoning ? { reasoning_content: reasoning } : {},
|
|
3912
4092
|
tool_calls: calls.map((c) => ({
|
|
3913
4093
|
id: c.id,
|
|
3914
4094
|
type: "function",
|
|
3915
4095
|
function: { name: c.name ?? "", arguments: JSON.stringify(c.arguments ?? {}) }
|
|
3916
4096
|
}))
|
|
3917
4097
|
});
|
|
3918
|
-
} else if (text) {
|
|
3919
|
-
messages.push({ role: "assistant", content: text });
|
|
4098
|
+
} else if (text || reasoning) {
|
|
4099
|
+
messages.push({ role: "assistant", content: text, ...reasoning ? { reasoning_content: reasoning } : {} });
|
|
3920
4100
|
}
|
|
3921
4101
|
} else if (m.role === "toolResult") {
|
|
3922
4102
|
messages.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content: extractViewText(m.content) });
|
|
@@ -3937,20 +4117,27 @@ function viewToAnthropicCore(view) {
|
|
|
3937
4117
|
if (text) messages.push({ role: "user", content: [{ type: "text", text }] });
|
|
3938
4118
|
} else if (m.role === "assistant") {
|
|
3939
4119
|
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
3940
|
-
const
|
|
3941
|
-
(b) => b !== null && typeof b === "object" && b.type === "toolCall"
|
|
3942
|
-
);
|
|
3943
|
-
const text = extractViewText(m.content);
|
|
4120
|
+
const typed = blocks;
|
|
3944
4121
|
const content = [];
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
4122
|
+
for (const b of typed) {
|
|
4123
|
+
if (b === null || typeof b !== "object") continue;
|
|
4124
|
+
if (b.type === "thinking" && typeof b.thinking === "string" && b.thinking.trim().length > 0) {
|
|
4125
|
+
content.push({
|
|
4126
|
+
type: "thinking",
|
|
4127
|
+
thinking: b.thinking,
|
|
4128
|
+
...typeof b.thinkingSignature === "string" && b.thinkingSignature ? { signature: b.thinkingSignature } : {}
|
|
4129
|
+
});
|
|
4130
|
+
} else if (b.type === "text" && typeof b.text === "string" && stripRefTag(b.text).trim().length > 0) {
|
|
4131
|
+
content.push({ type: "text", text: stripRefTag(b.text) });
|
|
4132
|
+
} else if (b.type === "toolCall") {
|
|
4133
|
+
let input = {};
|
|
4134
|
+
try {
|
|
4135
|
+
input = b.arguments && typeof b.arguments === "object" ? b.arguments : JSON.parse(JSON.stringify(b.arguments ?? {}));
|
|
4136
|
+
} catch {
|
|
4137
|
+
input = {};
|
|
4138
|
+
}
|
|
4139
|
+
content.push({ type: "tool_use", id: b.id, name: b.name ?? "", input });
|
|
3952
4140
|
}
|
|
3953
|
-
content.push({ type: "tool_use", id: c.id, name: c.name ?? "", input });
|
|
3954
4141
|
}
|
|
3955
4142
|
if (content.length > 0) messages.push({ role: "assistant", content });
|
|
3956
4143
|
} else if (m.role === "toolResult") {
|
|
@@ -4263,6 +4450,9 @@ ${acp}`);
|
|
|
4263
4450
|
slot.rejectStreak = ok ? 0 : slot.rejectStreak + 1;
|
|
4264
4451
|
return slot.rejectStreak;
|
|
4265
4452
|
}
|
|
4453
|
+
function rejectStreakFor(ctx) {
|
|
4454
|
+
return slotForMode(ctx, sidOf(ctx)).rejectStreak;
|
|
4455
|
+
}
|
|
4266
4456
|
return {
|
|
4267
4457
|
core,
|
|
4268
4458
|
get adapter() {
|
|
@@ -4285,6 +4475,7 @@ ${acp}`);
|
|
|
4285
4475
|
commitFoldState,
|
|
4286
4476
|
recordRebuiltOutput,
|
|
4287
4477
|
noteCompressOutcome,
|
|
4478
|
+
rejectStreakFor,
|
|
4288
4479
|
forgetSession,
|
|
4289
4480
|
primeFold,
|
|
4290
4481
|
acquireLock
|
|
@@ -5096,7 +5287,7 @@ function stem2(word) {
|
|
|
5096
5287
|
return w;
|
|
5097
5288
|
}
|
|
5098
5289
|
var CJK2 = /[\u3400-\u9fff\uf900-\ufaff\u3040-\u30ff\uac00-\ud7af]/;
|
|
5099
|
-
var
|
|
5290
|
+
var CJK_RUN = new RegExp(`${CJK2.source}+`, "g");
|
|
5100
5291
|
var LATIN_WORD2 = /[a-z][a-z0-9_]*[a-z0-9]|[a-z0-9]/g;
|
|
5101
5292
|
function tokenize2(text, opts = {}) {
|
|
5102
5293
|
const lower = text.toLowerCase();
|
|
@@ -5108,7 +5299,7 @@ function tokenize2(text, opts = {}) {
|
|
|
5108
5299
|
tokens.push(w);
|
|
5109
5300
|
}
|
|
5110
5301
|
}
|
|
5111
|
-
const cjkRuns = lower.match(
|
|
5302
|
+
const cjkRuns = lower.match(CJK_RUN) ?? [];
|
|
5112
5303
|
for (const run of cjkRuns) {
|
|
5113
5304
|
if (run.length === 1) {
|
|
5114
5305
|
tokens.push(run);
|
|
@@ -5472,7 +5663,7 @@ async function statusReport(runtime, ctx) {
|
|
|
5472
5663
|
const coveredIds = collectCoveredMessageIds(state);
|
|
5473
5664
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
5474
5665
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
5475
|
-
const versionStr = "0.
|
|
5666
|
+
const versionStr = "0.3.0" ? `billion-context-omp@${"0.3.0"}` : void 0;
|
|
5476
5667
|
return buildStatusPanel({
|
|
5477
5668
|
version: versionStr,
|
|
5478
5669
|
tokenCount: sessionTokens,
|
|
@@ -5637,7 +5828,7 @@ function stampAndDetect(selfPath, version, now = Date.now()) {
|
|
|
5637
5828
|
}
|
|
5638
5829
|
|
|
5639
5830
|
// src/update.ts
|
|
5640
|
-
import { readFile, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
5831
|
+
import { readFile, writeFile as writeFile2, mkdir as mkdir2, access } from "fs/promises";
|
|
5641
5832
|
import { join as join4, dirname as dirname3 } from "path";
|
|
5642
5833
|
import { fileURLToPath } from "url";
|
|
5643
5834
|
import { execFile } from "child_process";
|
|
@@ -5703,43 +5894,112 @@ async function findExtensionDir() {
|
|
|
5703
5894
|
dir = parent;
|
|
5704
5895
|
}
|
|
5705
5896
|
}
|
|
5706
|
-
|
|
5897
|
+
var runNpmImpl = (args, cwd) => new Promise((resolve2) => {
|
|
5898
|
+
execFile(
|
|
5899
|
+
"npm",
|
|
5900
|
+
args,
|
|
5901
|
+
{ cwd, timeout: 6e4, shell: process.platform === "win32", maxBuffer: 4 * 1024 * 1024 },
|
|
5902
|
+
(err, stdout, stderr) => {
|
|
5903
|
+
if (err) logWarn("update", { event: "install-exec-failed", error: err.message, stderr: String(stderr).slice(0, 300) });
|
|
5904
|
+
resolve2({ code: err ? 1 : 0, stdout: String(stdout), stderr: String(stderr) });
|
|
5905
|
+
}
|
|
5906
|
+
);
|
|
5907
|
+
});
|
|
5908
|
+
var runNpm = runNpmImpl;
|
|
5909
|
+
var runNodeImpl = (args) => new Promise((resolve2) => {
|
|
5910
|
+
execFile(
|
|
5911
|
+
process.execPath,
|
|
5912
|
+
args,
|
|
5913
|
+
{ timeout: 15e3, maxBuffer: 4 * 1024 * 1024, shell: false },
|
|
5914
|
+
(err, stdout, stderr) => {
|
|
5915
|
+
resolve2({ code: err ? 1 : 0, stdout: String(stdout), stderr: String(stderr) });
|
|
5916
|
+
}
|
|
5917
|
+
);
|
|
5918
|
+
});
|
|
5919
|
+
var runNode = runNodeImpl;
|
|
5920
|
+
function declaredEntries(pkg) {
|
|
5921
|
+
const entries = /* @__PURE__ */ new Set();
|
|
5922
|
+
for (const ext of pkg.omp?.extensions ?? []) {
|
|
5923
|
+
if (typeof ext === "string") entries.add(ext);
|
|
5924
|
+
}
|
|
5925
|
+
const dot = pkg.exports?.["."];
|
|
5926
|
+
if (typeof dot === "string") entries.add(dot);
|
|
5927
|
+
else if (dot && typeof dot.import === "string") entries.add(dot.import);
|
|
5928
|
+
if (typeof pkg.main === "string") entries.add(pkg.main);
|
|
5929
|
+
return [...entries];
|
|
5930
|
+
}
|
|
5931
|
+
async function verifyInstall(npmDir, latest) {
|
|
5932
|
+
const extDir = join4(npmDir, "node_modules", PACKAGE_NAME);
|
|
5933
|
+
const pkg = await readPackageJson(join4(extDir, "package.json"));
|
|
5934
|
+
if (!pkg) return { ok: false, reason: "package-json-missing" };
|
|
5935
|
+
if (pkg.version !== latest) return { ok: false, reason: `version-mismatch:${pkg.version ?? "none"}` };
|
|
5936
|
+
const entries = declaredEntries(pkg);
|
|
5937
|
+
if (entries.length === 0) return { ok: false, reason: "no-entry-declared" };
|
|
5938
|
+
for (const rel of entries) {
|
|
5939
|
+
try {
|
|
5940
|
+
await access(join4(extDir, rel));
|
|
5941
|
+
} catch {
|
|
5942
|
+
return { ok: false, reason: `entry-missing:${rel}` };
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
const smokeEntry = pkg.omp?.extensions?.[0] ?? entries[0];
|
|
5946
|
+
if (!smokeEntry) return { ok: false, reason: "no-entry-declared" };
|
|
5947
|
+
const smoke = `const{pathToFileURL}=require("node:url");import(pathToFileURL(process.argv[1]).href).then(()=>{},(e)=>{console.error(e&&e.stack||e);process.exit(1)});`;
|
|
5948
|
+
const r = await runNode(["-e", smoke, join4(extDir, smokeEntry)]);
|
|
5949
|
+
if (r.code !== 0) return { ok: false, reason: `entry-import-failed:${r.stderr.slice(0, 500)}` };
|
|
5950
|
+
return { ok: true };
|
|
5951
|
+
}
|
|
5952
|
+
var installArgs = (version) => [
|
|
5953
|
+
"install",
|
|
5954
|
+
`${PACKAGE_NAME}@${version}`,
|
|
5955
|
+
// --no-save: the auto-updater must never mutate the host's package.json
|
|
5956
|
+
// or lockfile.
|
|
5957
|
+
"--no-save",
|
|
5958
|
+
"--silent",
|
|
5959
|
+
"--no-audit",
|
|
5960
|
+
"--no-fund"
|
|
5961
|
+
];
|
|
5962
|
+
async function autoInstallLatest(latest, npmDirOverride) {
|
|
5707
5963
|
if (!SEMVER_RE.test(latest)) {
|
|
5708
5964
|
logWarn("update", { event: "install-abort", reason: "semver", latest });
|
|
5709
|
-
return
|
|
5965
|
+
return "failed";
|
|
5710
5966
|
}
|
|
5711
5967
|
const extDir = await findExtensionDir();
|
|
5712
5968
|
if (!extDir) {
|
|
5713
5969
|
logWarn("update", { event: "install-abort", reason: "extdir-not-found", moduleUrl: import.meta.url });
|
|
5714
|
-
return
|
|
5970
|
+
return "failed";
|
|
5715
5971
|
}
|
|
5716
|
-
const npmDir = findNpmRoot(extDir);
|
|
5972
|
+
const npmDir = npmDirOverride ?? findNpmRoot(extDir);
|
|
5717
5973
|
if (!npmDir) {
|
|
5718
5974
|
logWarn("update", { event: "install-abort", reason: "npmroot-not-found", extDir });
|
|
5719
|
-
return
|
|
5975
|
+
return "failed";
|
|
5720
5976
|
}
|
|
5977
|
+
const prevVersion = (await readPackageJson(join4(npmDir, "node_modules", PACKAGE_NAME, "package.json")))?.version ?? "0.3.0";
|
|
5721
5978
|
try {
|
|
5722
5979
|
const keepAlive = setInterval(() => {
|
|
5723
5980
|
}, 500);
|
|
5724
5981
|
try {
|
|
5725
|
-
const
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
);
|
|
5735
|
-
|
|
5736
|
-
|
|
5982
|
+
const res = await runNpm(installArgs(latest), npmDir);
|
|
5983
|
+
if (res.code !== 0) {
|
|
5984
|
+
logWarn("update", { event: "auto-install-failed", latest, stderr: res.stderr.slice(0, 2e3) });
|
|
5985
|
+
return "failed";
|
|
5986
|
+
}
|
|
5987
|
+
const verify = await verifyInstall(npmDir, latest);
|
|
5988
|
+
if (!verify.ok) {
|
|
5989
|
+
const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.3.0";
|
|
5990
|
+
logWarn("update", { event: "auto-install-verify-failed", latest, reason: verify.reason, rollbackTo });
|
|
5991
|
+
const rb = await runNpm(installArgs(rollbackTo), npmDir);
|
|
5992
|
+
logInfo("update", { event: "rollback", from: latest, to: rollbackTo, ok: rb.code === 0 });
|
|
5993
|
+
return "rolled-back";
|
|
5994
|
+
}
|
|
5995
|
+
logInfo("update", { event: "auto-installed", from: prevVersion, to: latest });
|
|
5996
|
+
return "ok";
|
|
5737
5997
|
} finally {
|
|
5738
5998
|
clearInterval(keepAlive);
|
|
5739
5999
|
}
|
|
5740
6000
|
} catch (e) {
|
|
5741
6001
|
logWarn("update", { event: "install-throw", error: e instanceof Error ? e.message : String(e) });
|
|
5742
|
-
return
|
|
6002
|
+
return "failed";
|
|
5743
6003
|
}
|
|
5744
6004
|
}
|
|
5745
6005
|
async function checkForUpdate(autoUpdate, notify) {
|
|
@@ -5766,7 +6026,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
5766
6026
|
const data = await res.json();
|
|
5767
6027
|
const latest = data.version;
|
|
5768
6028
|
if (!latest) return;
|
|
5769
|
-
const current = runtimeVersion ?? "0.
|
|
6029
|
+
const current = runtimeVersion ?? "0.3.0";
|
|
5770
6030
|
const hasUpdate = isNewer(latest, current);
|
|
5771
6031
|
debug.event("update-check", {
|
|
5772
6032
|
current,
|
|
@@ -5775,16 +6035,16 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
5775
6035
|
});
|
|
5776
6036
|
logInfo("update", { event: "check", current, latest, hasUpdate });
|
|
5777
6037
|
if (hasUpdate) {
|
|
5778
|
-
const
|
|
5779
|
-
if (
|
|
6038
|
+
const outcome = await autoInstallLatest(latest);
|
|
6039
|
+
if (!notify) return;
|
|
6040
|
+
if (outcome === "ok") {
|
|
6041
|
+
notify(`\x1B[32m\u2714 ACP auto-updated ${current} \u2192 ${latest}. Restart omp to finish.\x1B[0m`);
|
|
6042
|
+
} else if (outcome === "rolled-back") {
|
|
5780
6043
|
notify(
|
|
5781
|
-
`\x1B[
|
|
5782
|
-
);
|
|
5783
|
-
logInfo("update", { event: "auto-installed", from: current, to: latest });
|
|
5784
|
-
} else if (!installed && notify) {
|
|
5785
|
-
notify(
|
|
5786
|
-
`${PACKAGE_NAME} ${latest} available (you have ${current}). Run: omp install ${PACKAGE_NAME}@latest`
|
|
6044
|
+
`\x1B[33m\u26A0 ${PACKAGE_NAME} ${latest} failed verification and was rolled back. Keeping ${current}. A later release will auto-update.\x1B[0m`
|
|
5787
6045
|
);
|
|
6046
|
+
} else {
|
|
6047
|
+
notify(`${PACKAGE_NAME} ${latest} available (you have ${current}). Run: omp install ${PACKAGE_NAME}@latest`);
|
|
5788
6048
|
}
|
|
5789
6049
|
}
|
|
5790
6050
|
} catch (e) {
|
|
@@ -6022,10 +6282,11 @@ function applyUserConfig(adapter, user) {
|
|
|
6022
6282
|
function createAcpExtension(adapter = {}) {
|
|
6023
6283
|
return (pi) => {
|
|
6024
6284
|
const runtime = createRuntime(adapter);
|
|
6285
|
+
const warnDelivery = makeDeliveryWarner();
|
|
6025
6286
|
wireSessionLifecycle(pi, runtime);
|
|
6026
|
-
wireContextTransform(pi, runtime);
|
|
6287
|
+
wireContextTransform(pi, runtime, warnDelivery);
|
|
6027
6288
|
wireSystemPrompt(pi, runtime);
|
|
6028
|
-
wireProviderTransform(pi, runtime);
|
|
6289
|
+
wireProviderTransform(pi, runtime, warnDelivery);
|
|
6029
6290
|
wireProviderDebug(pi);
|
|
6030
6291
|
wireToolGuardrails(pi, runtime);
|
|
6031
6292
|
pi.registerTool(makeCompressTool(runtime));
|
|
@@ -6039,26 +6300,14 @@ function createAcpExtension(adapter = {}) {
|
|
|
6039
6300
|
}
|
|
6040
6301
|
var index_default = createAcpExtension();
|
|
6041
6302
|
function wireSessionLifecycle(pi, runtime) {
|
|
6042
|
-
|
|
6303
|
+
const prepareAndPrime = async (ctx, phase) => {
|
|
6043
6304
|
const sid = ctx.sessionManager.getSessionId();
|
|
6044
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.8" : null });
|
|
6045
|
-
const selfPath = import.meta.url;
|
|
6046
|
-
const conflict = stampAndDetect(selfPath, true ? "0.2.8" : null);
|
|
6047
|
-
if (conflict) {
|
|
6048
|
-
logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
|
|
6049
|
-
try {
|
|
6050
|
-
if (ctx.hasUI) {
|
|
6051
|
-
ctx.ui.notify(`\u26A0 billion-context-omp loaded TWICE (also from ${conflict.path}). Two instances corrupt compression state \u2014 remove one (check 'omp plugin list' vs config.yml extensions).`);
|
|
6052
|
-
}
|
|
6053
|
-
} catch {
|
|
6054
|
-
}
|
|
6055
|
-
}
|
|
6056
6305
|
try {
|
|
6057
6306
|
const user = await loadUserConfig(ctx.cwd);
|
|
6058
6307
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
6059
6308
|
if (runtime.adapter.debug !== void 0) setDebugEnabled(runtime.adapter.debug);
|
|
6060
6309
|
} catch (e) {
|
|
6061
|
-
logThrow("config", e, { sid, phase
|
|
6310
|
+
logThrow("config", e, { sid, phase });
|
|
6062
6311
|
}
|
|
6063
6312
|
try {
|
|
6064
6313
|
runtime.setPrompts(resolvePrompts(runtime.adapter.prompts, { acknowledgeRisk: runtime.adapter.acknowledgePromptsRisk === true }));
|
|
@@ -6067,10 +6316,35 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
6067
6316
|
runtime.setPrompts(defaultPrompts);
|
|
6068
6317
|
}
|
|
6069
6318
|
runtime.primeFold(ctx);
|
|
6319
|
+
};
|
|
6320
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
6321
|
+
const sid = ctx.sessionManager.getSessionId();
|
|
6322
|
+
const modelInfo = ctx.model;
|
|
6323
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.3.0" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
|
|
6324
|
+
const selfPath = import.meta.url;
|
|
6325
|
+
const conflict = stampAndDetect(selfPath, true ? "0.3.0" : null);
|
|
6326
|
+
if (conflict) {
|
|
6327
|
+
logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
|
|
6328
|
+
try {
|
|
6329
|
+
if (ctx.hasUI) {
|
|
6330
|
+
ctx.ui.notify(`\u26A0 billion-context-omp loaded TWICE (also from ${conflict.path}). Two instances corrupt compression state \u2014 remove one (check 'omp plugin list' vs config.yml extensions).`);
|
|
6331
|
+
}
|
|
6332
|
+
} catch {
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6335
|
+
await prepareAndPrime(ctx, "session_start");
|
|
6070
6336
|
void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|
|
6071
6337
|
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
6072
6338
|
}).catch((e) => logThrow("update", e, { sid, phase: "session_start" }));
|
|
6073
6339
|
});
|
|
6340
|
+
pi.on("session_switch", async (event, ctx) => {
|
|
6341
|
+
logInfo("session", { event: "switch", sid: ctx.sessionManager.getSessionId(), reason: event.reason, previous: event.previousSessionFile ?? null });
|
|
6342
|
+
await prepareAndPrime(ctx, "session_switch");
|
|
6343
|
+
});
|
|
6344
|
+
pi.on("session_branch", async (_event, ctx) => {
|
|
6345
|
+
logInfo("session", { event: "branch", sid: ctx.sessionManager.getSessionId() });
|
|
6346
|
+
await prepareAndPrime(ctx, "session_branch");
|
|
6347
|
+
});
|
|
6074
6348
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
6075
6349
|
try {
|
|
6076
6350
|
runtime.forgetSession(ctx.sessionManager.getSessionId());
|
|
@@ -6114,6 +6388,7 @@ async function transformStream(ctx, runtime, input, mode) {
|
|
|
6114
6388
|
runtime.commitFoldState(ctx, turn.state);
|
|
6115
6389
|
logInfo("turn", {
|
|
6116
6390
|
sid,
|
|
6391
|
+
model: ctx.model?.id ?? null,
|
|
6117
6392
|
inMsgs: coreMessages.length,
|
|
6118
6393
|
outMsgs: turn.messages.length,
|
|
6119
6394
|
tokens: tokenCount,
|
|
@@ -6141,6 +6416,11 @@ async function transformStream(ctx, runtime, input, mode) {
|
|
|
6141
6416
|
activeAfter: turn.state.blocks.filter((b) => b.active).length
|
|
6142
6417
|
});
|
|
6143
6418
|
const rebuilt = coreOutToAgentMessages(turn.messages, originalById);
|
|
6419
|
+
const rejectedPair = lastRejectedCompressPair(input);
|
|
6420
|
+
if (rejectedPair) {
|
|
6421
|
+
rebuilt.push(...rejectedPair);
|
|
6422
|
+
debug.event("rejected-pair-visible", { sid, callId: rejectedPair[1].toolCallId ?? null });
|
|
6423
|
+
}
|
|
6144
6424
|
debug.event("core-out", {
|
|
6145
6425
|
sid,
|
|
6146
6426
|
coreOutMsgs: turn.messages.length,
|
|
@@ -6152,38 +6432,48 @@ async function transformStream(ctx, runtime, input, mode) {
|
|
|
6152
6432
|
if (turn.nudge?.shouldInject) {
|
|
6153
6433
|
const lastUser = [...input].reverse().find((m) => m.role === "user");
|
|
6154
6434
|
const tailText = lastUser ? JSON.stringify(lastUser.content ?? "") : "";
|
|
6155
|
-
const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
|
|
6435
|
+
const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached") || tailText.includes("compress calls were rejected in a row");
|
|
6156
6436
|
if (isFeedbackView) {
|
|
6157
6437
|
debug.event("nudge-feedback-skip", { sid: ctx.sessionManager.getSessionId(), msgs: input.length });
|
|
6158
6438
|
} else {
|
|
6159
6439
|
const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
|
|
6440
|
+
const rejectStreak = runtime.rejectStreakFor(ctx);
|
|
6160
6441
|
const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
|
|
6164
|
-
if (suppressed) {
|
|
6165
|
-
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
6442
|
+
if (rejectStreak >= LOOP_GUARD_STOP) {
|
|
6443
|
+
turn.state.nudge.lastNudgeShownTokens = epochReset ? 0 : preTurnNudgeShownTokens;
|
|
6166
6444
|
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
6167
|
-
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6168
|
-
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6169
|
-
} else {
|
|
6170
6445
|
nudgeInjected = true;
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6446
|
+
rebuilt.push(holdMessage(rejectStreak));
|
|
6447
|
+
logInfo("nudge", { sid, event: "hold-injected", streak: rejectStreak, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6448
|
+
debug.event("nudge-hold", { sid, streak: rejectStreak });
|
|
6449
|
+
} else {
|
|
6450
|
+
const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
|
|
6451
|
+
const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
|
|
6452
|
+
const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
|
|
6453
|
+
if (suppressed) {
|
|
6454
|
+
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
6455
|
+
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
6456
|
+
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6457
|
+
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6458
|
+
} else {
|
|
6459
|
+
nudgeInjected = true;
|
|
6460
|
+
{
|
|
6461
|
+
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
6462
|
+
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
6463
|
+
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
6464
|
+
const example = top ? `
|
|
6176
6465
|
|
|
6177
6466
|
Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6467
|
+
rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example));
|
|
6468
|
+
if (emergency) {
|
|
6469
|
+
logWarn("nudge", { sid: ctx.sessionManager.getSessionId(), event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
|
|
6470
|
+
}
|
|
6471
|
+
if (debugOn2 && ctx.hasUI) {
|
|
6472
|
+
ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
|
|
6184
6473
|
${rendered.text}${example}`);
|
|
6474
|
+
}
|
|
6475
|
+
debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
6185
6476
|
}
|
|
6186
|
-
debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
6187
6477
|
}
|
|
6188
6478
|
}
|
|
6189
6479
|
}
|
|
@@ -6208,10 +6498,13 @@ ${rendered.text}${example}`);
|
|
|
6208
6498
|
}).catch((e) => logThrow("update", e, { sid, phase: "context" }));
|
|
6209
6499
|
return result;
|
|
6210
6500
|
}
|
|
6211
|
-
function wireContextTransform(pi, runtime) {
|
|
6501
|
+
function wireContextTransform(pi, runtime, warnDelivery) {
|
|
6212
6502
|
pi.on("context", async (event, ctx) => {
|
|
6213
6503
|
if (resolveTransformMode(runtime.adapter, ctx.model) === "provider") {
|
|
6214
|
-
|
|
6504
|
+
const sid = ctx.sessionManager.getSessionId();
|
|
6505
|
+
debug.event("context-observer-skip", { sid, msgs: event.messages?.length ?? 0 });
|
|
6506
|
+
const warning = providerDeliveryWarning(runtime.adapter, ctx.model);
|
|
6507
|
+
if (warning) warnDelivery(ctx, sid, warning);
|
|
6215
6508
|
return void 0;
|
|
6216
6509
|
}
|
|
6217
6510
|
const result = await transformStream(ctx, runtime, event.messages ?? [], "context");
|
|
@@ -6219,7 +6512,20 @@ function wireContextTransform(pi, runtime) {
|
|
|
6219
6512
|
return { messages: result.rebuilt };
|
|
6220
6513
|
});
|
|
6221
6514
|
}
|
|
6222
|
-
function
|
|
6515
|
+
function makeDeliveryWarner() {
|
|
6516
|
+
const warned = /* @__PURE__ */ new Set();
|
|
6517
|
+
return (ctx, sid, warning) => {
|
|
6518
|
+
const dedup = `${sid}:${warning.key}`;
|
|
6519
|
+
if (warned.has(dedup)) return;
|
|
6520
|
+
warned.add(dedup);
|
|
6521
|
+
logWarn("provider-transform", { sid, event: "undelivered", reason: warning.reason });
|
|
6522
|
+
try {
|
|
6523
|
+
if (ctx.hasUI) ctx.ui.notify(warning.message);
|
|
6524
|
+
} catch {
|
|
6525
|
+
}
|
|
6526
|
+
};
|
|
6527
|
+
}
|
|
6528
|
+
function wireProviderTransform(pi, runtime, warnDelivery) {
|
|
6223
6529
|
pi.on("before_provider_request", async (event, ctx) => {
|
|
6224
6530
|
if (resolveTransformMode(runtime.adapter, ctx.model) !== "provider") return void 0;
|
|
6225
6531
|
const payload = event.payload;
|
|
@@ -6228,6 +6534,13 @@ function wireProviderTransform(pi, runtime) {
|
|
|
6228
6534
|
const fmt2 = detectProviderWireFormat(payload);
|
|
6229
6535
|
if (fmt2 === null) {
|
|
6230
6536
|
debug.event("provider-transform-unknown-format", { sid });
|
|
6537
|
+
if (runtime.adapter.transformMode === "provider") {
|
|
6538
|
+
warnDelivery(ctx, sid, {
|
|
6539
|
+
key: "unknown-wire-format",
|
|
6540
|
+
reason: "explicit provider mode but the wire body has no codec path (unknown format) \u2014 payload passes through",
|
|
6541
|
+
message: '\u26A0 billion-context-omp: transformMode "provider" is set, but this wire body has no codec path \u2014 compression is NOT applied here. Remove the override to use context mode.'
|
|
6542
|
+
});
|
|
6543
|
+
}
|
|
6231
6544
|
return void 0;
|
|
6232
6545
|
}
|
|
6233
6546
|
const representable = payloadRepresentable(payload, fmt2);
|
|
@@ -6241,13 +6554,15 @@ function wireProviderTransform(pi, runtime) {
|
|
|
6241
6554
|
if (msgs.length === 0) return void 0;
|
|
6242
6555
|
const result = await transformStreamCore(ctx, runtime, msgs, fmt2);
|
|
6243
6556
|
if (!result) return void 0;
|
|
6244
|
-
const
|
|
6245
|
-
const
|
|
6557
|
+
const inMessages = payload.messages ?? [];
|
|
6558
|
+
const rebuilt = fmt2 === "openai" ? restoreOpenaiWireFidelity(inMessages, coreToPayloadMessages(result.coreOut, fmt2, cacheControls)) : coreToPayloadMessages(result.coreOut, fmt2, cacheControls);
|
|
6559
|
+
const outMsgs = rebuilt.length;
|
|
6560
|
+
const inMsgs = inMessages.length;
|
|
6246
6561
|
if (outMsgs !== inMsgs) {
|
|
6247
6562
|
logInfo("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudge: result.nudgeInjected ? "injected" : "idle" });
|
|
6248
6563
|
}
|
|
6249
6564
|
debug.event("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudgeInjected: result.nudgeInjected });
|
|
6250
|
-
return { ...payload, messages:
|
|
6565
|
+
return { ...payload, messages: rebuilt };
|
|
6251
6566
|
} catch (e) {
|
|
6252
6567
|
logThrow("provider-transform", e, { sid, fmt: fmt2 });
|
|
6253
6568
|
return void 0;
|
|
@@ -6321,42 +6636,57 @@ async function transformStreamCore(ctx, runtime, wireMsgs, fmt2) {
|
|
|
6321
6636
|
turn.state,
|
|
6322
6637
|
{ config, tokenCount }
|
|
6323
6638
|
);
|
|
6639
|
+
const rejectedPair = lastRejectedPairCore(wireMsgs);
|
|
6640
|
+
if (rejectedPair) {
|
|
6641
|
+
coreOut.push(...rejectedPair);
|
|
6642
|
+
debug.event("rejected-pair-visible", { sid, space: "core", callId: rejectedPair[1]?.toolCallId ?? null });
|
|
6643
|
+
}
|
|
6324
6644
|
let nudgeInjected = false;
|
|
6325
6645
|
if (turn.nudge?.shouldInject) {
|
|
6326
6646
|
const lastUser = [...wireMsgs].reverse().find((m) => m.role === "user");
|
|
6327
6647
|
const tailText = lastUser ? lastUser.text ?? "" : "";
|
|
6328
|
-
const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
|
|
6648
|
+
const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached") || tailText.includes("compress calls were rejected in a row");
|
|
6329
6649
|
if (isFeedbackView) {
|
|
6330
6650
|
debug.event("nudge-feedback-skip", { sid, msgs: wireMsgs.length });
|
|
6331
6651
|
} else {
|
|
6332
6652
|
const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
|
|
6333
6653
|
const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
|
|
6334
|
-
const
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
if (suppressed) {
|
|
6338
|
-
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
6654
|
+
const rejectStreak = runtime.rejectStreakFor(ctx);
|
|
6655
|
+
if (rejectStreak >= LOOP_GUARD_STOP) {
|
|
6656
|
+
turn.state.nudge.lastNudgeShownTokens = epochReset ? 0 : preTurnNudgeShownTokens;
|
|
6339
6657
|
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
6340
|
-
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6341
|
-
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6342
|
-
} else {
|
|
6343
6658
|
nudgeInjected = true;
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6659
|
+
coreOut.push({ id: `acp_hold_${Date.now()}`, role: "user", contentType: "text", text: holdText(rejectStreak) });
|
|
6660
|
+
logInfo("nudge", { sid, event: "hold-injected", streak: rejectStreak, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6661
|
+
debug.event("nudge-hold", { sid, streak: rejectStreak });
|
|
6662
|
+
} else {
|
|
6663
|
+
const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
|
|
6664
|
+
const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
|
|
6665
|
+
const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
|
|
6666
|
+
if (suppressed) {
|
|
6667
|
+
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
6668
|
+
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
6669
|
+
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6670
|
+
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6671
|
+
} else {
|
|
6672
|
+
nudgeInjected = true;
|
|
6673
|
+
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
6674
|
+
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
6675
|
+
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
6676
|
+
const example = top ? `
|
|
6348
6677
|
|
|
6349
6678
|
Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6679
|
+
if (emergency) {
|
|
6680
|
+
logWarn("nudge", { sid, event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
|
|
6681
|
+
}
|
|
6682
|
+
const debugOn2 = debug.enabled;
|
|
6683
|
+
if (debugOn2 && ctx.hasUI) {
|
|
6684
|
+
ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
|
|
6356
6685
|
${rendered.text}${example}`);
|
|
6686
|
+
}
|
|
6687
|
+
debug.event("nudge-injected", { sid, voice: rendered.voice, channels: ["wire", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
6688
|
+
coreOut.push({ id: `acp_nudge_${Date.now()}`, role: "user", contentType: "text", text: nudgeText(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example) });
|
|
6357
6689
|
}
|
|
6358
|
-
debug.event("nudge-injected", { sid, voice: rendered.voice, channels: ["wire", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
6359
|
-
coreOut.push({ id: `acp_nudge_${Date.now()}`, role: "user", contentType: "text", text: nudgeText(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example) });
|
|
6360
6690
|
}
|
|
6361
6691
|
}
|
|
6362
6692
|
}
|
|
@@ -6389,18 +6719,18 @@ function wireProviderDebug(pi) {
|
|
|
6389
6719
|
pi.on("after_provider_response", (event, ctx) => {
|
|
6390
6720
|
if (!debug.enabled) return;
|
|
6391
6721
|
const h = event.headers;
|
|
6392
|
-
const
|
|
6722
|
+
const cache2 = {};
|
|
6393
6723
|
for (const [k, v] of Object.entries(h)) {
|
|
6394
6724
|
const lk = k.toLowerCase();
|
|
6395
6725
|
if (lk.includes("cache") || lk.includes("usage") || lk.includes("token") || lk.includes("rate") || lk.includes("x-")) {
|
|
6396
|
-
|
|
6726
|
+
cache2[lk] = v;
|
|
6397
6727
|
}
|
|
6398
6728
|
}
|
|
6399
6729
|
debug.event("provider-response", {
|
|
6400
6730
|
sid: ctx.sessionManager.getSessionId(),
|
|
6401
6731
|
status: event.status,
|
|
6402
6732
|
requestId: event.requestId ?? null,
|
|
6403
|
-
cache
|
|
6733
|
+
cache: cache2
|
|
6404
6734
|
});
|
|
6405
6735
|
});
|
|
6406
6736
|
}
|
|
@@ -6432,6 +6762,16 @@ function nudgeMessage(nudge, blocks, prompts, example) {
|
|
|
6432
6762
|
timestamp: Date.now()
|
|
6433
6763
|
};
|
|
6434
6764
|
}
|
|
6765
|
+
function holdText(streak) {
|
|
6766
|
+
return `[ACP hold] Your last ${streak} compress calls were rejected in a row. Do NOT call compress again now and do NOT retry the same range \u2014 the compress reminder is suspended until context actually changes. Continue the actual task. If context still needs relief, run acp_status first and target ONLY a range that meets the minimum size.`;
|
|
6767
|
+
}
|
|
6768
|
+
function holdMessage(streak) {
|
|
6769
|
+
return {
|
|
6770
|
+
role: "user",
|
|
6771
|
+
content: [{ type: "text", text: holdText(streak) }],
|
|
6772
|
+
timestamp: Date.now()
|
|
6773
|
+
};
|
|
6774
|
+
}
|
|
6435
6775
|
export {
|
|
6436
6776
|
createAcpExtension,
|
|
6437
6777
|
index_default as default
|