[Sync] Lua Hyprland chain, licence cleanup

This commit is contained in:
Coja committed 2026-11-09 18:25:35 +01:00
1 parent 93b4f695ce
commit 47166d877f
183 files changed
+5618 -5839

No files matched your search

+78 -18
View File
@@ -3,31 +3,94 @@
*
* Compaction is otherwise silent, which is why "compacting fails a lot" took code
* reading and arithmetic to diagnose rather than a glance at a log. This makes it
* visible, and specifically names the failure mode that was breaking it here before
* 2026-08-16.
* visible and, for the one reason that signals trouble, says WHICH trouble.
*
* pi reports a `reason` on every compaction:
* "manual" - you ran /compact
* "threshold" - the healthy path: contextTokens passed contextWindow - reserveTokens
* "overflow" - the window was ALREADY full; this is recovery, not prevention
* "overflow" - recovery, not prevention: the previous assistant turn ended badly,
* pi compacts and (when `willRetry`) re-runs that turn once
*
* An "overflow" compaction means the turn blew past contextWindow before the threshold
* could fire, which happens whenever `compaction.reserveTokens` is smaller than the
* model's `maxTokens`. pi then gets one recovery attempt, summarizing from an
* already-overflowed context — and when that does not fit either, the session dies with
* "Context overflow recovery failed after one compact-and-retry attempt".
* Since pi 0.84.3 "overflow" covers TWO different situations, and the numbers on the
* last assistant message tell them apart (CLAUDE.md > Compaction tuning has the
* arithmetic for both):
*
* So: seeing "threshold" is fine. Seeing "overflow" means the settings are wrong for the
* model in use, and this says so at the moment it happens instead of leaving you to
* infer it later. See CLAUDE.md > Compaction tuning.
* 1. A real overflow — the server rejected the prompt (stopReason "error" matching
* pi's overflow patterns), or reported more input than the declared window.
* Fix: contextWindow / reserveTokens / maxTokens are wrong for the model in use.
* 2. Truncated-reply recovery — the reply stopped with stopReason "length" while
* usage.output is BELOW the model's maxTokens. The request itself carried a smaller
* max_tokens: before every call pi-ai clamps it to
* `contextWindow − its own token estimate − 4096` (clampMaxTokensToContext,
* CONTEXT_SAFETY_TOKENS) and then shrinks the thinking budget so that only 1024
* answer tokens remain. The window was never full. On a 24k window with maxTokens
* 4096 the clamp bites at any estimate above 16384 — the same number as the
* compaction threshold with reserveTokens 8192 — so it hits in the gaps the threshold
* check never sees: the first request after a compaction (no server usage to anchor
* on, so the system prompt and the tool schemas join the estimate) and the first call
* of a user turn (the new message). Thinking models feel it first: 2048 of thinking
* leaves a clamped reply about 1024 tokens of answer, and a `write` payload is cut.
*
* The old message here blamed every "overflow" on `reserveTokens < maxTokens`, which is
* wrong for case 2 — the one that actually shows up (Ornith, 2026-10-22). Now the
* notification names the case and prints the numbers (output vs maxTokens, context vs
* window, the estimate the clamp implies), so the next occurrence is diagnosable from the
* screen instead of from a session file.
*
* Note there is no event for a *failed* auto-compaction — `onError` exists only on the
* manual `ctx.compact()` call — so catching the overflow entry is the earliest reliable
* warning available.
* warning available. Observe-only: it never alters the compaction itself.
*/
import type { AssistantMessage } from "@earendil-works/pi-ai";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
/** pi-ai's CONTEXT_SAFETY_TOKENS: the margin clampMaxTokensToContext keeps below the window. */
const PI_CONTEXT_SAFETY_TOKENS = 4096;
/** Last assistant message on the branch — the turn that triggered the recovery. */
function lastAssistant(entries: readonly unknown[]): AssistantMessage | undefined {
for (let i = entries.length - 1; i >= 0; i--) {
const e = entries[i] as { type?: string; message?: { role?: string } };
if (e.type === "message" && e.message?.role === "assistant") return e.message as AssistantMessage;
}
return undefined;
}
/** One line saying which kind of "overflow" this is, with the numbers that prove it. */
export function describeOverflow(last: AssistantMessage | undefined, window: number, maxTokens: number): string {
if (!last) return "no assistant message on the branch to inspect.";
const u = last.usage;
const output = u?.output ?? 0;
const context = (u?.input ?? 0) + (u?.cacheRead ?? 0) + (u?.cacheWrite ?? 0) + output;
const ctxStr = window > 0 ? `${context}/${window}` : `${context}`;
const checkWindow = "check contextWindow against the server ctx-size (CLAUDE.md > Compaction tuning).";
if (last.stopReason === "error") {
return `the server rejected the request at context ${ctxStr} (${last.errorMessage ?? "no message"}) — a real overflow; ${checkWindow}`;
}
if (last.stopReason === "length") {
if (output === 0 && window > 0 && context >= window * 0.99) {
return `the server produced no tokens at context ${ctxStr} — the prompt alone filled the window; ${checkWindow}`;
}
if (window > 0 && context >= window - 64) {
return (
`the server ran out of context mid-reply (${ctxStr}, output ${output}/${maxTokens}) — the prompt was ` +
`under-estimated (big tool results or images count more than chars ÷ 4); not a reserveTokens problem.`
);
}
if (maxTokens > 0 && output < maxTokens) {
const estimate = window > 0 ? ` pi's estimate at request time ≈ ${window - PI_CONTEXT_SAFETY_TOKENS - output}, server-side context ${ctxStr}.` : "";
return (
`truncated-reply recovery, NOT a full window: the reply stopped at ${output} of ${maxTokens} max tokens, ` +
`so pi had clamped max_tokens to window − estimate − ${PI_CONTEXT_SAFETY_TOKENS}.${estimate} ` +
`reserveTokens is not the knob — see CLAUDE.md > Compaction tuning > max_tokens clamp.`
);
}
}
return `stopReason ${last.stopReason}, context ${ctxStr}, output ${output}/${maxTokens} — input reported above the declared window; ${checkWindow}`;
}
export default function (pi: ExtensionAPI) {
let overflowCount = 0;
@@ -36,12 +99,9 @@ export default function (pi: ExtensionAPI) {
if (event.reason === "overflow") {
overflowCount++;
ctx.ui.notify(
`Compacting in OVERFLOW recovery (#${overflowCount}) — the context window was already ` +
`full before compaction could fire. Raise compaction.reserveTokens above this model's ` +
`maxTokens, or cap maxTokens in models.json. See CLAUDE.md > Compaction tuning.`,
"warning",
);
const model = ctx.model;
const verdict = describeOverflow(lastAssistant(event.branchEntries), model?.contextWindow ?? 0, model?.maxTokens ?? 0);
ctx.ui.notify(`Compacting in OVERFLOW recovery (#${overflowCount}) — ${verdict}`, "warning");
}
// Never alter the compaction itself — observe only.