pi-invisible-continue 0.2.0 → 0.2.1
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/README.md +1 -1
- package/continue.ts +70 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ User types "/continue"
|
|
|
60
60
|
|
|
61
61
|
### Trade-off: bypasses AgentSession
|
|
62
62
|
|
|
63
|
-
`agent.prompt([])` is called on the `Agent` directly, bypassing `AgentSession._runAgentPrompt()`. This means auto-
|
|
63
|
+
`agent.prompt([])` is called on the `Agent` directly, bypassing `AgentSession._runAgentPrompt()`. This means auto-compaction is not triggered after a `/continue`. Auto-retry is not a concern — [pi-retry](https://github.com/monotykamary/pi-retry) covers that gap by listening for `agent_end` events and re-triggering the loop on errors. Since the agent still emits events through `AgentSession`'s subscriber, pi-retry's handlers fire normally even after an `agent.prompt([])` continuation.
|
|
64
64
|
|
|
65
65
|
---
|
|
66
66
|
|
package/continue.ts
CHANGED
|
@@ -9,19 +9,24 @@ import {
|
|
|
9
9
|
* pi-invisible-continue — resume the agentic loop without the LLM seeing any new prompt.
|
|
10
10
|
*
|
|
11
11
|
* Strategy:
|
|
12
|
-
* - Monkey-patch Agent.prototype.
|
|
12
|
+
* - Monkey-patch Agent.prototype.subscribe to capture the Agent instance
|
|
13
13
|
* - /continue calls agent.prompt([]) directly, starting a fresh agent loop
|
|
14
14
|
* with an empty prompt — no message is injected into context at all
|
|
15
15
|
* - The LLM receives the exact same message list it had before
|
|
16
16
|
* - No session JSONL artifact, no convertToLlm involvement, no filter needed
|
|
17
17
|
*
|
|
18
|
-
* This bypasses AgentSession._runAgentPrompt, so auto-
|
|
19
|
-
*
|
|
18
|
+
* This bypasses AgentSession._runAgentPrompt, so auto-compaction is not
|
|
19
|
+
* triggered after a manual /continue. Auto-retry is not a concern — pi-retry
|
|
20
|
+
* covers that gap via its agent_end handler, which still fires because the
|
|
21
|
+
* agent's processEvents propagates to AgentSession's subscriber normally.
|
|
20
22
|
*/
|
|
21
23
|
|
|
22
24
|
// Capture the live Agent instance when AgentSession subscribes to it.
|
|
23
25
|
// subscribe() is called during AgentSession construction — fires on both
|
|
24
26
|
// fresh sessions and session resumes, unlike prompt().
|
|
27
|
+
//
|
|
28
|
+
// Chain the previous patch (if pi-retry or pi-vcc already patched it)
|
|
29
|
+
// so all extensions can coexist.
|
|
25
30
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
31
|
let _agent: Agent | null = null;
|
|
27
32
|
const _origSubscribe = Agent.prototype.subscribe as (this: Agent, ...args: any[]) => any;
|
|
@@ -30,6 +35,43 @@ Agent.prototype.subscribe = function (this: Agent, ...args: any[]) {
|
|
|
30
35
|
return _origSubscribe.apply(this, args);
|
|
31
36
|
};
|
|
32
37
|
|
|
38
|
+
// Mutex: only one invisible continue may be in-flight at a time.
|
|
39
|
+
// Without this, concurrent /continue (or /continue during pi-retry/pi-vcc
|
|
40
|
+
// auto-continuation) race through waitForIdle() and both call prompt([]),
|
|
41
|
+
// producing "Agent is already processing".
|
|
42
|
+
let _continueInProgress = false;
|
|
43
|
+
|
|
44
|
+
// Monkey-patch continue() so the session's built-in loop cooperates with
|
|
45
|
+
// our mutex. Without this, the session's continue() could race our
|
|
46
|
+
// prompt([]) call and throw "Agent is already processing".
|
|
47
|
+
// Chains the previous patch (pi-retry, pi-vcc) so all mutexes are respected.
|
|
48
|
+
const _origContinue = Agent.prototype.continue;
|
|
49
|
+
Agent.prototype.continue = function (this: Agent) {
|
|
50
|
+
const self = this;
|
|
51
|
+
return (async (): Promise<void> => {
|
|
52
|
+
while (_continueInProgress) {
|
|
53
|
+
await new Promise(r => setTimeout(r, 10));
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await _origContinue.call(self);
|
|
57
|
+
} catch (e: any) {
|
|
58
|
+
const msg = e?.message ?? '';
|
|
59
|
+
// After an invisible continue finishes, the transcript ends with a
|
|
60
|
+
// fresh assistant message. The session's continue() sees this and
|
|
61
|
+
// would throw. Catch and swallow — the while-loop will poll
|
|
62
|
+
// _handlePostAgentRun() again, find no error, and exit cleanly.
|
|
63
|
+
if (
|
|
64
|
+
msg.includes('Cannot continue from message role') ||
|
|
65
|
+
msg.includes('Cannot continue from an assistant message') ||
|
|
66
|
+
msg.includes('Agent is already processing')
|
|
67
|
+
) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
throw e;
|
|
71
|
+
}
|
|
72
|
+
})();
|
|
73
|
+
};
|
|
74
|
+
|
|
33
75
|
export default function (pi: ExtensionAPI) {
|
|
34
76
|
pi.registerCommand("continue", {
|
|
35
77
|
description: CONTINUE_COMMAND_DESCRIPTION,
|
|
@@ -37,6 +79,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
37
79
|
await runContinueCommand(ctx, args);
|
|
38
80
|
},
|
|
39
81
|
});
|
|
82
|
+
|
|
83
|
+
pi.on("session_start", () => {
|
|
84
|
+
_continueInProgress = false;
|
|
85
|
+
});
|
|
40
86
|
}
|
|
41
87
|
|
|
42
88
|
async function runContinueCommand(
|
|
@@ -82,5 +128,25 @@ async function runContinueCommand(
|
|
|
82
128
|
await ctx.waitForIdle();
|
|
83
129
|
}
|
|
84
130
|
|
|
85
|
-
|
|
131
|
+
// Guard: if pi-retry or pi-vcc already has an invisible continue in-flight,
|
|
132
|
+
// skip — their prompt([]) will resume the loop.
|
|
133
|
+
if (_continueInProgress) {
|
|
134
|
+
ctx.ui.notify(
|
|
135
|
+
"pi-invisible-continue: Another invisible continue is already in progress.",
|
|
136
|
+
"info",
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
_continueInProgress = true;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
await _agent.waitForIdle();
|
|
144
|
+
try {
|
|
145
|
+
await _agent.prompt([]);
|
|
146
|
+
} catch {
|
|
147
|
+
// Agent is already processing — something else is driving.
|
|
148
|
+
}
|
|
149
|
+
} finally {
|
|
150
|
+
_continueInProgress = false;
|
|
151
|
+
}
|
|
86
152
|
}
|
package/package.json
CHANGED