billion-context-pi 0.1.62 → 0.1.63-pr.352.144
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/config.d.ts +10 -0
- package/dist/degeneration.d.ts +98 -0
- package/dist/index.js +209 -12
- package/dist/index.js.map +1 -1
- package/dist/reasoning-drop.d.ts +13 -6
- package/dist/user-config.d.ts +4 -1
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Config, type Prompts } from "acp-kernel";
|
|
2
2
|
import type { CompressReasoningConfig } from "./reasoning-drop.js";
|
|
3
|
+
import type { DegenerationGuardConfig } from "./degeneration.js";
|
|
3
4
|
import type { ThrottleRetryConfig } from "./throttle-retry.js";
|
|
4
5
|
/** Per-role delegate defaults. Lets long-lived automation pin a cheaper or
|
|
5
6
|
* more capable model and a thinking level per delegate role, so the main
|
|
@@ -222,6 +223,15 @@ export interface AdapterConfig {
|
|
|
222
223
|
* warn=3, abort=5. Stops greedy small models looping on byte-identical
|
|
223
224
|
* tool calls (issue #308). */
|
|
224
225
|
repetitionGuard?: boolean | RepetitionGuardConfig;
|
|
226
|
+
/** Character-level degenerate-repeat guard (see DegenerationGuardConfig).
|
|
227
|
+
* Collapses long single-codepoint runs (e.g. 4655×「【」) in assistant
|
|
228
|
+
* text/thinking of the outgoing view and injects a one-shot recovery notice
|
|
229
|
+
* after a degenerated turn — breaking the abort loop where pi replays the
|
|
230
|
+
* degenerated thinking back to the provider on every request (issue #351).
|
|
231
|
+
* Distinct from `repetitionGuard`, which is tool-call level. Accepts a
|
|
232
|
+
* boolean shorthand (`false` disables) or an object. Default: enabled,
|
|
233
|
+
* minRun=200. */
|
|
234
|
+
degenerationGuard?: boolean | DegenerationGuardConfig;
|
|
225
235
|
/** Legacy flat alias for `delegate.displayUsage`. Kept for backward
|
|
226
236
|
* compatibility with existing acp.json files. Prefer `delegate.displayUsage`. */
|
|
227
237
|
displayUsage?: "merged" | "separate";
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { SessionMessageEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
type AgentMessage = SessionMessageEntry["message"];
|
|
3
|
+
/** [#351] Character-level degenerate-repeat guard. Models occasionally
|
|
4
|
+
* degenerate into long single-codepoint runs (observed in the wild: a
|
|
5
|
+
* 5968-char thinking block ending in 4655 consecutive 「【」, escalating over
|
|
6
|
+
* turns until the turn aborts). This is distinct from `repetitionGuard`
|
|
7
|
+
* (issue #308), which breaks byte-identical TOOL-CALL loops; here the
|
|
8
|
+
* attractor is inside the generated text/thinking itself.
|
|
9
|
+
*
|
|
10
|
+
* Why the adapter must act: pi replays prior assistant thinking back to the
|
|
11
|
+
* provider on every subsequent request (openai-completions sends it as
|
|
12
|
+
* `reasoning_content`, or as plain text when `requiresThinkingAsText`), and
|
|
13
|
+
* an aborted turn's partial message persists in the session log. A
|
|
14
|
+
* degenerated tail therefore rides along every later prompt, where the model
|
|
15
|
+
* sees its own previous output ending in thousands of repeated characters —
|
|
16
|
+
* a continuation bias that re-triggers the same degeneration, aborting the
|
|
17
|
+
* next turn too. The session dies in an abort loop with no recovery path.
|
|
18
|
+
*
|
|
19
|
+
* The pass below collapses runs >= minRun in assistant text/thinking of the
|
|
20
|
+
* OUTGOING view (persisted history is never modified) and appends a one-shot
|
|
21
|
+
* recovery notice while the degenerated message is the most recent assistant
|
|
22
|
+
* turn. Position-based self-limiting: a fresh model turn makes the old
|
|
23
|
+
* message non-last, so the notice stops appearing without any persistent
|
|
24
|
+
* state (and cannot accumulate — #223 lesson). */
|
|
25
|
+
export interface DegenerationGuardConfig {
|
|
26
|
+
/** Master switch. Default: true. `false` disables the pass entirely
|
|
27
|
+
* (kill-switch). */
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
/** Minimum length of a single-codepoint run (counted in codepoints) before
|
|
30
|
+
* it is treated as degeneration. Legitimate single-char runs in coding
|
|
31
|
+
* sessions (markdown hrules, dotted leaders, comment banners) stay well
|
|
32
|
+
* under this; observed pre-degeneration drift maxed at ~60 before the
|
|
33
|
+
* catastrophic 4655×「【」 run. Default: 200. Values below 8 are raised to
|
|
34
|
+
* 8: the collapse marker must stay shorter than any detectable run, and a
|
|
35
|
+
* sub-8 threshold would start matching ordinary dotted leaders/hrules. */
|
|
36
|
+
minRun?: number;
|
|
37
|
+
}
|
|
38
|
+
export declare const DEFAULT_DEGENERATION_GUARD: Required<DegenerationGuardConfig>;
|
|
39
|
+
/** Resolve the guard config, handling the boolean shorthand (`false`
|
|
40
|
+
* disables). Invalid minRun values (< 2 or non-numeric) fall back to the
|
|
41
|
+
* default with a logged warning — they never fail the session. Valid values
|
|
42
|
+
* below MIN_VALID_MIN_RUN are raised to it. */
|
|
43
|
+
export declare function resolveDegenerationGuard(cfg?: boolean | DegenerationGuardConfig): Required<DegenerationGuardConfig>;
|
|
44
|
+
/** One maximal run of a repeated codepoint. */
|
|
45
|
+
export interface DegenerateRun {
|
|
46
|
+
/** The repeated codepoint (string of length 1 or 2 — surrogate pairs kept whole). */
|
|
47
|
+
char: string;
|
|
48
|
+
/** Run length in codepoints. */
|
|
49
|
+
count: number;
|
|
50
|
+
/** UTF-16 index of the run start in the original string. */
|
|
51
|
+
index: number;
|
|
52
|
+
}
|
|
53
|
+
/** Find maximal runs of a single repeated codepoint with length >= minRun.
|
|
54
|
+
* Codepoint-safe: surrogate pairs count as one unit, so a run of astral
|
|
55
|
+
* characters is detected like any other. Returns [] for empty input or
|
|
56
|
+
* minRun < 2 (a "run" shorter than 2 carries no signal). */
|
|
57
|
+
export declare function findDegenerateRuns(text: string, minRun: number): DegenerateRun[];
|
|
58
|
+
/** Collapse every degenerate run in `text` into a short marker that names the
|
|
59
|
+
* character and its original count. Pure, idempotent, fail-safe. Returns the
|
|
60
|
+
* input unchanged when there is nothing to collapse. */
|
|
61
|
+
export declare function collapseDegenerateRuns(text: string, minRun: number): string;
|
|
62
|
+
/** Evidence of one rewritten message: which blocks changed and what was collapsed. */
|
|
63
|
+
export interface CollapseEvidence {
|
|
64
|
+
/** Index of the rewritten message in the input array. */
|
|
65
|
+
msgIndex: number;
|
|
66
|
+
/** Block kinds rewritten ("content" for string content, else "text"/"thinking"). */
|
|
67
|
+
blocks: string[];
|
|
68
|
+
/** Runs collapsed across those blocks. */
|
|
69
|
+
runs: DegenerateRun[];
|
|
70
|
+
}
|
|
71
|
+
/** Request-time pass: collapse degenerate single-codepoint runs in ASSISTANT
|
|
72
|
+
* messages' text/thinking blocks (string content included). Every position is
|
|
73
|
+
* scanned — including the current turn's partial assistant message, which is
|
|
74
|
+
* exactly the one pi will replay back to the provider on the next request.
|
|
75
|
+
* Tool-call arguments are never touched (rewriting them would desync the
|
|
76
|
+
* model's view from the call that actually executed). Pure: returns the same
|
|
77
|
+
* array reference when nothing changed; idempotent; fail-safe (any error
|
|
78
|
+
* returns the input unchanged). Persisted history is never modified. */
|
|
79
|
+
export declare function collapseAssistantDegeneration(messages: AgentMessage[], cfg?: boolean | DegenerationGuardConfig): {
|
|
80
|
+
messages: AgentMessage[];
|
|
81
|
+
evidence: CollapseEvidence[];
|
|
82
|
+
};
|
|
83
|
+
/** Scan backward for the LAST assistant message and collect its degenerate
|
|
84
|
+
* runs. Call it with the PERSISTED originals in session order (not the
|
|
85
|
+
* outgoing view): thinking-only aborted turns never reach the outgoing view
|
|
86
|
+
* (projectMessage drops them), yet they are still "the previous turn".
|
|
87
|
+
* Returns null when absent or clean. Gates the recovery notice: because the
|
|
88
|
+
* notice fires only while the degenerated message is the most recent
|
|
89
|
+
* assistant turn, it self-limits — once the model produces a new turn the
|
|
90
|
+
* old message is no longer last and the notice disappears on the next
|
|
91
|
+
* rebuild (no persistent state needed). */
|
|
92
|
+
export declare function lastAssistantRuns(messages: AgentMessage[], minRun: number): DegenerateRun[] | null;
|
|
93
|
+
/** One-shot recovery notice appended while a degenerated assistant message is
|
|
94
|
+
* the most recent turn: tells the model the repeated segment carries no
|
|
95
|
+
* information and was truncated, and to resume from the last valid step
|
|
96
|
+
* instead of continuing the attractor. */
|
|
97
|
+
export declare function degenerationNotice(runs: DegenerateRun[]): AgentMessage;
|
|
98
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -3982,25 +3982,47 @@ function hasCompressCall(content) {
|
|
|
3982
3982
|
return b2?.type === "toolCall" && b2.name === "compress";
|
|
3983
3983
|
});
|
|
3984
3984
|
}
|
|
3985
|
+
function compressCallIds(content) {
|
|
3986
|
+
if (!Array.isArray(content)) return [];
|
|
3987
|
+
return content.filter((p) => {
|
|
3988
|
+
const b2 = p;
|
|
3989
|
+
return b2?.type === "toolCall" && b2.name === "compress";
|
|
3990
|
+
}).map((p) => p.id).filter((id) => typeof id === "string");
|
|
3991
|
+
}
|
|
3992
|
+
function resultIndexByCallId(messages) {
|
|
3993
|
+
const map = /* @__PURE__ */ new Map();
|
|
3994
|
+
for (let i = 0; i < messages.length; i++) {
|
|
3995
|
+
const msg = messages[i];
|
|
3996
|
+
if (msg?.role !== "toolResult" || typeof msg.toolCallId !== "string") continue;
|
|
3997
|
+
if (!map.has(msg.toolCallId)) map.set(msg.toolCallId, i);
|
|
3998
|
+
}
|
|
3999
|
+
return map;
|
|
4000
|
+
}
|
|
3985
4001
|
function reasoningLength(content) {
|
|
3986
4002
|
if (!Array.isArray(content)) return 0;
|
|
3987
4003
|
return content.reduce((n, p) => isThinking(p) ? n + p.thinking.length : n, 0);
|
|
3988
4004
|
}
|
|
4005
|
+
function countThinkingChars(messages) {
|
|
4006
|
+
return messages.reduce((n, m2) => n + reasoningLength(m2.content), 0);
|
|
4007
|
+
}
|
|
3989
4008
|
function dropCompressReasoning(messages, cfg) {
|
|
3990
4009
|
const { drop, threshold } = resolveReasoningDrop(cfg);
|
|
3991
4010
|
if (!drop || messages.length === 0) return messages;
|
|
3992
4011
|
try {
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
if (messages[i].role === "user") lastUser = i;
|
|
3996
|
-
}
|
|
3997
|
-
if (lastUser < 0) return messages;
|
|
4012
|
+
const last = messages.length - 1;
|
|
4013
|
+
const resultAt = resultIndexByCallId(messages);
|
|
3998
4014
|
let changed = false;
|
|
3999
4015
|
const out = messages.slice();
|
|
4000
|
-
for (let i = 0; i
|
|
4016
|
+
for (let i = 0; i <= last; i++) {
|
|
4001
4017
|
const msg = messages[i];
|
|
4002
4018
|
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
4003
4019
|
if (!hasCompressCall(msg.content)) continue;
|
|
4020
|
+
const ids = compressCallIds(msg.content);
|
|
4021
|
+
const closed = ids.length > 0 && ids.every((id) => {
|
|
4022
|
+
const ri = resultAt.get(id);
|
|
4023
|
+
return ri !== void 0 && ri > i && ri < last;
|
|
4024
|
+
});
|
|
4025
|
+
if (!closed) continue;
|
|
4004
4026
|
if (reasoningLength(msg.content) <= threshold) continue;
|
|
4005
4027
|
out[i] = {
|
|
4006
4028
|
...msg,
|
|
@@ -4717,6 +4739,8 @@ var KNOWN = /* @__PURE__ */ new Set([
|
|
|
4717
4739
|
"displayUsage",
|
|
4718
4740
|
"throttleRetry",
|
|
4719
4741
|
"outputHeadroomMaxPct",
|
|
4742
|
+
"repetitionGuard",
|
|
4743
|
+
"degenerationGuard",
|
|
4720
4744
|
"prompts",
|
|
4721
4745
|
"acknowledgePromptsRisk"
|
|
4722
4746
|
]);
|
|
@@ -16814,7 +16838,7 @@ async function statusReport(runtime, ctx) {
|
|
|
16814
16838
|
const sentTokens = estimateTokens(coreMessages, coveredIds, imageTokens) + systemPromptTokens;
|
|
16815
16839
|
const viewSentTokens = adjustedTokenCount(runtime.core, coreMessages, state, config, sentTokens, imageTokens, systemPromptTokens);
|
|
16816
16840
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: anchorStale ? viewSentTokens : Math.max(viewSentTokens, realUsage?.tokens ?? 0) });
|
|
16817
|
-
const versionStr = "0.1.
|
|
16841
|
+
const versionStr = "0.1.63" ? `billion-context-pi@${"0.1.63"}` : void 0;
|
|
16818
16842
|
let text = buildStatusPanel({
|
|
16819
16843
|
version: versionStr,
|
|
16820
16844
|
tokenCount: sessionTokens,
|
|
@@ -16835,6 +16859,159 @@ async function statusReport(runtime, ctx) {
|
|
|
16835
16859
|
return text;
|
|
16836
16860
|
}
|
|
16837
16861
|
|
|
16862
|
+
// src/degeneration.ts
|
|
16863
|
+
var DEFAULT_DEGENERATION_GUARD = { enabled: true, minRun: 200 };
|
|
16864
|
+
var MIN_VALID_MIN_RUN = 8;
|
|
16865
|
+
function resolveDegenerationGuard(cfg) {
|
|
16866
|
+
if (cfg === false) return { enabled: false, minRun: DEFAULT_DEGENERATION_GUARD.minRun };
|
|
16867
|
+
const c = typeof cfg === "object" && cfg !== null ? cfg : {};
|
|
16868
|
+
let minRun = DEFAULT_DEGENERATION_GUARD.minRun;
|
|
16869
|
+
if (c.minRun !== void 0) {
|
|
16870
|
+
const n = c.minRun;
|
|
16871
|
+
if (typeof n === "number" && Number.isFinite(n) && n >= 2) {
|
|
16872
|
+
minRun = Math.max(MIN_VALID_MIN_RUN, Math.floor(n));
|
|
16873
|
+
} else {
|
|
16874
|
+
logWarn("config", { event: "degeneration-guard-invalid", field: "minRun", value: n, fallback: DEFAULT_DEGENERATION_GUARD.minRun });
|
|
16875
|
+
}
|
|
16876
|
+
}
|
|
16877
|
+
return { enabled: c.enabled !== false, minRun };
|
|
16878
|
+
}
|
|
16879
|
+
function findDegenerateRuns(text, minRun) {
|
|
16880
|
+
const runs2 = [];
|
|
16881
|
+
if (!text || !Number.isFinite(minRun) || minRun < 2) return runs2;
|
|
16882
|
+
const chars = Array.from(text);
|
|
16883
|
+
let utf16 = 0;
|
|
16884
|
+
let k = 0;
|
|
16885
|
+
while (k < chars.length) {
|
|
16886
|
+
const ch = chars[k];
|
|
16887
|
+
let m2 = k + 1;
|
|
16888
|
+
while (m2 < chars.length && chars[m2] === ch) m2++;
|
|
16889
|
+
const count = m2 - k;
|
|
16890
|
+
if (count >= minRun) runs2.push({ char: ch, count, index: utf16 });
|
|
16891
|
+
utf16 += count * ch.length;
|
|
16892
|
+
k = m2;
|
|
16893
|
+
}
|
|
16894
|
+
return runs2;
|
|
16895
|
+
}
|
|
16896
|
+
function describeChar(ch) {
|
|
16897
|
+
if (ch === " ") return "space";
|
|
16898
|
+
if (ch === "\n") return "newline";
|
|
16899
|
+
if (ch === " ") return "tab";
|
|
16900
|
+
if (ch === "\r") return "carriage-return";
|
|
16901
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
16902
|
+
if (cp < 32 || cp === 127) return `U+${cp.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
16903
|
+
return JSON.stringify(ch);
|
|
16904
|
+
}
|
|
16905
|
+
function applyRuns(text, runs2, minRun) {
|
|
16906
|
+
const keep = Math.max(1, Math.min(3, minRun - 1));
|
|
16907
|
+
let out = "";
|
|
16908
|
+
let last = 0;
|
|
16909
|
+
for (const r of runs2) {
|
|
16910
|
+
out += text.slice(last, r.index);
|
|
16911
|
+
out += `${r.char.repeat(keep)}\u2026 [${r.count}\xD7 identical chars cut \u2014 degenerate repeat]`;
|
|
16912
|
+
last = r.index + r.count * r.char.length;
|
|
16913
|
+
}
|
|
16914
|
+
out += text.slice(last);
|
|
16915
|
+
return out;
|
|
16916
|
+
}
|
|
16917
|
+
function collapseAssistantDegeneration(messages, cfg) {
|
|
16918
|
+
const { enabled, minRun } = resolveDegenerationGuard(cfg);
|
|
16919
|
+
if (!enabled || messages.length === 0) return { messages, evidence: [] };
|
|
16920
|
+
try {
|
|
16921
|
+
const evidence = [];
|
|
16922
|
+
let changed = false;
|
|
16923
|
+
const out = messages.slice();
|
|
16924
|
+
for (let i = 0; i < messages.length; i++) {
|
|
16925
|
+
const msg = messages[i];
|
|
16926
|
+
if (msg.role !== "assistant") continue;
|
|
16927
|
+
const c = msg.content;
|
|
16928
|
+
if (typeof c === "string") {
|
|
16929
|
+
const runs3 = findDegenerateRuns(c, minRun);
|
|
16930
|
+
if (runs3.length > 0) {
|
|
16931
|
+
out[i] = { ...msg, content: applyRuns(c, runs3, minRun) };
|
|
16932
|
+
evidence.push({ msgIndex: i, blocks: ["content"], runs: runs3 });
|
|
16933
|
+
changed = true;
|
|
16934
|
+
}
|
|
16935
|
+
continue;
|
|
16936
|
+
}
|
|
16937
|
+
if (!Array.isArray(c)) continue;
|
|
16938
|
+
const blocks = [];
|
|
16939
|
+
const runs2 = [];
|
|
16940
|
+
let blockChanged = false;
|
|
16941
|
+
const nc = c.map((p) => {
|
|
16942
|
+
const b2 = p;
|
|
16943
|
+
if (b2?.type === "text" && typeof b2.text === "string") {
|
|
16944
|
+
const r = findDegenerateRuns(b2.text, minRun);
|
|
16945
|
+
if (r.length > 0) {
|
|
16946
|
+
blockChanged = true;
|
|
16947
|
+
blocks.push("text");
|
|
16948
|
+
runs2.push(...r);
|
|
16949
|
+
return { ...b2, text: applyRuns(b2.text, r, minRun) };
|
|
16950
|
+
}
|
|
16951
|
+
return p;
|
|
16952
|
+
}
|
|
16953
|
+
if (b2?.type === "thinking" && typeof b2.thinking === "string") {
|
|
16954
|
+
const r = findDegenerateRuns(b2.thinking, minRun);
|
|
16955
|
+
if (r.length > 0) {
|
|
16956
|
+
blockChanged = true;
|
|
16957
|
+
blocks.push("thinking");
|
|
16958
|
+
runs2.push(...r);
|
|
16959
|
+
return { ...b2, thinking: applyRuns(b2.thinking, r, minRun) };
|
|
16960
|
+
}
|
|
16961
|
+
return p;
|
|
16962
|
+
}
|
|
16963
|
+
return p;
|
|
16964
|
+
});
|
|
16965
|
+
if (blockChanged) {
|
|
16966
|
+
out[i] = { ...msg, content: nc };
|
|
16967
|
+
evidence.push({ msgIndex: i, blocks, runs: runs2 });
|
|
16968
|
+
changed = true;
|
|
16969
|
+
}
|
|
16970
|
+
}
|
|
16971
|
+
return { messages: changed ? out : messages, evidence };
|
|
16972
|
+
} catch {
|
|
16973
|
+
return { messages, evidence: [] };
|
|
16974
|
+
}
|
|
16975
|
+
}
|
|
16976
|
+
function lastAssistantRuns(messages, minRun) {
|
|
16977
|
+
try {
|
|
16978
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
16979
|
+
const msg = messages[i];
|
|
16980
|
+
if (msg.role !== "assistant") continue;
|
|
16981
|
+
const runs2 = [];
|
|
16982
|
+
const c = msg.content;
|
|
16983
|
+
if (typeof c === "string") {
|
|
16984
|
+
runs2.push(...findDegenerateRuns(c, minRun));
|
|
16985
|
+
} else if (Array.isArray(c)) {
|
|
16986
|
+
for (const p of c) {
|
|
16987
|
+
const b2 = p;
|
|
16988
|
+
if (b2?.type === "text" && typeof b2.text === "string") runs2.push(...findDegenerateRuns(b2.text, minRun));
|
|
16989
|
+
else if (b2?.type === "thinking" && typeof b2.thinking === "string") runs2.push(...findDegenerateRuns(b2.thinking, minRun));
|
|
16990
|
+
}
|
|
16991
|
+
}
|
|
16992
|
+
return runs2.length > 0 ? runs2 : null;
|
|
16993
|
+
}
|
|
16994
|
+
return null;
|
|
16995
|
+
} catch {
|
|
16996
|
+
return null;
|
|
16997
|
+
}
|
|
16998
|
+
}
|
|
16999
|
+
function degenerationNotice(runs2) {
|
|
17000
|
+
const sorted = [...runs2].sort((a, b2) => b2.count - a.count);
|
|
17001
|
+
const top = sorted[0];
|
|
17002
|
+
const extra = sorted.length > 1 ? ` and ${sorted.length - 1} other repeated segment(s)` : "";
|
|
17003
|
+
return {
|
|
17004
|
+
role: "user",
|
|
17005
|
+
content: [
|
|
17006
|
+
{
|
|
17007
|
+
type: "text",
|
|
17008
|
+
text: `[ACP recovery notice] Your previous turn ended in degenerate generation: its output contained ${top.count} consecutive repetitions of ${describeChar(top.char)}${extra}. That repeated segment carries no information and has been truncated in the context above. Do not reproduce it or continue the pattern. Resume your task from your last valid step.`
|
|
17009
|
+
}
|
|
17010
|
+
],
|
|
17011
|
+
timestamp: Date.now()
|
|
17012
|
+
};
|
|
17013
|
+
}
|
|
17014
|
+
|
|
16838
17015
|
// src/system-prompt.ts
|
|
16839
17016
|
function buildAcpSystemPrompt(prompts) {
|
|
16840
17017
|
return `
|
|
@@ -17348,7 +17525,7 @@ async function autoInstallLatest(latest, extDirOverride) {
|
|
|
17348
17525
|
"--no-fund",
|
|
17349
17526
|
"--no-save"
|
|
17350
17527
|
];
|
|
17351
|
-
const prevVersion = (await readPackageJson(join11(extDir, "package.json")))?.version ?? "0.1.
|
|
17528
|
+
const prevVersion = (await readPackageJson(join11(extDir, "package.json")))?.version ?? "0.1.63";
|
|
17352
17529
|
const { code, stderr } = await runNpmImpl(installArgs(latest), { cwd: npmDir, timeout: 6e4 });
|
|
17353
17530
|
if (code !== 0) {
|
|
17354
17531
|
logWarn("update", {
|
|
@@ -17366,7 +17543,7 @@ async function autoInstallLatest(latest, extDirOverride) {
|
|
|
17366
17543
|
}
|
|
17367
17544
|
const verify = await verifyInstall(npmDir, latest);
|
|
17368
17545
|
if (!verify.ok) {
|
|
17369
|
-
const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.
|
|
17546
|
+
const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.63";
|
|
17370
17547
|
logWarn("update", { event: "auto-install-verify-failed", latest, reason: verify.reason, rollbackTo });
|
|
17371
17548
|
const rb = await runNpmImpl(installArgs(rollbackTo), { cwd: npmDir, timeout: 6e4 });
|
|
17372
17549
|
logInfo("update", { event: "rollback", from: latest, to: rollbackTo, ok: rb.code === 0 });
|
|
@@ -17441,7 +17618,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
17441
17618
|
}
|
|
17442
17619
|
const latest = await fetchLatestVersion(tag);
|
|
17443
17620
|
if (!latest) return;
|
|
17444
|
-
const current = runtimeVersion ?? "0.1.
|
|
17621
|
+
const current = runtimeVersion ?? "0.1.63";
|
|
17445
17622
|
const hasUpdate = isVersionNewer(latest, current);
|
|
17446
17623
|
debug.event("update-check", {
|
|
17447
17624
|
current,
|
|
@@ -17619,7 +17796,7 @@ function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
|
|
|
17619
17796
|
setDelegatePolicy(DEFAULT_DELEGATE_POLICY);
|
|
17620
17797
|
const sid = ctx.sessionManager.getSessionId();
|
|
17621
17798
|
const modelInfo = ctx.model;
|
|
17622
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.
|
|
17799
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.63" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
|
|
17623
17800
|
try {
|
|
17624
17801
|
await runtime.reloadConfig(ctx.cwd);
|
|
17625
17802
|
const delegateCfg = resolveDelegate(runtime.adapter);
|
|
@@ -17662,6 +17839,7 @@ function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
|
|
|
17662
17839
|
closeLogStream();
|
|
17663
17840
|
});
|
|
17664
17841
|
}
|
|
17842
|
+
var lastDegNoticeKey = null;
|
|
17665
17843
|
function wireContextTransform(pi, runtime, standDownIfProxied) {
|
|
17666
17844
|
pi.on("context", async (event, ctx) => {
|
|
17667
17845
|
if (runtime.refused) return;
|
|
@@ -17770,9 +17948,28 @@ function wireContextTransform(pi, runtime, standDownIfProxied) {
|
|
|
17770
17948
|
const reasoningDrop = runtime.reasoningDropFor(ctx);
|
|
17771
17949
|
const droppedThinking = dropCompressReasoning(rebuilt, reasoningDrop);
|
|
17772
17950
|
if (droppedThinking !== rebuilt) {
|
|
17773
|
-
|
|
17951
|
+
const droppedChars = countThinkingChars(rebuilt) - countThinkingChars(droppedThinking);
|
|
17952
|
+
debug.event("reasoning-drop", { sid, droppedChars, drop: reasoningDrop.drop, threshold: reasoningDrop.threshold });
|
|
17774
17953
|
}
|
|
17775
17954
|
rebuilt = droppedThinking;
|
|
17955
|
+
const degCfg = resolveDegenerationGuard(runtime.adapter.degenerationGuard);
|
|
17956
|
+
const tailRuns = degCfg.enabled ? lastAssistantRuns([...originalById.values()], degCfg.minRun) : null;
|
|
17957
|
+
const deg = collapseAssistantDegeneration(rebuilt, degCfg);
|
|
17958
|
+
if (deg.messages !== rebuilt) {
|
|
17959
|
+
rebuilt = deg.messages;
|
|
17960
|
+
const maxRun = deg.evidence.reduce((m2, e) => e.runs.reduce((x2, r) => Math.max(x2, r.count), m2), 0);
|
|
17961
|
+
logWarn("degeneration", { sid, event: "runs-collapsed", msgs: deg.evidence.length, maxRun, minRun: degCfg.minRun });
|
|
17962
|
+
debug.event("degeneration-collapsed", { sid, msgs: deg.evidence.length, maxRun });
|
|
17963
|
+
}
|
|
17964
|
+
if (tailRuns) {
|
|
17965
|
+
rebuilt.push(degenerationNotice(tailRuns));
|
|
17966
|
+
const top = [...tailRuns].sort((a, b2) => b2.count - a.count)[0];
|
|
17967
|
+
logInfo("degeneration", { sid, event: "recovery-notice", maxRun: top.count });
|
|
17968
|
+
if (ctx.hasUI && lastDegNoticeKey !== `${sid}:${top.count}:${top.char.codePointAt(0)}`) {
|
|
17969
|
+
lastDegNoticeKey = `${sid}:${top.count}:${top.char.codePointAt(0)}`;
|
|
17970
|
+
ctx.ui.notify(`[ACP] previous turn ended in degenerate generation (${top.count}\xD7 repeat) \u2014 the repeated segment was truncated above and a recovery notice injected.`);
|
|
17971
|
+
}
|
|
17972
|
+
}
|
|
17776
17973
|
const debugOn2 = debug.enabled;
|
|
17777
17974
|
const turnKey = lastUserMessageId(entries) ?? sid;
|
|
17778
17975
|
const compressOutcomes = collectCompressOutcomes(entries, turnStartIndex(entries));
|