@mariozechner/pi-coding-agent 0.56.1 → 0.56.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 (55) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +1 -1
  3. package/dist/core/agent-session.d.ts +1 -0
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +50 -17
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/auth-storage.d.ts +2 -1
  8. package/dist/core/auth-storage.d.ts.map +1 -1
  9. package/dist/core/auth-storage.js +25 -1
  10. package/dist/core/auth-storage.js.map +1 -1
  11. package/dist/core/compaction/utils.d.ts +3 -0
  12. package/dist/core/compaction/utils.d.ts.map +1 -1
  13. package/dist/core/compaction/utils.js +16 -1
  14. package/dist/core/compaction/utils.js.map +1 -1
  15. package/dist/core/extensions/loader.d.ts.map +1 -1
  16. package/dist/core/extensions/loader.js +4 -4
  17. package/dist/core/extensions/loader.js.map +1 -1
  18. package/dist/core/model-resolver.d.ts.map +1 -1
  19. package/dist/core/model-resolver.js +2 -2
  20. package/dist/core/model-resolver.js.map +1 -1
  21. package/dist/core/settings-manager.d.ts +4 -0
  22. package/dist/core/settings-manager.d.ts.map +1 -1
  23. package/dist/core/settings-manager.js +36 -2
  24. package/dist/core/settings-manager.js.map +1 -1
  25. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  26. package/dist/modes/interactive/components/footer.js +8 -23
  27. package/dist/modes/interactive/components/footer.js.map +1 -1
  28. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  29. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  30. package/dist/modes/interactive/components/settings-selector.js +10 -0
  31. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  32. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  33. package/dist/modes/interactive/components/tool-execution.js +14 -4
  34. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  35. package/dist/modes/interactive/components/tree-selector.d.ts +4 -2
  36. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  37. package/dist/modes/interactive/components/tree-selector.js +4 -3
  38. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  39. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  40. package/dist/modes/interactive/interactive-mode.js +19 -5
  41. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  42. package/docs/compaction.md +2 -0
  43. package/docs/custom-provider.md +11 -7
  44. package/docs/extensions.md +13 -1
  45. package/docs/models.md +5 -1
  46. package/docs/settings.md +1 -0
  47. package/docs/tmux.md +39 -0
  48. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  49. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  50. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  51. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  52. package/examples/extensions/with-deps/index.ts +1 -5
  53. package/examples/extensions/with-deps/package-lock.json +2 -2
  54. package/examples/extensions/with-deps/package.json +1 -1
  55. package/package.json +4 -4
@@ -1013,11 +1013,12 @@ export class AgentSession {
1013
1013
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1014
1014
  }
1015
1015
  const previousModel = this.model;
1016
+ const thinkingLevel = this._getThinkingLevelForModelSwitch();
1016
1017
  this.agent.setModel(model);
1017
1018
  this.sessionManager.appendModelChange(model.provider, model.id);
1018
1019
  this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1019
1020
  // Re-clamp thinking level for new model's capabilities
1020
- this.setThinkingLevel(this.thinkingLevel);
1021
+ this.setThinkingLevel(thinkingLevel);
1021
1022
  await this._emitModelSelect(model, previousModel, "set");
1022
1023
  }
1023
1024
  /**
@@ -1062,15 +1063,16 @@ export class AgentSession {
1062
1063
  const len = scopedModels.length;
1063
1064
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1064
1065
  const next = scopedModels[nextIndex];
1066
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
1065
1067
  // Apply model
1066
1068
  this.agent.setModel(next.model);
1067
1069
  this.sessionManager.appendModelChange(next.model.provider, next.model.id);
1068
1070
  this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1069
1071
  // Apply thinking level.
1070
1072
  // - Explicit scoped model thinking level overrides current session level
1071
- // - Undefined scoped model thinking level inherits current session level
1073
+ // - Undefined scoped model thinking level inherits the current session preference
1072
1074
  // setThinkingLevel clamps to model capabilities.
1073
- this.setThinkingLevel(next.thinkingLevel ?? this.thinkingLevel);
1075
+ this.setThinkingLevel(thinkingLevel);
1074
1076
  await this._emitModelSelect(next.model, currentModel, "cycle");
1075
1077
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1076
1078
  }
@@ -1089,11 +1091,12 @@ export class AgentSession {
1089
1091
  if (!apiKey) {
1090
1092
  throw new Error(`No API key for ${nextModel.provider}/${nextModel.id}`);
1091
1093
  }
1094
+ const thinkingLevel = this._getThinkingLevelForModelSwitch();
1092
1095
  this.agent.setModel(nextModel);
1093
1096
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
1094
1097
  this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1095
1098
  // Re-clamp thinking level for new model's capabilities
1096
- this.setThinkingLevel(this.thinkingLevel);
1099
+ this.setThinkingLevel(thinkingLevel);
1097
1100
  await this._emitModelSelect(nextModel, currentModel, "cycle");
1098
1101
  return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
1099
1102
  }
@@ -1113,7 +1116,9 @@ export class AgentSession {
1113
1116
  this.agent.setThinkingLevel(effectiveLevel);
1114
1117
  if (isChanging) {
1115
1118
  this.sessionManager.appendThinkingLevelChange(effectiveLevel);
1116
- this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
1119
+ if (this.supportsThinking() || effectiveLevel !== "off") {
1120
+ this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
1121
+ }
1117
1122
  }
1118
1123
  }
1119
1124
  /**
@@ -1151,6 +1156,15 @@ export class AgentSession {
1151
1156
  supportsThinking() {
1152
1157
  return !!this.model?.reasoning;
1153
1158
  }
1159
+ _getThinkingLevelForModelSwitch(explicitLevel) {
1160
+ if (explicitLevel !== undefined) {
1161
+ return explicitLevel;
1162
+ }
1163
+ if (!this.supportsThinking()) {
1164
+ return this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
1165
+ }
1166
+ return this.thinkingLevel;
1167
+ }
1154
1168
  _clampThinkingLevel(level, availableLevels) {
1155
1169
  const ordered = THINKING_LEVELS_WITH_XHIGH;
1156
1170
  const available = new Set(availableLevels);
@@ -1322,15 +1336,16 @@ export class AgentSession {
1322
1336
  // to a larger-context model (e.g. codex) - the overflow error from the old model
1323
1337
  // shouldn't trigger compaction for the new model.
1324
1338
  const sameModel = this.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;
1325
- // Skip overflow check if the error is from before a compaction in the current path.
1326
- // This handles the case where an error was kept after compaction (in the "kept" region).
1327
- // The error shouldn't trigger another compaction since we already compacted.
1328
- // Example: opus fails → switch to codex → compact → switch back to opus → opus error
1329
- // is still in context but shouldn't trigger compaction again.
1339
+ // Skip compaction checks if this assistant message is older than the latest
1340
+ // compaction boundary. This prevents a stale pre-compaction usage/error
1341
+ // from retriggering compaction on the first prompt after compaction.
1330
1342
  const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
1331
- const errorIsFromBeforeCompaction = compactionEntry !== null && assistantMessage.timestamp < new Date(compactionEntry.timestamp).getTime();
1343
+ const assistantIsFromBeforeCompaction = compactionEntry !== null && assistantMessage.timestamp <= new Date(compactionEntry.timestamp).getTime();
1344
+ if (assistantIsFromBeforeCompaction) {
1345
+ return;
1346
+ }
1332
1347
  // Case 1: Overflow - LLM returned context overflow error
1333
- if (sameModel && !errorIsFromBeforeCompaction && isContextOverflow(assistantMessage, contextWindow)) {
1348
+ if (sameModel && isContextOverflow(assistantMessage, contextWindow)) {
1334
1349
  if (this._overflowRecoveryAttempted) {
1335
1350
  this._emit({
1336
1351
  type: "auto_compaction_end",
@@ -1351,11 +1366,29 @@ export class AgentSession {
1351
1366
  await this._runAutoCompaction("overflow", true);
1352
1367
  return;
1353
1368
  }
1354
- // Case 2: Threshold - turn succeeded but context is getting large
1355
- // Skip if this was an error (non-overflow errors don't have usage data)
1356
- if (assistantMessage.stopReason === "error")
1357
- return;
1358
- const contextTokens = calculateContextTokens(assistantMessage.usage);
1369
+ // Case 2: Threshold - context is getting large
1370
+ // For error messages (no usage data), estimate from last successful response.
1371
+ // This ensures sessions that hit persistent API errors (e.g. 529) can still compact.
1372
+ let contextTokens;
1373
+ if (assistantMessage.stopReason === "error") {
1374
+ const messages = this.agent.state.messages;
1375
+ const estimate = estimateContextTokens(messages);
1376
+ if (estimate.lastUsageIndex === null)
1377
+ return; // No usage data at all
1378
+ // Verify the usage source is post-compaction. Kept pre-compaction messages
1379
+ // have stale usage reflecting the old (larger) context and would falsely
1380
+ // trigger compaction right after one just finished.
1381
+ const usageMsg = messages[estimate.lastUsageIndex];
1382
+ if (compactionEntry &&
1383
+ usageMsg.role === "assistant" &&
1384
+ usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
1385
+ return;
1386
+ }
1387
+ contextTokens = estimate.tokens;
1388
+ }
1389
+ else {
1390
+ contextTokens = calculateContextTokens(assistantMessage.usage);
1391
+ }
1359
1392
  if (shouldCompact(contextTokens, contextWindow, settings)) {
1360
1393
  await this._runAutoCompaction("threshold", false);
1361
1394
  }