lsd-pi 1.1.10 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/slash-commands/index.js +2 -0
- package/dist/resources/extensions/slash-commands/init.js +47 -0
- package/dist/resources/extensions/slash-commands/plan.js +231 -50
- package/dist/resources/extensions/slash-commands/tools.js +14 -27
- package/dist/resources/extensions/subagent/index.js +5 -10
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +11 -5
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-lsd-md.test.js +59 -7
- package/packages/pi-coding-agent/dist/core/resource-loader-lsd-md.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -4
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +18 -7
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.js +80 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +12 -5
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +23 -9
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +32 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +34 -25
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -5
- package/packages/pi-coding-agent/src/core/resource-loader-lsd-md.test.ts +67 -7
- package/packages/pi-coding-agent/src/core/resource-loader.ts +4 -4
- package/packages/pi-coding-agent/src/core/sdk.test.ts +100 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +23 -8
- package/packages/pi-coding-agent/src/core/settings-manager.ts +36 -15
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +41 -10
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +11 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +43 -27
- package/pkg/package.json +1 -1
- package/src/resources/extensions/slash-commands/index.ts +2 -0
- package/src/resources/extensions/slash-commands/init.ts +55 -0
- package/src/resources/extensions/slash-commands/plan.ts +268 -52
- package/src/resources/extensions/slash-commands/tools.ts +15 -29
- package/src/resources/extensions/subagent/index.ts +5 -10
|
@@ -1804,7 +1804,7 @@ export class AgentSession {
|
|
|
1804
1804
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
1805
1805
|
}
|
|
1806
1806
|
|
|
1807
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1807
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
|
|
1808
1808
|
await this._applyModelChange(model, thinkingLevel, "set", options);
|
|
1809
1809
|
}
|
|
1810
1810
|
|
|
@@ -1841,7 +1841,7 @@ export class AgentSession {
|
|
|
1841
1841
|
|
|
1842
1842
|
// Explicit scoped model thinking level overrides current session level;
|
|
1843
1843
|
// undefined scoped model thinking level inherits the current session preference.
|
|
1844
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
|
|
1844
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
|
|
1845
1845
|
await this._applyModelChange(next.model, thinkingLevel, "cycle", options);
|
|
1846
1846
|
|
|
1847
1847
|
return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
|
|
@@ -1859,7 +1859,7 @@ export class AgentSession {
|
|
|
1859
1859
|
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
1860
1860
|
const nextModel = availableModels[nextIndex];
|
|
1861
1861
|
|
|
1862
|
-
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1862
|
+
const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
|
|
1863
1863
|
await this._applyModelChange(nextModel, thinkingLevel, "cycle", options);
|
|
1864
1864
|
|
|
1865
1865
|
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
@@ -1942,11 +1942,19 @@ export class AgentSession {
|
|
|
1942
1942
|
return !!this.model?.reasoning;
|
|
1943
1943
|
}
|
|
1944
1944
|
|
|
1945
|
-
private _getThinkingLevelForModelSwitch(explicitLevel?: ThinkingLevel): ThinkingLevel {
|
|
1945
|
+
private _getThinkingLevelForModelSwitch(targetModel: Model<any>, explicitLevel?: ThinkingLevel): ThinkingLevel {
|
|
1946
1946
|
if (explicitLevel !== undefined) {
|
|
1947
1947
|
return explicitLevel;
|
|
1948
1948
|
}
|
|
1949
|
-
if (
|
|
1949
|
+
if (
|
|
1950
|
+
targetModel.provider === "anthropic" &&
|
|
1951
|
+
targetModel.reasoning === true &&
|
|
1952
|
+
this.settingsManager.getAnthropicAdaptiveByDefault() &&
|
|
1953
|
+
supportsAdaptiveThinking(targetModel.id)
|
|
1954
|
+
) {
|
|
1955
|
+
return "adaptive";
|
|
1956
|
+
}
|
|
1957
|
+
if (!targetModel.reasoning) {
|
|
1950
1958
|
return this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
1951
1959
|
}
|
|
1952
1960
|
return this.thinkingLevel;
|
|
@@ -7,7 +7,7 @@ import test from "node:test";
|
|
|
7
7
|
import { DefaultResourceLoader } from "./resource-loader.js";
|
|
8
8
|
import { SettingsManager } from "./settings-manager.js";
|
|
9
9
|
|
|
10
|
-
test("resource loader reads global
|
|
10
|
+
test("resource loader reads global LSD.md from the app root", async (t) => {
|
|
11
11
|
const tmp = mkdtempSync(join(tmpdir(), "resource-loader-lsd-md-"));
|
|
12
12
|
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ test("resource loader reads global lsd.md from the app root", async (t) => {
|
|
|
16
16
|
const cwd = join(tmp, "project");
|
|
17
17
|
mkdirSync(agentDir, { recursive: true });
|
|
18
18
|
mkdirSync(cwd, { recursive: true });
|
|
19
|
-
writeFileSync(join(appRoot, "
|
|
19
|
+
writeFileSync(join(appRoot, "LSD.md"), "# global lsd\n", "utf-8");
|
|
20
20
|
|
|
21
21
|
const loader = new DefaultResourceLoader({
|
|
22
22
|
cwd,
|
|
@@ -31,11 +31,11 @@ test("resource loader reads global lsd.md from the app root", async (t) => {
|
|
|
31
31
|
|
|
32
32
|
const contextFiles = loader.getAgentsFiles().agentsFiles;
|
|
33
33
|
assert.equal(contextFiles.length, 1);
|
|
34
|
-
assert.equal(contextFiles[0]?.path, join(appRoot, "
|
|
34
|
+
assert.equal(contextFiles[0]?.path, join(appRoot, "LSD.md"));
|
|
35
35
|
assert.equal(contextFiles[0]?.content, "# global lsd\n");
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
test("resource loader
|
|
38
|
+
test("resource loader uses project LSD.md before CLAUDE.md or AGENTS.md in the same directory", async (t) => {
|
|
39
39
|
const tmp = mkdtempSync(join(tmpdir(), "resource-loader-lsd-md-"));
|
|
40
40
|
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
41
41
|
|
|
@@ -44,9 +44,9 @@ test("resource loader reads ancestor .lsd/lsd.md before AGENTS.md in the same di
|
|
|
44
44
|
const projectRoot = join(tmp, "project");
|
|
45
45
|
const cwd = join(projectRoot, "src", "feature");
|
|
46
46
|
mkdirSync(agentDir, { recursive: true });
|
|
47
|
-
mkdirSync(join(projectRoot, ".lsd"), { recursive: true });
|
|
48
47
|
mkdirSync(cwd, { recursive: true });
|
|
49
|
-
writeFileSync(join(projectRoot, ".
|
|
48
|
+
writeFileSync(join(projectRoot, "LSD.md"), "# project lsd\n", "utf-8");
|
|
49
|
+
writeFileSync(join(projectRoot, "CLAUDE.md"), "# claude fallback\n", "utf-8");
|
|
50
50
|
writeFileSync(join(projectRoot, "AGENTS.md"), "# agents fallback\n", "utf-8");
|
|
51
51
|
|
|
52
52
|
const loader = new DefaultResourceLoader({
|
|
@@ -62,6 +62,66 @@ test("resource loader reads ancestor .lsd/lsd.md before AGENTS.md in the same di
|
|
|
62
62
|
|
|
63
63
|
const contextFiles = loader.getAgentsFiles().agentsFiles;
|
|
64
64
|
assert.equal(contextFiles.length, 1);
|
|
65
|
-
assert.equal(contextFiles[0]?.path, join(projectRoot, ".
|
|
65
|
+
assert.equal(contextFiles[0]?.path, join(projectRoot, "LSD.md"));
|
|
66
66
|
assert.equal(contextFiles[0]?.content, "# project lsd\n");
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
test("resource loader loads both global and project LSD.md files", async (t) => {
|
|
70
|
+
const tmp = mkdtempSync(join(tmpdir(), "resource-loader-lsd-md-"));
|
|
71
|
+
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
72
|
+
|
|
73
|
+
const appRoot = join(tmp, ".lsd");
|
|
74
|
+
const agentDir = join(appRoot, "agent");
|
|
75
|
+
const projectRoot = join(tmp, "project");
|
|
76
|
+
const cwd = join(projectRoot, "src", "feature");
|
|
77
|
+
mkdirSync(agentDir, { recursive: true });
|
|
78
|
+
mkdirSync(cwd, { recursive: true });
|
|
79
|
+
writeFileSync(join(appRoot, "LSD.md"), "# global lsd\n", "utf-8");
|
|
80
|
+
writeFileSync(join(projectRoot, "LSD.md"), "# project lsd\n", "utf-8");
|
|
81
|
+
|
|
82
|
+
const loader = new DefaultResourceLoader({
|
|
83
|
+
cwd,
|
|
84
|
+
agentDir,
|
|
85
|
+
settingsManager: SettingsManager.inMemory(),
|
|
86
|
+
noExtensions: true,
|
|
87
|
+
noSkills: true,
|
|
88
|
+
noPromptTemplates: true,
|
|
89
|
+
noThemes: true,
|
|
90
|
+
});
|
|
91
|
+
await loader.reload();
|
|
92
|
+
|
|
93
|
+
const contextFiles = loader.getAgentsFiles().agentsFiles;
|
|
94
|
+
assert.equal(contextFiles.length, 2);
|
|
95
|
+
assert.equal(contextFiles[0]?.path, join(appRoot, "LSD.md"));
|
|
96
|
+
assert.equal(contextFiles[1]?.path, join(projectRoot, "LSD.md"));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("resource loader falls back to CLAUDE.md when no LSD.md exists", async (t) => {
|
|
100
|
+
const tmp = mkdtempSync(join(tmpdir(), "resource-loader-lsd-md-"));
|
|
101
|
+
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
102
|
+
|
|
103
|
+
const appRoot = join(tmp, ".lsd");
|
|
104
|
+
const agentDir = join(appRoot, "agent");
|
|
105
|
+
const projectRoot = join(tmp, "project");
|
|
106
|
+
const cwd = join(projectRoot, "src", "feature");
|
|
107
|
+
mkdirSync(agentDir, { recursive: true });
|
|
108
|
+
mkdirSync(cwd, { recursive: true });
|
|
109
|
+
writeFileSync(join(projectRoot, "CLAUDE.md"), "# claude fallback\n", "utf-8");
|
|
110
|
+
writeFileSync(join(projectRoot, "AGENTS.md"), "# agents fallback\n", "utf-8");
|
|
111
|
+
|
|
112
|
+
const loader = new DefaultResourceLoader({
|
|
113
|
+
cwd,
|
|
114
|
+
agentDir,
|
|
115
|
+
settingsManager: SettingsManager.inMemory(),
|
|
116
|
+
noExtensions: true,
|
|
117
|
+
noSkills: true,
|
|
118
|
+
noPromptTemplates: true,
|
|
119
|
+
noThemes: true,
|
|
120
|
+
});
|
|
121
|
+
await loader.reload();
|
|
122
|
+
|
|
123
|
+
const contextFiles = loader.getAgentsFiles().agentsFiles;
|
|
124
|
+
assert.equal(contextFiles.length, 1);
|
|
125
|
+
assert.equal(contextFiles[0]?.path, join(projectRoot, "CLAUDE.md"));
|
|
126
|
+
assert.equal(contextFiles[0]?.content, "# claude fallback\n");
|
|
127
|
+
});
|
|
@@ -71,14 +71,14 @@ function tryReadContextFile(filePath: string): { path: string; content: string }
|
|
|
71
71
|
|
|
72
72
|
function loadContextFileFromDir(dir: string): { path: string; content: string } | null {
|
|
73
73
|
const candidates = [
|
|
74
|
-
join(dir, "lsd.md"),
|
|
75
|
-
join(dir, ".lsd", "lsd.md"),
|
|
76
|
-
join(dir, CONFIG_DIR_NAME, "lsd.md"),
|
|
77
74
|
join(dir, "LSD.md"),
|
|
78
75
|
join(dir, ".lsd", "LSD.md"),
|
|
79
76
|
join(dir, CONFIG_DIR_NAME, "LSD.md"),
|
|
80
|
-
join(dir, "
|
|
77
|
+
join(dir, "lsd.md"),
|
|
78
|
+
join(dir, ".lsd", "lsd.md"),
|
|
79
|
+
join(dir, CONFIG_DIR_NAME, "lsd.md"),
|
|
81
80
|
join(dir, "CLAUDE.md"),
|
|
81
|
+
join(dir, "AGENTS.md"),
|
|
82
82
|
];
|
|
83
83
|
for (const filePath of new Set(candidates)) {
|
|
84
84
|
const loaded = tryReadContextFile(filePath);
|
|
@@ -80,3 +80,103 @@ test("createAgentSession restores last session model even when API key cannot be
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
});
|
|
83
|
+
|
|
84
|
+
test("createAgentSession defaults to adaptive for supported Anthropic models when enabled", async () => {
|
|
85
|
+
const tempDir = join(
|
|
86
|
+
process.cwd(),
|
|
87
|
+
".tmp-tests",
|
|
88
|
+
`sdk-anthropic-adaptive-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
89
|
+
);
|
|
90
|
+
mkdirSync(tempDir, { recursive: true });
|
|
91
|
+
tempDirs.push(tempDir);
|
|
92
|
+
|
|
93
|
+
const authStorage = AuthStorage.inMemory({
|
|
94
|
+
anthropic: { type: "api_key", key: "test-anthropic-key" },
|
|
95
|
+
});
|
|
96
|
+
const modelRegistry = new ModelRegistry(authStorage, join(tempDir, "models.json"));
|
|
97
|
+
const settingsManager = SettingsManager.inMemory();
|
|
98
|
+
settingsManager.setDefaultModelAndProvider("anthropic", "claude-sonnet-4-6");
|
|
99
|
+
settingsManager.setDefaultThinkingLevel("high");
|
|
100
|
+
settingsManager.setAnthropicAdaptiveByDefault(true);
|
|
101
|
+
const sessionManager = SessionManager.inMemory(tempDir);
|
|
102
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
103
|
+
cwd: tempDir,
|
|
104
|
+
agentDir: tempDir,
|
|
105
|
+
settingsManager,
|
|
106
|
+
noExtensions: true,
|
|
107
|
+
noSkills: true,
|
|
108
|
+
noPromptTemplates: true,
|
|
109
|
+
noThemes: true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const { session } = await createAgentSession({
|
|
113
|
+
cwd: tempDir,
|
|
114
|
+
agentDir: tempDir,
|
|
115
|
+
authStorage,
|
|
116
|
+
modelRegistry,
|
|
117
|
+
settingsManager,
|
|
118
|
+
sessionManager,
|
|
119
|
+
resourceLoader,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
assert.equal(session.model?.provider, "anthropic");
|
|
123
|
+
assert.equal(session.model?.id, "claude-sonnet-4-6");
|
|
124
|
+
assert.equal(session.thinkingLevel, "adaptive");
|
|
125
|
+
assert.equal(settingsManager.getDefaultThinkingLevel(), "high");
|
|
126
|
+
session.dispose();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("AgentSession switches supported Anthropic models to adaptive when the setting is enabled", async () => {
|
|
130
|
+
const tempDir = join(
|
|
131
|
+
process.cwd(),
|
|
132
|
+
".tmp-tests",
|
|
133
|
+
`sdk-anthropic-switch-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
134
|
+
);
|
|
135
|
+
mkdirSync(tempDir, { recursive: true });
|
|
136
|
+
tempDirs.push(tempDir);
|
|
137
|
+
|
|
138
|
+
const authStorage = AuthStorage.inMemory({
|
|
139
|
+
anthropic: { type: "api_key", key: "test-anthropic-key" },
|
|
140
|
+
openai: { type: "api_key", key: "test-openai-key" },
|
|
141
|
+
});
|
|
142
|
+
const modelRegistry = new ModelRegistry(authStorage, join(tempDir, "models.json"));
|
|
143
|
+
const settingsManager = SettingsManager.inMemory();
|
|
144
|
+
settingsManager.setDefaultModelAndProvider("openai", "gpt-5.4");
|
|
145
|
+
settingsManager.setDefaultThinkingLevel("high");
|
|
146
|
+
settingsManager.setAnthropicAdaptiveByDefault(true);
|
|
147
|
+
const sessionManager = SessionManager.inMemory(tempDir);
|
|
148
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
149
|
+
cwd: tempDir,
|
|
150
|
+
agentDir: tempDir,
|
|
151
|
+
settingsManager,
|
|
152
|
+
noExtensions: true,
|
|
153
|
+
noSkills: true,
|
|
154
|
+
noPromptTemplates: true,
|
|
155
|
+
noThemes: true,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const { session } = await createAgentSession({
|
|
159
|
+
cwd: tempDir,
|
|
160
|
+
agentDir: tempDir,
|
|
161
|
+
authStorage,
|
|
162
|
+
modelRegistry,
|
|
163
|
+
settingsManager,
|
|
164
|
+
sessionManager,
|
|
165
|
+
resourceLoader,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
assert.equal(session.model?.provider, "openai");
|
|
169
|
+
assert.equal(session.thinkingLevel, "high");
|
|
170
|
+
|
|
171
|
+
const anthropicModel = modelRegistry.getAvailable().find((model) =>
|
|
172
|
+
model.provider === "anthropic" && model.id === "claude-sonnet-4-6"
|
|
173
|
+
);
|
|
174
|
+
if (!anthropicModel) throw new Error("expected claude-sonnet-4-6 to be available");
|
|
175
|
+
|
|
176
|
+
await session.setModel(anthropicModel);
|
|
177
|
+
|
|
178
|
+
assert.equal(session.model?.provider, "anthropic");
|
|
179
|
+
assert.equal(session.model?.id, "claude-sonnet-4-6");
|
|
180
|
+
assert.equal(session.thinkingLevel, "adaptive");
|
|
181
|
+
session.dispose();
|
|
182
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { Agent, type AgentMessage, type ThinkingLevel } from "@gsd/pi-agent-core";
|
|
3
|
-
import type
|
|
3
|
+
import { supportsAdaptiveThinking, type Message, type Model } from "@gsd/pi-ai";
|
|
4
4
|
import { getAgentDir, getDocsPath } from "../config.js";
|
|
5
5
|
import { AgentSession } from "./agent-session.js";
|
|
6
6
|
import { AuthStorage } from "./auth-storage.js";
|
|
@@ -140,6 +140,17 @@ function getDefaultAgentDir(): string {
|
|
|
140
140
|
return getAgentDir();
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
function shouldPreferAdaptiveThinkingByDefault(
|
|
144
|
+
model: Model<any> | undefined,
|
|
145
|
+
settingsManager: SettingsManager,
|
|
146
|
+
): boolean {
|
|
147
|
+
return !!model &&
|
|
148
|
+
model.provider === "anthropic" &&
|
|
149
|
+
model.reasoning === true &&
|
|
150
|
+
settingsManager.getAnthropicAdaptiveByDefault() &&
|
|
151
|
+
supportsAdaptiveThinking(model.id);
|
|
152
|
+
}
|
|
153
|
+
|
|
143
154
|
/**
|
|
144
155
|
* Create an AgentSession with the specified options.
|
|
145
156
|
*
|
|
@@ -252,6 +263,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
252
263
|
thinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
253
264
|
}
|
|
254
265
|
|
|
266
|
+
const preferAdaptiveByDefault = shouldPreferAdaptiveThinkingByDefault(model, settingsManager);
|
|
267
|
+
if (preferAdaptiveByDefault) {
|
|
268
|
+
thinkingLevel = "adaptive";
|
|
269
|
+
}
|
|
270
|
+
|
|
255
271
|
// Clamp to model capabilities
|
|
256
272
|
if (!model || !model.reasoning) {
|
|
257
273
|
thinkingLevel = "off";
|
|
@@ -259,13 +275,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
259
275
|
|
|
260
276
|
const editMode = settingsManager.getEditMode();
|
|
261
277
|
const toolProfile = settingsManager.getToolProfile();
|
|
262
|
-
const
|
|
263
|
-
?
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
: ["read", "bash", "edit", "write", "lsp", "bg_shell", "tool_search", "tool_enable", "Skill", "subagent", "await_subagent"];
|
|
278
|
+
const balancedToolNames = editMode === "hashline"
|
|
279
|
+
? ["hashline_read", "bash", "hashline_edit", "write", "lsp", "bg_shell", "tool_search", "tool_enable", "Skill", "subagent", "await_subagent", "ask_user_questions"]
|
|
280
|
+
: ["read", "bash", "edit", "write", "lsp", "bg_shell", "tool_search", "tool_enable", "Skill", "subagent", "await_subagent", "ask_user_questions"];
|
|
281
|
+
const defaultActiveToolNames: string[] = toolProfile === "full"
|
|
282
|
+
? Object.keys(allTools)
|
|
283
|
+
: balancedToolNames;
|
|
269
284
|
const initialActiveToolNames: string[] = options.tools
|
|
270
285
|
? options.tools.map((t) => t.name).filter((n): n is string => typeof n === "string" && n in allTools)
|
|
271
286
|
: defaultActiveToolNames;
|
|
@@ -127,9 +127,12 @@ export interface Settings {
|
|
|
127
127
|
planModeReasoningModel?: string;
|
|
128
128
|
planModeReviewModel?: string;
|
|
129
129
|
planModeCodingModel?: string;
|
|
130
|
+
autoSuggestPlanMode?: boolean; // default: false — append a system-prompt instruction so the LLM proposes plan mode for large tasks
|
|
131
|
+
autoSwitchPlanModel?: boolean; // default: false — enable opusplan-style model switching (auto-switch to reasoning model on entry, new-session option on approval)
|
|
130
132
|
permissionMode?: "danger-full-access" | "accept-on-edit" | "auto" | "plan";
|
|
131
133
|
classifierModel?: string;
|
|
132
134
|
defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive";
|
|
135
|
+
anthropicAdaptiveByDefault?: boolean; // default: false — prefer adaptive thinking when using supported Anthropic models
|
|
133
136
|
transport?: TransportSetting; // default: "sse"
|
|
134
137
|
steeringMode?: "all" | "one-at-a-time";
|
|
135
138
|
followUpMode?: "all" | "one-at-a-time";
|
|
@@ -149,8 +152,8 @@ export interface Settings {
|
|
|
149
152
|
prompts?: string[]; // Array of local prompt template paths or directories
|
|
150
153
|
themes?: string[]; // Array of local theme file paths or directories
|
|
151
154
|
enableSkillCommands?: boolean; // default: true - register skills as /skill:name commands
|
|
152
|
-
toolSearch?: boolean; // legacy boolean toggle
|
|
153
|
-
toolProfile?: "
|
|
155
|
+
toolSearch?: boolean; // legacy boolean toggle from deprecated minimal profile; retained for migration only
|
|
156
|
+
toolProfile?: "balanced" | "full"; // default: "balanced"
|
|
154
157
|
terminal?: TerminalSettings;
|
|
155
158
|
images?: ImageSettings;
|
|
156
159
|
enabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)
|
|
@@ -806,6 +809,22 @@ export class SettingsManager {
|
|
|
806
809
|
this.setGlobalSetting("planModeCodingModel", modelRef.trim());
|
|
807
810
|
}
|
|
808
811
|
|
|
812
|
+
getAutoSuggestPlanMode(): boolean {
|
|
813
|
+
return this.settings.autoSuggestPlanMode ?? false;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
setAutoSuggestPlanMode(enabled: boolean): void {
|
|
817
|
+
this.setGlobalSetting("autoSuggestPlanMode", enabled);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
getAutoSwitchPlanModel(): boolean {
|
|
821
|
+
return this.settings.autoSwitchPlanModel ?? false;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
setAutoSwitchPlanModel(enabled: boolean): void {
|
|
825
|
+
this.setGlobalSetting("autoSwitchPlanModel", enabled);
|
|
826
|
+
}
|
|
827
|
+
|
|
809
828
|
setPermissionMode(mode: "danger-full-access" | "accept-on-edit" | "auto" | "plan"): void {
|
|
810
829
|
this.setGlobalSetting("permissionMode", mode);
|
|
811
830
|
}
|
|
@@ -882,6 +901,14 @@ export class SettingsManager {
|
|
|
882
901
|
this.setGlobalSetting("defaultThinkingLevel", level);
|
|
883
902
|
}
|
|
884
903
|
|
|
904
|
+
getAnthropicAdaptiveByDefault(): boolean {
|
|
905
|
+
return this.settings.anthropicAdaptiveByDefault ?? false;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
setAnthropicAdaptiveByDefault(enabled: boolean): void {
|
|
909
|
+
this.setGlobalSetting("anthropicAdaptiveByDefault", enabled);
|
|
910
|
+
}
|
|
911
|
+
|
|
885
912
|
getTransport(): TransportSetting {
|
|
886
913
|
return this.settings.transport ?? "sse";
|
|
887
914
|
}
|
|
@@ -1067,24 +1094,18 @@ export class SettingsManager {
|
|
|
1067
1094
|
this.setGlobalSetting("enableSkillCommands", enabled);
|
|
1068
1095
|
}
|
|
1069
1096
|
|
|
1070
|
-
getToolProfile(): "
|
|
1097
|
+
getToolProfile(): "balanced" | "full" {
|
|
1071
1098
|
const profile = this.settings.toolProfile;
|
|
1072
|
-
if (profile === "
|
|
1073
|
-
|
|
1099
|
+
if (profile === "balanced" || profile === "full") return profile;
|
|
1100
|
+
// Migrate legacy minimal/toolSearch settings to balanced.
|
|
1101
|
+
if (this.settings.toolSearch !== undefined) return "balanced";
|
|
1074
1102
|
return "balanced";
|
|
1075
1103
|
}
|
|
1076
1104
|
|
|
1077
|
-
setToolProfile(profile: "
|
|
1105
|
+
setToolProfile(profile: "balanced" | "full"): void {
|
|
1078
1106
|
this.setGlobalSetting("toolProfile", profile);
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
getToolSearch(): boolean {
|
|
1083
|
-
return this.getToolProfile() === "minimal";
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
setToolSearch(enabled: boolean): void {
|
|
1087
|
-
this.setToolProfile(enabled ? "minimal" : "balanced");
|
|
1107
|
+
// Keep legacy field in sync with deprecated minimal mode removal.
|
|
1108
|
+
this.setGlobalSetting("toolSearch", false);
|
|
1088
1109
|
}
|
|
1089
1110
|
|
|
1090
1111
|
getThinkingBudgets(): ThinkingBudgetsSettings | undefined {
|
|
@@ -33,12 +33,13 @@ export interface SettingsConfig {
|
|
|
33
33
|
planModeReasoningModel: string;
|
|
34
34
|
planModeReviewModel: string;
|
|
35
35
|
planModeCodingModel: string;
|
|
36
|
+
autoSuggestPlanMode: boolean;
|
|
37
|
+
autoSwitchPlanModel: boolean;
|
|
36
38
|
showImages: boolean;
|
|
37
39
|
autoResizeImages: boolean;
|
|
38
40
|
blockImages: boolean;
|
|
39
41
|
enableSkillCommands: boolean;
|
|
40
|
-
|
|
41
|
-
toolProfile: "minimal" | "balanced";
|
|
42
|
+
toolProfile: "balanced" | "full";
|
|
42
43
|
codexRotate: boolean;
|
|
43
44
|
cacheTimer: boolean;
|
|
44
45
|
pinLastPrompt: boolean;
|
|
@@ -46,6 +47,7 @@ export interface SettingsConfig {
|
|
|
46
47
|
followUpMode: "all" | "one-at-a-time";
|
|
47
48
|
transport: Transport;
|
|
48
49
|
thinkingLevel: ThinkingLevel;
|
|
50
|
+
anthropicAdaptiveByDefault: boolean;
|
|
49
51
|
availableThinkingLevels: ThinkingLevel[];
|
|
50
52
|
currentTheme: string;
|
|
51
53
|
availableThemes: string[];
|
|
@@ -87,6 +89,8 @@ export interface SettingsCallbacks {
|
|
|
87
89
|
onPlanModeReasoningModelChange: (modelRef: string) => void;
|
|
88
90
|
onPlanModeReviewModelChange: (modelRef: string) => void;
|
|
89
91
|
onPlanModeCodingModelChange: (modelRef: string) => void;
|
|
92
|
+
onAutoSuggestPlanModeChange: (enabled: boolean) => void;
|
|
93
|
+
onAutoSwitchPlanModelChange: (enabled: boolean) => void;
|
|
90
94
|
onAutoDreamChange: (enabled: boolean) => void;
|
|
91
95
|
onAutoMemoryChange: (enabled: boolean) => void;
|
|
92
96
|
onTelegramLiveRelayAutoConnectChange: (enabled: boolean) => void;
|
|
@@ -96,8 +100,7 @@ export interface SettingsCallbacks {
|
|
|
96
100
|
onAutoResizeImagesChange: (enabled: boolean) => void;
|
|
97
101
|
onBlockImagesChange: (blocked: boolean) => void;
|
|
98
102
|
onEnableSkillCommandsChange: (enabled: boolean) => void;
|
|
99
|
-
|
|
100
|
-
onToolProfileChange: (profile: "minimal" | "balanced") => void;
|
|
103
|
+
onToolProfileChange: (profile: "balanced" | "full") => void;
|
|
101
104
|
onCodexRotateChange: (enabled: boolean) => void;
|
|
102
105
|
onCacheTimerChange: (enabled: boolean) => void;
|
|
103
106
|
onPinLastPromptChange: (enabled: boolean) => void;
|
|
@@ -105,6 +108,7 @@ export interface SettingsCallbacks {
|
|
|
105
108
|
onFollowUpModeChange: (mode: "all" | "one-at-a-time") => void;
|
|
106
109
|
onTransportChange: (transport: Transport) => void;
|
|
107
110
|
onThinkingLevelChange: (level: ThinkingLevel) => void;
|
|
111
|
+
onAnthropicAdaptiveByDefaultChange: (enabled: boolean) => void;
|
|
108
112
|
onThemeChange: (theme: string) => void;
|
|
109
113
|
onThemePreview?: (theme: string) => void;
|
|
110
114
|
onThemeAccentChange: (accent: string) => void;
|
|
@@ -255,6 +259,20 @@ export class SettingsSelectorComponent extends Container {
|
|
|
255
259
|
currentValue: config.planModeCodingModel,
|
|
256
260
|
submenu: config.planModeCodingModelSubmenu,
|
|
257
261
|
},
|
|
262
|
+
{
|
|
263
|
+
id: "auto-suggest-plan-mode",
|
|
264
|
+
label: "Auto-suggest plan mode",
|
|
265
|
+
description: "Ask the LLM to suggest plan mode for large/complex tasks",
|
|
266
|
+
currentValue: config.autoSuggestPlanMode ? "true" : "false",
|
|
267
|
+
values: ["true", "false"],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "auto-switch-plan-model",
|
|
271
|
+
label: "OpusPlan mode",
|
|
272
|
+
description: "On plan entry: auto-switch to Plan reasoning model (must be set). On approval: offer new session with Plan coding model + worker agent",
|
|
273
|
+
currentValue: config.autoSwitchPlanModel ? "true" : "false",
|
|
274
|
+
values: ["true", "false"],
|
|
275
|
+
},
|
|
258
276
|
{
|
|
259
277
|
id: "steering-mode",
|
|
260
278
|
label: "Steering mode",
|
|
@@ -356,6 +374,13 @@ export class SettingsSelectorComponent extends Container {
|
|
|
356
374
|
() => done(),
|
|
357
375
|
),
|
|
358
376
|
},
|
|
377
|
+
{
|
|
378
|
+
id: "anthropic-adaptive-default",
|
|
379
|
+
label: "Anthropic adaptive",
|
|
380
|
+
description: "Default to adaptive thinking for supported Anthropic models",
|
|
381
|
+
currentValue: config.anthropicAdaptiveByDefault ? "true" : "false",
|
|
382
|
+
values: ["true", "false"],
|
|
383
|
+
},
|
|
359
384
|
{
|
|
360
385
|
id: "theme",
|
|
361
386
|
label: "Theme",
|
|
@@ -455,9 +480,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
455
480
|
items.splice(skillCommandsIndex + 1, 0, {
|
|
456
481
|
id: "tool-profile",
|
|
457
482
|
label: "Tool profile",
|
|
458
|
-
description: "Choose the default active tool set: balanced for day-to-day coding,
|
|
483
|
+
description: "Choose the default active tool set: balanced for day-to-day coding, full to enable all available tools",
|
|
459
484
|
currentValue: config.toolProfile,
|
|
460
|
-
values: ["balanced", "
|
|
485
|
+
values: ["balanced", "full"],
|
|
461
486
|
});
|
|
462
487
|
|
|
463
488
|
// Codex rotate toggle (insert after tool-profile)
|
|
@@ -633,6 +658,12 @@ export class SettingsSelectorComponent extends Container {
|
|
|
633
658
|
case "plan-mode-coding-model":
|
|
634
659
|
callbacks.onPlanModeCodingModelChange(newValue);
|
|
635
660
|
break;
|
|
661
|
+
case "auto-suggest-plan-mode":
|
|
662
|
+
callbacks.onAutoSuggestPlanModeChange(newValue === "true");
|
|
663
|
+
break;
|
|
664
|
+
case "auto-switch-plan-model":
|
|
665
|
+
callbacks.onAutoSwitchPlanModelChange(newValue === "true");
|
|
666
|
+
break;
|
|
636
667
|
case "show-images":
|
|
637
668
|
callbacks.onShowImagesChange(newValue === "true");
|
|
638
669
|
break;
|
|
@@ -645,11 +676,8 @@ export class SettingsSelectorComponent extends Container {
|
|
|
645
676
|
case "skill-commands":
|
|
646
677
|
callbacks.onEnableSkillCommandsChange(newValue === "true");
|
|
647
678
|
break;
|
|
648
|
-
case "tool-search":
|
|
649
|
-
callbacks.onToolSearchChange(newValue === "true");
|
|
650
|
-
break;
|
|
651
679
|
case "tool-profile":
|
|
652
|
-
callbacks.onToolProfileChange(newValue as "
|
|
680
|
+
callbacks.onToolProfileChange(newValue as "balanced" | "full");
|
|
653
681
|
break;
|
|
654
682
|
case "codex-rotate":
|
|
655
683
|
callbacks.onCodexRotateChange(newValue === "true");
|
|
@@ -681,6 +709,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
681
709
|
case "transport":
|
|
682
710
|
callbacks.onTransportChange(newValue as Transport);
|
|
683
711
|
break;
|
|
712
|
+
case "anthropic-adaptive-default":
|
|
713
|
+
callbacks.onAnthropicAdaptiveByDefaultChange(newValue === "true");
|
|
714
|
+
break;
|
|
684
715
|
case "hide-thinking":
|
|
685
716
|
callbacks.onHideThinkingBlockChange(newValue === "true");
|
|
686
717
|
break;
|
|
@@ -508,6 +508,17 @@ export class ToolExecutionComponent extends Container {
|
|
|
508
508
|
if (resultComponent !== undefined) {
|
|
509
509
|
this.contentBox.addChild(resultComponent);
|
|
510
510
|
customRendererHasContent = true;
|
|
511
|
+
// bg_shell: show /bg hint after start/list/digest actions
|
|
512
|
+
if (this.toolName === 'bg_shell' && this.result && !this.isPartial) {
|
|
513
|
+
const details = this.result.details as Record<string, unknown> | undefined;
|
|
514
|
+
const action = details?.action as string | undefined;
|
|
515
|
+
if (action === 'start' || action === 'list' || action === 'digest') {
|
|
516
|
+
this.contentBox.addChild(new Text(
|
|
517
|
+
'\n' + theme.fg('muted', 'Tip: run /bg to manage background processes'),
|
|
518
|
+
0, 0,
|
|
519
|
+
));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
511
522
|
}
|
|
512
523
|
}
|
|
513
524
|
} catch {
|