@monotykamary/pi-vcc 0.7.1 → 0.7.2
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/package.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const CODEX_OUTPUT_LIMIT_COMPACT_INSTRUCTION = "__pi_vcc_codex_output_limit__";
|
|
2
|
+
|
|
3
|
+
/** Identify the Codex provider error used when a response reaches its output cap. */
|
|
4
|
+
export const isCodexOutputLimitError = (message: unknown): boolean => {
|
|
5
|
+
if (!message || typeof message !== "object") return false;
|
|
6
|
+
|
|
7
|
+
const candidate = message as {
|
|
8
|
+
role?: unknown;
|
|
9
|
+
provider?: unknown;
|
|
10
|
+
api?: unknown;
|
|
11
|
+
stopReason?: unknown;
|
|
12
|
+
errorMessage?: unknown;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const isCodexMessage =
|
|
16
|
+
candidate.provider === "openai-codex" ||
|
|
17
|
+
candidate.api === "openai-codex-responses";
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
candidate.role === "assistant" &&
|
|
21
|
+
isCodexMessage &&
|
|
22
|
+
candidate.stopReason === "error" &&
|
|
23
|
+
typeof candidate.errorMessage === "string" &&
|
|
24
|
+
/maximum output token limit/i.test(candidate.errorMessage)
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { writeFileSync } from "fs";
|
|
3
3
|
import { compile, type CompileInput } from "../core/summarize";
|
|
4
|
+
import {
|
|
5
|
+
CODEX_OUTPUT_LIMIT_COMPACT_INSTRUCTION,
|
|
6
|
+
isCodexOutputLimitError,
|
|
7
|
+
} from "../core/codex-output-limit";
|
|
4
8
|
import { loadSettings, type PiVccSettings } from "../core/settings";
|
|
5
9
|
import { triggerInvisibleContinue } from "../core/invisible-continue";
|
|
6
10
|
import { countPiVccCompactionsFromSession, ordinalSuffix } from "../core/compaction-count";
|
|
@@ -16,6 +20,7 @@ export interface CompactionStats {
|
|
|
16
20
|
|
|
17
21
|
let lastStats: CompactionStats | null = null;
|
|
18
22
|
let lastCompactWasPiVcc = false;
|
|
23
|
+
let lastCompactWasCodexOutputLimit = false;
|
|
19
24
|
let lastCompactHandledByVcc = false;
|
|
20
25
|
export const getLastCompactionStats = () => lastStats;
|
|
21
26
|
|
|
@@ -387,6 +392,20 @@ const REASON_MESSAGES: Record<OwnCutCancelReason, string> = {
|
|
|
387
392
|
too_few_live_messages: "pi-vcc: Too few messages to compact",
|
|
388
393
|
};
|
|
389
394
|
|
|
395
|
+
export const shouldResumeAfterCompaction = (
|
|
396
|
+
lastMsg: unknown,
|
|
397
|
+
allowCodexOutputLimit = false,
|
|
398
|
+
): boolean => {
|
|
399
|
+
if (!lastMsg || typeof lastMsg !== "object") return false;
|
|
400
|
+
const message = lastMsg as { role?: unknown; stopReason?: unknown };
|
|
401
|
+
if (message.role !== "assistant") return false;
|
|
402
|
+
if (message.stopReason === "stop" || message.stopReason === "aborted") return false;
|
|
403
|
+
if (message.stopReason === "error") {
|
|
404
|
+
return allowCodexOutputLimit && isCodexOutputLimitError(lastMsg);
|
|
405
|
+
}
|
|
406
|
+
return true;
|
|
407
|
+
};
|
|
408
|
+
|
|
390
409
|
export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
391
410
|
pi.on("session_before_compact", (event, ctx) => {
|
|
392
411
|
const { preparation, branchEntries, customInstructions } = event;
|
|
@@ -409,10 +428,13 @@ export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
|
409
428
|
// per-model threshold's perspective, but this is preferable to blocking
|
|
410
429
|
// an explicit user action (/compact).
|
|
411
430
|
const isPiVcc = customInstructions === PI_VCC_COMPACT_INSTRUCTION;
|
|
431
|
+
const isCodexOutputLimitCompaction =
|
|
432
|
+
customInstructions === CODEX_OUTPUT_LIMIT_COMPACT_INSTRUCTION;
|
|
433
|
+
const isPiVccHandled = isPiVcc || isCodexOutputLimitCompaction;
|
|
412
434
|
|
|
413
|
-
// Always handle explicit /pi-vcc
|
|
435
|
+
// Always handle explicit /pi-vcc and Codex output-limit markers.
|
|
414
436
|
// Otherwise, only handle when user opted in via settings.
|
|
415
|
-
if (!
|
|
437
|
+
if (!isPiVccHandled && !settings.overrideDefaultCompaction) return;
|
|
416
438
|
|
|
417
439
|
// Budget the kept tail so that summary + kept + system/tools + the model's
|
|
418
440
|
// OUTPUT budget (maxTokens) all fit in the window. Without reserving
|
|
@@ -582,6 +604,7 @@ export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
|
582
604
|
};
|
|
583
605
|
|
|
584
606
|
lastCompactWasPiVcc = isPiVcc;
|
|
607
|
+
lastCompactWasCodexOutputLimit = isCodexOutputLimitCompaction;
|
|
585
608
|
lastCompactHandledByVcc = true;
|
|
586
609
|
|
|
587
610
|
// Signal to neuralwatt-mcr that pi-vcc is handling compaction
|
|
@@ -610,7 +633,9 @@ export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
|
610
633
|
pi.on("session_compact", (event, ctx) => {
|
|
611
634
|
// Only act when pi-vcc drove the compaction
|
|
612
635
|
if (!lastCompactHandledByVcc) return;
|
|
636
|
+
const wasCodexOutputLimitCompaction = lastCompactWasCodexOutputLimit;
|
|
613
637
|
lastCompactHandledByVcc = false;
|
|
638
|
+
lastCompactWasCodexOutputLimit = false;
|
|
614
639
|
|
|
615
640
|
// Fire success toast for /compact path only (delayed to let UI settle).
|
|
616
641
|
// /pi-vcc path uses its own onComplete callback in the command handler.
|
|
@@ -641,12 +666,14 @@ export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
|
641
666
|
// - Last message is user/toolResult (agent can continue naturally)
|
|
642
667
|
// - Last message is assistant with stopReason=stop (task finished)
|
|
643
668
|
// - Last message is assistant with stopReason=aborted (user cancelled)
|
|
644
|
-
// - Last message is assistant with stopReason=error
|
|
645
|
-
//
|
|
669
|
+
// - Last message is assistant with stopReason=error, unless this was
|
|
670
|
+
// the pi-vcc Codex maximum-output recovery compaction
|
|
646
671
|
//
|
|
647
672
|
// We DO continue when:
|
|
648
673
|
// - Last message is assistant with stopReason=toolUse (mid-tool cycle)
|
|
649
674
|
// - Last message is assistant with stopReason=length (hit max tokens)
|
|
675
|
+
// - Last message is the known Codex maximum-output error from the
|
|
676
|
+
// pi-vcc recovery compaction
|
|
650
677
|
// - Compact-all (firstKeptEntryId="") — context is just the summary,
|
|
651
678
|
// the agent needs to re-enter the loop to continue the task
|
|
652
679
|
try {
|
|
@@ -660,21 +687,10 @@ export const registerBeforeCompactHook = (pi: ExtensionAPI) => {
|
|
|
660
687
|
break;
|
|
661
688
|
}
|
|
662
689
|
}
|
|
663
|
-
if (!lastMsg
|
|
664
|
-
|
|
665
|
-
// Agent completed its turn cleanly — no continuation needed
|
|
666
|
-
if (lastMsg.stopReason === "stop") return;
|
|
667
|
-
|
|
668
|
-
// Agent was aborted by user — don't auto-continue
|
|
669
|
-
if (lastMsg.stopReason === "aborted") return;
|
|
670
|
-
|
|
671
|
-
// Agent hit an error — pi-retry handles this via its agent_end handler.
|
|
672
|
-
// If we also fire triggerInvisibleContinue, both extensions race
|
|
673
|
-
// to call prompt([]), causing "Agent is already processing" (wasteful)
|
|
674
|
-
// or a duplicate continuation. Let pi-retry own error retries.
|
|
675
|
-
if (lastMsg.stopReason === "error") return;
|
|
690
|
+
if (!shouldResumeAfterCompaction(lastMsg, wasCodexOutputLimitCompaction)) return;
|
|
676
691
|
|
|
677
|
-
// Agent was mid-task (toolUse or length)
|
|
692
|
+
// Agent was mid-task (toolUse or length). The Codex recovery marker
|
|
693
|
+
// additionally permits its maximum-output error to continue.
|
|
678
694
|
triggerInvisibleContinue();
|
|
679
695
|
} catch {
|
|
680
696
|
// Non-critical — if context inspection fails, don't block compaction
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
CODEX_OUTPUT_LIMIT_COMPACT_INSTRUCTION,
|
|
4
|
+
isCodexOutputLimitError,
|
|
5
|
+
} from "../core/codex-output-limit";
|
|
2
6
|
import { loadSettings, getModelThreshold, resolveTriggerTokens } from "../core/settings";
|
|
3
7
|
|
|
8
|
+
type ProactiveContext = {
|
|
9
|
+
model?: any;
|
|
10
|
+
getContextUsage?: () => any;
|
|
11
|
+
compact?: (options?: any) => void;
|
|
12
|
+
ui?: any;
|
|
13
|
+
};
|
|
14
|
+
|
|
4
15
|
const formatTokens = (n: number): string => {
|
|
5
16
|
if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
|
|
6
17
|
return String(n);
|
|
@@ -36,7 +47,7 @@ export const resetProactiveState = () => {
|
|
|
36
47
|
* if so. Safe to call from multiple event handlers — cooldown prevents
|
|
37
48
|
* double-triggering.
|
|
38
49
|
*/
|
|
39
|
-
const checkAndTrigger = (ctx:
|
|
50
|
+
const checkAndTrigger = (ctx: ProactiveContext, source: string) => {
|
|
40
51
|
const settings = loadSettings();
|
|
41
52
|
const threshold = getModelThreshold(settings, ctx.model);
|
|
42
53
|
|
|
@@ -78,6 +89,32 @@ const checkAndTrigger = (ctx: { model?: any; getContextUsage?: () => any; compac
|
|
|
78
89
|
ctx.compact?.();
|
|
79
90
|
};
|
|
80
91
|
|
|
92
|
+
/** Force compaction for Codex responses that report an output limit as an error. */
|
|
93
|
+
const triggerCodexOutputLimitCompaction = (ctx: ProactiveContext) => {
|
|
94
|
+
if (isCoolingDown()) return;
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
ctx?.ui?.notify?.(
|
|
98
|
+
"pi-vcc: Codex reached its maximum output token limit. Compacting...",
|
|
99
|
+
"info",
|
|
100
|
+
);
|
|
101
|
+
} catch {}
|
|
102
|
+
|
|
103
|
+
setCooldown();
|
|
104
|
+
proactiveTriggerActive = true;
|
|
105
|
+
ctx.compact?.({ customInstructions: CODEX_OUTPUT_LIMIT_COMPACT_INSTRUCTION });
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const lastAssistantMessage = (event: unknown): unknown => {
|
|
109
|
+
const messages = (event as any)?.messages;
|
|
110
|
+
if (!Array.isArray(messages)) return undefined;
|
|
111
|
+
|
|
112
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
113
|
+
if (messages[i]?.role === "assistant") return messages[i];
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
};
|
|
117
|
+
|
|
81
118
|
/**
|
|
82
119
|
* Registers proactive configured compaction thresholds.
|
|
83
120
|
*
|
|
@@ -105,8 +142,14 @@ const checkAndTrigger = (ctx: { model?: any; getContextUsage?: () => any; compac
|
|
|
105
142
|
* "Compacting..." then "Skipped compaction" notifications.
|
|
106
143
|
*/
|
|
107
144
|
export const registerProactiveThresholdHook = (pi: ExtensionAPI) => {
|
|
108
|
-
//
|
|
109
|
-
|
|
145
|
+
// Codex reports some output-limit responses as errors instead of the
|
|
146
|
+
// standard "length" stop reason. The error has no usable context usage,
|
|
147
|
+
// so pi-core cannot discover the need to compact from its normal checks.
|
|
148
|
+
pi.on("agent_end", (event, ctx) => {
|
|
149
|
+
if (isCodexOutputLimitError(lastAssistantMessage(event))) {
|
|
150
|
+
triggerCodexOutputLimitCompaction(ctx);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
110
153
|
checkAndTrigger(ctx, "auto");
|
|
111
154
|
});
|
|
112
155
|
|