@averyyy/pi-coding-agent 0.84.3-piclient.1 → 0.84.4-piclient.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 (95) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +5 -4
  3. package/dist/bundle/chunks/{chunk-GPPBJGBU.js → chunk-2KVJKXS2.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-QI42RBZ4.js → chunk-WJEX5SB4.js} +74 -74
  5. package/dist/bundle/chunks/{google-generative-ai-XDKMGBCJ.js → google-generative-ai-YMUPJBKR.js} +1 -1
  6. package/dist/bundle/chunks/{google-vertex-D5FGEO3Y.js → google-vertex-MPWMV4OF.js} +1 -1
  7. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +13 -0
  8. package/dist/bundle/chunks/{mistral-conversations-YK73UAOZ.js → mistral-conversations-Q3AWZJAZ.js} +1 -1
  9. package/dist/bundle/chunks/{openai-completions-JD4WAC3R.js → openai-completions-ERMU2SS7.js} +1 -1
  10. package/dist/bundle/cli.js +1 -1
  11. package/dist/bundle/index.js +1 -1
  12. package/dist/bundle/rpc-entry.js +1 -1
  13. package/dist/cli/startup-ui.d.ts.map +1 -1
  14. package/dist/cli/startup-ui.js +2 -1
  15. package/dist/cli/startup-ui.js.map +1 -1
  16. package/dist/core/agent-session.d.ts +15 -1
  17. package/dist/core/agent-session.d.ts.map +1 -1
  18. package/dist/core/agent-session.js +122 -21
  19. package/dist/core/agent-session.js.map +1 -1
  20. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  21. package/dist/core/compaction/branch-summarization.js +8 -1
  22. package/dist/core/compaction/branch-summarization.js.map +1 -1
  23. package/dist/core/compaction/compaction.d.ts +9 -4
  24. package/dist/core/compaction/compaction.d.ts.map +1 -1
  25. package/dist/core/compaction/compaction.js +42 -22
  26. package/dist/core/compaction/compaction.js.map +1 -1
  27. package/dist/core/extensions/index.d.ts +1 -1
  28. package/dist/core/extensions/index.d.ts.map +1 -1
  29. package/dist/core/extensions/index.js.map +1 -1
  30. package/dist/core/extensions/runner.d.ts +5 -0
  31. package/dist/core/extensions/runner.d.ts.map +1 -1
  32. package/dist/core/extensions/runner.js +45 -1
  33. package/dist/core/extensions/runner.js.map +1 -1
  34. package/dist/core/extensions/types.d.ts +18 -1
  35. package/dist/core/extensions/types.d.ts.map +1 -1
  36. package/dist/core/extensions/types.js.map +1 -1
  37. package/dist/core/pi-server-client.d.ts.map +1 -1
  38. package/dist/core/pi-server-client.js +26 -9
  39. package/dist/core/pi-server-client.js.map +1 -1
  40. package/dist/core/session-manager.d.ts.map +1 -1
  41. package/dist/core/session-manager.js +4 -2
  42. package/dist/core/session-manager.js.map +1 -1
  43. package/dist/core/settings-manager.d.ts +8 -1
  44. package/dist/core/settings-manager.d.ts.map +1 -1
  45. package/dist/core/settings-manager.js +17 -0
  46. package/dist/core/settings-manager.js.map +1 -1
  47. package/dist/index.d.ts +2 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +1 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/main.d.ts.map +1 -1
  52. package/dist/main.js +2 -0
  53. package/dist/main.js.map +1 -1
  54. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  55. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  56. package/dist/modes/interactive/components/settings-selector.js +10 -0
  57. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  58. package/dist/modes/interactive/interactive-mode.d.ts +5 -0
  59. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  60. package/dist/modes/interactive/interactive-mode.js +65 -28
  61. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  62. package/dist/modes/rpc/rpc-client.d.ts +7 -0
  63. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  64. package/dist/modes/rpc/rpc-client.js +7 -0
  65. package/dist/modes/rpc/rpc-client.js.map +1 -1
  66. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  67. package/dist/modes/rpc/rpc-mode.js +3 -0
  68. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  69. package/dist/modes/rpc/rpc-types.d.ts +12 -0
  70. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  71. package/dist/modes/rpc/rpc-types.js.map +1 -1
  72. package/dist/utils/shell.d.ts.map +1 -1
  73. package/dist/utils/shell.js +6 -4
  74. package/dist/utils/shell.js.map +1 -1
  75. package/docs/compaction.md +2 -0
  76. package/docs/custom-provider.md +7 -7
  77. package/docs/environment-variables.md +3 -0
  78. package/docs/extensions.md +18 -0
  79. package/docs/keybindings.md +1 -1
  80. package/docs/quickstart.md +1 -1
  81. package/docs/rpc.md +23 -0
  82. package/docs/settings.md +12 -4
  83. package/docs/terminal-setup.md +12 -0
  84. package/docs/usage.md +4 -3
  85. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  86. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  87. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  88. package/examples/extensions/gondolin/package-lock.json +2 -2
  89. package/examples/extensions/gondolin/package.json +1 -1
  90. package/examples/extensions/sandbox/package-lock.json +2 -2
  91. package/examples/extensions/sandbox/package.json +1 -1
  92. package/examples/extensions/with-deps/package-lock.json +2 -2
  93. package/examples/extensions/with-deps/package.json +1 -1
  94. package/package.json +6 -6
  95. package/dist/bundle/chunks/dist-RDWOYWHR.js +0 -13
@@ -102,6 +102,16 @@ function getPiServerFailurePhase(message) {
102
102
  }
103
103
  return undefined;
104
104
  }
105
+ function getPiServerFailureRetryable(message) {
106
+ for (const diagnostic of message.diagnostics ?? []) {
107
+ if (diagnostic.type !== "pi_server_failure")
108
+ continue;
109
+ const retryable = diagnostic.details?.retryable;
110
+ if (typeof retryable === "boolean")
111
+ return retryable;
112
+ }
113
+ return undefined;
114
+ }
105
115
  function toolTextContent(content) {
106
116
  return content
107
117
  .filter((part) => part.type === "text")
@@ -170,6 +180,8 @@ export class AgentSession {
170
180
  _followUpMessages = [];
171
181
  /** Messages queued to be included with the next user prompt as context ("asides"). */
172
182
  _pendingNextTurnMessages = [];
183
+ /** Context-only custom messages queued during a run, flushed once the current turn's tool results are in. */
184
+ _pendingCustomMessages = [];
173
185
  // Compaction state
174
186
  _compactionAbortController = undefined;
175
187
  _autoCompactionAbortController = undefined;
@@ -472,6 +484,11 @@ export class AgentSession {
472
484
  followUp: [...this._followUpMessages],
473
485
  });
474
486
  }
487
+ async _emitSessionCompactFailed(event) {
488
+ if (this._extensionRunner.hasHandlers("session_compact_failed")) {
489
+ await this._extensionRunner.emit({ type: "session_compact_failed", ...event });
490
+ }
491
+ }
475
492
  _getIdleWaitPromise() {
476
493
  if (!this._idleWaitPromise) {
477
494
  this._idleWaitPromise = new Promise((resolve) => {
@@ -562,6 +579,14 @@ export class AgentSession {
562
579
  }
563
580
  }
564
581
  }
582
+ // A turn ends after its assistant message and every tool result has been appended,
583
+ // so this is the first point in the run where a context-only custom message can be
584
+ // inserted without landing between a tool call and its result. Flushing after the
585
+ // extension and listener dispatch above also picks up messages that turn_end
586
+ // handlers queued.
587
+ if (event.type === "turn_end") {
588
+ this._flushPendingCustomMessages();
589
+ }
565
590
  };
566
591
  _willRetryAfterAgentEnd(event) {
567
592
  const settings = this.settingsManager.getRetrySettings();
@@ -990,6 +1015,7 @@ export class AgentSession {
990
1015
  finally {
991
1016
  this._systemPromptOverride = undefined;
992
1017
  this._flushPendingBashMessages();
1018
+ this._flushPendingCustomMessages();
993
1019
  await this._emitAgentSettled();
994
1020
  }
995
1021
  }
@@ -1148,8 +1174,9 @@ export class AgentSession {
1148
1174
  preflightResult?.(true);
1149
1175
  return;
1150
1176
  }
1151
- // Flush any pending bash messages before the new prompt
1177
+ // Flush any pending bash and custom messages before the new prompt
1152
1178
  this._flushPendingBashMessages();
1179
+ this._flushPendingCustomMessages();
1153
1180
  // Validate model
1154
1181
  if (!this.model) {
1155
1182
  throw new Error(formatNoModelSelectedMessage());
@@ -1366,8 +1393,9 @@ export class AgentSession {
1366
1393
  /**
1367
1394
  * Send a custom message to the session. Creates a CustomMessageEntry.
1368
1395
  *
1369
- * Handles three cases:
1396
+ * Handles four cases:
1370
1397
  * - Streaming: queues message, processed when loop pulls from queue
1398
+ * - Streaming + triggerTurn false: appended to state/session once the current turn ends
1371
1399
  * - Not streaming + triggerTurn: appends to state/session, starts new turn
1372
1400
  * - Not streaming + no trigger: appends to state/session, no turn
1373
1401
  *
@@ -1399,11 +1427,34 @@ export class AgentSession {
1399
1427
  else if (options?.triggerTurn) {
1400
1428
  await this._runAgentPrompt(appMessage);
1401
1429
  }
1430
+ else if (this.isStreaming) {
1431
+ // Appending now would put the message between an assistant tool call and its
1432
+ // result, which providers that validate message order reject on replay. Defer
1433
+ // to the end of the turn. Nothing is emitted yet: message events must not
1434
+ // describe messages the session tree does not contain.
1435
+ this._pendingCustomMessages.push(appMessage);
1436
+ }
1402
1437
  else {
1403
- this.agent.state.messages.push(appMessage);
1404
- this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
1405
- this._emit({ type: "message_start", message: appMessage });
1406
- this._emit({ type: "message_end", message: appMessage });
1438
+ this._appendCustomMessage(appMessage);
1439
+ }
1440
+ }
1441
+ _appendCustomMessage(appMessage) {
1442
+ this.agent.state.messages.push(appMessage);
1443
+ this.sessionManager.appendCustomMessageEntry(appMessage.customType, appMessage.content, appMessage.display, appMessage.details);
1444
+ this._emit({ type: "message_start", message: appMessage });
1445
+ this._emit({ type: "message_end", message: appMessage });
1446
+ }
1447
+ /**
1448
+ * Append custom messages queued while the agent was running.
1449
+ * Called once the current turn's tool results are in agent state and session history.
1450
+ */
1451
+ _flushPendingCustomMessages() {
1452
+ if (this._pendingCustomMessages.length === 0)
1453
+ return;
1454
+ const pending = this._pendingCustomMessages;
1455
+ this._pendingCustomMessages = [];
1456
+ for (const appMessage of pending) {
1457
+ this._appendCustomMessage(appMessage);
1407
1458
  }
1408
1459
  }
1409
1460
  /**
@@ -1518,6 +1569,7 @@ export class AgentSession {
1518
1569
  this.sessionManager.appendModelChange(model.provider, model.id);
1519
1570
  if (options.persist) {
1520
1571
  this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1572
+ this._addPersistedDefaultToNonEmptyScope(model);
1521
1573
  }
1522
1574
  // Apply thinking level for the new model.
1523
1575
  // Per-model thinking level overrides take priority over the global default.
@@ -1525,6 +1577,20 @@ export class AgentSession {
1525
1577
  this.setThinkingLevel(thinkingLevel);
1526
1578
  await this._emitModelSelect(model, previousModel, "set");
1527
1579
  }
1580
+ _addPersistedDefaultToNonEmptyScope(model) {
1581
+ if (this._scopedModels.length === 0)
1582
+ return;
1583
+ if (this._scopedModels.some((scoped) => modelsAreEqual(scoped.model, model)))
1584
+ return;
1585
+ this._scopedModels = [...this._scopedModels, { model }];
1586
+ const enabledModels = this.settingsManager.getEnabledModels();
1587
+ if (!enabledModels?.length)
1588
+ return;
1589
+ const modelReference = `${model.provider}/${model.id}`;
1590
+ if (enabledModels.some((pattern) => pattern.toLowerCase() === modelReference.toLowerCase()))
1591
+ return;
1592
+ this.settingsManager.setEnabledModels([...enabledModels, modelReference]);
1593
+ }
1528
1594
  /**
1529
1595
  * Cycle to next/previous model.
1530
1596
  * Uses scoped models (from --models flag) if available, otherwise all available models.
@@ -1555,6 +1621,7 @@ export class AgentSession {
1555
1621
  this.sessionManager.appendModelChange(next.model.provider, next.model.id);
1556
1622
  if (options.persist) {
1557
1623
  this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1624
+ this._addPersistedDefaultToNonEmptyScope(next.model);
1558
1625
  }
1559
1626
  // Apply thinking level for the new model.
1560
1627
  // - Explicit scoped model thinking level overrides defaults
@@ -1581,6 +1648,7 @@ export class AgentSession {
1581
1648
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
1582
1649
  if (options.persist) {
1583
1650
  this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1651
+ this._addPersistedDefaultToNonEmptyScope(nextModel);
1584
1652
  }
1585
1653
  // Apply thinking level for the new model.
1586
1654
  // Model persistence does not implicitly rewrite the global thinking default.
@@ -1920,20 +1988,24 @@ export class AgentSession {
1920
1988
  // independent of the configured context size or any context-clamped provider request limit.
1921
1989
  // A successful response over the configured window should compact but must not retry: the
1922
1990
  // assistant answer already completed and agent.continue() cannot continue from an assistant.
1991
+ const contextOverflow = sameModel && isContextOverflow(assistantMessage, contextWindow);
1923
1992
  const recoverableLength = sameModel && isRecoverableLength(assistantMessage, this.model?.maxTokens ?? 0);
1924
- if (sameModel && (isContextOverflow(assistantMessage, contextWindow) || recoverableLength)) {
1993
+ if (contextOverflow || recoverableLength) {
1925
1994
  const willRetry = assistantMessage.stopReason !== "stop";
1926
1995
  if (!willRetry) {
1927
1996
  return await this._runAutoCompaction("overflow", false);
1928
1997
  }
1929
1998
  if (this._overflowRecoveryAttempted) {
1999
+ const errorMessage = contextOverflow
2000
+ ? "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."
2001
+ : "Truncated response recovery failed after one compact-and-retry attempt.";
1930
2002
  this._emit({
1931
2003
  type: "compaction_end",
1932
2004
  reason: "overflow",
1933
2005
  result: undefined,
1934
2006
  aborted: false,
1935
2007
  willRetry: false,
1936
- errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.",
2008
+ errorMessage,
1937
2009
  });
1938
2010
  return false;
1939
2011
  }
@@ -1963,16 +2035,16 @@ export class AgentSession {
1963
2035
  if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
1964
2036
  const messages = this.agent.state.messages;
1965
2037
  const estimate = estimateContextTokens(messages);
1966
- if (estimate.lastUsageIndex === null)
1967
- return false; // No usage data at all
1968
- // Verify the usage source is post-compaction. Kept pre-compaction messages
1969
- // have stale usage reflecting the old (larger) context and would falsely
1970
- // trigger compaction right after one just finished.
1971
- const usageMsg = messages[estimate.lastUsageIndex];
1972
- if (compactionTimestamp > 0 &&
1973
- usageMsg.role === "assistant" &&
1974
- usageMsg.timestamp <= compactionTimestamp) {
1975
- return false;
2038
+ // When a valid usage entry exists, verify it is post-compaction. Kept
2039
+ // pre-compaction messages have stale usage reflecting the old (larger)
2040
+ // context and would falsely trigger compaction right after one finished.
2041
+ if (estimate.lastUsageIndex !== null) {
2042
+ const usageMsg = messages[estimate.lastUsageIndex];
2043
+ if (compactionTimestamp > 0 &&
2044
+ usageMsg.role === "assistant" &&
2045
+ usageMsg.timestamp <= compactionTimestamp) {
2046
+ return false;
2047
+ }
1976
2048
  }
1977
2049
  contextTokens = estimate.tokens;
1978
2050
  }
@@ -1990,6 +2062,7 @@ export class AgentSession {
1990
2062
  async _runAutoCompaction(reason, willRetry, preparationOverride, preparationOptions) {
1991
2063
  const settings = this.settingsManager.getCompactionSettings();
1992
2064
  let started = false;
2065
+ let fromExtension = false;
1993
2066
  try {
1994
2067
  if (!this.model) {
1995
2068
  return false;
@@ -2029,7 +2102,6 @@ export class AgentSession {
2029
2102
  this._autoCompactionAbortController = new AbortController();
2030
2103
  started = true;
2031
2104
  let extensionCompaction;
2032
- let fromExtension = false;
2033
2105
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
2034
2106
  const extensionResult = (await this._extensionRunner.emit({
2035
2107
  type: "session_before_compact",
@@ -2048,6 +2120,12 @@ export class AgentSession {
2048
2120
  aborted: true,
2049
2121
  willRetry: false,
2050
2122
  });
2123
+ await this._emitSessionCompactFailed({
2124
+ reason,
2125
+ aborted: true,
2126
+ willRetry: false,
2127
+ fromExtension: false,
2128
+ });
2051
2129
  return false;
2052
2130
  }
2053
2131
  if (extensionResult?.compaction) {
@@ -2085,6 +2163,12 @@ export class AgentSession {
2085
2163
  aborted: true,
2086
2164
  willRetry: false,
2087
2165
  });
2166
+ await this._emitSessionCompactFailed({
2167
+ reason,
2168
+ aborted: true,
2169
+ willRetry: false,
2170
+ fromExtension,
2171
+ });
2088
2172
  return false;
2089
2173
  }
2090
2174
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
@@ -2141,6 +2225,15 @@ export class AgentSession {
2141
2225
  ? `Context overflow recovery failed: ${errorMessage}`
2142
2226
  : `Auto-compaction failed: ${errorMessage}`,
2143
2227
  });
2228
+ await this._emitSessionCompactFailed({
2229
+ reason,
2230
+ errorMessage: reason === "overflow"
2231
+ ? `Context overflow recovery failed: ${errorMessage}`
2232
+ : `Auto-compaction failed: ${errorMessage}`,
2233
+ aborted: false,
2234
+ willRetry: false,
2235
+ fromExtension,
2236
+ });
2144
2237
  }
2145
2238
  return false;
2146
2239
  }
@@ -2554,6 +2647,9 @@ export class AgentSession {
2554
2647
  * Check if an error is retryable. Provider error text is retryable by default,
2555
2648
  * except usage/quota/balance limits and session or context failures.
2556
2649
  * Context overflow errors are NOT retryable (handled by compaction instead).
2650
+ * Pi-server diagnostics may mark recovery/history failures as non-retryable;
2651
+ * transient session_init/tree_sync outages stay retryable so the next attempt
2652
+ * re-runs the whole stream path.
2557
2653
  */
2558
2654
  _isRetryableError(message) {
2559
2655
  // Context overflow is handled by compaction, not retry.
@@ -2561,9 +2657,14 @@ export class AgentSession {
2561
2657
  return false;
2562
2658
  if (this._isPostAssistantPiServerSyncFailure(message))
2563
2659
  return false;
2564
- const piServerPhase = getPiServerFailurePhase(message);
2565
- if (piServerPhase && piServerPhase !== "provider_stream")
2660
+ const piServerRetryable = getPiServerFailureRetryable(message);
2661
+ if (piServerRetryable === false)
2566
2662
  return false;
2663
+ if (piServerRetryable !== true) {
2664
+ const piServerPhase = getPiServerFailurePhase(message);
2665
+ if (piServerPhase && piServerPhase !== "provider_stream")
2666
+ return false;
2667
+ }
2567
2668
  return isRetryableAssistantError(message);
2568
2669
  }
2569
2670
  /**