@astralyn/pi 0.84.8 → 0.84.10

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 (270) hide show
  1. package/CHANGELOG.md +17 -2
  2. package/README.md +3 -2
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +29 -9
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/file-processor.d.ts.map +1 -1
  8. package/dist/cli/file-processor.js +2 -1
  9. package/dist/cli/file-processor.js.map +1 -1
  10. package/dist/config.d.ts +4 -3
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +22 -14
  13. package/dist/config.js.map +1 -1
  14. package/dist/core/agent-session.d.ts +26 -7
  15. package/dist/core/agent-session.d.ts.map +1 -1
  16. package/dist/core/agent-session.js +83 -64
  17. package/dist/core/agent-session.js.map +1 -1
  18. package/dist/core/auth-storage.d.ts.map +1 -1
  19. package/dist/core/auth-storage.js +6 -7
  20. package/dist/core/auth-storage.js.map +1 -1
  21. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  22. package/dist/core/compaction/branch-summarization.js +3 -0
  23. package/dist/core/compaction/branch-summarization.js.map +1 -1
  24. package/dist/core/compaction/compaction.d.ts +4 -3
  25. package/dist/core/compaction/compaction.d.ts.map +1 -1
  26. package/dist/core/compaction/compaction.js +41 -32
  27. package/dist/core/compaction/compaction.js.map +1 -1
  28. package/dist/core/compaction-controller.d.ts +2 -1
  29. package/dist/core/compaction-controller.d.ts.map +1 -1
  30. package/dist/core/compaction-controller.js +64 -21
  31. package/dist/core/compaction-controller.js.map +1 -1
  32. package/dist/core/defaults.d.ts +1 -0
  33. package/dist/core/defaults.d.ts.map +1 -1
  34. package/dist/core/defaults.js +9 -0
  35. package/dist/core/defaults.js.map +1 -1
  36. package/dist/core/extensions/index.d.ts +2 -2
  37. package/dist/core/extensions/index.d.ts.map +1 -1
  38. package/dist/core/extensions/index.js +1 -1
  39. package/dist/core/extensions/index.js.map +1 -1
  40. package/dist/core/extensions/loader.d.ts.map +1 -1
  41. package/dist/core/extensions/loader.js +115 -48
  42. package/dist/core/extensions/loader.js.map +1 -1
  43. package/dist/core/extensions/types.d.ts +38 -9
  44. package/dist/core/extensions/types.d.ts.map +1 -1
  45. package/dist/core/extensions/types.js +3 -0
  46. package/dist/core/extensions/types.js.map +1 -1
  47. package/dist/core/footer-data-provider.d.ts +1 -1
  48. package/dist/core/footer-data-provider.js +1 -1
  49. package/dist/core/footer-data-provider.js.map +1 -1
  50. package/dist/core/keybindings.d.ts +20 -19
  51. package/dist/core/keybindings.d.ts.map +1 -1
  52. package/dist/core/keybindings.js +26 -5
  53. package/dist/core/keybindings.js.map +1 -1
  54. package/dist/core/model-config.d.ts +5 -0
  55. package/dist/core/model-config.d.ts.map +1 -1
  56. package/dist/core/model-config.js +3 -1
  57. package/dist/core/model-config.js.map +1 -1
  58. package/dist/core/model-resolver.d.ts +1 -0
  59. package/dist/core/model-resolver.d.ts.map +1 -1
  60. package/dist/core/model-resolver.js +14 -8
  61. package/dist/core/model-resolver.js.map +1 -1
  62. package/dist/core/models-store.d.ts.map +1 -1
  63. package/dist/core/models-store.js +2 -1
  64. package/dist/core/models-store.js.map +1 -1
  65. package/dist/core/package-manager.d.ts.map +1 -1
  66. package/dist/core/package-manager.js +22 -14
  67. package/dist/core/package-manager.js.map +1 -1
  68. package/dist/core/pi-manifest.d.ts.map +1 -1
  69. package/dist/core/pi-manifest.js +2 -1
  70. package/dist/core/pi-manifest.js.map +1 -1
  71. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  72. package/dist/core/remote-catalog-provider.js +2 -1
  73. package/dist/core/remote-catalog-provider.js.map +1 -1
  74. package/dist/core/resource-loader.d.ts.map +1 -1
  75. package/dist/core/resource-loader.js +3 -2
  76. package/dist/core/resource-loader.js.map +1 -1
  77. package/dist/core/sdk.d.ts +2 -2
  78. package/dist/core/sdk.d.ts.map +1 -1
  79. package/dist/core/sdk.js +10 -3
  80. package/dist/core/sdk.js.map +1 -1
  81. package/dist/core/session-export.d.ts +4 -0
  82. package/dist/core/session-export.d.ts.map +1 -0
  83. package/dist/core/session-export.js +32 -0
  84. package/dist/core/session-export.js.map +1 -0
  85. package/dist/core/session-manager.d.ts.map +1 -1
  86. package/dist/core/session-manager.js +4 -3
  87. package/dist/core/session-manager.js.map +1 -1
  88. package/dist/core/settings-diagnostics.d.ts +9 -0
  89. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  90. package/dist/core/settings-diagnostics.js +21 -0
  91. package/dist/core/settings-diagnostics.js.map +1 -0
  92. package/dist/core/settings-manager.d.ts +9 -0
  93. package/dist/core/settings-manager.d.ts.map +1 -1
  94. package/dist/core/settings-manager.js +51 -10
  95. package/dist/core/settings-manager.js.map +1 -1
  96. package/dist/core/skills.d.ts.map +1 -1
  97. package/dist/core/skills.js +51 -33
  98. package/dist/core/skills.js.map +1 -1
  99. package/dist/core/slash-commands.d.ts.map +1 -1
  100. package/dist/core/slash-commands.js +2 -1
  101. package/dist/core/slash-commands.js.map +1 -1
  102. package/dist/core/system-prompt.d.ts.map +1 -1
  103. package/dist/core/system-prompt.js +11 -2
  104. package/dist/core/system-prompt.js.map +1 -1
  105. package/dist/core/tools/bash.d.ts +19 -1
  106. package/dist/core/tools/bash.d.ts.map +1 -1
  107. package/dist/core/tools/bash.js +59 -37
  108. package/dist/core/tools/bash.js.map +1 -1
  109. package/dist/core/tools/edit-diff.d.ts +0 -5
  110. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  111. package/dist/core/tools/edit-diff.js +2 -5
  112. package/dist/core/tools/edit-diff.js.map +1 -1
  113. package/dist/core/tools/edit.d.ts.map +1 -1
  114. package/dist/core/tools/edit.js +20 -4
  115. package/dist/core/tools/edit.js.map +1 -1
  116. package/dist/core/tools/index.d.ts +4 -1
  117. package/dist/core/tools/index.d.ts.map +1 -1
  118. package/dist/core/tools/index.js +18 -1
  119. package/dist/core/tools/index.js.map +1 -1
  120. package/dist/core/tools/powershell.d.ts +16 -0
  121. package/dist/core/tools/powershell.d.ts.map +1 -0
  122. package/dist/core/tools/powershell.js +39 -0
  123. package/dist/core/tools/powershell.js.map +1 -0
  124. package/dist/core/trust-manager.d.ts.map +1 -1
  125. package/dist/core/trust-manager.js +2 -1
  126. package/dist/core/trust-manager.js.map +1 -1
  127. package/dist/extensions/background/registry.d.ts +2 -3
  128. package/dist/extensions/background/registry.d.ts.map +1 -1
  129. package/dist/extensions/background/registry.js +2 -6
  130. package/dist/extensions/background/registry.js.map +1 -1
  131. package/dist/extensions/llama/index.d.ts.map +1 -1
  132. package/dist/extensions/llama/index.js +2 -0
  133. package/dist/extensions/llama/index.js.map +1 -1
  134. package/dist/extensions/llama/provider.d.ts.map +1 -1
  135. package/dist/extensions/llama/provider.js +6 -2
  136. package/dist/extensions/llama/provider.js.map +1 -1
  137. package/dist/extensions/question/types.d.ts +0 -1
  138. package/dist/extensions/question/types.d.ts.map +1 -1
  139. package/dist/extensions/question/types.js +1 -1
  140. package/dist/extensions/question/types.js.map +1 -1
  141. package/dist/extensions/router/register.d.ts +0 -1
  142. package/dist/extensions/router/register.d.ts.map +1 -1
  143. package/dist/extensions/router/register.js +0 -3
  144. package/dist/extensions/router/register.js.map +1 -1
  145. package/dist/extensions/router/stream.d.ts +0 -2
  146. package/dist/extensions/router/stream.d.ts.map +1 -1
  147. package/dist/extensions/router/stream.js +1 -12
  148. package/dist/extensions/router/stream.js.map +1 -1
  149. package/dist/extensions/subagent/index.js +2 -2
  150. package/dist/extensions/subagent/index.js.map +1 -1
  151. package/dist/extensions/subagent/render.d.ts +6 -2
  152. package/dist/extensions/subagent/render.d.ts.map +1 -1
  153. package/dist/extensions/subagent/render.js +143 -132
  154. package/dist/extensions/subagent/render.js.map +1 -1
  155. package/dist/extensions/subagent/text.d.ts +0 -1
  156. package/dist/extensions/subagent/text.d.ts.map +1 -1
  157. package/dist/extensions/subagent/text.js +1 -30
  158. package/dist/extensions/subagent/text.js.map +1 -1
  159. package/dist/extensions/subagent/ui/model-list.d.ts +0 -3
  160. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  161. package/dist/extensions/subagent/ui/model-list.js +1 -10
  162. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  163. package/dist/index.d.ts +5 -5
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js +4 -4
  166. package/dist/index.js.map +1 -1
  167. package/dist/main.d.ts +4 -0
  168. package/dist/main.d.ts.map +1 -1
  169. package/dist/main.js +18 -15
  170. package/dist/main.js.map +1 -1
  171. package/dist/migrations.js +5 -5
  172. package/dist/migrations.js.map +1 -1
  173. package/dist/modes/interactive/components/index.d.ts +0 -1
  174. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/index.js +0 -1
  176. package/dist/modes/interactive/components/index.js.map +1 -1
  177. package/dist/modes/interactive/components/model-selector.d.ts +9 -3
  178. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  179. package/dist/modes/interactive/components/model-selector.js +54 -11
  180. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  181. package/dist/modes/interactive/components/settings-selector.d.ts +7 -2
  182. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  183. package/dist/modes/interactive/components/settings-selector.js +118 -55
  184. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  185. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  186. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  187. package/dist/modes/interactive/components/settings-submenu.js +151 -0
  188. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  189. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  190. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  191. package/dist/modes/interactive/components/thinking-selector.js +68 -17
  192. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  193. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  194. package/dist/modes/interactive/external-editor.js +2 -1
  195. package/dist/modes/interactive/external-editor.js.map +1 -1
  196. package/dist/modes/interactive/interactive-mode.d.ts +11 -0
  197. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  198. package/dist/modes/interactive/interactive-mode.js +167 -105
  199. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  200. package/dist/modes/interactive/session-share.d.ts +16 -0
  201. package/dist/modes/interactive/session-share.d.ts.map +1 -0
  202. package/dist/modes/interactive/session-share.js +188 -0
  203. package/dist/modes/interactive/session-share.js.map +1 -0
  204. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  205. package/dist/modes/interactive/theme/theme.js +3 -2
  206. package/dist/modes/interactive/theme/theme.js.map +1 -1
  207. package/dist/modes/json-event.d.ts +16 -13
  208. package/dist/modes/json-event.d.ts.map +1 -1
  209. package/dist/modes/json-event.js +20 -6
  210. package/dist/modes/json-event.js.map +1 -1
  211. package/dist/package-manager-cli.d.ts +1 -0
  212. package/dist/package-manager-cli.d.ts.map +1 -1
  213. package/dist/package-manager-cli.js +186 -2
  214. package/dist/package-manager-cli.js.map +1 -1
  215. package/dist/utils/frontmatter.d.ts.map +1 -1
  216. package/dist/utils/frontmatter.js +2 -1
  217. package/dist/utils/frontmatter.js.map +1 -1
  218. package/dist/utils/management-http.d.ts +5 -3
  219. package/dist/utils/management-http.d.ts.map +1 -1
  220. package/dist/utils/management-http.js +14 -10
  221. package/dist/utils/management-http.js.map +1 -1
  222. package/dist/utils/paths.d.ts +0 -5
  223. package/dist/utils/paths.d.ts.map +1 -1
  224. package/dist/utils/paths.js +0 -7
  225. package/dist/utils/paths.js.map +1 -1
  226. package/dist/utils/shell.d.ts +3 -0
  227. package/dist/utils/shell.d.ts.map +1 -1
  228. package/dist/utils/shell.js +17 -5
  229. package/dist/utils/shell.js.map +1 -1
  230. package/dist/utils/text.d.ts +8 -0
  231. package/dist/utils/text.d.ts.map +1 -0
  232. package/dist/utils/text.js +9 -0
  233. package/dist/utils/text.js.map +1 -0
  234. package/docs/bundled/extensions/subagent.md +40 -23
  235. package/docs/compaction.md +19 -2
  236. package/docs/custom-provider.md +5 -2
  237. package/docs/environment-variables.md +8 -8
  238. package/docs/extensions.md +22 -10
  239. package/docs/json.md +7 -2
  240. package/docs/keybindings.md +10 -8
  241. package/docs/llama-cpp.md +6 -4
  242. package/docs/models.md +10 -3
  243. package/docs/packages.md +2 -1
  244. package/docs/rpc.md +9 -3
  245. package/docs/sdk.md +12 -2
  246. package/docs/settings.md +16 -5
  247. package/docs/skills.md +5 -4
  248. package/docs/terminal-setup.md +11 -9
  249. package/docs/usage.md +6 -2
  250. package/docs/windows.md +24 -2
  251. package/examples/extensions/border-status-editor.ts +1 -1
  252. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  253. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  254. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  255. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  256. package/examples/extensions/doom-overlay/doom/build.sh +0 -0
  257. package/examples/extensions/git-checkpoint.ts +2 -2
  258. package/examples/extensions/gondolin/package-lock.json +2 -2
  259. package/examples/extensions/gondolin/package.json +1 -1
  260. package/examples/extensions/notify.ts +3 -1
  261. package/examples/extensions/sandbox/package-lock.json +2 -2
  262. package/examples/extensions/sandbox/package.json +1 -1
  263. package/examples/extensions/titlebar-spinner.ts +1 -1
  264. package/examples/extensions/with-deps/package-lock.json +2 -2
  265. package/examples/extensions/with-deps/package.json +1 -1
  266. package/examples/rpc-extension-ui.ts +1 -1
  267. package/examples/sdk/06-extensions.ts +1 -1
  268. package/examples/sdk/README.md +1 -1
  269. package/npm-shrinkwrap.json +196 -257
  270. package/package.json +6 -8
@@ -12,27 +12,27 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { readFileSync } from "node:fs";
16
16
  import { basename, dirname } from "node:path";
17
17
  import { contentText } from "@earendil-works/pi-ai";
18
18
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
19
19
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
20
20
  import { stripFrontmatter } from "../utils/frontmatter.js";
21
- import { resolvePath } from "../utils/paths.js";
22
21
  import { sleep } from "../utils/sleep.js";
23
22
  import { normalizeToolResultImages } from "../utils/tool-result-images.js";
24
23
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
25
24
  import { executeBashWithOperations } from "./bash-executor.js";
26
25
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
27
26
  import { CONTEXT_REMAINS_OVER_COMPACTION_THRESHOLD, CompactionController, estimateMessagesTokens, } from "./compaction-controller.js";
28
- import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
27
+ import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
29
28
  import { exportSessionToHtml } from "./export-html/index.js";
30
29
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
31
30
  import { ExtensionRunner, STALE_EXTENSION_CONTEXT_MESSAGE, wrapRegisteredTools, } from "./extensions/index.js";
32
31
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
33
32
  import { ModelRegistry } from "./model-registry.js";
34
33
  import { expandPromptTemplate } from "./prompt-templates.js";
35
- import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
34
+ import { exportSessionToJsonl } from "./session-export.js";
35
+ import { getLatestCompactionEntry } from "./session-manager.js";
36
36
  import { createSyntheticSourceInfo } from "./source-info.js";
37
37
  import { buildSystemPrompt } from "./system-prompt.js";
38
38
  import { createLocalBashOperations } from "./tools/bash.js";
@@ -65,8 +65,6 @@ function withoutDeletedHeaders(headers) {
65
65
  // ============================================================================
66
66
  // Constants
67
67
  // ============================================================================
68
- /** Standard thinking levels */
69
- const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
70
68
  // ============================================================================
71
69
  // AgentSession Class
72
70
  // ============================================================================
@@ -187,6 +185,7 @@ export class AgentSession {
187
185
  getExtensionRunner: () => this._extensionRunner,
188
186
  isManualCompactionActive: () => this._compactionAbortController !== undefined,
189
187
  emit: (event) => this._emit(event),
188
+ emitSessionCompactFailed: (event) => this._emitSessionCompactFailed(event),
190
189
  getRequiredRequestAuth: (model) => this._getRequiredRequestAuth(model),
191
190
  getSummarizationRequestAuth: (model, streamFunction) => this._getSummarizationRequestAuth(model, streamFunction),
192
191
  summarizationRetryCallbacks: (source) => this._summarizationRetryCallbacks(source),
@@ -365,6 +364,11 @@ export class AgentSession {
365
364
  followUp: [...this._followUpMessages],
366
365
  });
367
366
  }
367
+ async _emitSessionCompactFailed(event) {
368
+ if (this._extensionRunner.hasHandlers("session_compact_failed")) {
369
+ await this._extensionRunner.emit({ type: "session_compact_failed", ...event });
370
+ }
371
+ }
368
372
  _getIdleWaitPromise() {
369
373
  if (!this._idleWaitPromise) {
370
374
  this._idleWaitPromise = new Promise((resolve) => {
@@ -1202,19 +1206,24 @@ export class AgentSession {
1202
1206
  }
1203
1207
  /**
1204
1208
  * Set model directly.
1205
- * Validates that auth is configured, saves to session and settings.
1209
+ * Validates that auth is configured and saves to the session transcript.
1210
+ * Persists to global defaults only when options.persist is true.
1206
1211
  * @throws Error if no auth is configured for the model
1207
1212
  */
1208
- async setModel(model) {
1213
+ async setModel(model, options = {}) {
1209
1214
  if (!(await this._modelRuntime.checkAuth(model.provider))) {
1210
1215
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1211
1216
  }
1212
1217
  const previousModel = this.model;
1213
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1218
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
1214
1219
  this.agent.state.model = model;
1215
1220
  this.sessionManager.appendModelChange(model.provider, model.id);
1216
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1217
- // Re-clamp thinking level for new model's capabilities
1221
+ if (options.persist) {
1222
+ this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1223
+ }
1224
+ // Apply thinking level for the new model.
1225
+ // Per-model thinking level overrides take priority over the global default.
1226
+ // Model persistence does not implicitly rewrite the global thinking default.
1218
1227
  this.setThinkingLevel(thinkingLevel);
1219
1228
  await this._emitModelSelect(model, previousModel, "set");
1220
1229
  }
@@ -1224,13 +1233,13 @@ export class AgentSession {
1224
1233
  * @param direction - "forward" (default) or "backward"
1225
1234
  * @returns The new model info, or undefined if only one model available
1226
1235
  */
1227
- async cycleModel(direction = "forward") {
1236
+ async cycleModel(direction = "forward", options = {}) {
1228
1237
  if (this._scopedModels.length > 0) {
1229
- return this._cycleScopedModel(direction);
1238
+ return this._cycleScopedModel(direction, options);
1230
1239
  }
1231
- return this._cycleAvailableModel(direction);
1240
+ return this._cycleAvailableModel(direction, options);
1232
1241
  }
1233
- async _cycleScopedModel(direction) {
1242
+ async _cycleScopedModel(direction, options) {
1234
1243
  const availableIds = new Set(this._modelRuntime.getAvailableSnapshot().map((model) => `${model.provider}\0${model.id}`));
1235
1244
  const scopedModels = this._scopedModels.filter((scoped) => availableIds.has(`${scoped.model.provider}\0${scoped.model.id}`));
1236
1245
  if (scopedModels.length <= 1)
@@ -1242,20 +1251,23 @@ export class AgentSession {
1242
1251
  const len = scopedModels.length;
1243
1252
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1244
1253
  const next = scopedModels[nextIndex];
1245
- const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
1254
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
1246
1255
  // Apply model
1247
1256
  this.agent.state.model = next.model;
1248
1257
  this.sessionManager.appendModelChange(next.model.provider, next.model.id);
1249
- this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1250
- // Apply thinking level.
1251
- // - Explicit scoped model thinking level overrides current session level
1252
- // - Undefined scoped model thinking level inherits the current session preference
1258
+ if (options.persist) {
1259
+ this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1260
+ }
1261
+ // Apply thinking level for the new model.
1262
+ // - Explicit scoped model thinking level overrides defaults
1263
+ // - Per-model thinking level overrides take priority over the global default
1253
1264
  // setThinkingLevel clamps to model capabilities.
1265
+ // Model persistence does not implicitly rewrite the global thinking default.
1254
1266
  this.setThinkingLevel(thinkingLevel);
1255
1267
  await this._emitModelSelect(next.model, currentModel, "cycle");
1256
1268
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1257
1269
  }
1258
- async _cycleAvailableModel(direction) {
1270
+ async _cycleAvailableModel(direction, options) {
1259
1271
  const availableModels = this._modelRuntime.getAvailableSnapshot();
1260
1272
  if (availableModels.length <= 1)
1261
1273
  return undefined;
@@ -1266,11 +1278,14 @@ export class AgentSession {
1266
1278
  const len = availableModels.length;
1267
1279
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1268
1280
  const nextModel = availableModels[nextIndex];
1269
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1281
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
1270
1282
  this.agent.state.model = nextModel;
1271
1283
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
1272
- this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1273
- // Re-clamp thinking level for new model's capabilities
1284
+ if (options.persist) {
1285
+ this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1286
+ }
1287
+ // Apply thinking level for the new model.
1288
+ // Model persistence does not implicitly rewrite the global thinking default.
1274
1289
  this.setThinkingLevel(thinkingLevel);
1275
1290
  await this._emitModelSelect(nextModel, currentModel, "cycle");
1276
1291
  return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
@@ -1281,20 +1296,21 @@ export class AgentSession {
1281
1296
  /**
1282
1297
  * Set thinking level.
1283
1298
  * Clamps to model capabilities based on available thinking levels.
1284
- * Saves to session and settings only if the level actually changes.
1299
+ * Saves the clamped level to the session transcript only if the level actually changes.
1300
+ * Persists the requested level to global defaults only when options.persist is true.
1285
1301
  */
1286
- setThinkingLevel(level) {
1302
+ setThinkingLevel(level, options = {}) {
1287
1303
  const availableLevels = this.getAvailableThinkingLevels();
1288
1304
  const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
1289
1305
  // Only persist if actually changing
1290
1306
  const previousLevel = this.agent.state.thinkingLevel;
1291
1307
  const isChanging = effectiveLevel !== previousLevel;
1292
1308
  this.agent.state.thinkingLevel = effectiveLevel;
1309
+ if (options.persist) {
1310
+ this.settingsManager.setDefaultThinkingLevel(level);
1311
+ }
1293
1312
  if (isChanging) {
1294
1313
  this.sessionManager.appendThinkingLevelChange(effectiveLevel);
1295
- if (this.supportsThinking() || effectiveLevel !== "off") {
1296
- this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
1297
- }
1298
1314
  this._emit({ type: "thinking_level_changed", level: effectiveLevel });
1299
1315
  void this._extensionRunner.emit({
1300
1316
  type: "thinking_level_select",
@@ -1307,14 +1323,14 @@ export class AgentSession {
1307
1323
  * Cycle to next thinking level.
1308
1324
  * @returns New level, or undefined if model doesn't support thinking
1309
1325
  */
1310
- cycleThinkingLevel() {
1326
+ cycleThinkingLevel(options = {}) {
1311
1327
  if (!this.supportsThinking())
1312
1328
  return undefined;
1313
1329
  const levels = this.getAvailableThinkingLevels();
1314
1330
  const currentIndex = levels.indexOf(this.thinkingLevel);
1315
1331
  const nextIndex = (currentIndex + 1) % levels.length;
1316
1332
  const nextLevel = levels[nextIndex];
1317
- this.setThinkingLevel(nextLevel);
1333
+ this.setThinkingLevel(nextLevel, options);
1318
1334
  return nextLevel;
1319
1335
  }
1320
1336
  /**
@@ -1323,7 +1339,7 @@ export class AgentSession {
1323
1339
  */
1324
1340
  getAvailableThinkingLevels() {
1325
1341
  if (!this.model)
1326
- return THINKING_LEVELS;
1342
+ return [...THINKING_LEVEL_OPTIONS];
1327
1343
  return getSupportedThinkingLevels(this.model);
1328
1344
  }
1329
1345
  /**
@@ -1332,14 +1348,18 @@ export class AgentSession {
1332
1348
  supportsThinking() {
1333
1349
  return !!this.model?.reasoning;
1334
1350
  }
1335
- _getThinkingLevelForModelSwitch(explicitLevel) {
1351
+ _getThinkingLevelForModelSwitch(targetModel, explicitLevel) {
1336
1352
  if (explicitLevel !== undefined) {
1337
1353
  return explicitLevel;
1338
1354
  }
1339
- if (!this.supportsThinking()) {
1340
- return this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
1355
+ // Per-model default takes priority when switching to a model that has one
1356
+ if (targetModel) {
1357
+ const perModel = this.settingsManager.getModelThinkingLevel(targetModel.provider, targetModel.id);
1358
+ if (perModel !== undefined) {
1359
+ return perModel;
1360
+ }
1341
1361
  }
1342
- return this.thinkingLevel;
1362
+ return this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL;
1343
1363
  }
1344
1364
  _clampThinkingLevel(level, _availableLevels) {
1345
1365
  return this.model ? clampThinkingLevel(this.model, level) : "off";
@@ -1370,15 +1390,29 @@ export class AgentSession {
1370
1390
  // =========================================================================
1371
1391
  // Compaction
1372
1392
  // =========================================================================
1393
+ /** Generate Pi's built-in manual compaction summary. */
1394
+ async _runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, signal, env, reason) {
1395
+ return compact(preparation, requestModel, apiKey, headers, customInstructions, signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), undefined);
1396
+ }
1373
1397
  /**
1374
1398
  * Manually compact the session context.
1375
- * Aborts current agent operation first.
1399
+ *
1400
+ * This is the manual entry point used by `/compact`, RPC, and extensions. It is
1401
+ * separate from automatic threshold/overflow compaction, which is delegated to
1402
+ * CompactionController. After preparation and the `session_before_compact` hook,
1403
+ * the default manual path calls the lower-level `compact()` function imported
1404
+ * from `./compaction/index.ts`, unless the hook cancels or supplies a custom result.
1405
+ *
1406
+ * Aborts the current agent operation first. Manual compaction never retries or
1407
+ * continues the interrupted agent turn.
1408
+ *
1376
1409
  * @param customInstructions Optional instructions for the compaction summary
1377
1410
  */
1378
1411
  async compact(customInstructions) {
1379
1412
  await this.abort();
1380
1413
  this._compactionAbortController = new AbortController();
1381
1414
  this._emit({ type: "compaction_start", reason: "manual" });
1415
+ let fromExtension = false;
1382
1416
  try {
1383
1417
  if (!this.model) {
1384
1418
  throw new Error(formatNoModelSelectedMessage());
@@ -1396,7 +1430,6 @@ export class AgentSession {
1396
1430
  throw new Error("Nothing to compact (session too small)");
1397
1431
  }
1398
1432
  let extensionCompaction;
1399
- let fromExtension = false;
1400
1433
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1401
1434
  const result = (await this._extensionRunner.emit({
1402
1435
  type: "session_before_compact",
@@ -1430,8 +1463,8 @@ export class AgentSession {
1430
1463
  details = extensionCompaction.details;
1431
1464
  }
1432
1465
  else {
1433
- // Generate compaction result
1434
- const result = await compact(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }));
1466
+ // Shared default summary generator, also used by automatic compaction.
1467
+ const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
1435
1468
  summary = result.summary;
1436
1469
  firstKeptEntryId = result.firstKeptEntryId;
1437
1470
  tokensBefore = result.tokensBefore;
@@ -1479,6 +1512,7 @@ export class AgentSession {
1479
1512
  catch (error) {
1480
1513
  const message = error instanceof Error ? error.message : String(error);
1481
1514
  const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1515
+ const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
1482
1516
  this._compactionAbortController = undefined;
1483
1517
  this._emit({
1484
1518
  type: "compaction_end",
@@ -1486,7 +1520,14 @@ export class AgentSession {
1486
1520
  result: undefined,
1487
1521
  aborted,
1488
1522
  willRetry: false,
1489
- errorMessage: aborted ? undefined : `Compaction failed: ${message}`,
1523
+ errorMessage,
1524
+ });
1525
+ await this._emitSessionCompactFailed({
1526
+ reason: "manual",
1527
+ errorMessage,
1528
+ aborted,
1529
+ willRetry: false,
1530
+ fromExtension,
1490
1531
  });
1491
1532
  throw error;
1492
1533
  }
@@ -2390,29 +2431,7 @@ export class AgentSession {
2390
2431
  * @returns The resolved output file path.
2391
2432
  */
2392
2433
  exportToJsonl(outputPath) {
2393
- const filePath = resolvePath(outputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`, process.cwd());
2394
- const dir = dirname(filePath);
2395
- if (!existsSync(dir)) {
2396
- mkdirSync(dir, { recursive: true });
2397
- }
2398
- const header = {
2399
- type: "session",
2400
- version: CURRENT_SESSION_VERSION,
2401
- id: this.sessionManager.getSessionId(),
2402
- timestamp: new Date().toISOString(),
2403
- cwd: this.sessionManager.getCwd(),
2404
- };
2405
- const branchEntries = this.sessionManager.getBranch();
2406
- const lines = [JSON.stringify(header)];
2407
- // Re-chain parentIds to form a linear sequence
2408
- let prevId = null;
2409
- for (const entry of branchEntries) {
2410
- const linear = { ...entry, parentId: prevId };
2411
- lines.push(JSON.stringify(linear));
2412
- prevId = entry.id;
2413
- }
2414
- writeFileSync(filePath, `${lines.join("\n")}\n`);
2415
- return filePath;
2434
+ return exportSessionToJsonl(this.sessionManager, outputPath);
2416
2435
  }
2417
2436
  // =========================================================================
2418
2437
  // Utilities