pi-invisible-continue 0.2.1 → 0.2.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.
Files changed (2) hide show
  1. package/continue.ts +36 -11
  2. package/package.json +1 -1
package/continue.ts CHANGED
@@ -42,8 +42,18 @@ Agent.prototype.subscribe = function (this: Agent, ...args: any[]) {
42
42
  let _continueInProgress = false;
43
43
 
44
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".
45
+ // our mutex AND can convert the "Cannot continue from assistant" error
46
+ // into a prompt([]) call when the agent was mid-task.
47
+ // Without this, the session's continue() would throw when the last message
48
+ // is an assistant (common after compaction), and the agent loop would die
49
+ // leaving mid-task work unfinished.
50
+ //
51
+ // When continue() throws "Cannot continue from message role: assistant":
52
+ // - stopReason "stop" → agent finished cleanly, don't continue
53
+ // - stopReason "aborted" → user cancelled, don't continue
54
+ // - stopReason "error" → pi-retry handles errors, don't race it
55
+ // - stopReason "toolUse" or "length" → mid-task, fall back to prompt([])
56
+ //
47
57
  // Chains the previous patch (pi-retry, pi-vcc) so all mutexes are respected.
48
58
  const _origContinue = Agent.prototype.continue;
49
59
  Agent.prototype.continue = function (this: Agent) {
@@ -56,15 +66,30 @@ Agent.prototype.continue = function (this: Agent) {
56
66
  await _origContinue.call(self);
57
67
  } catch (e: any) {
58
68
  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
- ) {
69
+ if (msg.includes('Cannot continue from message role') ||
70
+ msg.includes('Cannot continue from an assistant message')) {
71
+ // Check stopReason only continue if the agent was mid-task
72
+ const lastMsg = self.state.messages[self.state.messages.length - 1];
73
+ if (lastMsg?.role === 'assistant' &&
74
+ lastMsg.stopReason !== 'stop' &&
75
+ lastMsg.stopReason !== 'aborted' &&
76
+ lastMsg.stopReason !== 'error') {
77
+ // Agent was mid-task — fall back to prompt([])
78
+ if (!_continueInProgress) {
79
+ _continueInProgress = true;
80
+ try {
81
+ await self.prompt([]);
82
+ } catch {
83
+ // Agent already processing or other transient error
84
+ } finally {
85
+ _continueInProgress = false;
86
+ }
87
+ }
88
+ }
89
+ // For stop/aborted/error: return void, the session loop exits naturally
90
+ return;
91
+ }
92
+ if (msg.includes('Agent is already processing')) {
68
93
  return;
69
94
  }
70
95
  throw e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-invisible-continue",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Invisible session continuation for pi — resume the agentic loop without sending ANY prompt the LLM can see",
5
5
  "type": "module",
6
6
  "author": "Tom X Nguyen",