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

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 (172) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +2 -2
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +4 -4
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/config-selector.d.ts +4 -2
  7. package/dist/cli/config-selector.d.ts.map +1 -1
  8. package/dist/cli/config-selector.js +1 -1
  9. package/dist/cli/config-selector.js.map +1 -1
  10. package/dist/core/agent-session.d.ts +20 -4
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +95 -22
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/auth-storage.d.ts.map +1 -1
  15. package/dist/core/auth-storage.js +13 -5
  16. package/dist/core/auth-storage.js.map +1 -1
  17. package/dist/core/cache-stats.d.ts +49 -0
  18. package/dist/core/cache-stats.d.ts.map +1 -0
  19. package/dist/core/cache-stats.js +101 -0
  20. package/dist/core/cache-stats.js.map +1 -0
  21. package/dist/core/compaction/compaction.d.ts +1 -2
  22. package/dist/core/compaction/compaction.d.ts.map +1 -1
  23. package/dist/core/compaction/compaction.js +56 -81
  24. package/dist/core/compaction/compaction.js.map +1 -1
  25. package/dist/core/extensions/index.d.ts +1 -1
  26. package/dist/core/extensions/index.d.ts.map +1 -1
  27. package/dist/core/extensions/index.js.map +1 -1
  28. package/dist/core/extensions/loader.d.ts.map +1 -1
  29. package/dist/core/extensions/loader.js +6 -0
  30. package/dist/core/extensions/loader.js.map +1 -1
  31. package/dist/core/extensions/runner.d.ts +5 -3
  32. package/dist/core/extensions/runner.d.ts.map +1 -1
  33. package/dist/core/extensions/runner.js +38 -0
  34. package/dist/core/extensions/runner.js.map +1 -1
  35. package/dist/core/extensions/types.d.ts +33 -11
  36. package/dist/core/extensions/types.d.ts.map +1 -1
  37. package/dist/core/extensions/types.js.map +1 -1
  38. package/dist/core/index.d.ts +1 -1
  39. package/dist/core/index.d.ts.map +1 -1
  40. package/dist/core/index.js.map +1 -1
  41. package/dist/core/keybindings.d.ts.map +1 -1
  42. package/dist/core/keybindings.js +3 -6
  43. package/dist/core/keybindings.js.map +1 -1
  44. package/dist/core/model-registry.d.ts +4 -6
  45. package/dist/core/model-registry.d.ts.map +1 -1
  46. package/dist/core/model-registry.js +34 -8
  47. package/dist/core/model-registry.js.map +1 -1
  48. package/dist/core/model-resolver.d.ts +10 -0
  49. package/dist/core/model-resolver.d.ts.map +1 -1
  50. package/dist/core/model-resolver.js +14 -17
  51. package/dist/core/model-resolver.js.map +1 -1
  52. package/dist/core/package-manager.d.ts +4 -1
  53. package/dist/core/package-manager.d.ts.map +1 -1
  54. package/dist/core/package-manager.js +68 -26
  55. package/dist/core/package-manager.js.map +1 -1
  56. package/dist/core/provider-attribution.d.ts.map +1 -1
  57. package/dist/core/provider-attribution.js +0 -10
  58. package/dist/core/provider-attribution.js.map +1 -1
  59. package/dist/core/resource-loader.d.ts +2 -2
  60. package/dist/core/resource-loader.d.ts.map +1 -1
  61. package/dist/core/resource-loader.js +6 -7
  62. package/dist/core/resource-loader.js.map +1 -1
  63. package/dist/core/sdk.d.ts +1 -1
  64. package/dist/core/sdk.d.ts.map +1 -1
  65. package/dist/core/sdk.js +8 -1
  66. package/dist/core/sdk.js.map +1 -1
  67. package/dist/core/session-manager.d.ts +20 -1
  68. package/dist/core/session-manager.d.ts.map +1 -1
  69. package/dist/core/session-manager.js +97 -68
  70. package/dist/core/session-manager.js.map +1 -1
  71. package/dist/core/settings-manager.d.ts +9 -3
  72. package/dist/core/settings-manager.d.ts.map +1 -1
  73. package/dist/core/settings-manager.js +10 -1
  74. package/dist/core/settings-manager.js.map +1 -1
  75. package/dist/core/slash-commands.d.ts +1 -0
  76. package/dist/core/slash-commands.d.ts.map +1 -1
  77. package/dist/core/slash-commands.js +3 -3
  78. package/dist/core/slash-commands.js.map +1 -1
  79. package/dist/core/tools/bash.d.ts.map +1 -1
  80. package/dist/core/tools/bash.js +20 -5
  81. package/dist/core/tools/bash.js.map +1 -1
  82. package/dist/core/tools/edit.d.ts.map +1 -1
  83. package/dist/core/tools/edit.js +2 -2
  84. package/dist/core/tools/edit.js.map +1 -1
  85. package/dist/index.d.ts +3 -2
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +2 -1
  88. package/dist/index.js.map +1 -1
  89. package/dist/main.d.ts +2 -2
  90. package/dist/main.d.ts.map +1 -1
  91. package/dist/main.js +1 -0
  92. package/dist/main.js.map +1 -1
  93. package/dist/modes/interactive/components/config-selector.d.ts +34 -3
  94. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  95. package/dist/modes/interactive/components/config-selector.js +280 -25
  96. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  97. package/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  98. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
  99. package/dist/modes/interactive/components/custom-entry.js +52 -0
  100. package/dist/modes/interactive/components/custom-entry.js.map +1 -0
  101. package/dist/modes/interactive/components/footer.d.ts +4 -0
  102. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/footer.js +1 -1
  104. package/dist/modes/interactive/components/footer.js.map +1 -1
  105. package/dist/modes/interactive/components/oauth-selector.d.ts +3 -1
  106. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/oauth-selector.js +17 -6
  108. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  109. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  110. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  111. package/dist/modes/interactive/components/settings-selector.js +12 -1
  112. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  113. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  114. package/dist/modes/interactive/components/thinking-selector.js +2 -1
  115. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  116. package/dist/modes/interactive/interactive-mode.d.ts +15 -3
  117. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  118. package/dist/modes/interactive/interactive-mode.js +302 -61
  119. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  120. package/dist/modes/interactive/theme/dark.json +1 -0
  121. package/dist/modes/interactive/theme/light.json +1 -0
  122. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  123. package/dist/modes/interactive/theme/theme.d.ts +3 -2
  124. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  125. package/dist/modes/interactive/theme/theme.js +10 -3
  126. package/dist/modes/interactive/theme/theme.js.map +1 -1
  127. package/dist/modes/print-mode.d.ts.map +1 -1
  128. package/dist/modes/print-mode.js +1 -1
  129. package/dist/modes/print-mode.js.map +1 -1
  130. package/dist/modes/rpc/rpc-client.d.ts +6 -6
  131. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  132. package/dist/modes/rpc/rpc-client.js +3 -3
  133. package/dist/modes/rpc/rpc-client.js.map +1 -1
  134. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  135. package/dist/modes/rpc/rpc-mode.js +4 -1
  136. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  137. package/dist/package-manager-cli.d.ts +2 -2
  138. package/dist/package-manager-cli.d.ts.map +1 -1
  139. package/dist/package-manager-cli.js +71 -17
  140. package/dist/package-manager-cli.js.map +1 -1
  141. package/dist/utils/clipboard-image.d.ts.map +1 -1
  142. package/dist/utils/clipboard-image.js +1 -1
  143. package/dist/utils/clipboard-image.js.map +1 -1
  144. package/docs/custom-provider.md +4 -3
  145. package/docs/extensions.md +75 -18
  146. package/docs/models.md +49 -8
  147. package/docs/packages.md +2 -2
  148. package/docs/rpc.md +15 -5
  149. package/docs/sdk.md +51 -1
  150. package/docs/session-format.md +18 -8
  151. package/docs/settings.md +3 -2
  152. package/docs/themes.md +5 -3
  153. package/docs/tui.md +1 -1
  154. package/docs/usage.md +2 -2
  155. package/examples/extensions/README.md +1 -0
  156. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  157. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  158. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  159. package/examples/extensions/entry-renderer.ts +41 -0
  160. package/examples/extensions/gondolin/package-lock.json +2 -2
  161. package/examples/extensions/gondolin/package.json +1 -1
  162. package/examples/extensions/preset.ts +2 -2
  163. package/examples/extensions/question.ts +1 -0
  164. package/examples/extensions/reload-runtime.ts +2 -2
  165. package/examples/extensions/sandbox/package-lock.json +2 -2
  166. package/examples/extensions/sandbox/package.json +1 -1
  167. package/examples/extensions/with-deps/package-lock.json +2 -2
  168. package/examples/extensions/with-deps/package.json +1 -1
  169. package/examples/sdk/12-full-control.ts +1 -1
  170. package/examples/sdk/README.md +1 -1
  171. package/npm-shrinkwrap.json +12 -12
  172. package/package.json +4 -4
@@ -72,6 +72,9 @@ export class AgentSession {
72
72
  // Event subscription state
73
73
  _unsubscribeAgent;
74
74
  _eventListeners = [];
75
+ _isAgentRunActive = false;
76
+ _idleWaitPromise;
77
+ _resolveIdleWait;
75
78
  /** Tracks pending steering messages for UI display. Removed when delivered. */
76
79
  _steeringMessages = [];
77
80
  /** Tracks pending follow-up messages for UI display. Removed when delivered. */
@@ -265,6 +268,33 @@ export class AgentSession {
265
268
  followUp: [...this._followUpMessages],
266
269
  });
267
270
  }
271
+ _getIdleWaitPromise() {
272
+ if (!this._idleWaitPromise) {
273
+ this._idleWaitPromise = new Promise((resolve) => {
274
+ this._resolveIdleWait = resolve;
275
+ });
276
+ }
277
+ return this._idleWaitPromise;
278
+ }
279
+ _resolveIdleWaitIfIdle() {
280
+ if (this._isAgentRunActive || !this._resolveIdleWait) {
281
+ return;
282
+ }
283
+ const resolve = this._resolveIdleWait;
284
+ this._idleWaitPromise = undefined;
285
+ this._resolveIdleWait = undefined;
286
+ resolve();
287
+ }
288
+ async _emitAgentSettled() {
289
+ this._isAgentRunActive = false;
290
+ try {
291
+ await this._extensionRunner.emit({ type: "agent_settled" });
292
+ this._emit({ type: "agent_settled" });
293
+ }
294
+ finally {
295
+ this._resolveIdleWaitIfIdle();
296
+ }
297
+ }
268
298
  // Track last assistant message for auto-compaction check
269
299
  _lastAssistantMessage = undefined;
270
300
  /** Internal handler for agent events - shared by subscribe and reconnect */
@@ -426,7 +456,16 @@ export class AgentSession {
426
456
  };
427
457
  const replacement = await this._extensionRunner.emitMessageEnd(extensionEvent);
428
458
  if (replacement) {
429
- this._replaceMessageInPlace(event.message, replacement);
459
+ // Untyped extension handlers can return messages with null/missing content;
460
+ // normalize so it never enters agent state or session history.
461
+ const normalized = (replacement.role === "user" ||
462
+ replacement.role === "assistant" ||
463
+ replacement.role === "toolResult" ||
464
+ replacement.role === "custom") &&
465
+ replacement.content == null
466
+ ? { ...replacement, content: [] }
467
+ : replacement;
468
+ this._replaceMessageInPlace(event.message, normalized);
430
469
  }
431
470
  }
432
471
  else if (event.type === "tool_execution_start") {
@@ -529,9 +568,13 @@ export class AgentSession {
529
568
  get thinkingLevel() {
530
569
  return this.agent.state.thinkingLevel;
531
570
  }
532
- /** Whether agent is currently streaming a response */
571
+ /** Whether the session is currently processing an agent run or post-run continuation. */
533
572
  get isStreaming() {
534
- return this.agent.state.isStreaming;
573
+ return this._isAgentRunActive;
574
+ }
575
+ /** Whether the session has no active agent run, retry, auto-compaction, or queued continuation. */
576
+ get isIdle() {
577
+ return !this._isAgentRunActive;
535
578
  }
536
579
  /** Current effective system prompt (includes any per-turn extension modifications) */
537
580
  get systemPrompt() {
@@ -683,6 +726,7 @@ export class AgentSession {
683
726
  // Prompting
684
727
  // =========================================================================
685
728
  async _runAgentPrompt(messages) {
729
+ this._isAgentRunActive = true;
686
730
  try {
687
731
  await this.agent.prompt(messages);
688
732
  while (await this._handlePostAgentRun()) {
@@ -692,6 +736,7 @@ export class AgentSession {
692
736
  finally {
693
737
  this._systemPromptOverride = undefined;
694
738
  this._flushPendingBashMessages();
739
+ await this._emitAgentSettled();
695
740
  }
696
741
  }
697
742
  async _handlePostAgentRun() {
@@ -823,7 +868,8 @@ export class AgentSession {
823
868
  messages.push({
824
869
  role: "custom",
825
870
  customType: msg.customType,
826
- content: msg.content,
871
+ // Untyped extensions can pass null/missing content; normalize at ingestion.
872
+ content: msg.content ?? [],
827
873
  display: msg.display,
828
874
  details: msg.details,
829
875
  timestamp: Date.now(),
@@ -1002,7 +1048,8 @@ export class AgentSession {
1002
1048
  const appMessage = {
1003
1049
  role: "custom",
1004
1050
  customType: message.customType,
1005
- content: message.content,
1051
+ // Untyped extensions can pass null/missing content; normalize at ingestion.
1052
+ content: message.content ?? [],
1006
1053
  display: message.display,
1007
1054
  details: message.details,
1008
1055
  timestamp: Date.now(),
@@ -1100,7 +1147,13 @@ export class AgentSession {
1100
1147
  async abort() {
1101
1148
  this.abortRetry();
1102
1149
  this.agent.abort();
1103
- await this.agent.waitForIdle();
1150
+ await this.waitForIdle();
1151
+ }
1152
+ async waitForIdle() {
1153
+ if (this.isIdle) {
1154
+ return;
1155
+ }
1156
+ await this._getIdleWaitPromise();
1104
1157
  }
1105
1158
  // =========================================================================
1106
1159
  // Model Management
@@ -1785,7 +1838,11 @@ export class AgentSession {
1785
1838
  });
1786
1839
  },
1787
1840
  appendEntry: (customType, data) => {
1788
- this.sessionManager.appendCustomEntry(customType, data);
1841
+ const entryId = this.sessionManager.appendCustomEntry(customType, data);
1842
+ const entry = this.sessionManager.getEntry(entryId);
1843
+ if (entry) {
1844
+ this._emit({ type: "entry_appended", entry });
1845
+ }
1789
1846
  },
1790
1847
  setSessionName: (name) => {
1791
1848
  this.setSessionName(name);
@@ -1811,7 +1868,7 @@ export class AgentSession {
1811
1868
  setThinkingLevel: (level) => this.setThinkingLevel(level),
1812
1869
  }, {
1813
1870
  getModel: () => this.model,
1814
- isIdle: () => !this.isStreaming,
1871
+ isIdle: () => this.isIdle,
1815
1872
  isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
1816
1873
  getSignal: () => this.agent.signal,
1817
1874
  abort: () => {
@@ -2369,28 +2426,44 @@ export class AgentSession {
2369
2426
  return "";
2370
2427
  }
2371
2428
  /**
2372
- * Get session statistics.
2429
+ * Get session statistics. Aggregates over ALL session entries (including
2430
+ * history that was compacted away), so token/cost totals reflect what was
2431
+ * actually billed across the session.
2373
2432
  */
2374
2433
  getSessionStats() {
2375
- const state = this.state;
2376
- const userMessages = state.messages.filter((m) => m.role === "user").length;
2377
- const assistantMessages = state.messages.filter((m) => m.role === "assistant").length;
2378
- const toolResults = state.messages.filter((m) => m.role === "toolResult").length;
2434
+ let userMessages = 0;
2435
+ let assistantMessages = 0;
2436
+ let toolResults = 0;
2437
+ let totalMessages = 0;
2379
2438
  let toolCalls = 0;
2380
2439
  let totalInput = 0;
2381
2440
  let totalOutput = 0;
2382
2441
  let totalCacheRead = 0;
2383
2442
  let totalCacheWrite = 0;
2384
2443
  let totalCost = 0;
2385
- for (const message of state.messages) {
2386
- if (message.role === "assistant") {
2444
+ for (const entry of this.sessionManager.getEntries()) {
2445
+ if (entry.type !== "message")
2446
+ continue;
2447
+ totalMessages++;
2448
+ const message = entry.message;
2449
+ if (message.role === "user") {
2450
+ userMessages++;
2451
+ }
2452
+ else if (message.role === "toolResult") {
2453
+ toolResults++;
2454
+ }
2455
+ else if (message.role === "assistant") {
2456
+ assistantMessages++;
2387
2457
  const assistantMsg = message;
2388
- toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
2389
- totalInput += assistantMsg.usage.input;
2390
- totalOutput += assistantMsg.usage.output;
2391
- totalCacheRead += assistantMsg.usage.cacheRead;
2392
- totalCacheWrite += assistantMsg.usage.cacheWrite;
2393
- totalCost += assistantMsg.usage.cost.total;
2458
+ if (Array.isArray(assistantMsg.content)) {
2459
+ toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
2460
+ }
2461
+ const usage = assistantMsg.usage;
2462
+ totalInput += usage.input;
2463
+ totalOutput += usage.output;
2464
+ totalCacheRead += usage.cacheRead;
2465
+ totalCacheWrite += usage.cacheWrite;
2466
+ totalCost += usage.cost.total;
2394
2467
  }
2395
2468
  }
2396
2469
  return {
@@ -2400,7 +2473,7 @@ export class AgentSession {
2400
2473
  assistantMessages,
2401
2474
  toolCalls,
2402
2475
  toolResults,
2403
- totalMessages: state.messages.length,
2476
+ totalMessages,
2404
2477
  tokens: {
2405
2478
  input: totalInput,
2406
2479
  output: totalOutput,