@earendil-works/pi-coding-agent 0.80.2 → 0.80.3

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 (118) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/README.md +2 -1
  3. package/dist/cli/file-processor.d.ts.map +1 -1
  4. package/dist/cli/file-processor.js +13 -25
  5. package/dist/cli/file-processor.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +2 -1
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +38 -28
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/extensions/index.d.ts +1 -1
  11. package/dist/core/extensions/index.d.ts.map +1 -1
  12. package/dist/core/extensions/index.js.map +1 -1
  13. package/dist/core/extensions/loader.d.ts.map +1 -1
  14. package/dist/core/extensions/loader.js +4 -0
  15. package/dist/core/extensions/loader.js.map +1 -1
  16. package/dist/core/extensions/types.d.ts +8 -1
  17. package/dist/core/extensions/types.d.ts.map +1 -1
  18. package/dist/core/extensions/types.js.map +1 -1
  19. package/dist/core/http-dispatcher.d.ts.map +1 -1
  20. package/dist/core/http-dispatcher.js +28 -1
  21. package/dist/core/http-dispatcher.js.map +1 -1
  22. package/dist/core/model-resolver.d.ts.map +1 -1
  23. package/dist/core/model-resolver.js +1 -1
  24. package/dist/core/model-resolver.js.map +1 -1
  25. package/dist/core/resource-loader.d.ts.map +1 -1
  26. package/dist/core/resource-loader.js +2 -0
  27. package/dist/core/resource-loader.js.map +1 -1
  28. package/dist/core/session-manager.d.ts +1 -1
  29. package/dist/core/session-manager.d.ts.map +1 -1
  30. package/dist/core/session-manager.js +7 -4
  31. package/dist/core/session-manager.js.map +1 -1
  32. package/dist/core/settings-manager.d.ts +5 -0
  33. package/dist/core/settings-manager.d.ts.map +1 -1
  34. package/dist/core/settings-manager.js +19 -0
  35. package/dist/core/settings-manager.js.map +1 -1
  36. package/dist/core/timings.d.ts +4 -2
  37. package/dist/core/timings.d.ts.map +1 -1
  38. package/dist/core/timings.js +24 -14
  39. package/dist/core/timings.js.map +1 -1
  40. package/dist/core/tools/read.d.ts.map +1 -1
  41. package/dist/core/tools/read.js +12 -25
  42. package/dist/core/tools/read.js.map +1 -1
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/main.d.ts.map +1 -1
  47. package/dist/main.js +16 -4
  48. package/dist/main.js.map +1 -1
  49. package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  50. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  51. package/dist/modes/interactive/components/assistant-message.js +23 -15
  52. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  53. package/dist/modes/interactive/components/extension-editor.d.ts +3 -1
  54. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  55. package/dist/modes/interactive/components/extension-editor.js +12 -3
  56. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  57. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  58. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/settings-selector.js +13 -1
  60. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  61. package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  62. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
  63. package/dist/modes/interactive/components/status-indicator.js +60 -0
  64. package/dist/modes/interactive/components/status-indicator.js.map +1 -0
  65. package/dist/modes/interactive/components/user-message.d.ts +6 -2
  66. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  67. package/dist/modes/interactive/components/user-message.js +19 -6
  68. package/dist/modes/interactive/components/user-message.js.map +1 -1
  69. package/dist/modes/interactive/interactive-mode.d.ts +6 -7
  70. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  71. package/dist/modes/interactive/interactive-mode.js +115 -135
  72. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  73. package/dist/modes/rpc/rpc-client.d.ts +15 -0
  74. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  75. package/dist/modes/rpc/rpc-client.js +14 -0
  76. package/dist/modes/rpc/rpc-client.js.map +1 -1
  77. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  78. package/dist/modes/rpc/rpc-mode.js +16 -0
  79. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  80. package/dist/modes/rpc/rpc-types.d.ts +26 -0
  81. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  82. package/dist/modes/rpc/rpc-types.js.map +1 -1
  83. package/dist/rpc-entry.d.ts +3 -0
  84. package/dist/rpc-entry.d.ts.map +1 -0
  85. package/dist/rpc-entry.js +10 -0
  86. package/dist/rpc-entry.js.map +1 -0
  87. package/dist/utils/image-convert.d.ts +1 -0
  88. package/dist/utils/image-convert.d.ts.map +1 -1
  89. package/dist/utils/image-convert.js +21 -15
  90. package/dist/utils/image-convert.js.map +1 -1
  91. package/dist/utils/image-process.d.ts +18 -0
  92. package/dist/utils/image-process.d.ts.map +1 -0
  93. package/dist/utils/image-process.js +83 -0
  94. package/dist/utils/image-process.js.map +1 -0
  95. package/dist/utils/mime.d.ts.map +1 -1
  96. package/dist/utils/mime.js +41 -0
  97. package/dist/utils/mime.js.map +1 -1
  98. package/docs/extensions.md +14 -0
  99. package/docs/keybindings.md +1 -1
  100. package/docs/providers.md +2 -1
  101. package/docs/rpc.md +58 -0
  102. package/docs/settings.md +10 -0
  103. package/docs/usage.md +2 -2
  104. package/examples/extensions/custom-compaction.ts +1 -0
  105. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  106. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  107. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  108. package/examples/extensions/gondolin/package-lock.json +2 -2
  109. package/examples/extensions/gondolin/package.json +1 -1
  110. package/examples/extensions/handoff.ts +1 -1
  111. package/examples/extensions/qna.ts +1 -1
  112. package/examples/extensions/sandbox/package-lock.json +2 -2
  113. package/examples/extensions/sandbox/package.json +1 -1
  114. package/examples/extensions/summarize.ts +1 -0
  115. package/examples/extensions/with-deps/package-lock.json +2 -2
  116. package/examples/extensions/with-deps/package.json +1 -1
  117. package/npm-shrinkwrap.json +12 -12
  118. package/package.json +8 -5
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
16
  import { basename, dirname } from "node:path";
17
- import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
17
+ import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
18
18
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
19
19
  import { stripFrontmatter } from "../utils/frontmatter.js";
20
20
  import { resolvePath } from "../utils/paths.js";
@@ -120,6 +120,7 @@ export class AgentSession {
120
120
  // Base system prompt (without extension appends) - used to apply fresh appends each turn
121
121
  _baseSystemPrompt = "";
122
122
  _baseSystemPromptOptions;
123
+ _systemPromptOverride;
123
124
  constructor(config) {
124
125
  this.agent = config.agent;
125
126
  this.sessionManager = config.sessionManager;
@@ -139,6 +140,7 @@ export class AgentSession {
139
140
  // (session persistence, extensions, auto-compaction, retry logic)
140
141
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
141
142
  this._installAgentToolHooks();
143
+ this._installAgentNextTurnRefresh();
142
144
  this._buildRuntime({
143
145
  activeToolNames: this._initialActiveToolNames,
144
146
  includeAllExtensionTools: true,
@@ -227,6 +229,26 @@ export class AgentSession {
227
229
  };
228
230
  };
229
231
  }
232
+ _installAgentNextTurnRefresh() {
233
+ const previousPrepareNextTurnWithContext = this.agent.prepareNextTurnWithContext ??
234
+ (this.agent.prepareNextTurn
235
+ ? async (_turn, signal) => await this.agent.prepareNextTurn?.(signal)
236
+ : undefined);
237
+ this.agent.prepareNextTurnWithContext = async (turn, signal) => {
238
+ const previousSnapshot = await previousPrepareNextTurnWithContext?.(turn, signal);
239
+ const previousContext = previousSnapshot?.context ?? turn.context;
240
+ return {
241
+ ...previousSnapshot,
242
+ context: {
243
+ ...previousContext,
244
+ systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
245
+ tools: this.agent.state.tools.slice(),
246
+ },
247
+ model: this.agent.state.model,
248
+ thinkingLevel: this.agent.state.thinkingLevel,
249
+ };
250
+ };
251
+ }
230
252
  // =========================================================================
231
253
  // Event Subscription
232
254
  // =========================================================================
@@ -560,7 +582,7 @@ export class AgentSession {
560
582
  this.agent.state.tools = tools;
561
583
  // Rebuild base system prompt with new tool set
562
584
  this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
563
- this.agent.state.systemPrompt = this._baseSystemPrompt;
585
+ this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
564
586
  }
565
587
  /** Whether compaction or branch summarization is currently running */
566
588
  get isCompacting() {
@@ -668,6 +690,7 @@ export class AgentSession {
668
690
  }
669
691
  }
670
692
  finally {
693
+ this._systemPromptOverride = undefined;
671
694
  this._flushPendingBashMessages();
672
695
  }
673
696
  }
@@ -769,18 +792,11 @@ export class AgentSession {
769
792
  }
770
793
  throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
771
794
  }
772
- // Check if we need to compact before sending (catches aborted responses)
795
+ // Check if we need to compact before sending (catches aborted responses).
796
+ // The user's new prompt is sent below, so do not call agent.continue() here.
773
797
  const lastAssistant = this._findLastAssistantMessage();
774
- if (lastAssistant && (await this._checkCompaction(lastAssistant, false))) {
775
- try {
776
- await this.agent.continue();
777
- while (await this._handlePostAgentRun()) {
778
- await this.agent.continue();
779
- }
780
- }
781
- finally {
782
- this._flushPendingBashMessages();
783
- }
798
+ if (lastAssistant) {
799
+ await this._checkCompaction(lastAssistant, false);
784
800
  }
785
801
  // Build messages array (custom message if any, then user message)
786
802
  messages = [];
@@ -815,11 +831,13 @@ export class AgentSession {
815
831
  }
816
832
  }
817
833
  // Apply extension-modified system prompt, or reset to base
818
- if (result?.systemPrompt) {
834
+ if (result?.systemPrompt !== undefined) {
835
+ this._systemPromptOverride = result.systemPrompt;
819
836
  this.agent.state.systemPrompt = result.systemPrompt;
820
837
  }
821
838
  else {
822
839
  // Ensure we're using the base prompt (in case previous turn had modifications)
840
+ this._systemPromptOverride = undefined;
823
841
  this.agent.state.systemPrompt = this._baseSystemPrompt;
824
842
  }
825
843
  }
@@ -1970,25 +1988,15 @@ export class AgentSession {
1970
1988
  // =========================================================================
1971
1989
  // Auto-Retry
1972
1990
  // =========================================================================
1973
- _isNonRetryableProviderLimitError(errorMessage) {
1974
- return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(errorMessage);
1975
- }
1976
1991
  /**
1977
1992
  * Check if an error is retryable (overloaded, rate limit, server errors).
1978
1993
  * Context overflow errors are NOT retryable (handled by compaction instead).
1979
1994
  */
1980
1995
  _isRetryableError(message) {
1981
- if (message.stopReason !== "error" || !message.errorMessage)
1982
- return false;
1983
- // Context overflow is handled by compaction, not retry
1984
- const contextWindow = this.model?.contextWindow ?? 0;
1985
- if (isContextOverflow(message, contextWindow))
1986
- return false;
1987
- const err = message.errorMessage;
1988
- if (this._isNonRetryableProviderLimitError(err))
1996
+ // Context overflow is handled by compaction, not retry.
1997
+ if (isContextOverflow(message, this.model?.contextWindow ?? 0))
1989
1998
  return false;
1990
- // Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded
1991
- return /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(err);
1999
+ return isRetryableAssistantError(message);
1992
2000
  }
1993
2001
  /**
1994
2002
  * Prepare a retryable error for continuation with exponential backoff.
@@ -2154,7 +2162,9 @@ export class AgentSession {
2154
2162
  */
2155
2163
  setSessionName(name) {
2156
2164
  this.sessionManager.appendSessionInfo(name);
2157
- this._emit({ type: "session_info_changed", name: this.sessionManager.getSessionName() });
2165
+ const event = { type: "session_info_changed", name: this.sessionManager.getSessionName() };
2166
+ this._emit(event);
2167
+ void this._extensionRunner.emit(event);
2158
2168
  }
2159
2169
  // =========================================================================
2160
2170
  // Tree Navigation