@oh-my-pi/pi-coding-agent 3.31.0 → 3.32.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/CHANGELOG.md +14 -0
- package/package.json +4 -4
- package/src/core/custom-commands/bundled/review/index.ts +5 -7
- package/src/core/sdk.ts +10 -2
- package/src/core/slash-commands.ts +39 -13
- package/src/core/tools/index.ts +7 -1
- package/src/core/tools/lsp/client.ts +26 -10
- package/src/core/tools/lsp/index.ts +17 -3
- package/src/core/tools/task/commands.ts +4 -0
- package/src/core/tools/task/index.ts +3 -2
- package/src/main.ts +2 -2
- package/src/modes/interactive/interactive-mode.ts +0 -25
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [3.32.0] - 2026-01-08
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added progress indicator when starting LSP servers at session startup
|
|
9
|
+
- Added bundled `/init` slash command available by default
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Changed LSP server warmup to use a 5-second timeout, falling back to lazy initialization for slow servers
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Fixed Task tool subagent model selection to inherit explicit CLI `--model` overrides
|
|
18
|
+
|
|
5
19
|
## [3.31.0] - 2026-01-08
|
|
6
20
|
|
|
7
21
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.32.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"ompConfig": {
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@mariozechner/pi-ai": "^0.37.8",
|
|
43
|
-
"@oh-my-pi/pi-agent-core": "3.
|
|
44
|
-
"@oh-my-pi/pi-git-tool": "3.
|
|
45
|
-
"@oh-my-pi/pi-tui": "3.
|
|
43
|
+
"@oh-my-pi/pi-agent-core": "3.32.0",
|
|
44
|
+
"@oh-my-pi/pi-git-tool": "3.32.0",
|
|
45
|
+
"@oh-my-pi/pi-tui": "3.32.0",
|
|
46
46
|
"@openai/agents": "^0.3.7",
|
|
47
47
|
"@sinclair/typebox": "^0.34.46",
|
|
48
48
|
"ajv": "^8.17.1",
|
|
@@ -435,13 +435,11 @@ export function createReviewCommand(api: CustomCommandAPI): CustomCommand {
|
|
|
435
435
|
if (hasDiff) {
|
|
436
436
|
const stats = parseDiff(diffResult.stdout);
|
|
437
437
|
// Even if all files filtered, include the custom instructions
|
|
438
|
-
return (
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
) + `\n\n### Additional Instructions\n\n${instructions}`
|
|
444
|
-
);
|
|
438
|
+
return `${buildReviewPrompt(
|
|
439
|
+
`Custom review: ${instructions.split("\n")[0].slice(0, 60)}...`,
|
|
440
|
+
stats,
|
|
441
|
+
diffResult.stdout,
|
|
442
|
+
)}\n\n### Additional Instructions\n\n${instructions}`;
|
|
445
443
|
}
|
|
446
444
|
|
|
447
445
|
// No diff available, just pass instructions
|
package/src/core/sdk.ts
CHANGED
|
@@ -61,7 +61,7 @@ import { logger } from "./logger";
|
|
|
61
61
|
import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp/index";
|
|
62
62
|
import { convertToLlm } from "./messages";
|
|
63
63
|
import { ModelRegistry } from "./model-registry";
|
|
64
|
-
import { parseModelString } from "./model-resolver";
|
|
64
|
+
import { formatModelString, parseModelString } from "./model-resolver";
|
|
65
65
|
import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./prompt-templates";
|
|
66
66
|
import { SessionManager } from "./session-manager";
|
|
67
67
|
import { type Settings, SettingsManager, type SkillsSettings } from "./settings-manager";
|
|
@@ -520,6 +520,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
520
520
|
time("loadSession");
|
|
521
521
|
const hasExistingSession = existingSession.messages.length > 0;
|
|
522
522
|
|
|
523
|
+
const hasExplicitModel = options.model !== undefined;
|
|
523
524
|
let model = options.model;
|
|
524
525
|
let modelFallbackMessage: string | undefined;
|
|
525
526
|
|
|
@@ -617,6 +618,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
617
618
|
requireCompleteTool: options.requireCompleteTool,
|
|
618
619
|
getSessionFile: () => sessionManager.getSessionFile() ?? null,
|
|
619
620
|
getSessionSpawns: () => options.spawns ?? "*",
|
|
621
|
+
getModelString: () => (hasExplicitModel && model ? formatModelString(model) : undefined),
|
|
620
622
|
settings: settingsManager,
|
|
621
623
|
};
|
|
622
624
|
|
|
@@ -931,7 +933,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
931
933
|
let lspServers: CreateAgentSessionResult["lspServers"];
|
|
932
934
|
if (settingsManager.getLspDiagnosticsOnWrite()) {
|
|
933
935
|
try {
|
|
934
|
-
const result = await warmupLspServers(cwd
|
|
936
|
+
const result = await warmupLspServers(cwd, {
|
|
937
|
+
onConnecting: (serverNames) => {
|
|
938
|
+
if (options.hasUI && serverNames.length > 0) {
|
|
939
|
+
process.stderr.write(chalk.gray(`Starting LSP servers: ${serverNames.join(", ")}...\n`));
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
});
|
|
935
943
|
lspServers = result.servers;
|
|
936
944
|
time("warmupLspServers");
|
|
937
945
|
} catch {
|
|
@@ -2,6 +2,7 @@ import { slashCommandCapability } from "../capability/slash-command";
|
|
|
2
2
|
import type { SlashCommand } from "../discovery";
|
|
3
3
|
import { loadSync } from "../discovery";
|
|
4
4
|
import { parseFrontmatter } from "../discovery/helpers";
|
|
5
|
+
import { EMBEDDED_COMMAND_TEMPLATES } from "./tools/task/commands";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Represents a custom slash command loaded from a file
|
|
@@ -15,6 +16,25 @@ export interface FileSlashCommand {
|
|
|
15
16
|
_source?: { providerName: string; level: "user" | "project" | "native" };
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
const EMBEDDED_SLASH_COMMANDS = EMBEDDED_COMMAND_TEMPLATES;
|
|
20
|
+
|
|
21
|
+
function parseCommandTemplate(content: string): { description: string; body: string } {
|
|
22
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
23
|
+
const frontmatterDesc = typeof frontmatter.description === "string" ? frontmatter.description.trim() : "";
|
|
24
|
+
|
|
25
|
+
// Get description from frontmatter or first non-empty line
|
|
26
|
+
let description = frontmatterDesc;
|
|
27
|
+
if (!description) {
|
|
28
|
+
const firstLine = body.split("\n").find((line) => line.trim());
|
|
29
|
+
if (firstLine) {
|
|
30
|
+
description = firstLine.slice(0, 60);
|
|
31
|
+
if (firstLine.length > 60) description += "...";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { description, body };
|
|
36
|
+
}
|
|
37
|
+
|
|
18
38
|
/**
|
|
19
39
|
* Parse command arguments respecting quoted strings (bash-style)
|
|
20
40
|
* Returns array of arguments
|
|
@@ -90,19 +110,8 @@ export interface LoadSlashCommandsOptions {
|
|
|
90
110
|
export function loadSlashCommands(options: LoadSlashCommandsOptions = {}): FileSlashCommand[] {
|
|
91
111
|
const result = loadSync<SlashCommand>(slashCommandCapability.id, { cwd: options.cwd });
|
|
92
112
|
|
|
93
|
-
|
|
94
|
-
const {
|
|
95
|
-
const frontmatterDesc = typeof frontmatter.description === "string" ? frontmatter.description.trim() : "";
|
|
96
|
-
|
|
97
|
-
// Get description from frontmatter or first non-empty line
|
|
98
|
-
let description = frontmatterDesc;
|
|
99
|
-
if (!description) {
|
|
100
|
-
const firstLine = body.split("\n").find((line) => line.trim());
|
|
101
|
-
if (firstLine) {
|
|
102
|
-
description = firstLine.slice(0, 60);
|
|
103
|
-
if (firstLine.length > 60) description += "...";
|
|
104
|
-
}
|
|
105
|
-
}
|
|
113
|
+
const fileCommands: FileSlashCommand[] = result.items.map((cmd) => {
|
|
114
|
+
const { description, body } = parseCommandTemplate(cmd.content);
|
|
106
115
|
|
|
107
116
|
// Format source label: "via ProviderName Level"
|
|
108
117
|
const capitalizedLevel = cmd.level.charAt(0).toUpperCase() + cmd.level.slice(1);
|
|
@@ -116,6 +125,23 @@ export function loadSlashCommands(options: LoadSlashCommandsOptions = {}): FileS
|
|
|
116
125
|
_source: { providerName: cmd._source.providerName, level: cmd.level },
|
|
117
126
|
};
|
|
118
127
|
});
|
|
128
|
+
|
|
129
|
+
const seenNames = new Set(fileCommands.map((cmd) => cmd.name));
|
|
130
|
+
for (const cmd of EMBEDDED_SLASH_COMMANDS) {
|
|
131
|
+
const name = cmd.name.replace(/\.md$/, "");
|
|
132
|
+
if (seenNames.has(name)) continue;
|
|
133
|
+
|
|
134
|
+
const { description, body } = parseCommandTemplate(cmd.content);
|
|
135
|
+
fileCommands.push({
|
|
136
|
+
name,
|
|
137
|
+
description,
|
|
138
|
+
content: body,
|
|
139
|
+
source: "bundled",
|
|
140
|
+
});
|
|
141
|
+
seenNames.add(name);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return fileCommands;
|
|
119
145
|
}
|
|
120
146
|
|
|
121
147
|
/**
|
package/src/core/tools/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ export {
|
|
|
17
17
|
getLspStatus,
|
|
18
18
|
type LspServerStatus,
|
|
19
19
|
type LspToolDetails,
|
|
20
|
+
type LspWarmupOptions,
|
|
20
21
|
type LspWarmupResult,
|
|
21
22
|
warmupLspServers,
|
|
22
23
|
} from "./lsp/index";
|
|
@@ -93,6 +94,8 @@ export interface ToolSession {
|
|
|
93
94
|
getSessionFile: () => string | null;
|
|
94
95
|
/** Get session spawns */
|
|
95
96
|
getSessionSpawns: () => string | null;
|
|
97
|
+
/** Get resolved model string if explicitly set for this session */
|
|
98
|
+
getModelString?: () => string | undefined;
|
|
96
99
|
/** Settings manager (optional) */
|
|
97
100
|
settings?: {
|
|
98
101
|
getImageAutoResize(): boolean;
|
|
@@ -148,7 +151,10 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
148
151
|
|
|
149
152
|
const entries = requestedTools
|
|
150
153
|
? requestedTools.filter((name) => name in allTools).map((name) => [name, allTools[name]] as const)
|
|
151
|
-
: [
|
|
154
|
+
: [
|
|
155
|
+
...Object.entries(BUILTIN_TOOLS),
|
|
156
|
+
...(includeComplete ? ([["complete", HIDDEN_TOOLS.complete]] as const) : []),
|
|
157
|
+
];
|
|
152
158
|
const results = await Promise.all(entries.map(([, factory]) => factory(session)));
|
|
153
159
|
const tools = results.filter((t): t is Tool => t !== null);
|
|
154
160
|
|
|
@@ -373,10 +373,16 @@ async function sendResponse(
|
|
|
373
373
|
// Client Management
|
|
374
374
|
// =============================================================================
|
|
375
375
|
|
|
376
|
+
/** Timeout for warmup initialize requests (5 seconds) */
|
|
377
|
+
export const WARMUP_TIMEOUT_MS = 5000;
|
|
378
|
+
|
|
376
379
|
/**
|
|
377
380
|
* Get or create an LSP client for the given server configuration and working directory.
|
|
381
|
+
* @param config - Server configuration
|
|
382
|
+
* @param cwd - Working directory
|
|
383
|
+
* @param initTimeoutMs - Optional timeout for the initialize request (defaults to 30s)
|
|
378
384
|
*/
|
|
379
|
-
export async function getOrCreateClient(config: ServerConfig, cwd: string): Promise<LspClient> {
|
|
385
|
+
export async function getOrCreateClient(config: ServerConfig, cwd: string, initTimeoutMs?: number): Promise<LspClient> {
|
|
380
386
|
const key = `${config.command}:${cwd}`;
|
|
381
387
|
|
|
382
388
|
// Check if client already exists
|
|
@@ -430,14 +436,20 @@ export async function getOrCreateClient(config: ServerConfig, cwd: string): Prom
|
|
|
430
436
|
|
|
431
437
|
try {
|
|
432
438
|
// Send initialize request
|
|
433
|
-
const initResult = (await sendRequest(
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
439
|
+
const initResult = (await sendRequest(
|
|
440
|
+
client,
|
|
441
|
+
"initialize",
|
|
442
|
+
{
|
|
443
|
+
processId: process.pid,
|
|
444
|
+
rootUri: fileToUri(cwd),
|
|
445
|
+
rootPath: cwd,
|
|
446
|
+
capabilities: CLIENT_CAPABILITIES,
|
|
447
|
+
initializationOptions: config.initOptions ?? {},
|
|
448
|
+
workspaceFolders: [{ uri: fileToUri(cwd), name: cwd.split("/").pop() ?? "workspace" }],
|
|
449
|
+
},
|
|
450
|
+
undefined, // signal
|
|
451
|
+
initTimeoutMs,
|
|
452
|
+
)) as { capabilities?: unknown };
|
|
441
453
|
|
|
442
454
|
if (!initResult) {
|
|
443
455
|
throw new Error("Failed to initialize LSP: no response");
|
|
@@ -662,6 +674,9 @@ export function shutdownClient(key: string): void {
|
|
|
662
674
|
// LSP Protocol Methods
|
|
663
675
|
// =============================================================================
|
|
664
676
|
|
|
677
|
+
/** Default timeout for LSP requests (30 seconds) */
|
|
678
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
679
|
+
|
|
665
680
|
/**
|
|
666
681
|
* Send an LSP request and wait for response.
|
|
667
682
|
*/
|
|
@@ -670,6 +685,7 @@ export async function sendRequest(
|
|
|
670
685
|
method: string,
|
|
671
686
|
params: unknown,
|
|
672
687
|
signal?: AbortSignal,
|
|
688
|
+
timeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,
|
|
673
689
|
): Promise<unknown> {
|
|
674
690
|
// Atomically increment and capture request ID
|
|
675
691
|
const id = ++client.requestId;
|
|
@@ -712,7 +728,7 @@ export async function sendRequest(
|
|
|
712
728
|
cleanup();
|
|
713
729
|
reject(err);
|
|
714
730
|
}
|
|
715
|
-
},
|
|
731
|
+
}, timeoutMs);
|
|
716
732
|
if (signal) {
|
|
717
733
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
718
734
|
if (signal.aborted) {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
sendRequest,
|
|
20
20
|
setIdleTimeout,
|
|
21
21
|
syncContent,
|
|
22
|
+
WARMUP_TIMEOUT_MS,
|
|
22
23
|
} from "./client";
|
|
23
24
|
import { getLinterClient } from "./clients";
|
|
24
25
|
import { getServersForFile, hasCapability, type LspConfig, loadConfig } from "./config";
|
|
@@ -72,23 +73,36 @@ export interface LspWarmupResult {
|
|
|
72
73
|
}>;
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
/** Options for warming up LSP servers */
|
|
77
|
+
export interface LspWarmupOptions {
|
|
78
|
+
/** Called when starting to connect to servers */
|
|
79
|
+
onConnecting?: (serverNames: string[]) => void;
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
/**
|
|
76
83
|
* Warm up LSP servers for a directory by connecting to all detected servers.
|
|
77
84
|
* This should be called at startup to avoid cold-start delays.
|
|
78
85
|
*
|
|
79
86
|
* @param cwd - Working directory to detect and start servers for
|
|
87
|
+
* @param options - Optional callbacks for progress reporting
|
|
80
88
|
* @returns Status of each server that was started
|
|
81
89
|
*/
|
|
82
|
-
export async function warmupLspServers(cwd: string): Promise<LspWarmupResult> {
|
|
90
|
+
export async function warmupLspServers(cwd: string, options?: LspWarmupOptions): Promise<LspWarmupResult> {
|
|
83
91
|
const config = await loadConfig(cwd);
|
|
84
92
|
setIdleTimeout(config.idleTimeoutMs);
|
|
85
93
|
const servers: LspWarmupResult["servers"] = [];
|
|
86
94
|
const lspServers = getLspServers(config);
|
|
87
95
|
|
|
88
|
-
//
|
|
96
|
+
// Notify caller which servers we're connecting to
|
|
97
|
+
if (lspServers.length > 0 && options?.onConnecting) {
|
|
98
|
+
options.onConnecting(lspServers.map(([name]) => name));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Start all detected servers in parallel with a short timeout
|
|
102
|
+
// Servers that don't respond quickly will be initialized lazily on first use
|
|
89
103
|
const results = await Promise.allSettled(
|
|
90
104
|
lspServers.map(async ([name, serverConfig]) => {
|
|
91
|
-
const client = await getOrCreateClient(serverConfig, cwd);
|
|
105
|
+
const client = await getOrCreateClient(serverConfig, cwd, WARMUP_TIMEOUT_MS);
|
|
92
106
|
return { name, client, fileTypes: serverConfig.fileTypes };
|
|
93
107
|
}),
|
|
94
108
|
);
|
|
@@ -12,13 +12,17 @@ import { loadSync } from "../../../discovery";
|
|
|
12
12
|
import architectPlanMd from "../../../prompts/architect-plan.md" with { type: "text" };
|
|
13
13
|
import implementMd from "../../../prompts/implement.md" with { type: "text" };
|
|
14
14
|
import implementWithCriticMd from "../../../prompts/implement-with-critic.md" with { type: "text" };
|
|
15
|
+
import initMd from "../../../prompts/init.md" with { type: "text" };
|
|
15
16
|
|
|
16
17
|
const EMBEDDED_COMMANDS: { name: string; content: string }[] = [
|
|
17
18
|
{ name: "architect-plan.md", content: architectPlanMd },
|
|
18
19
|
{ name: "implement-with-critic.md", content: implementWithCriticMd },
|
|
19
20
|
{ name: "implement.md", content: implementMd },
|
|
21
|
+
{ name: "init.md", content: initMd },
|
|
20
22
|
];
|
|
21
23
|
|
|
24
|
+
export const EMBEDDED_COMMAND_TEMPLATES: ReadonlyArray<{ name: string; content: string }> = EMBEDDED_COMMANDS;
|
|
25
|
+
|
|
22
26
|
/** Workflow command definition */
|
|
23
27
|
export interface WorkflowCommand {
|
|
24
28
|
name: string;
|
|
@@ -135,6 +135,7 @@ export async function createTaskTool(
|
|
|
135
135
|
const startTime = Date.now();
|
|
136
136
|
const { agents, projectAgentsDir } = await discoverAgents(session.cwd);
|
|
137
137
|
const { agent: agentName, context, model, output: outputSchema } = params;
|
|
138
|
+
const modelOverride = model ?? session.getModelString?.();
|
|
138
139
|
|
|
139
140
|
// Validate agent exists
|
|
140
141
|
const agent = getAgent(agents, agentName);
|
|
@@ -323,7 +324,7 @@ export async function createTaskTool(
|
|
|
323
324
|
toolCount: 0,
|
|
324
325
|
tokens: 0,
|
|
325
326
|
durationMs: 0,
|
|
326
|
-
modelOverride
|
|
327
|
+
modelOverride,
|
|
327
328
|
description: t.description,
|
|
328
329
|
});
|
|
329
330
|
}
|
|
@@ -342,7 +343,7 @@ export async function createTaskTool(
|
|
|
342
343
|
index,
|
|
343
344
|
taskId: task.taskId,
|
|
344
345
|
context: undefined, // Already prepended above
|
|
345
|
-
modelOverride
|
|
346
|
+
modelOverride,
|
|
346
347
|
outputSchema,
|
|
347
348
|
sessionFile,
|
|
348
349
|
persistArtifacts: !!artifactsDir,
|
package/src/main.ts
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* createAgentSession() options. The SDK does the heavy lifting.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { type ImageContent, supportsXhigh } from "@mariozechner/pi-ai";
|
|
9
|
-
import chalk from "chalk";
|
|
10
8
|
import { homedir, tmpdir } from "node:os";
|
|
11
9
|
import { join, resolve } from "node:path";
|
|
10
|
+
import { type ImageContent, supportsXhigh } from "@mariozechner/pi-ai";
|
|
11
|
+
import chalk from "chalk";
|
|
12
12
|
import { type Args, parseArgs, printHelp } from "./cli/args";
|
|
13
13
|
import { processFileArguments } from "./cli/file-processor";
|
|
14
14
|
import { listModels } from "./cli/list-models";
|
|
@@ -1906,31 +1906,6 @@ export class InteractiveMode {
|
|
|
1906
1906
|
this.voiceSupervisor.notifyProgress(text);
|
|
1907
1907
|
}
|
|
1908
1908
|
|
|
1909
|
-
private async toggleVoiceListening(): Promise<void> {
|
|
1910
|
-
if (!this.settingsManager.getVoiceEnabled()) {
|
|
1911
|
-
this.settingsManager.setVoiceEnabled(true);
|
|
1912
|
-
this.showStatus("Voice mode enabled.");
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
if (this.voiceAutoModeEnabled) {
|
|
1916
|
-
this.voiceAutoModeEnabled = false;
|
|
1917
|
-
this.stopVoiceProgressTimer();
|
|
1918
|
-
await this.voiceSupervisor.stop();
|
|
1919
|
-
this.setVoiceStatus(undefined);
|
|
1920
|
-
this.showStatus("Voice mode disabled.");
|
|
1921
|
-
return;
|
|
1922
|
-
}
|
|
1923
|
-
|
|
1924
|
-
this.voiceAutoModeEnabled = true;
|
|
1925
|
-
try {
|
|
1926
|
-
await this.voiceSupervisor.start();
|
|
1927
|
-
} catch (error) {
|
|
1928
|
-
this.voiceAutoModeEnabled = false;
|
|
1929
|
-
this.setVoiceStatus(undefined);
|
|
1930
|
-
this.showError(error instanceof Error ? error.message : String(error));
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
1909
|
private async submitVoiceText(text: string): Promise<void> {
|
|
1935
1910
|
const cleaned = text.trim();
|
|
1936
1911
|
if (!cleaned) {
|