supipowers 1.2.5 → 1.2.6
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/package.json +1 -1
- package/src/commands/commit.ts +8 -0
- package/src/commands/fix-pr.ts +20 -49
- package/src/commands/mcp.ts +14 -0
- package/src/commands/model.ts +3 -2
- package/src/commands/plan.ts +8 -9
- package/src/commands/qa.ts +15 -0
- package/src/commands/release.ts +16 -1
- package/src/commands/review.ts +7 -8
- package/src/config/model-resolver.ts +87 -0
- package/src/fix-pr/config.ts +0 -5
- package/src/fix-pr/prompt-builder.ts +7 -6
- package/src/fix-pr/types.ts +0 -11
- package/src/git/commit.ts +74 -26
- package/src/planning/approval-flow.ts +14 -1
- package/src/platform/omp.ts +5 -2
- package/src/platform/types.ts +2 -1
package/package.json
CHANGED
package/src/commands/commit.ts
CHANGED
|
@@ -5,8 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Platform } from "../platform/types.js";
|
|
7
7
|
import type { PlatformContext } from "../platform/types.js";
|
|
8
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
8
9
|
import { analyzeAndCommit } from "../git/commit.js";
|
|
9
10
|
|
|
11
|
+
modelRegistry.register({
|
|
12
|
+
id: "commit",
|
|
13
|
+
category: "command",
|
|
14
|
+
label: "Commit",
|
|
15
|
+
harnessRoleHint: "default",
|
|
16
|
+
});
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* Register the command for autocomplete and /help listing.
|
|
12
20
|
* Actual execution goes through handleCommit via the TUI dispatch.
|
package/src/commands/fix-pr.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Platform } from "../platform/types.js";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { loadFixPrConfig, saveFixPrConfig
|
|
4
|
+
import { loadFixPrConfig, saveFixPrConfig } from "../fix-pr/config.js";
|
|
5
5
|
import { buildFixPrOrchestratorPrompt } from "../fix-pr/prompt-builder.js";
|
|
6
6
|
import type { FixPrConfig, CommentReplyPolicy } from "../fix-pr/types.js";
|
|
7
7
|
import {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "../storage/fix-pr-sessions.js";
|
|
13
13
|
import { notifyInfo, notifyError, notifyWarning } from "../notifications/renderer.js";
|
|
14
14
|
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
15
|
-
import { resolveModelForAction, createModelBridge } from "../config/model-resolver.js";
|
|
15
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
16
16
|
import { loadModelConfig } from "../config/model-config.js";
|
|
17
17
|
import { detectBotReviewers } from "../fix-pr/bot-detector.js";
|
|
18
18
|
|
|
@@ -23,6 +23,14 @@ modelRegistry.register({
|
|
|
23
23
|
harnessRoleHint: "default",
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
modelRegistry.register({
|
|
27
|
+
id: "task",
|
|
28
|
+
category: "sub-agent",
|
|
29
|
+
parent: "fix-pr",
|
|
30
|
+
label: "Task (sub-agent)",
|
|
31
|
+
harnessRoleHint: "default",
|
|
32
|
+
});
|
|
33
|
+
|
|
26
34
|
function getScriptsDir(): string {
|
|
27
35
|
return path.join(path.dirname(new URL(import.meta.url).pathname), "..", "fix-pr", "scripts");
|
|
28
36
|
}
|
|
@@ -42,6 +50,12 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
42
50
|
platform.registerCommand("supi:fix-pr", {
|
|
43
51
|
description: "Fix PR review comments with token-optimized agent orchestration",
|
|
44
52
|
async handler(args: string | undefined, ctx: any): Promise<void> {
|
|
53
|
+
// Resolve and apply model override early — before any logic that might fail
|
|
54
|
+
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
55
|
+
const bridge = createModelBridge(platform);
|
|
56
|
+
const resolved = resolveModelForAction("fix-pr", modelRegistry, modelConfig, bridge);
|
|
57
|
+
await applyModelOverride(platform, ctx, "fix-pr", resolved);
|
|
58
|
+
|
|
45
59
|
// ── Step 1: Detect PR ──────────────────────────────────────────
|
|
46
60
|
let prNumber: number | null = null;
|
|
47
61
|
let repo: string | null = null;
|
|
@@ -178,6 +192,9 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
// ── Step 6: Build and send prompt ──────────────────────────────
|
|
195
|
+
// Resolve task model (sub-agents: planner, fixer). Falls back to fix-pr model.
|
|
196
|
+
const taskResolved = resolveModelForAction("task", modelRegistry, modelConfig, bridge);
|
|
197
|
+
const taskModel = taskResolved.model ?? resolved.model ?? "claude-sonnet-4-6";
|
|
181
198
|
const prompt = buildFixPrOrchestratorPrompt({
|
|
182
199
|
prNumber,
|
|
183
200
|
repo,
|
|
@@ -187,15 +204,9 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
187
204
|
config,
|
|
188
205
|
iteration: ledger.iteration,
|
|
189
206
|
skillContent,
|
|
207
|
+
taskModel,
|
|
190
208
|
});
|
|
191
209
|
|
|
192
|
-
// Resolve model for this action
|
|
193
|
-
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
194
|
-
const bridge = createModelBridge(platform);
|
|
195
|
-
const resolved = resolveModelForAction("fix-pr", modelRegistry, modelConfig, bridge);
|
|
196
|
-
if (resolved.source !== "main" && platform.setModel && resolved.model) {
|
|
197
|
-
platform.setModel(resolved.model);
|
|
198
|
-
}
|
|
199
210
|
|
|
200
211
|
platform.sendMessage(
|
|
201
212
|
{
|
|
@@ -234,10 +245,6 @@ const ITERATION_OPTIONS = [
|
|
|
234
245
|
"5",
|
|
235
246
|
];
|
|
236
247
|
|
|
237
|
-
const MODEL_TIER_OPTIONS = [
|
|
238
|
-
"high — thorough reasoning, more tokens",
|
|
239
|
-
"low — fast execution, fewer tokens",
|
|
240
|
-
];
|
|
241
248
|
|
|
242
249
|
async function runSetupWizard(ctx: any): Promise<FixPrConfig | null> {
|
|
243
250
|
|
|
@@ -270,46 +277,10 @@ async function runSetupWizard(ctx: any): Promise<FixPrConfig | null> {
|
|
|
270
277
|
if (!iterChoice) return null;
|
|
271
278
|
const maxIterations = parseInt(iterChoice, 10);
|
|
272
279
|
|
|
273
|
-
// 4. Model preferences
|
|
274
|
-
const orchestratorTier = await ctx.ui.select(
|
|
275
|
-
"Orchestrator model tier (assessment & grouping)",
|
|
276
|
-
MODEL_TIER_OPTIONS,
|
|
277
|
-
{ helpText: "Higher tier = more thorough analysis" },
|
|
278
|
-
);
|
|
279
|
-
if (!orchestratorTier) return null;
|
|
280
|
-
|
|
281
|
-
const plannerTier = await ctx.ui.select(
|
|
282
|
-
"Planner model tier (fix planning)",
|
|
283
|
-
MODEL_TIER_OPTIONS,
|
|
284
|
-
{ helpText: "Higher tier = more detailed plans" },
|
|
285
|
-
);
|
|
286
|
-
if (!plannerTier) return null;
|
|
287
|
-
|
|
288
|
-
const fixerTier = await ctx.ui.select(
|
|
289
|
-
"Fixer model tier (code changes)",
|
|
290
|
-
MODEL_TIER_OPTIONS,
|
|
291
|
-
{ helpText: "Lower tier usually sufficient for execution" },
|
|
292
|
-
);
|
|
293
|
-
if (!fixerTier) return null;
|
|
294
|
-
|
|
295
280
|
const config: FixPrConfig = {
|
|
296
281
|
reviewer: { type: "none", triggerMethod: null },
|
|
297
282
|
commentPolicy,
|
|
298
283
|
loop: { delaySeconds, maxIterations },
|
|
299
|
-
models: {
|
|
300
|
-
orchestrator: {
|
|
301
|
-
...DEFAULT_FIX_PR_CONFIG.models.orchestrator,
|
|
302
|
-
tier: orchestratorTier.startsWith("high") ? "high" : "low",
|
|
303
|
-
},
|
|
304
|
-
planner: {
|
|
305
|
-
...DEFAULT_FIX_PR_CONFIG.models.planner,
|
|
306
|
-
tier: plannerTier.startsWith("high") ? "high" : "low",
|
|
307
|
-
},
|
|
308
|
-
fixer: {
|
|
309
|
-
...DEFAULT_FIX_PR_CONFIG.models.fixer,
|
|
310
|
-
tier: fixerTier.startsWith("high") ? "high" : "low",
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
284
|
};
|
|
314
285
|
|
|
315
286
|
return config;
|
package/src/commands/mcp.ts
CHANGED
|
@@ -8,6 +8,16 @@ import { generateReadme, writeReadme, writeToolsCache, generateSkill, writeSkill
|
|
|
8
8
|
import { MCPC_EXIT } from "../mcp/types.js";
|
|
9
9
|
import type { McpTool, ServerConfig, HostMcpServer } from "../mcp/types.js";
|
|
10
10
|
import { lookupMcpServer, pickBestMatch } from "../mcp/registry.js";
|
|
11
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
12
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
13
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
14
|
+
|
|
15
|
+
modelRegistry.register({
|
|
16
|
+
id: "mcp",
|
|
17
|
+
category: "command",
|
|
18
|
+
label: "MCP",
|
|
19
|
+
harnessRoleHint: "slow",
|
|
20
|
+
});
|
|
11
21
|
|
|
12
22
|
export interface ParsedMcpArgs {
|
|
13
23
|
subcommand?: string;
|
|
@@ -789,6 +799,10 @@ export function registerMcpCommand(platform: Platform): void {
|
|
|
789
799
|
platform.registerCommand("supi:mcp", {
|
|
790
800
|
description: "Manage MCP servers — add, remove, enable, disable, refresh",
|
|
791
801
|
async handler(args: string | undefined, ctx: any) {
|
|
802
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
803
|
+
const bridge = createModelBridge(platform);
|
|
804
|
+
const resolved = resolveModelForAction("mcp", modelRegistry, modelCfg, bridge);
|
|
805
|
+
await applyModelOverride(platform, ctx, "mcp", resolved);
|
|
792
806
|
if (args) {
|
|
793
807
|
// CLI mode — parse and dispatch
|
|
794
808
|
await handleMcpCli(platform, ctx, parseCliArgs(args));
|
package/src/commands/model.ts
CHANGED
|
@@ -39,7 +39,7 @@ function buildDashboard(
|
|
|
39
39
|
bridge: ModelPlatformBridge,
|
|
40
40
|
): string {
|
|
41
41
|
const config = loadModelConfig(paths, cwd);
|
|
42
|
-
const lines: string[] = ["
|
|
42
|
+
const lines: string[] = ["\n Model Configuration\n", ` ${"action".padEnd(20)} ${"model".padEnd(24)} ${"thinking".padEnd(10)} source`];
|
|
43
43
|
|
|
44
44
|
let lastCategory: "command" | "sub-agent" | null = null;
|
|
45
45
|
let lastParent: string | undefined = undefined;
|
|
@@ -56,10 +56,11 @@ function buildDashboard(
|
|
|
56
56
|
const modelDisplay = (resolved.source === "main" && source === "main"
|
|
57
57
|
? "—"
|
|
58
58
|
: resolved.model) ?? "—";
|
|
59
|
+
const thinkingDisplay = resolved.thinkingLevel ?? "—";
|
|
59
60
|
const sourceDisplay = formatSource(source);
|
|
60
61
|
|
|
61
62
|
lines.push(
|
|
62
|
-
` ${action.id.padEnd(20)} ${modelDisplay.padEnd(
|
|
63
|
+
` ${action.id.padEnd(20)} ${modelDisplay.padEnd(24)} ${thinkingDisplay.padEnd(10)} ${sourceDisplay}`,
|
|
63
64
|
);
|
|
64
65
|
|
|
65
66
|
lastCategory = action.category;
|
package/src/commands/plan.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { buildPlanningPrompt, buildQuickPlanPrompt } from "../planning/prompt-bu
|
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
15
|
-
import { resolveModelForAction, createModelBridge } from "../config/model-resolver.js";
|
|
15
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
16
16
|
import { loadModelConfig } from "../config/model-config.js";
|
|
17
17
|
import { startPlanTracking } from "../planning/approval-flow.js";
|
|
18
18
|
|
|
@@ -38,6 +38,12 @@ export function registerPlanCommand(platform: Platform): void {
|
|
|
38
38
|
platform.registerCommand("supi:plan", {
|
|
39
39
|
description: "Start collaborative planning for a feature or task",
|
|
40
40
|
async handler(args: string | undefined, ctx: any) {
|
|
41
|
+
// Resolve and apply model override early — before any logic that might fail
|
|
42
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
43
|
+
const bridge = createModelBridge(platform);
|
|
44
|
+
const resolved = resolveModelForAction("plan", modelRegistry, modelCfg, bridge);
|
|
45
|
+
await applyModelOverride(platform, ctx, "plan", resolved);
|
|
46
|
+
|
|
41
47
|
const skillPath = findSkillPath("planning");
|
|
42
48
|
let skillContent = "";
|
|
43
49
|
if (skillPath) {
|
|
@@ -133,13 +139,6 @@ export function registerPlanCommand(platform: Platform): void {
|
|
|
133
139
|
prompt += "\n\n" + buildVisualInstructions(visualUrl, visualSessionDir);
|
|
134
140
|
}
|
|
135
141
|
|
|
136
|
-
// Resolve model for this action
|
|
137
|
-
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
138
|
-
const bridge = createModelBridge(platform);
|
|
139
|
-
const resolved = resolveModelForAction("plan", modelRegistry, modelConfig, bridge);
|
|
140
|
-
if (resolved.source !== "main" && platform.setModel && resolved.model) {
|
|
141
|
-
platform.setModel(resolved.model);
|
|
142
|
-
}
|
|
143
142
|
|
|
144
143
|
platform.sendMessage(
|
|
145
144
|
{
|
|
@@ -151,7 +150,7 @@ export function registerPlanCommand(platform: Platform): void {
|
|
|
151
150
|
);
|
|
152
151
|
|
|
153
152
|
// Track planning state for the approval flow (agent_end hook)
|
|
154
|
-
startPlanTracking(ctx.cwd, platform.paths, ctx.newSession?.bind(ctx));
|
|
153
|
+
startPlanTracking(ctx.cwd, platform.paths, ctx.newSession?.bind(ctx), resolved);
|
|
155
154
|
|
|
156
155
|
notifyInfo(ctx, "Planning started", args ? `Topic: ${args}` : "Describe what you want to build");
|
|
157
156
|
},
|
package/src/commands/qa.ts
CHANGED
|
@@ -8,6 +8,16 @@ import { createNewE2eSession } from "../qa/session.js";
|
|
|
8
8
|
import { buildE2eOrchestratorPrompt } from "../qa/prompt-builder.js";
|
|
9
9
|
import { findActiveSession, getSessionDir } from "../storage/qa-sessions.js";
|
|
10
10
|
import type { E2eQaConfig, AppType, E2eRegression } from "../qa/types.js";
|
|
11
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
12
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
13
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
14
|
+
|
|
15
|
+
modelRegistry.register({
|
|
16
|
+
id: "qa",
|
|
17
|
+
category: "command",
|
|
18
|
+
label: "QA",
|
|
19
|
+
harnessRoleHint: "slow",
|
|
20
|
+
});
|
|
11
21
|
|
|
12
22
|
function getScriptsDir(): string {
|
|
13
23
|
return path.join(path.dirname(new URL(import.meta.url).pathname), "..", "qa", "scripts");
|
|
@@ -104,6 +114,11 @@ export function registerQaCommand(platform: Platform): void {
|
|
|
104
114
|
platform.registerCommand("supi:qa", {
|
|
105
115
|
description: "Run autonomous E2E product testing pipeline with playwright",
|
|
106
116
|
async handler(args: string | undefined, ctx: any) {
|
|
117
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
118
|
+
const bridge = createModelBridge(platform);
|
|
119
|
+
const resolved = resolveModelForAction("qa", modelRegistry, modelCfg, bridge);
|
|
120
|
+
await applyModelOverride(platform, ctx, "qa", resolved);
|
|
121
|
+
|
|
107
122
|
const scriptsDir = getScriptsDir();
|
|
108
123
|
|
|
109
124
|
// ── Step 1: Detect app type ─────────────────────────────────────
|
package/src/commands/release.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Platform } from "../platform/types.js";
|
|
2
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
3
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
4
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
2
5
|
import type { ReleaseChannel, BumpType } from "../types.js";
|
|
3
6
|
import { loadConfig, updateConfig } from "../config/loader.js";
|
|
4
7
|
import { detectChannels } from "../release/detector.js";
|
|
@@ -10,6 +13,13 @@ import { notifyInfo, notifySuccess, notifyError } from "../notifications/rendere
|
|
|
10
13
|
import { analyzeAndCommit } from "../git/commit.js";
|
|
11
14
|
import { getWorkingTreeStatus } from "../git/status.js";
|
|
12
15
|
|
|
16
|
+
modelRegistry.register({
|
|
17
|
+
id: "release",
|
|
18
|
+
category: "command",
|
|
19
|
+
label: "Release",
|
|
20
|
+
harnessRoleHint: "slow",
|
|
21
|
+
});
|
|
22
|
+
|
|
13
23
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
14
24
|
const STATUS_KEY = "supi-release";
|
|
15
25
|
const WIDGET_KEY = "supi-release";
|
|
@@ -185,7 +195,12 @@ export function registerReleaseCommand(platform: Platform): void {
|
|
|
185
195
|
* TUI-only handler — called from the input event dispatcher in bootstrap.ts.
|
|
186
196
|
* Runs the full release flow without triggering the outer LLM session.
|
|
187
197
|
*/
|
|
188
|
-
export function handleRelease(platform: Platform, ctx: any, args?: string): void {
|
|
198
|
+
export async function handleRelease(platform: Platform, ctx: any, args?: string): Promise<void> {
|
|
199
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
200
|
+
const bridge = createModelBridge(platform);
|
|
201
|
+
const resolved = resolveModelForAction("release", modelRegistry, modelCfg, bridge);
|
|
202
|
+
await applyModelOverride(platform, ctx, "release", resolved);
|
|
203
|
+
|
|
189
204
|
if (!ctx.hasUI) {
|
|
190
205
|
ctx.ui.notify("Release requires interactive mode", "warning");
|
|
191
206
|
return;
|
package/src/commands/review.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { buildReviewPrompt } from "../quality/gate-runner.js";
|
|
|
5
5
|
import { isLspAvailable } from "../lsp/detector.js";
|
|
6
6
|
import { notifyInfo, notifyWarning } from "../notifications/renderer.js";
|
|
7
7
|
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
8
|
-
import { resolveModelForAction, createModelBridge } from "../config/model-resolver.js";
|
|
8
|
+
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
9
9
|
import { loadModelConfig } from "../config/model-config.js";
|
|
10
10
|
|
|
11
11
|
modelRegistry.register({
|
|
@@ -19,6 +19,12 @@ export function registerReviewCommand(platform: Platform): void {
|
|
|
19
19
|
platform.registerCommand("supi:review", {
|
|
20
20
|
description: "Run quality gates at chosen depth (quick/thorough/full-regression)",
|
|
21
21
|
async handler(args: string | undefined, ctx: any) {
|
|
22
|
+
// Resolve and apply model override early — before any logic that might fail
|
|
23
|
+
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
24
|
+
const bridge = createModelBridge(platform);
|
|
25
|
+
const resolved = resolveModelForAction("review", modelRegistry, modelCfg, bridge);
|
|
26
|
+
await applyModelOverride(platform, ctx, "review", resolved);
|
|
27
|
+
|
|
22
28
|
const config = loadConfig(platform.paths, ctx.cwd);
|
|
23
29
|
|
|
24
30
|
let profileOverride: string | undefined;
|
|
@@ -96,13 +102,6 @@ export function registerReviewCommand(platform: Platform): void {
|
|
|
96
102
|
|
|
97
103
|
notifyInfo(ctx, `Review started`, `profile: ${profile.name}`);
|
|
98
104
|
|
|
99
|
-
// Resolve model for this action
|
|
100
|
-
const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
|
|
101
|
-
const bridge = createModelBridge(platform);
|
|
102
|
-
const resolved = resolveModelForAction("review", modelRegistry, modelConfig, bridge);
|
|
103
|
-
if (resolved.source !== "main" && platform.setModel && resolved.model) {
|
|
104
|
-
platform.setModel(resolved.model);
|
|
105
|
-
}
|
|
106
105
|
|
|
107
106
|
platform.sendMessage(
|
|
108
107
|
{
|
|
@@ -111,3 +111,90 @@ export function createModelBridge(platform: Platform): ModelPlatformBridge {
|
|
|
111
111
|
},
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Apply a resolved model override to the current session.
|
|
117
|
+
*
|
|
118
|
+
* Resolves the string model ID to an OMP Model object via the context's
|
|
119
|
+
* modelRegistry, then calls platform.setModel() with it. Also applies
|
|
120
|
+
* thinking level if specified.
|
|
121
|
+
*
|
|
122
|
+
* @param platform - The platform adapter
|
|
123
|
+
* @param ctx - Command handler context (must have modelRegistry.getAvailable())
|
|
124
|
+
* @param actionId - The action being configured (e.g. "plan", "review") — used in notification
|
|
125
|
+
* @param resolved - The resolved model from resolveModelForAction()
|
|
126
|
+
* @returns true if model was applied, false if skipped or failed
|
|
127
|
+
*/
|
|
128
|
+
export async function applyModelOverride(
|
|
129
|
+
platform: Platform,
|
|
130
|
+
ctx: any,
|
|
131
|
+
actionId: string,
|
|
132
|
+
resolved: ResolvedModel,
|
|
133
|
+
): Promise<boolean> {
|
|
134
|
+
// Skip if resolution fell through to the main session model (nothing to change)
|
|
135
|
+
if (resolved.source === "main") return false;
|
|
136
|
+
|
|
137
|
+
const modelId = resolved.model;
|
|
138
|
+
if (!modelId) return false;
|
|
139
|
+
|
|
140
|
+
// Apply thinking level (independent of model switch success)
|
|
141
|
+
if (resolved.thinkingLevel && platform.setThinkingLevel) {
|
|
142
|
+
platform.setThinkingLevel(resolved.thinkingLevel);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!platform.setModel) return false;
|
|
146
|
+
|
|
147
|
+
// Resolve string model ID to full OMP Model object via the context's model registry.
|
|
148
|
+
// OMP's setModel expects a Model object (with provider, id, api, etc.), not a string.
|
|
149
|
+
const available = ctx.modelRegistry?.getAvailable?.() as any[] | undefined;
|
|
150
|
+
if (!available) return false;
|
|
151
|
+
|
|
152
|
+
const modelObj = available.find((m: any) => {
|
|
153
|
+
if (!m?.id) return false;
|
|
154
|
+
if (modelId === m.id) return true;
|
|
155
|
+
if (modelId === `${m.provider}/${m.id}`) return true;
|
|
156
|
+
return modelId.includes("/") ? false : m.id === modelId;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (!modelObj) return false;
|
|
160
|
+
|
|
161
|
+
// Save current model so we can restore after the agent turn completes.
|
|
162
|
+
// OMP's extension API setModel() persists to settings (calls session.setModel,
|
|
163
|
+
// not session.setModelTemporary). We must restore to avoid permanently
|
|
164
|
+
// overriding the user's default model.
|
|
165
|
+
const originalModel = ctx.model;
|
|
166
|
+
|
|
167
|
+
const applied = await platform.setModel(modelObj);
|
|
168
|
+
if (!applied) return false;
|
|
169
|
+
|
|
170
|
+
// Show persistent model override info in the footer status bar.
|
|
171
|
+
// ctx.ui.notify() is transient and gets immediately replaced by progress widgets;
|
|
172
|
+
// setStatus persists alongside them.
|
|
173
|
+
const STATUS_KEY = "supi-model";
|
|
174
|
+
const displayName = modelObj.name ?? modelObj.id ?? modelId;
|
|
175
|
+
const sourceLabel =
|
|
176
|
+
resolved.source === "action" ? `configured for ${actionId}` :
|
|
177
|
+
resolved.source === "default" ? "supipowers default" :
|
|
178
|
+
"harness role";
|
|
179
|
+
let detail = sourceLabel;
|
|
180
|
+
if (resolved.thinkingLevel) {
|
|
181
|
+
detail += ` \u00b7 ${resolved.thinkingLevel} thinking`;
|
|
182
|
+
}
|
|
183
|
+
ctx.ui?.setStatus?.(STATUS_KEY, `Model: ${displayName} (${detail})`);
|
|
184
|
+
|
|
185
|
+
// Register a one-shot agent_end hook to restore the original model
|
|
186
|
+
// and clear the status bar entry.
|
|
187
|
+
{
|
|
188
|
+
let restored = false;
|
|
189
|
+
platform.on("agent_end", async () => {
|
|
190
|
+
if (restored) return;
|
|
191
|
+
restored = true;
|
|
192
|
+
ctx.ui?.setStatus?.(STATUS_KEY, undefined);
|
|
193
|
+
if (originalModel) {
|
|
194
|
+
await platform.setModel!(originalModel);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return true;
|
|
200
|
+
}
|
package/src/fix-pr/config.ts
CHANGED
|
@@ -13,11 +13,6 @@ export const DEFAULT_FIX_PR_CONFIG: FixPrConfig = {
|
|
|
13
13
|
reviewer: { type: "none", triggerMethod: null },
|
|
14
14
|
commentPolicy: "answer-selective",
|
|
15
15
|
loop: { delaySeconds: 180, maxIterations: 3 },
|
|
16
|
-
models: {
|
|
17
|
-
orchestrator: { provider: "anthropic", model: "claude-opus-4-6", tier: "high" },
|
|
18
|
-
planner: { provider: "anthropic", model: "claude-opus-4-6", tier: "high" },
|
|
19
|
-
fixer: { provider: "anthropic", model: "claude-sonnet-4-6", tier: "low" },
|
|
20
|
-
},
|
|
21
16
|
};
|
|
22
17
|
|
|
23
18
|
export function loadFixPrConfig(paths: PlatformPaths, cwd: string): FixPrConfig | null {
|
|
@@ -10,6 +10,8 @@ export interface FixPrPromptOptions {
|
|
|
10
10
|
config: FixPrConfig;
|
|
11
11
|
iteration: number;
|
|
12
12
|
skillContent: string;
|
|
13
|
+
/** Resolved model ID for sub-agent tasks (planner, fixer roles). */
|
|
14
|
+
taskModel: string;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
function buildReplyInstructions(config: FixPrConfig): string {
|
|
@@ -47,8 +49,8 @@ function buildReplyInstructions(config: FixPrConfig): string {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): string {
|
|
50
|
-
const { prNumber, repo, comments, sessionDir, scriptsDir, config, iteration, skillContent } = options;
|
|
51
|
-
const { loop,
|
|
52
|
+
const { prNumber, repo, comments, sessionDir, scriptsDir, config, iteration, skillContent, taskModel } = options;
|
|
53
|
+
const { loop, reviewer } = config;
|
|
52
54
|
const maxIter = loop.maxIterations;
|
|
53
55
|
const delay = loop.delaySeconds;
|
|
54
56
|
|
|
@@ -190,11 +192,10 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
190
192
|
sections.push(
|
|
191
193
|
"## Model Guidance",
|
|
192
194
|
"",
|
|
193
|
-
`- **Orchestrator** (
|
|
194
|
-
`- **Planner** (
|
|
195
|
-
`- **Fixer** (code changes): ${models.fixer.model} (${models.fixer.tier} tier) — focused execution`,
|
|
195
|
+
`- **Orchestrator** (this session): handles assessment & grouping`,
|
|
196
|
+
`- **Planner & Fixer** (sub-agents): use model \`${taskModel}\``,
|
|
196
197
|
"",
|
|
197
|
-
"
|
|
198
|
+
"Sub-agents inherit the task model for planning and code changes.",
|
|
198
199
|
);
|
|
199
200
|
|
|
200
201
|
return sections.join("\n");
|
package/src/fix-pr/types.ts
CHANGED
|
@@ -4,12 +4,6 @@ export type ReviewerType = "coderabbit" | "copilot" | "gemini" | "none";
|
|
|
4
4
|
/** How to handle comment replies */
|
|
5
5
|
export type CommentReplyPolicy = "answer-all" | "answer-selective" | "no-answer";
|
|
6
6
|
|
|
7
|
-
/** Model preference for a specific role */
|
|
8
|
-
export interface ModelPref {
|
|
9
|
-
provider: string;
|
|
10
|
-
model: string;
|
|
11
|
-
tier: "low" | "high";
|
|
12
|
-
}
|
|
13
7
|
|
|
14
8
|
/** Per-repo fix-pr configuration */
|
|
15
9
|
export interface FixPrConfig {
|
|
@@ -22,11 +16,6 @@ export interface FixPrConfig {
|
|
|
22
16
|
delaySeconds: number;
|
|
23
17
|
maxIterations: number;
|
|
24
18
|
};
|
|
25
|
-
models: {
|
|
26
|
-
orchestrator: ModelPref;
|
|
27
|
-
planner: ModelPref;
|
|
28
|
-
fixer: ModelPref;
|
|
29
|
-
};
|
|
30
19
|
}
|
|
31
20
|
|
|
32
21
|
/** A PR review comment from GitHub API */
|
package/src/git/commit.ts
CHANGED
|
@@ -10,6 +10,9 @@ import { validateCommitMessage } from "./commit-msg.js";
|
|
|
10
10
|
import { getWorkingTreeStatus } from "./status.js";
|
|
11
11
|
import { discoverCommitConventions } from "./conventions.js";
|
|
12
12
|
import { notifyInfo, notifyError, notifySuccess } from "../notifications/renderer.js";
|
|
13
|
+
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
14
|
+
import { resolveModelForAction, createModelBridge } from "../config/model-resolver.js";
|
|
15
|
+
import { loadModelConfig } from "../config/model-config.js";
|
|
13
16
|
|
|
14
17
|
// ── Public types ───────────────────────────────────────────
|
|
15
18
|
|
|
@@ -213,6 +216,7 @@ function createProgress(ctx: any) {
|
|
|
213
216
|
dispose() {
|
|
214
217
|
stopTimer();
|
|
215
218
|
ctx.ui.setStatus?.(STATUS_KEY, undefined);
|
|
219
|
+
ctx.ui.setStatus?.("supi-model", undefined);
|
|
216
220
|
ctx.ui.setWidget?.(WIDGET_KEY, undefined);
|
|
217
221
|
},
|
|
218
222
|
};
|
|
@@ -292,8 +296,26 @@ export async function analyzeAndCommit(
|
|
|
292
296
|
|
|
293
297
|
if (platform.capabilities.agentSessions) {
|
|
294
298
|
progress.activate(4, `${fileList.length} file(s)`);
|
|
295
|
-
|
|
299
|
+
// Resolve the commit sub-agent model from config (falls back to session default)
|
|
300
|
+
const modelCfg = loadModelConfig(platform.paths, cwd);
|
|
301
|
+
const bridge = createModelBridge(platform);
|
|
302
|
+
const commitModel = resolveModelForAction("commit", modelRegistry, modelCfg, bridge);
|
|
303
|
+
|
|
304
|
+
// Show model override in status bar if not using the main session model
|
|
305
|
+
if (commitModel.source !== "main" && commitModel.model) {
|
|
306
|
+
const sourceLabel =
|
|
307
|
+
commitModel.source === "action" ? "configured for commit" :
|
|
308
|
+
commitModel.source === "default" ? "supipowers default" :
|
|
309
|
+
"harness role";
|
|
310
|
+
let detail = sourceLabel;
|
|
311
|
+
if (commitModel.thinkingLevel) {
|
|
312
|
+
detail += ` \u00b7 ${commitModel.thinkingLevel} thinking`;
|
|
313
|
+
}
|
|
314
|
+
ctx.ui?.setStatus?.("supi-model", `Model: ${commitModel.model} (${detail})`);
|
|
315
|
+
}
|
|
316
|
+
plan = await tryAgentPlan(platform, cwd, prompt, commitModel.model);
|
|
296
317
|
if (plan) {
|
|
318
|
+
plan = validatePlanFiles(plan, fileList);
|
|
297
319
|
progress.complete(4, `${plan.commits.length} commit(s)`);
|
|
298
320
|
} else {
|
|
299
321
|
progress.skip(4, "unavailable");
|
|
@@ -332,7 +354,7 @@ export async function analyzeAndCommit(
|
|
|
332
354
|
|
|
333
355
|
// 7. Execute commits
|
|
334
356
|
progress.activate(6, `0/${plan.commits.length}`);
|
|
335
|
-
return executeCommitPlan(platform, ctx, cwd, plan, progress);
|
|
357
|
+
return executeCommitPlan(platform, ctx, cwd, plan, fileList, progress);
|
|
336
358
|
} finally {
|
|
337
359
|
// Always clean up, even on unexpected errors
|
|
338
360
|
progress.dispose();
|
|
@@ -345,10 +367,11 @@ async function tryAgentPlan(
|
|
|
345
367
|
platform: Platform,
|
|
346
368
|
cwd: string,
|
|
347
369
|
prompt: string,
|
|
370
|
+
model?: string,
|
|
348
371
|
): Promise<CommitPlan | null> {
|
|
349
372
|
let session: Awaited<ReturnType<Platform["createAgentSession"]>> | null = null;
|
|
350
373
|
try {
|
|
351
|
-
session = await platform.createAgentSession({ cwd, hasUI: false });
|
|
374
|
+
session = await platform.createAgentSession({ cwd, hasUI: false, ...(model ? { model } : {}) });
|
|
352
375
|
|
|
353
376
|
const agentDone = new Promise<void>((resolve) => {
|
|
354
377
|
session!.subscribe((event: any) => {
|
|
@@ -426,6 +449,26 @@ async function manualFallback(
|
|
|
426
449
|
return { committed: 1, messages: [message] };
|
|
427
450
|
}
|
|
428
451
|
|
|
452
|
+
// ── Plan validation ────────────────────────────────────────
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Filter an AI-generated commit plan against the actual staged file list.
|
|
456
|
+
* Removes hallucinated paths that aren't staged, and drops empty groups.
|
|
457
|
+
* Falls back to the original plan if filtering would leave nothing.
|
|
458
|
+
*/
|
|
459
|
+
export function validatePlanFiles(plan: CommitPlan, stagedFiles: string[]): CommitPlan {
|
|
460
|
+
const stagedSet = new Set(stagedFiles);
|
|
461
|
+
const validCommits = plan.commits
|
|
462
|
+
.map((group) => ({
|
|
463
|
+
...group,
|
|
464
|
+
files: group.files.filter((f) => stagedSet.has(f)),
|
|
465
|
+
}))
|
|
466
|
+
.filter((group) => group.files.length > 0);
|
|
467
|
+
|
|
468
|
+
return validCommits.length > 0 ? { commits: validCommits } : plan;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
|
|
429
472
|
// ── Commit execution ───────────────────────────────────────
|
|
430
473
|
|
|
431
474
|
async function executeCommitPlan(
|
|
@@ -433,38 +476,45 @@ async function executeCommitPlan(
|
|
|
433
476
|
ctx: any,
|
|
434
477
|
cwd: string,
|
|
435
478
|
plan: CommitPlan,
|
|
479
|
+
stagedFiles: string[],
|
|
436
480
|
progress: ReturnType<typeof createProgress>,
|
|
437
481
|
): Promise<CommitResult | null> {
|
|
438
482
|
const exec = platform.exec.bind(platform);
|
|
439
483
|
const committedMessages: string[] = [];
|
|
440
484
|
|
|
485
|
+
// Snapshot the full index as a tree object. This lets us restore the
|
|
486
|
+
// staging area for each commit group via `git read-tree` — which reads
|
|
487
|
+
// from git's object store and never consults .gitignore.
|
|
488
|
+
const writeTreeResult = await exec("git", ["write-tree"], { cwd });
|
|
489
|
+
if (writeTreeResult.code !== 0) {
|
|
490
|
+
progress.dispose();
|
|
491
|
+
notifyError(ctx, "Commit failed", "Could not snapshot index (git write-tree)");
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
const savedTree = writeTreeResult.stdout.trim();
|
|
495
|
+
|
|
441
496
|
for (let i = 0; i < plan.commits.length; i++) {
|
|
442
497
|
const group = plan.commits[i];
|
|
443
498
|
const header = formatCommitMessage(group).split("\n")[0];
|
|
444
499
|
progress.detail(`${i + 1}/${plan.commits.length}: ${header}`);
|
|
445
500
|
|
|
446
|
-
//
|
|
447
|
-
await exec("git", ["
|
|
501
|
+
// Restore the full saved index (no gitignore involvement)
|
|
502
|
+
await exec("git", ["read-tree", savedTree], { cwd });
|
|
448
503
|
|
|
449
|
-
//
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const reason = addResult.stderr?.trim() || "git add returned non-zero";
|
|
455
|
-
return reportPartialFailure(ctx, exec, cwd, committedMessages, {
|
|
456
|
-
step: `Commit ${i + 1}/${plan.commits.length}`,
|
|
457
|
-
error: `Could not stage files (${failedFiles}): ${reason}`,
|
|
458
|
-
});
|
|
504
|
+
// Unstage everything NOT in this group
|
|
505
|
+
const groupSet = new Set(group.files);
|
|
506
|
+
const filesToUnstage = stagedFiles.filter((f) => !groupSet.has(f));
|
|
507
|
+
if (filesToUnstage.length > 0) {
|
|
508
|
+
await exec("git", ["reset", "HEAD", "--", ...filesToUnstage], { cwd });
|
|
459
509
|
}
|
|
460
510
|
|
|
461
|
-
// Build commit message
|
|
462
511
|
const message = formatCommitMessage(group);
|
|
463
|
-
|
|
464
512
|
const commitResult = await commitStaged(exec, cwd, message);
|
|
465
513
|
if (!commitResult.success) {
|
|
466
514
|
progress.dispose();
|
|
467
|
-
|
|
515
|
+
// Restore full staging area so the user isn't left with a partial index
|
|
516
|
+
await exec("git", ["read-tree", savedTree], { cwd });
|
|
517
|
+
return reportPartialFailure(ctx, committedMessages, {
|
|
468
518
|
step: `Commit ${i + 1}/${plan.commits.length}`,
|
|
469
519
|
error: commitResult.error!,
|
|
470
520
|
});
|
|
@@ -473,6 +523,10 @@ async function executeCommitPlan(
|
|
|
473
523
|
committedMessages.push(message);
|
|
474
524
|
}
|
|
475
525
|
|
|
526
|
+
// Restore the saved index so any staged files NOT in the plan remain staged.
|
|
527
|
+
// Files already committed now match HEAD, so they appear as not-staged.
|
|
528
|
+
await exec("git", ["read-tree", savedTree], { cwd });
|
|
529
|
+
|
|
476
530
|
progress.complete(6, `${committedMessages.length} done`);
|
|
477
531
|
progress.dispose();
|
|
478
532
|
notifySuccess(
|
|
@@ -486,18 +540,12 @@ async function executeCommitPlan(
|
|
|
486
540
|
|
|
487
541
|
/**
|
|
488
542
|
* Report a mid-plan failure with context on what succeeded and what failed.
|
|
489
|
-
* Re-stages remaining files so the user isn't left with a half-reset index.
|
|
490
543
|
*/
|
|
491
|
-
|
|
544
|
+
function reportPartialFailure(
|
|
492
545
|
ctx: any,
|
|
493
|
-
exec: Platform["exec"],
|
|
494
|
-
cwd: string,
|
|
495
546
|
committedMessages: string[],
|
|
496
547
|
failure: { step: string; error: string },
|
|
497
|
-
):
|
|
498
|
-
// Re-stage everything so the user isn't stuck with a partial index
|
|
499
|
-
await exec("git", ["add", "-A"], { cwd });
|
|
500
|
-
|
|
548
|
+
): CommitResult | null {
|
|
501
549
|
const lines: string[] = [];
|
|
502
550
|
lines.push(`Failed at ${failure.step}: ${failure.error}`);
|
|
503
551
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Platform } from "../platform/types.js";
|
|
2
|
+
import type { ResolvedModel } from "../types.js";
|
|
3
|
+
import { applyModelOverride } from "../config/model-resolver.js";
|
|
2
4
|
import { listPlans, readPlanFile } from "../storage/plans.js";
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -14,17 +16,21 @@ let plansBefore: string[] = [];
|
|
|
14
16
|
let planCwd: string = "";
|
|
15
17
|
/** newSession function captured from the command context at plan start. */
|
|
16
18
|
let capturedNewSession: ((options?: any) => Promise<{ cancelled: boolean }>) | null = null;
|
|
19
|
+
/** Resolved model for plan action — re-applied on execution handoff. */
|
|
20
|
+
let capturedResolvedModel: ResolvedModel | null = null;
|
|
17
21
|
|
|
18
22
|
/** Mark planning as started (called by plan command after sending steer). */
|
|
19
23
|
export function startPlanTracking(
|
|
20
24
|
cwd: string,
|
|
21
25
|
paths: any,
|
|
22
26
|
newSession?: (options?: any) => Promise<{ cancelled: boolean }>,
|
|
27
|
+
resolvedModel?: ResolvedModel,
|
|
23
28
|
): void {
|
|
24
29
|
planningActive = true;
|
|
25
30
|
planCwd = cwd;
|
|
26
31
|
plansBefore = listPlans(paths, cwd);
|
|
27
32
|
capturedNewSession = newSession ?? null;
|
|
33
|
+
capturedResolvedModel = resolvedModel ?? null;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
/** Cancel plan tracking (e.g., session change). */
|
|
@@ -33,6 +39,7 @@ export function cancelPlanTracking(): void {
|
|
|
33
39
|
plansBefore = [];
|
|
34
40
|
planCwd = "";
|
|
35
41
|
capturedNewSession = null;
|
|
42
|
+
capturedResolvedModel = null;
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
/** Whether a planning session is currently active. */
|
|
@@ -89,10 +96,16 @@ async function executeApproveFlow(
|
|
|
89
96
|
): Promise<void> {
|
|
90
97
|
const prompt = buildExecutionPrompt(planContent, planPath);
|
|
91
98
|
|
|
99
|
+
// Re-apply the plan model override for the execution turn.
|
|
100
|
+
// The planning turn's restore hook already fired (model reverted to default).
|
|
101
|
+
// We must switch again so the execution LLM turn uses the configured model.
|
|
102
|
+
if (capturedResolvedModel) {
|
|
103
|
+
await applyModelOverride(platform, ctx, "plan", capturedResolvedModel);
|
|
104
|
+
}
|
|
105
|
+
|
|
92
106
|
if (capturedNewSession) {
|
|
93
107
|
const result = await capturedNewSession();
|
|
94
108
|
if (result?.cancelled) {
|
|
95
|
-
// User dismissed the new-session prompt — keep plan state intact.
|
|
96
109
|
ctx.ui.notify("Session start cancelled. Plan saved; run /supi:plan again to execute.");
|
|
97
110
|
return;
|
|
98
111
|
}
|
package/src/platform/omp.ts
CHANGED
|
@@ -17,8 +17,11 @@ export function createOmpAdapter(api: any): Platform {
|
|
|
17
17
|
sendUserMessage: (text: string) => api.sendUserMessage(text),
|
|
18
18
|
registerMessageRenderer: (type, fn) => api.registerMessageRenderer(type, fn),
|
|
19
19
|
|
|
20
|
-
setModel(model:
|
|
21
|
-
api.setModel(model);
|
|
20
|
+
async setModel(model: any): Promise<boolean> {
|
|
21
|
+
return api.setModel(model);
|
|
22
|
+
},
|
|
23
|
+
setThinkingLevel(level: string, persist?: boolean): void {
|
|
24
|
+
api.setThinkingLevel?.(level, persist);
|
|
22
25
|
},
|
|
23
26
|
getCurrentModel(): string {
|
|
24
27
|
return api.getCurrentModel?.() ?? "unknown";
|
package/src/platform/types.ts
CHANGED
|
@@ -138,7 +138,8 @@ export interface Platform {
|
|
|
138
138
|
registerMessageRenderer<T>(type: string, renderer: any): void;
|
|
139
139
|
|
|
140
140
|
// Model access
|
|
141
|
-
setModel?(model:
|
|
141
|
+
setModel?(model: any): Promise<boolean>;
|
|
142
|
+
setThinkingLevel?(level: string, persist?: boolean): void;
|
|
142
143
|
getCurrentModel?(): string;
|
|
143
144
|
getModelForRole?(role: string): string | null;
|
|
144
145
|
|