@earendil-works/pi-coding-agent 0.86.0 → 0.87.0

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 (138) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +37 -0
  3. package/dist/bundle/chunks/{chunk-7YM6BE7Y.js → chunk-4DKZACXI.js} +53 -46
  4. package/dist/bundle/chunks/chunk-GV2E3GBU.js +2 -0
  5. package/dist/bundle/chunks/github-copilot.js +1 -1
  6. package/dist/bundle/chunks/meta.js +2 -0
  7. package/dist/bundle/chunks/{openai-completions-KQPOPEVM.js → openai-completions-XHML6MTL.js} +1 -1
  8. package/dist/bundle/chunks/{virtual-modules-XW5APVUE.js → virtual-modules-BNWPZYDH.js} +1 -1
  9. package/dist/bundle/cli-runtime.js +3 -0
  10. package/dist/bundle/cli.js +4 -2
  11. package/dist/bundle/index.js +1 -1
  12. package/dist/bundle/rpc-entry.js +1 -1
  13. package/dist/cli/args.d.ts.map +1 -1
  14. package/dist/cli/args.js +1 -0
  15. package/dist/cli/args.js.map +1 -1
  16. package/dist/cli/file-processor.d.ts +1 -1
  17. package/dist/cli/file-processor.d.ts.map +1 -1
  18. package/dist/cli/file-processor.js.map +1 -1
  19. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  20. package/dist/core/agent-session-runtime.js +1 -1
  21. package/dist/core/agent-session-runtime.js.map +1 -1
  22. package/dist/core/agent-session.d.ts +27 -3
  23. package/dist/core/agent-session.d.ts.map +1 -1
  24. package/dist/core/agent-session.js +389 -119
  25. package/dist/core/agent-session.js.map +1 -1
  26. package/dist/core/cache-warmer.d.ts +1 -0
  27. package/dist/core/cache-warmer.d.ts.map +1 -1
  28. package/dist/core/cache-warmer.js +15 -1
  29. package/dist/core/cache-warmer.js.map +1 -1
  30. package/dist/core/compaction/compaction.d.ts +3 -1
  31. package/dist/core/compaction/compaction.d.ts.map +1 -1
  32. package/dist/core/compaction/compaction.js +146 -48
  33. package/dist/core/compaction/compaction.js.map +1 -1
  34. package/dist/core/crash-log.d.ts +5 -0
  35. package/dist/core/crash-log.d.ts.map +1 -1
  36. package/dist/core/crash-log.js +68 -0
  37. package/dist/core/crash-log.js.map +1 -1
  38. package/dist/core/export-html/template.js +6 -1
  39. package/dist/core/extensions/index.d.ts +1 -1
  40. package/dist/core/extensions/index.d.ts.map +1 -1
  41. package/dist/core/extensions/index.js.map +1 -1
  42. package/dist/core/extensions/runner.d.ts +16 -3
  43. package/dist/core/extensions/runner.d.ts.map +1 -1
  44. package/dist/core/extensions/runner.js +110 -5
  45. package/dist/core/extensions/runner.js.map +1 -1
  46. package/dist/core/extensions/types.d.ts +77 -6
  47. package/dist/core/extensions/types.d.ts.map +1 -1
  48. package/dist/core/extensions/types.js.map +1 -1
  49. package/dist/core/index.d.ts +1 -1
  50. package/dist/core/index.d.ts.map +1 -1
  51. package/dist/core/index.js.map +1 -1
  52. package/dist/core/model-config.d.ts +52 -0
  53. package/dist/core/model-config.d.ts.map +1 -1
  54. package/dist/core/model-config.js +16 -0
  55. package/dist/core/model-config.js.map +1 -1
  56. package/dist/core/model-resolver.d.ts.map +1 -1
  57. package/dist/core/model-resolver.js +1 -0
  58. package/dist/core/model-resolver.js.map +1 -1
  59. package/dist/core/prompt-templates.d.ts +6 -1
  60. package/dist/core/prompt-templates.d.ts.map +1 -1
  61. package/dist/core/prompt-templates.js +61 -35
  62. package/dist/core/prompt-templates.js.map +1 -1
  63. package/dist/core/provider-composer.d.ts +1 -0
  64. package/dist/core/provider-composer.d.ts.map +1 -1
  65. package/dist/core/provider-composer.js +19 -0
  66. package/dist/core/provider-composer.js.map +1 -1
  67. package/dist/core/resource-loader.d.ts.map +1 -1
  68. package/dist/core/resource-loader.js +6 -2
  69. package/dist/core/resource-loader.js.map +1 -1
  70. package/dist/core/sdk.d.ts.map +1 -1
  71. package/dist/core/sdk.js +3 -4
  72. package/dist/core/sdk.js.map +1 -1
  73. package/dist/core/session-manager.d.ts +36 -9
  74. package/dist/core/session-manager.d.ts.map +1 -1
  75. package/dist/core/session-manager.js +97 -7
  76. package/dist/core/session-manager.js.map +1 -1
  77. package/dist/core/tools/read.d.ts +4 -1
  78. package/dist/core/tools/read.d.ts.map +1 -1
  79. package/dist/core/tools/read.js +5 -1
  80. package/dist/core/tools/read.js.map +1 -1
  81. package/dist/index.d.ts +2 -2
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +1 -1
  84. package/dist/index.js.map +1 -1
  85. package/dist/main.d.ts.map +1 -1
  86. package/dist/main.js +4 -3
  87. package/dist/main.js.map +1 -1
  88. package/dist/modes/interactive/bug-report.d.ts +2 -0
  89. package/dist/modes/interactive/bug-report.d.ts.map +1 -1
  90. package/dist/modes/interactive/bug-report.js +6 -5
  91. package/dist/modes/interactive/bug-report.js.map +1 -1
  92. package/dist/modes/interactive/components/extension-editor.d.ts +4 -1
  93. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  94. package/dist/modes/interactive/components/extension-editor.js +7 -2
  95. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  96. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  97. package/dist/modes/interactive/components/tree-selector.js +7 -0
  98. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  99. package/dist/modes/interactive/interactive-mode.d.ts +5 -1
  100. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  101. package/dist/modes/interactive/interactive-mode.js +74 -5
  102. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  103. package/dist/utils/clipboard-image.d.ts.map +1 -1
  104. package/dist/utils/clipboard-image.js +1 -12
  105. package/dist/utils/clipboard-image.js.map +1 -1
  106. package/dist/utils/clipboard.d.ts.map +1 -1
  107. package/dist/utils/clipboard.js +74 -20
  108. package/dist/utils/clipboard.js.map +1 -1
  109. package/dist/utils/mime.d.ts.map +1 -1
  110. package/dist/utils/mime.js +1 -1
  111. package/dist/utils/mime.js.map +1 -1
  112. package/dist/utils/tool-result-images.d.ts +3 -1
  113. package/dist/utils/tool-result-images.d.ts.map +1 -1
  114. package/dist/utils/tool-result-images.js +4 -1
  115. package/dist/utils/tool-result-images.js.map +1 -1
  116. package/dist/utils/wsl.d.ts +3 -0
  117. package/dist/utils/wsl.d.ts.map +1 -0
  118. package/dist/utils/wsl.js +15 -0
  119. package/dist/utils/wsl.js.map +1 -0
  120. package/docs/compaction.md +23 -4
  121. package/docs/extensions.md +70 -6
  122. package/docs/models.md +29 -1
  123. package/docs/providers.md +8 -0
  124. package/docs/sdk.md +13 -2
  125. package/docs/session-format.md +15 -2
  126. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  127. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  128. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  129. package/examples/extensions/gondolin/package-lock.json +2 -2
  130. package/examples/extensions/gondolin/package.json +1 -1
  131. package/examples/extensions/sandbox/package-lock.json +2 -2
  132. package/examples/extensions/sandbox/package.json +1 -1
  133. package/examples/extensions/with-deps/package-lock.json +2 -2
  134. package/examples/extensions/with-deps/package.json +1 -1
  135. package/examples/plugins/pi-example-plugin/src/session.ts +3 -2
  136. package/npm-shrinkwrap.json +20 -20
  137. package/package.json +8 -8
  138. package/dist/bundle/chunks/chunk-7IOYH2SY.js +0 -2
@@ -6,6 +6,7 @@ import * as crypto from "node:crypto";
6
6
  import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
+ import { isRetryableAssistantError, } from "@earendil-works/pi-ai/compat";
9
10
  import * as TuiLayouts from "@earendil-works/pi-tui";
10
11
  import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
11
12
  import chalk from "chalk";
@@ -15,12 +16,12 @@ import { parseSkillBlock } from "../../core/agent-session.js";
15
16
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
16
17
  import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
17
18
  import { formatCacheWarmingStatus, formatCacheWarmingUsage } from "../../core/cache-warmer.js";
18
- import { recordCrash, takeUnnotifiedCrash } from "../../core/crash-log.js";
19
+ import { findExtensionStackMatches, recordCrash, takeUnnotifiedCrash } from "../../core/crash-log.js";
19
20
  import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
20
21
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
21
22
  import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
22
23
  import { KeybindingsManager } from "../../core/keybindings.js";
23
- import { createCompactionSummaryMessage } from "../../core/messages.js";
24
+ import { createCompactionSummaryMessage, createCustomMessage } from "../../core/messages.js";
24
25
  import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScopeFromModels, } from "../../core/model-resolver.js";
25
26
  import { CredentialSynchronizationError } from "../../core/model-runtime.js";
26
27
  import { DefaultPackageManager } from "../../core/package-manager.js";
@@ -120,6 +121,22 @@ function isDeadTerminalError(error) {
120
121
  const code = error.code;
121
122
  return code !== undefined && DEAD_TERMINAL_ERROR_CODES.has(code);
122
123
  }
124
+ export function formatCrashExtensionHint(extensionMatches) {
125
+ const matches = Array.isArray(extensionMatches)
126
+ ? extensionMatches.filter((match) => typeof match === "string" && match.length > 0)
127
+ : [];
128
+ if (matches.length === 0)
129
+ return undefined;
130
+ const quoted = matches.map((match) => `\`${match}\``);
131
+ const labels = quoted.length === 1
132
+ ? quoted[0]
133
+ : quoted.length === 2
134
+ ? quoted.join(" and ")
135
+ : `${quoted.slice(0, -1).join(", ")}, and ${quoted[quoted.length - 1]}`;
136
+ const noun = matches.length === 1 ? "extension" : "extensions";
137
+ const pronoun = matches.length === 1 ? "it" : "them";
138
+ return `A stack frame came from loaded ${noun} ${labels}, which may be involved. Try disabling ${pronoun} with \`${APP_NAME} config\`, or run \`${APP_NAME} -ne\` to confirm.`;
139
+ }
123
140
  const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage. Disable this warning in /settings.";
124
141
  function isAnthropicSubscriptionAuthKey(apiKey) {
125
142
  return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
@@ -242,6 +259,7 @@ export class InteractiveMode {
242
259
  managedToolStatusStarted = false;
243
260
  // Streaming message tracking
244
261
  streamingComponent = undefined;
262
+ entriesRenderedByBoundaryCompaction = new Set();
245
263
  streamingMessage = undefined;
246
264
  // Tool execution tracking: toolCallId -> component
247
265
  pendingTools = new Map();
@@ -1539,6 +1557,10 @@ export class InteractiveMode {
1539
1557
  async handleFatalRuntimeError(prefix, error) {
1540
1558
  const message = error instanceof Error ? error.message : String(error);
1541
1559
  this.showError(`${prefix}: ${message}`);
1560
+ const extensionHint = this.getCrashExtensionHint(error);
1561
+ if (extensionHint) {
1562
+ this.chatContainer.addChild(new Text(theme.fg("warning", extensionHint), this.outputPad, 0));
1563
+ }
1542
1564
  if (this.recordCrash("fatal_error", error)) {
1543
1565
  this.chatContainer.addChild(new Text(theme.fg("muted", this.crashReportInstructions()), this.outputPad, 0));
1544
1566
  }
@@ -1546,6 +1568,14 @@ export class InteractiveMode {
1546
1568
  this.stop("transcript");
1547
1569
  process.exit(1);
1548
1570
  }
1571
+ getCrashExtensionHint(error) {
1572
+ try {
1573
+ return formatCrashExtensionHint(findExtensionStackMatches(error instanceof Error ? error.stack : undefined, this.session.resourceLoader.getExtensions().extensions));
1574
+ }
1575
+ catch {
1576
+ return undefined;
1577
+ }
1578
+ }
1549
1579
  /** Persist a crash so the next start can point the user at `/bug`. Returns false when nothing was written. */
1550
1580
  recordCrash(kind, error) {
1551
1581
  try {
@@ -1571,6 +1601,13 @@ export class InteractiveMode {
1571
1601
  this.chatContainer.addChild(new Text(theme.fg("muted", `If this looks like a ${APP_NAME} bug, /bug sends a report to the developers.`), this.outputPad, 0));
1572
1602
  this.ui.requestRender();
1573
1603
  }
1604
+ maybeSuggestBugReport(message) {
1605
+ if (message.stopReason !== "error" || isRetryableAssistantError(message))
1606
+ return;
1607
+ if (/\b(?:abort(?:ed)?|cancel(?:l?ed)?)\b/i.test(message.errorMessage ?? ""))
1608
+ return;
1609
+ this.suggestBugReport();
1610
+ }
1574
1611
  renderCurrentSessionState() {
1575
1612
  this.loadedResourcesContainer.clear();
1576
1613
  this.chatContainer.clear();
@@ -2636,6 +2673,8 @@ export class InteractiveMode {
2636
2673
  this.ui.requestRender();
2637
2674
  break;
2638
2675
  case "entry_appended":
2676
+ if (this.entriesRenderedByBoundaryCompaction.delete(event.entry.id))
2677
+ break;
2639
2678
  if (event.entry.type === "custom") {
2640
2679
  this.addCustomEntryToChat(event.entry);
2641
2680
  this.ui.requestRender();
@@ -2644,6 +2683,34 @@ export class InteractiveMode {
2644
2683
  this.addCacheWarmingUsage(event.entry);
2645
2684
  this.ui.requestRender();
2646
2685
  }
2686
+ else if (event.entry.type === "custom_message" && event.entry.display) {
2687
+ this.addMessageToChat(createCustomMessage(event.entry.customType, event.entry.content, event.entry.display, event.entry.details, event.entry.timestamp));
2688
+ this.ui.requestRender();
2689
+ }
2690
+ else if (event.entry.type === "compaction") {
2691
+ const entries = this.sessionManager.buildContextEntries();
2692
+ if (entries[0]?.id !== event.entry.id)
2693
+ break;
2694
+ this.chatContainer.clear();
2695
+ const branch = this.sessionManager.getBranch();
2696
+ const compactionIndex = branch.findIndex((entry) => entry.id === event.entry.id);
2697
+ const entriesAfterCompaction = new Set(branch.slice(compactionIndex + 1).map((entry) => entry.id));
2698
+ const retainedEntries = entries.slice(1);
2699
+ this.renderSessionEntries(retainedEntries.filter((entry) => !entriesAfterCompaction.has(entry.id)));
2700
+ this.addMessageToChat(createCompactionSummaryMessage(event.entry.summary, event.entry.tokensBefore, event.entry.timestamp));
2701
+ if (event.entry.usage) {
2702
+ this.addCompactionCostNotice({
2703
+ type: "compaction_cost",
2704
+ kind: "compaction",
2705
+ usage: event.entry.usage,
2706
+ });
2707
+ }
2708
+ this.renderSessionEntries(retainedEntries.filter((entry) => entriesAfterCompaction.has(entry.id)));
2709
+ for (const entryId of entriesAfterCompaction)
2710
+ this.entriesRenderedByBoundaryCompaction.add(entryId);
2711
+ this.footer.invalidate();
2712
+ this.ui.requestRender();
2713
+ }
2647
2714
  break;
2648
2715
  case "session_info_changed":
2649
2716
  this.updateTerminalTitle();
@@ -2724,8 +2791,7 @@ export class InteractiveMode {
2724
2791
  });
2725
2792
  }
2726
2793
  this.pendingTools.clear();
2727
- if (this.streamingMessage.stopReason === "error")
2728
- this.suggestBugReport();
2794
+ this.maybeSuggestBugReport(this.streamingMessage);
2729
2795
  }
2730
2796
  else {
2731
2797
  // Args are now complete - trigger diff computation for edit tools
@@ -2873,7 +2939,6 @@ export class InteractiveMode {
2873
2939
  // Show error only on final failure (success shows normal response)
2874
2940
  if (!event.success) {
2875
2941
  this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
2876
- this.suggestBugReport();
2877
2942
  }
2878
2943
  this.ui.requestRender();
2879
2944
  break;
@@ -3368,6 +3433,9 @@ export class InteractiveMode {
3368
3433
  catch { }
3369
3434
  console.error(`${APP_NAME} exiting due to uncaughtException:`);
3370
3435
  console.error(error);
3436
+ const extensionHint = this.getCrashExtensionHint(error);
3437
+ if (extensionHint)
3438
+ console.error(`\n${extensionHint}`);
3371
3439
  if (this.recordCrash("uncaught_exception", error)) {
3372
3440
  console.error(`\n${this.crashReportInstructions()}`);
3373
3441
  }
@@ -5258,6 +5326,7 @@ export class InteractiveMode {
5258
5326
  ui: this.ui,
5259
5327
  editorContainer: this.editorContainer,
5260
5328
  editor: this.editor,
5329
+ keybindings: this.keybindings,
5261
5330
  showStatus: (message) => this.showStatus(message),
5262
5331
  showError: (message) => this.showError(message),
5263
5332
  }, hint);