apex-code 0.0.1-alpha.8 → 0.0.1-alpha.9
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.
- package/CHANGELOG.md +83 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +29 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +21 -13
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +26 -7
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +163 -95
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -7
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +41 -32
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/durable-state/sqlite.d.ts.map +1 -1
- package/dist/core/durable-state/sqlite.js +26 -2
- package/dist/core/durable-state/sqlite.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +115 -48
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +38 -9
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/keybindings.d.ts +20 -19
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +3 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +14 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -14
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +9 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +53 -11
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +8 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +14 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +36 -20
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -4
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts +4 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +9 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +18 -15
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +10 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +51 -9
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +7 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +118 -56
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +11 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +164 -17
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +5 -4
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +16 -13
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +20 -6
- package/dist/modes/json-event.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +197 -5
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +17 -5
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/docs/compaction.md +15 -0
- package/docs/custom-provider.md +5 -2
- package/docs/extensions.md +17 -7
- package/docs/json.md +7 -2
- package/docs/keybindings.md +8 -8
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/packages.md +2 -2
- package/docs/rpc.md +9 -3
- package/docs/sdk.md +7 -2
- package/docs/settings.md +11 -3
- package/docs/skills.md +4 -3
- package/docs/terminal-setup.md +11 -7
- package/docs/usage.md +6 -2
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +2 -2
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/subagent/index.ts +6 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +276 -390
- package/package.json +9 -11
|
@@ -12,20 +12,19 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Modes use this class and add their own I/O layer on top.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
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, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, } 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, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
27
26
|
import { evictionBudget, installContextPipeline, isDefaultStreamFunction } from "./context/pipeline.js";
|
|
28
|
-
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
27
|
+
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
|
|
29
28
|
import { SessionEvidenceSink } from "./evidence.js";
|
|
30
29
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
31
30
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
@@ -36,7 +35,8 @@ import { evaluateToolCall } from "./permissions/gate.js";
|
|
|
36
35
|
import { createInteractiveResponder } from "./permissions/responder.js";
|
|
37
36
|
import { resolveEffectiveModeWithOrigin } from "./permissions/startup.js";
|
|
38
37
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
39
|
-
import {
|
|
38
|
+
import { exportSessionToJsonl } from "./session-export.js";
|
|
39
|
+
import { getLatestCompactionEntry, TODO_CUSTOM_ENTRY_TYPE } from "./session-manager.js";
|
|
40
40
|
import { slugifySkillCommandName } from "./skills.js";
|
|
41
41
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
42
42
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
@@ -80,8 +80,6 @@ function estimateMessagesTokens(messages) {
|
|
|
80
80
|
// ============================================================================
|
|
81
81
|
// Constants
|
|
82
82
|
// ============================================================================
|
|
83
|
-
/** Standard thinking levels */
|
|
84
|
-
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
|
|
85
83
|
// ============================================================================
|
|
86
84
|
// AgentSession Class
|
|
87
85
|
// ============================================================================
|
|
@@ -406,6 +404,11 @@ export class AgentSession {
|
|
|
406
404
|
followUp: [...this._followUpMessages],
|
|
407
405
|
});
|
|
408
406
|
}
|
|
407
|
+
async _emitSessionCompactFailed(event) {
|
|
408
|
+
if (this._extensionRunner.hasHandlers("session_compact_failed")) {
|
|
409
|
+
await this._extensionRunner.emit({ type: "session_compact_failed", ...event });
|
|
410
|
+
}
|
|
411
|
+
}
|
|
409
412
|
_getIdleWaitPromise() {
|
|
410
413
|
if (!this._idleWaitPromise) {
|
|
411
414
|
this._idleWaitPromise = new Promise((resolve) => {
|
|
@@ -1328,19 +1331,24 @@ export class AgentSession {
|
|
|
1328
1331
|
}
|
|
1329
1332
|
/**
|
|
1330
1333
|
* Set model directly.
|
|
1331
|
-
* Validates that auth is configured
|
|
1334
|
+
* Validates that auth is configured and saves to the session transcript.
|
|
1335
|
+
* Persists to global defaults only when options.persist is true.
|
|
1332
1336
|
* @throws Error if no auth is configured for the model
|
|
1333
1337
|
*/
|
|
1334
|
-
async setModel(model) {
|
|
1338
|
+
async setModel(model, options = {}) {
|
|
1335
1339
|
if (!(await this._modelRuntime.checkAuth(model.provider))) {
|
|
1336
1340
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
1337
1341
|
}
|
|
1338
1342
|
const previousModel = this.model;
|
|
1339
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1343
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
|
|
1340
1344
|
this.agent.state.model = model;
|
|
1341
1345
|
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
1342
|
-
|
|
1343
|
-
|
|
1346
|
+
if (options.persist) {
|
|
1347
|
+
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
1348
|
+
}
|
|
1349
|
+
// Apply thinking level for the new model.
|
|
1350
|
+
// Per-model thinking level overrides take priority over the global default.
|
|
1351
|
+
// Model persistence does not implicitly rewrite the global thinking default.
|
|
1344
1352
|
this.setThinkingLevel(thinkingLevel);
|
|
1345
1353
|
await this._emitModelSelect(model, previousModel, "set");
|
|
1346
1354
|
}
|
|
@@ -1350,13 +1358,13 @@ export class AgentSession {
|
|
|
1350
1358
|
* @param direction - "forward" (default) or "backward"
|
|
1351
1359
|
* @returns The new model info, or undefined if only one model available
|
|
1352
1360
|
*/
|
|
1353
|
-
async cycleModel(direction = "forward") {
|
|
1361
|
+
async cycleModel(direction = "forward", options = {}) {
|
|
1354
1362
|
if (this._scopedModels.length > 0) {
|
|
1355
|
-
return this._cycleScopedModel(direction);
|
|
1363
|
+
return this._cycleScopedModel(direction, options);
|
|
1356
1364
|
}
|
|
1357
|
-
return this._cycleAvailableModel(direction);
|
|
1365
|
+
return this._cycleAvailableModel(direction, options);
|
|
1358
1366
|
}
|
|
1359
|
-
async _cycleScopedModel(direction) {
|
|
1367
|
+
async _cycleScopedModel(direction, options) {
|
|
1360
1368
|
const availableIds = new Set(this._modelRuntime.getAvailableSnapshot().map((model) => `${model.provider}\0${model.id}`));
|
|
1361
1369
|
const scopedModels = this._scopedModels.filter((scoped) => availableIds.has(`${scoped.model.provider}\0${scoped.model.id}`));
|
|
1362
1370
|
if (scopedModels.length <= 1)
|
|
@@ -1368,20 +1376,23 @@ export class AgentSession {
|
|
|
1368
1376
|
const len = scopedModels.length;
|
|
1369
1377
|
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
1370
1378
|
const next = scopedModels[nextIndex];
|
|
1371
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
|
|
1379
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
|
|
1372
1380
|
// Apply model
|
|
1373
1381
|
this.agent.state.model = next.model;
|
|
1374
1382
|
this.sessionManager.appendModelChange(next.model.provider, next.model.id);
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
//
|
|
1383
|
+
if (options.persist) {
|
|
1384
|
+
this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
|
|
1385
|
+
}
|
|
1386
|
+
// Apply thinking level for the new model.
|
|
1387
|
+
// - Explicit scoped model thinking level overrides defaults
|
|
1388
|
+
// - Per-model thinking level overrides take priority over the global default
|
|
1379
1389
|
// setThinkingLevel clamps to model capabilities.
|
|
1390
|
+
// Model persistence does not implicitly rewrite the global thinking default.
|
|
1380
1391
|
this.setThinkingLevel(thinkingLevel);
|
|
1381
1392
|
await this._emitModelSelect(next.model, currentModel, "cycle");
|
|
1382
1393
|
return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
|
|
1383
1394
|
}
|
|
1384
|
-
async _cycleAvailableModel(direction) {
|
|
1395
|
+
async _cycleAvailableModel(direction, options) {
|
|
1385
1396
|
const availableModels = this._modelRuntime.getAvailableSnapshot();
|
|
1386
1397
|
if (availableModels.length <= 1)
|
|
1387
1398
|
return undefined;
|
|
@@ -1392,11 +1403,14 @@ export class AgentSession {
|
|
|
1392
1403
|
const len = availableModels.length;
|
|
1393
1404
|
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
1394
1405
|
const nextModel = availableModels[nextIndex];
|
|
1395
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1406
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
|
|
1396
1407
|
this.agent.state.model = nextModel;
|
|
1397
1408
|
this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
|
|
1398
|
-
|
|
1399
|
-
|
|
1409
|
+
if (options.persist) {
|
|
1410
|
+
this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
|
|
1411
|
+
}
|
|
1412
|
+
// Apply thinking level for the new model.
|
|
1413
|
+
// Model persistence does not implicitly rewrite the global thinking default.
|
|
1400
1414
|
this.setThinkingLevel(thinkingLevel);
|
|
1401
1415
|
await this._emitModelSelect(nextModel, currentModel, "cycle");
|
|
1402
1416
|
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
@@ -1407,20 +1421,21 @@ export class AgentSession {
|
|
|
1407
1421
|
/**
|
|
1408
1422
|
* Set thinking level.
|
|
1409
1423
|
* Clamps to model capabilities based on available thinking levels.
|
|
1410
|
-
* Saves to session
|
|
1424
|
+
* Saves the clamped level to the session transcript only if the level actually changes.
|
|
1425
|
+
* Persists the requested level to global defaults only when options.persist is true.
|
|
1411
1426
|
*/
|
|
1412
|
-
setThinkingLevel(level) {
|
|
1427
|
+
setThinkingLevel(level, options = {}) {
|
|
1413
1428
|
const availableLevels = this.getAvailableThinkingLevels();
|
|
1414
1429
|
const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
|
|
1415
1430
|
// Only persist if actually changing
|
|
1416
1431
|
const previousLevel = this.agent.state.thinkingLevel;
|
|
1417
1432
|
const isChanging = effectiveLevel !== previousLevel;
|
|
1418
1433
|
this.agent.state.thinkingLevel = effectiveLevel;
|
|
1434
|
+
if (options.persist) {
|
|
1435
|
+
this.settingsManager.setDefaultThinkingLevel(level);
|
|
1436
|
+
}
|
|
1419
1437
|
if (isChanging) {
|
|
1420
1438
|
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
1421
|
-
if (this.supportsThinking() || effectiveLevel !== "off") {
|
|
1422
|
-
this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
|
|
1423
|
-
}
|
|
1424
1439
|
this._emit({ type: "thinking_level_changed", level: effectiveLevel });
|
|
1425
1440
|
void this._extensionRunner.emit({
|
|
1426
1441
|
type: "thinking_level_select",
|
|
@@ -1433,14 +1448,14 @@ export class AgentSession {
|
|
|
1433
1448
|
* Cycle to next thinking level.
|
|
1434
1449
|
* @returns New level, or undefined if model doesn't support thinking
|
|
1435
1450
|
*/
|
|
1436
|
-
cycleThinkingLevel() {
|
|
1451
|
+
cycleThinkingLevel(options = {}) {
|
|
1437
1452
|
if (!this.supportsThinking())
|
|
1438
1453
|
return undefined;
|
|
1439
1454
|
const levels = this.getAvailableThinkingLevels();
|
|
1440
1455
|
const currentIndex = levels.indexOf(this.thinkingLevel);
|
|
1441
1456
|
const nextIndex = (currentIndex + 1) % levels.length;
|
|
1442
1457
|
const nextLevel = levels[nextIndex];
|
|
1443
|
-
this.setThinkingLevel(nextLevel);
|
|
1458
|
+
this.setThinkingLevel(nextLevel, options);
|
|
1444
1459
|
return nextLevel;
|
|
1445
1460
|
}
|
|
1446
1461
|
/**
|
|
@@ -1449,7 +1464,7 @@ export class AgentSession {
|
|
|
1449
1464
|
*/
|
|
1450
1465
|
getAvailableThinkingLevels() {
|
|
1451
1466
|
if (!this.model)
|
|
1452
|
-
return
|
|
1467
|
+
return [...THINKING_LEVEL_OPTIONS];
|
|
1453
1468
|
return getSupportedThinkingLevels(this.model);
|
|
1454
1469
|
}
|
|
1455
1470
|
/**
|
|
@@ -1458,14 +1473,18 @@ export class AgentSession {
|
|
|
1458
1473
|
supportsThinking() {
|
|
1459
1474
|
return !!this.model?.reasoning;
|
|
1460
1475
|
}
|
|
1461
|
-
_getThinkingLevelForModelSwitch(explicitLevel) {
|
|
1476
|
+
_getThinkingLevelForModelSwitch(targetModel, explicitLevel) {
|
|
1462
1477
|
if (explicitLevel !== undefined) {
|
|
1463
1478
|
return explicitLevel;
|
|
1464
1479
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1480
|
+
// Per-model default takes priority when switching to a model that has one
|
|
1481
|
+
if (targetModel) {
|
|
1482
|
+
const perModel = this.settingsManager.getModelThinkingLevel(targetModel.provider, targetModel.id);
|
|
1483
|
+
if (perModel !== undefined) {
|
|
1484
|
+
return perModel;
|
|
1485
|
+
}
|
|
1467
1486
|
}
|
|
1468
|
-
return this.thinkingLevel;
|
|
1487
|
+
return this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL;
|
|
1469
1488
|
}
|
|
1470
1489
|
_clampThinkingLevel(level, _availableLevels) {
|
|
1471
1490
|
return this.model ? clampThinkingLevel(this.model, level) : "off";
|
|
@@ -1496,15 +1515,30 @@ export class AgentSession {
|
|
|
1496
1515
|
// =========================================================================
|
|
1497
1516
|
// Compaction
|
|
1498
1517
|
// =========================================================================
|
|
1518
|
+
/** Generate Pi's built-in compaction summary for manual and automatic compaction. */
|
|
1519
|
+
async _runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, signal, env, reason) {
|
|
1520
|
+
return compact(preparation, requestModel, apiKey, headers, customInstructions, signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), undefined);
|
|
1521
|
+
}
|
|
1499
1522
|
/**
|
|
1500
1523
|
* Manually compact the session context.
|
|
1501
|
-
*
|
|
1524
|
+
*
|
|
1525
|
+
* This is the manual entry point used by `/compact`, RPC, and extensions. It is
|
|
1526
|
+
* separate from automatic threshold/overflow compaction, which enters through
|
|
1527
|
+
* `_checkCompaction()` and `_runAutoCompaction()`. After preparation and the
|
|
1528
|
+
* `session_before_compact` hook, both paths call the lower-level `compact()`
|
|
1529
|
+
* function imported from `./compaction/index.ts`, unless the hook cancels or
|
|
1530
|
+
* supplies a custom result.
|
|
1531
|
+
*
|
|
1532
|
+
* Aborts the current agent operation first. Manual compaction never retries or
|
|
1533
|
+
* continues the interrupted agent turn.
|
|
1534
|
+
*
|
|
1502
1535
|
* @param customInstructions Optional instructions for the compaction summary
|
|
1503
1536
|
*/
|
|
1504
1537
|
async compact(customInstructions) {
|
|
1505
1538
|
await this.abort();
|
|
1506
1539
|
this._compactionAbortController = new AbortController();
|
|
1507
1540
|
this._emit({ type: "compaction_start", reason: "manual" });
|
|
1541
|
+
let fromExtension = false;
|
|
1508
1542
|
try {
|
|
1509
1543
|
if (!this.model) {
|
|
1510
1544
|
throw new Error(formatNoModelSelectedMessage());
|
|
@@ -1522,7 +1556,6 @@ export class AgentSession {
|
|
|
1522
1556
|
throw new Error("Nothing to compact (session too small)");
|
|
1523
1557
|
}
|
|
1524
1558
|
let extensionCompaction;
|
|
1525
|
-
let fromExtension = false;
|
|
1526
1559
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1527
1560
|
const result = (await this._extensionRunner.emit({
|
|
1528
1561
|
type: "session_before_compact",
|
|
@@ -1555,8 +1588,8 @@ export class AgentSession {
|
|
|
1555
1588
|
details = extensionCompaction.details;
|
|
1556
1589
|
}
|
|
1557
1590
|
else {
|
|
1558
|
-
//
|
|
1559
|
-
const result = await
|
|
1591
|
+
// Shared default summary generator, also used by automatic compaction.
|
|
1592
|
+
const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
|
|
1560
1593
|
summary = result.summary;
|
|
1561
1594
|
firstKeptEntryId = result.firstKeptEntryId;
|
|
1562
1595
|
tokensBefore = result.tokensBefore;
|
|
@@ -1604,6 +1637,7 @@ export class AgentSession {
|
|
|
1604
1637
|
catch (error) {
|
|
1605
1638
|
const message = error instanceof Error ? error.message : String(error);
|
|
1606
1639
|
const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
|
|
1640
|
+
const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
|
|
1607
1641
|
this._compactionAbortController = undefined;
|
|
1608
1642
|
this._emit({
|
|
1609
1643
|
type: "compaction_end",
|
|
@@ -1611,7 +1645,14 @@ export class AgentSession {
|
|
|
1611
1645
|
result: undefined,
|
|
1612
1646
|
aborted,
|
|
1613
1647
|
willRetry: false,
|
|
1614
|
-
errorMessage
|
|
1648
|
+
errorMessage,
|
|
1649
|
+
});
|
|
1650
|
+
await this._emitSessionCompactFailed({
|
|
1651
|
+
reason: "manual",
|
|
1652
|
+
errorMessage,
|
|
1653
|
+
aborted,
|
|
1654
|
+
willRetry: false,
|
|
1655
|
+
fromExtension,
|
|
1615
1656
|
});
|
|
1616
1657
|
throw error;
|
|
1617
1658
|
}
|
|
@@ -1633,16 +1674,25 @@ export class AgentSession {
|
|
|
1633
1674
|
this._branchSummaryAbortController?.abort();
|
|
1634
1675
|
}
|
|
1635
1676
|
/**
|
|
1636
|
-
*
|
|
1637
|
-
*
|
|
1677
|
+
* Dispatch automatic compaction after `agent_end` or before prompt submission.
|
|
1678
|
+
* Manual compaction does not call this method; it enters through `compact()`.
|
|
1679
|
+
*
|
|
1680
|
+
* Automatic cases:
|
|
1681
|
+
* 1. Overflow with retry: a context-overflow error or recoverable length stop;
|
|
1682
|
+
* remove the failed assistant message, compact, and retry the turn once.
|
|
1683
|
+
* 2. Overflow without retry: a successful response exceeded the configured
|
|
1684
|
+
* context window; compact but preserve the completed response.
|
|
1685
|
+
* 3. Threshold without retry: valid or estimated context usage crossed the
|
|
1686
|
+
* configured threshold; compact without retrying the completed response.
|
|
1638
1687
|
*
|
|
1639
|
-
*
|
|
1640
|
-
*
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1688
|
+
* Each case calls `_runAutoCompaction()`. After preparation and the
|
|
1689
|
+
* `session_before_compact` hook, that method calls the lower-level `compact()`
|
|
1690
|
+
* function imported from `./compaction/index.ts`, unless the hook cancels or
|
|
1691
|
+
* supplies a custom result.
|
|
1643
1692
|
*
|
|
1644
1693
|
* @param assistantMessage The assistant message to check
|
|
1645
1694
|
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
1695
|
+
* @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
|
|
1646
1696
|
*/
|
|
1647
1697
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
1648
1698
|
const settings = this.settingsManager.getCompactionSettings();
|
|
@@ -1665,38 +1715,49 @@ export class AgentSession {
|
|
|
1665
1715
|
if (assistantIsFromBeforeCompaction) {
|
|
1666
1716
|
return false;
|
|
1667
1717
|
}
|
|
1668
|
-
//
|
|
1718
|
+
// Automatic cases 1 and 2: context overflow.
|
|
1669
1719
|
// A length stop is recoverable when output ended below the model's original desired limit,
|
|
1670
1720
|
// independent of the configured context size or any context-clamped provider request limit.
|
|
1671
|
-
|
|
1672
|
-
// assistant answer already completed and agent.continue() cannot continue from an assistant.
|
|
1721
|
+
const contextOverflow = sameModel && isContextOverflow(assistantMessage, contextWindow);
|
|
1673
1722
|
const recoverableLength = sameModel && isRecoverableLength(assistantMessage, this.model?.maxTokens ?? 0);
|
|
1674
|
-
if (
|
|
1723
|
+
if (contextOverflow || recoverableLength) {
|
|
1675
1724
|
const willRetry = assistantMessage.stopReason !== "stop";
|
|
1725
|
+
// Case 2: the response completed successfully. Compact, but do not retry because
|
|
1726
|
+
// agent.continue() cannot continue from a completed assistant response.
|
|
1676
1727
|
if (!willRetry) {
|
|
1677
1728
|
return await this._runAutoCompaction("overflow", false);
|
|
1678
1729
|
}
|
|
1679
1730
|
if (this._overflowRecoveryAttempted) {
|
|
1731
|
+
const errorMessage = contextOverflow
|
|
1732
|
+
? "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."
|
|
1733
|
+
: "Truncated response recovery failed after one compact-and-retry attempt.";
|
|
1680
1734
|
this._emit({
|
|
1681
1735
|
type: "compaction_end",
|
|
1682
1736
|
reason: "overflow",
|
|
1683
1737
|
result: undefined,
|
|
1684
1738
|
aborted: false,
|
|
1685
1739
|
willRetry: false,
|
|
1686
|
-
errorMessage
|
|
1740
|
+
errorMessage,
|
|
1741
|
+
});
|
|
1742
|
+
await this._emitSessionCompactFailed({
|
|
1743
|
+
reason: "overflow",
|
|
1744
|
+
errorMessage,
|
|
1745
|
+
aborted: false,
|
|
1746
|
+
willRetry: false,
|
|
1747
|
+
fromExtension: false,
|
|
1687
1748
|
});
|
|
1688
1749
|
return false;
|
|
1689
1750
|
}
|
|
1751
|
+
// Case 1: remove the failed or truncated message from agent state, compact, and
|
|
1752
|
+
// retry once. The message remains in session history but is excluded from retry context.
|
|
1690
1753
|
this._overflowRecoveryAttempted = true;
|
|
1691
|
-
// Remove the failed or truncated message from agent state. It remains in session history,
|
|
1692
|
-
// but must not be included in the compact-and-retry context.
|
|
1693
1754
|
const messages = this.agent.state.messages;
|
|
1694
1755
|
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
1695
1756
|
this.agent.state.messages = messages.slice(0, -1);
|
|
1696
1757
|
}
|
|
1697
1758
|
return await this._runAutoCompaction("overflow", willRetry);
|
|
1698
1759
|
}
|
|
1699
|
-
// Case
|
|
1760
|
+
// Case 3: threshold compaction without retry.
|
|
1700
1761
|
// For error messages or all-zero usage messages, estimate from the last valid response.
|
|
1701
1762
|
// This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
|
|
1702
1763
|
// responses can still compact and do not reset context accounting.
|
|
@@ -1705,16 +1766,18 @@ export class AgentSession {
|
|
|
1705
1766
|
if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
|
|
1706
1767
|
const messages = this.agent.state.messages;
|
|
1707
1768
|
const estimate = estimateContextTokens(messages);
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1769
|
+
// Without provider usage, estimate.tokens is the pure message-size estimate.
|
|
1770
|
+
// Only usage-backed estimates need the stale pre-compaction check.
|
|
1771
|
+
if (estimate.lastUsageIndex !== null) {
|
|
1772
|
+
// Verify the usage source is post-compaction. Kept pre-compaction messages
|
|
1773
|
+
// have stale usage reflecting the old (larger) context and would falsely
|
|
1774
|
+
// trigger compaction right after one just finished.
|
|
1775
|
+
const usageMsg = messages[estimate.lastUsageIndex];
|
|
1776
|
+
if (compactionEntry &&
|
|
1777
|
+
usageMsg.role === "assistant" &&
|
|
1778
|
+
usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
|
|
1779
|
+
return false;
|
|
1780
|
+
}
|
|
1718
1781
|
}
|
|
1719
1782
|
contextTokens = estimate.tokens;
|
|
1720
1783
|
}
|
|
@@ -1727,11 +1790,19 @@ export class AgentSession {
|
|
|
1727
1790
|
return false;
|
|
1728
1791
|
}
|
|
1729
1792
|
/**
|
|
1730
|
-
*
|
|
1793
|
+
* Execute threshold or overflow compaction. Manual compaction uses
|
|
1794
|
+
* `AgentSession.compact()` instead. Both paths call the lower-level `compact()`
|
|
1795
|
+
* function imported from `./compaction/index.ts` after preparation and extension
|
|
1796
|
+
* interception.
|
|
1797
|
+
*
|
|
1798
|
+
* @param reason Automatic trigger selected by `_checkCompaction()`
|
|
1799
|
+
* @param willRetry Whether to continue the interrupted turn after overflow compaction
|
|
1800
|
+
* @returns Whether the post-run loop should call `agent.continue()`
|
|
1731
1801
|
*/
|
|
1732
1802
|
async _runAutoCompaction(reason, willRetry) {
|
|
1733
1803
|
const settings = this.settingsManager.getCompactionSettings();
|
|
1734
1804
|
let started = false;
|
|
1805
|
+
let fromExtension = false;
|
|
1735
1806
|
try {
|
|
1736
1807
|
if (!this.model) {
|
|
1737
1808
|
return false;
|
|
@@ -1746,7 +1817,6 @@ export class AgentSession {
|
|
|
1746
1817
|
this._autoCompactionAbortController = new AbortController();
|
|
1747
1818
|
started = true;
|
|
1748
1819
|
let extensionCompaction;
|
|
1749
|
-
let fromExtension = false;
|
|
1750
1820
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1751
1821
|
const extensionResult = (await this._extensionRunner.emit({
|
|
1752
1822
|
type: "session_before_compact",
|
|
@@ -1765,6 +1835,12 @@ export class AgentSession {
|
|
|
1765
1835
|
aborted: true,
|
|
1766
1836
|
willRetry: false,
|
|
1767
1837
|
});
|
|
1838
|
+
await this._emitSessionCompactFailed({
|
|
1839
|
+
reason,
|
|
1840
|
+
aborted: true,
|
|
1841
|
+
willRetry: false,
|
|
1842
|
+
fromExtension: false,
|
|
1843
|
+
});
|
|
1768
1844
|
return false;
|
|
1769
1845
|
}
|
|
1770
1846
|
if (extensionResult?.compaction) {
|
|
@@ -1786,8 +1862,8 @@ export class AgentSession {
|
|
|
1786
1862
|
details = extensionCompaction.details;
|
|
1787
1863
|
}
|
|
1788
1864
|
else {
|
|
1789
|
-
//
|
|
1790
|
-
const compactResult = await
|
|
1865
|
+
// Shared default summary generator, also used by manual compaction.
|
|
1866
|
+
const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, env, reason);
|
|
1791
1867
|
summary = compactResult.summary;
|
|
1792
1868
|
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
1793
1869
|
tokensBefore = compactResult.tokensBefore;
|
|
@@ -1802,6 +1878,12 @@ export class AgentSession {
|
|
|
1802
1878
|
aborted: true,
|
|
1803
1879
|
willRetry: false,
|
|
1804
1880
|
});
|
|
1881
|
+
await this._emitSessionCompactFailed({
|
|
1882
|
+
reason,
|
|
1883
|
+
aborted: true,
|
|
1884
|
+
willRetry: false,
|
|
1885
|
+
fromExtension,
|
|
1886
|
+
});
|
|
1805
1887
|
return false;
|
|
1806
1888
|
}
|
|
1807
1889
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
@@ -1848,15 +1930,23 @@ export class AgentSession {
|
|
|
1848
1930
|
catch (error) {
|
|
1849
1931
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
1850
1932
|
if (started) {
|
|
1933
|
+
const formattedErrorMessage = reason === "overflow"
|
|
1934
|
+
? `Context overflow recovery failed: ${errorMessage}`
|
|
1935
|
+
: `Auto-compaction failed: ${errorMessage}`;
|
|
1851
1936
|
this._emit({
|
|
1852
1937
|
type: "compaction_end",
|
|
1853
1938
|
reason,
|
|
1854
1939
|
result: undefined,
|
|
1855
1940
|
aborted: false,
|
|
1856
1941
|
willRetry: false,
|
|
1857
|
-
errorMessage:
|
|
1858
|
-
|
|
1859
|
-
|
|
1942
|
+
errorMessage: formattedErrorMessage,
|
|
1943
|
+
});
|
|
1944
|
+
await this._emitSessionCompactFailed({
|
|
1945
|
+
reason,
|
|
1946
|
+
errorMessage: formattedErrorMessage,
|
|
1947
|
+
aborted: false,
|
|
1948
|
+
willRetry: false,
|
|
1949
|
+
fromExtension,
|
|
1860
1950
|
});
|
|
1861
1951
|
}
|
|
1862
1952
|
return false;
|
|
@@ -2780,29 +2870,7 @@ export class AgentSession {
|
|
|
2780
2870
|
* @returns The resolved output file path.
|
|
2781
2871
|
*/
|
|
2782
2872
|
exportToJsonl(outputPath) {
|
|
2783
|
-
|
|
2784
|
-
const dir = dirname(filePath);
|
|
2785
|
-
if (!existsSync(dir)) {
|
|
2786
|
-
mkdirSync(dir, { recursive: true });
|
|
2787
|
-
}
|
|
2788
|
-
const header = {
|
|
2789
|
-
type: "session",
|
|
2790
|
-
version: CURRENT_SESSION_VERSION,
|
|
2791
|
-
id: this.sessionManager.getSessionId(),
|
|
2792
|
-
timestamp: new Date().toISOString(),
|
|
2793
|
-
cwd: this.sessionManager.getCwd(),
|
|
2794
|
-
};
|
|
2795
|
-
const branchEntries = this.sessionManager.getBranch();
|
|
2796
|
-
const lines = [JSON.stringify(header)];
|
|
2797
|
-
// Re-chain parentIds to form a linear sequence
|
|
2798
|
-
let prevId = null;
|
|
2799
|
-
for (const entry of branchEntries) {
|
|
2800
|
-
const linear = { ...entry, parentId: prevId };
|
|
2801
|
-
lines.push(JSON.stringify(linear));
|
|
2802
|
-
prevId = entry.id;
|
|
2803
|
-
}
|
|
2804
|
-
writeFileSync(filePath, `${lines.join("\n")}\n`);
|
|
2805
|
-
return filePath;
|
|
2873
|
+
return exportSessionToJsonl(this.sessionManager, outputPath);
|
|
2806
2874
|
}
|
|
2807
2875
|
// =========================================================================
|
|
2808
2876
|
// Utilities
|