@co0ontty/wand 4.89.0 → 4.89.1
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/agent-dispatch.d.ts +5 -0
- package/dist/agent-dispatch.js +8 -0
- package/dist/ai-team-runner.d.ts +1 -1
- package/dist/ai-team-runner.js +13 -5
- package/dist/ai-team-types.js +3 -2
- package/dist/build-info.json +3 -3
- package/dist/commit-task-archive.d.ts +3 -0
- package/dist/commit-task-archive.js +15 -0
- package/dist/config.js +1 -0
- package/dist/decision-expert-employee.d.ts +1 -1
- package/dist/decision-expert-employee.js +3 -3
- package/dist/decision-expert-service.d.ts +6 -5
- package/dist/decision-expert-service.js +75 -49
- package/dist/decision-types.d.ts +2 -1
- package/dist/employee-text.d.ts +28 -0
- package/dist/employee-text.js +124 -0
- package/dist/git-quick-commit.d.ts +13 -4
- package/dist/git-quick-commit.js +112 -33
- package/dist/local-model-setup.d.ts +7 -1
- package/dist/local-model-setup.js +54 -7
- package/dist/local-model-types.d.ts +2 -1
- package/dist/process-manager.d.ts +4 -1
- package/dist/process-manager.js +5 -6
- package/dist/server-employee-routes.d.ts +2 -0
- package/dist/server-employee-routes.js +19 -45
- package/dist/server-file-routes.js +61 -25
- package/dist/server-local-model-routes.js +12 -0
- package/dist/server-session-routes.d.ts +2 -1
- package/dist/server-session-routes.js +13 -10
- package/dist/server-speech-routes.d.ts +2 -0
- package/dist/server-speech-routes.js +7 -1
- package/dist/server-task-routes.d.ts +3 -0
- package/dist/server-task-routes.js +13 -10
- package/dist/server.js +20 -8
- package/dist/session-ai-context.d.ts +6 -4
- package/dist/session-ai-context.js +10 -9
- package/dist/silicon-employee-dispatch.d.ts +4 -6
- package/dist/silicon-employee-dispatch.js +9 -6
- package/dist/speech-polisher-employee.d.ts +6 -0
- package/dist/speech-polisher-employee.js +23 -0
- package/dist/speech-polisher-identity.d.ts +4 -0
- package/dist/speech-polisher-identity.js +4 -0
- package/dist/speech-polisher-service.d.ts +31 -0
- package/dist/speech-polisher-service.js +88 -0
- package/dist/speech-service.d.ts +3 -1
- package/dist/speech-service.js +14 -3
- package/dist/speech-support.d.ts +6 -0
- package/dist/speech-support.js +26 -0
- package/dist/speech-types.d.ts +5 -0
- package/dist/storage.d.ts +2 -0
- package/dist/storage.js +18 -1
- package/dist/structured-session-manager.d.ts +3 -2
- package/dist/structured-session-manager.js +11 -7
- package/dist/system-employee.d.ts +6 -4
- package/dist/system-employee.js +8 -5
- package/dist/types.d.ts +3 -1
- package/dist/user-memory.d.ts +2 -0
- package/dist/user-memory.js +1 -1
- package/dist/web-ui/content/ai-teams.js +10 -10
- package/dist/web-ui/content/scripts.js +316 -204
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/content/tailwind.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -0
- package/dist/web-ui/embedded-assets.js +4 -3
- package/dist/web-ui/index.js +3 -3
- package/dist/web-ui/provider-identity.d.ts +2 -0
- package/dist/web-ui/provider-identity.js +7 -1
- package/dist/web-ui/scripts.d.ts +2 -0
- package/dist/web-ui/scripts.js +5 -0
- package/package.json +1 -1
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { AI_TEAM_MAX_CANDIDATES, SILICON_EMPLOYEE_AVATAR_MAX_CHARS, agentKey, isBuiltinSiliconEmployee, parseSiliconEmployeeTags, siliconEmployeeTags, } from "./ai-team-types.js";
|
|
3
3
|
import { getErrorMessage } from "./error-utils.js";
|
|
4
|
-
import { DECISION_EXPERT_KEY, isLocalDecisionModel } from "./decision-expert-identity.js";
|
|
5
|
-
import { OPENROUTER_FREE_SELECTOR, isOpenRouterFreeSelector } from "./openrouter-free-selection.js";
|
|
6
4
|
import { asyncRoute } from "./express-async.js";
|
|
7
5
|
import { bodyObject, sendRouteError, text } from "./server-request.js";
|
|
8
6
|
import { parseTaskAgent } from "./server-task-routes.js";
|
|
@@ -14,7 +12,7 @@ import { SESSION_PROVIDERS } from "./session-provider.js";
|
|
|
14
12
|
import { DEFAULT_WAND_TASK_AGENT_KIND } from "./task-types.js";
|
|
15
13
|
import { EMPLOYEE_KNOWLEDGE_MAX_ENTRIES } from "./employee-knowledge-types.js";
|
|
16
14
|
/** 员工起草没有会话上下文,按「默认 provider」解析,与任务标题 / 提示词优化一致。 */
|
|
17
|
-
function employeeDraftAiOptions(storage, config) {
|
|
15
|
+
function employeeDraftAiOptions(storage, config, free) {
|
|
18
16
|
if (!config)
|
|
19
17
|
return {};
|
|
20
18
|
const provider = config.defaultProvider ?? "claude";
|
|
@@ -25,7 +23,7 @@ function employeeDraftAiOptions(storage, config) {
|
|
|
25
23
|
command: provider === "qoder" ? "qodercli" : provider,
|
|
26
24
|
selectedModel: null,
|
|
27
25
|
thinkingEffort: config.defaultThinkingEffort,
|
|
28
|
-
}, config, storage.getSystemSiliconEmployee());
|
|
26
|
+
}, config, storage.getSystemSiliconEmployee(), free);
|
|
29
27
|
}
|
|
30
28
|
function sendEmployeeError(res, error, context) {
|
|
31
29
|
const message = getErrorMessage(error, "硅基员工操作失败。");
|
|
@@ -99,23 +97,23 @@ export function parseSystemEmployeeAgents(value, existing) {
|
|
|
99
97
|
if (body.systemKey !== undefined && body.systemKey !== existing.systemKey) {
|
|
100
98
|
throw new Error("该员工是内置的,身份不可修改。");
|
|
101
99
|
}
|
|
102
|
-
|
|
100
|
+
return parseEmployeeAgents(body.agents);
|
|
101
|
+
}
|
|
102
|
+
/** One validation owner for every employee candidate list, including legacy single agents. */
|
|
103
|
+
function parseEmployeeAgents(value) {
|
|
104
|
+
if (!Array.isArray(value))
|
|
103
105
|
throw new Error("请提供执行候选数组(CLI / SDK)。");
|
|
104
|
-
if (
|
|
106
|
+
if (value.length === 0)
|
|
105
107
|
throw new Error("至少需要一个执行候选。");
|
|
106
|
-
if (
|
|
108
|
+
if (value.length > AI_TEAM_MAX_CANDIDATES) {
|
|
107
109
|
throw new Error(`最多 ${AI_TEAM_MAX_CANDIDATES} 个执行候选。`);
|
|
108
110
|
}
|
|
109
|
-
const agents =
|
|
111
|
+
const agents = value.map((rawAgent) => {
|
|
110
112
|
const parsed = parseTaskAgent(rawAgent ?? {});
|
|
111
113
|
if (!parsed)
|
|
112
114
|
throw new Error("请选择有效的 CLI 工具或 Wand Agent。");
|
|
113
115
|
if (parsed.kind !== "structured")
|
|
114
116
|
throw new Error("硅基员工只支持结构化会话。");
|
|
115
|
-
if (existing.systemKey === DECISION_EXPERT_KEY && (parsed.provider !== "pi" || parsed.engine !== "sdk"
|
|
116
|
-
|| !(isLocalDecisionModel(parsed.model) || parsed.model === OPENROUTER_FREE_SELECTOR || isOpenRouterFreeSelector(parsed.model)))) {
|
|
117
|
-
throw new Error("决策专家仅支持 LAYA 或 Wand 免费分组的决策候选;请使用 Wand 内部执行入口。");
|
|
118
|
-
}
|
|
119
117
|
return parsed;
|
|
120
118
|
});
|
|
121
119
|
if (new Set(agents.map(agentKey)).size !== agents.length) {
|
|
@@ -132,40 +130,16 @@ export function parseSiliconEmployeeInput(value, existing, now) {
|
|
|
132
130
|
const prompt = text(body.prompt);
|
|
133
131
|
if (prompt.length > 20_000)
|
|
134
132
|
throw new Error("员工设定 Prompt 不能超过 20000 个字符。");
|
|
135
|
-
let
|
|
136
|
-
if (Array.isArray(body.agents))
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (body.agents.length > AI_TEAM_MAX_CANDIDATES) {
|
|
140
|
-
throw new Error(`最多 ${AI_TEAM_MAX_CANDIDATES} 个执行候选。`);
|
|
141
|
-
}
|
|
142
|
-
agents = body.agents.map((rawAgent) => {
|
|
143
|
-
const parsed = parseTaskAgent(rawAgent ?? {});
|
|
144
|
-
if (!parsed)
|
|
145
|
-
throw new Error("请选择有效的 CLI 工具或 Wand Agent。");
|
|
146
|
-
if (parsed.kind !== "structured")
|
|
147
|
-
throw new Error("硅基员工只支持结构化会话。");
|
|
148
|
-
return parsed;
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
else if (body.agents !== undefined && body.agents !== null) {
|
|
133
|
+
let candidates;
|
|
134
|
+
if (Array.isArray(body.agents))
|
|
135
|
+
candidates = body.agents;
|
|
136
|
+
else if (body.agents !== undefined && body.agents !== null)
|
|
152
137
|
throw new Error("候选执行配置必须是数组。");
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (!singleAgent)
|
|
157
|
-
throw new Error("请选择有效的 CLI 工具或 Wand Agent。");
|
|
158
|
-
if (singleAgent.kind !== "structured")
|
|
159
|
-
throw new Error("硅基员工只支持结构化会话。");
|
|
160
|
-
agents = [singleAgent];
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
138
|
+
else if (body.agent !== undefined && body.agent !== null)
|
|
139
|
+
candidates = [body.agent];
|
|
140
|
+
else
|
|
163
141
|
throw new Error("至少需要一个执行候选。");
|
|
164
|
-
|
|
165
|
-
const seenKeys = new Set(agents.map(agentKey));
|
|
166
|
-
if (seenKeys.size !== agents.length) {
|
|
167
|
-
throw new Error("执行候选存在重复。");
|
|
168
|
-
}
|
|
142
|
+
const agents = parseEmployeeAgents(candidates);
|
|
169
143
|
const avatar = parseAvatar(body.avatar, name);
|
|
170
144
|
const tags = parseSiliconEmployeeTags(body.tags === undefined ? existing?.tags ?? [] : body.tags);
|
|
171
145
|
const id = existing?.id ?? `e_${randomUUID().replace(/-/g, "")}`;
|
|
@@ -203,7 +177,7 @@ export function registerSiliconEmployeeRoutes(app, deps) {
|
|
|
203
177
|
const available = availableEmployeeProviders(resolveDefaults().provider, isProviderAvailable);
|
|
204
178
|
return available.length ? available : [...SESSION_PROVIDERS];
|
|
205
179
|
};
|
|
206
|
-
const generateDraft = deps.generateDraft ?? ((request) => generateSiliconEmployeeDraft(request.expectation, employeeDraftAiOptions(storage, config), {
|
|
180
|
+
const generateDraft = deps.generateDraft ?? ((request) => generateSiliconEmployeeDraft(request.expectation, employeeDraftAiOptions(storage, config, deps.free), {
|
|
207
181
|
cwd: config?.defaultCwd || process.cwd(),
|
|
208
182
|
language: config?.language ?? "",
|
|
209
183
|
existingNames: request.existingNames,
|
|
@@ -10,6 +10,7 @@ import { asyncRoute } from "./express-async.js";
|
|
|
10
10
|
import { sendRouteError } from "./server-request.js";
|
|
11
11
|
import { isBlockedFolderPath, isPathWithinBase, normalizeFolderPath } from "./middleware/path-safety.js";
|
|
12
12
|
import { parseBoundedInteger } from "./request-limits.js";
|
|
13
|
+
import { runGitRawAsync } from "./git-utils.js";
|
|
13
14
|
const execAsync = promisify(exec);
|
|
14
15
|
const DIRECTORY_MAX_ITEMS = 200;
|
|
15
16
|
const MAX_TEXT_PREVIEW_SIZE = 512 * 1024;
|
|
@@ -593,7 +594,10 @@ export function registerFileRoutes(app, deps) {
|
|
|
593
594
|
const cwd = typeof req.query.cwd === "string" ? req.query.cwd : defaultCwd;
|
|
594
595
|
const maxDepth = parseBoundedInteger(req.query.depth, 5, 0, 8);
|
|
595
596
|
const maxResults = parseBoundedInteger(req.query.limit, 50, 1, 200);
|
|
596
|
-
const
|
|
597
|
+
const includeGenerated = req.query.includeGenerated === "true";
|
|
598
|
+
// Internal metadata remains excluded even when the user opts into generated files.
|
|
599
|
+
const ignoredDirectories = new Set([".git", ".wand", ".wand-uploads", ".wand-team"]);
|
|
600
|
+
const generatedDirectories = new Set(["node_modules", ".next", "dist", "build", "coverage"]);
|
|
597
601
|
const maxVisitedEntries = 20_000;
|
|
598
602
|
let resolvedCwd;
|
|
599
603
|
try {
|
|
@@ -612,38 +616,70 @@ export function registerFileRoutes(app, deps) {
|
|
|
612
616
|
return;
|
|
613
617
|
}
|
|
614
618
|
try {
|
|
619
|
+
let gitIgnored = new Set();
|
|
620
|
+
try {
|
|
621
|
+
const ignored = await runGitRawAsync(["ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"], resolvedCwd, { timeout: 2_000, maxBuffer: 2 * 1024 * 1024 });
|
|
622
|
+
gitIgnored = new Set(ignored.split("\0").filter(Boolean).map((entry) => path.resolve(resolvedCwd, entry.replace(/\/$/, ""))));
|
|
623
|
+
}
|
|
624
|
+
catch {
|
|
625
|
+
// Ordinary directories and hosts without Git still retain the existing known-directory exclusions.
|
|
626
|
+
}
|
|
615
627
|
const results = [];
|
|
628
|
+
const deferred = [];
|
|
616
629
|
const queryLower = query.toLowerCase();
|
|
617
630
|
let visitedEntries = 0;
|
|
618
|
-
async function searchDir(dirPath, currentDepth) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (results.length >= maxResults || visitedEntries >= maxVisitedEntries)
|
|
624
|
-
break;
|
|
625
|
-
visitedEntries += 1;
|
|
626
|
-
if (entry.isDirectory() && ignoredDirectories.has(entry.name))
|
|
631
|
+
async function searchDir(dirPath, currentDepth, generated = false) {
|
|
632
|
+
const directories = [{ path: dirPath, depth: currentDepth }];
|
|
633
|
+
for (let index = 0; index < directories.length && visitedEntries < maxVisitedEntries; index += 1) {
|
|
634
|
+
const directory = directories[index];
|
|
635
|
+
if (directory.depth > maxDepth)
|
|
627
636
|
continue;
|
|
628
|
-
const
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
+
const entries = await readdir(directory.path, { withFileTypes: true });
|
|
638
|
+
for (const entry of entries) {
|
|
639
|
+
if (visitedEntries >= maxVisitedEntries)
|
|
640
|
+
break;
|
|
641
|
+
visitedEntries += 1;
|
|
642
|
+
if (entry.isDirectory() && ignoredDirectories.has(entry.name))
|
|
643
|
+
continue;
|
|
644
|
+
const entryPath = path.join(directory.path, entry.name);
|
|
645
|
+
if (!generated && entry.isDirectory() && generatedDirectories.has(entry.name)) {
|
|
646
|
+
if (includeGenerated)
|
|
647
|
+
deferred.push({ path: entryPath, name: entry.name, depth: directory.depth + 1 });
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (!generated && gitIgnored.has(entryPath))
|
|
651
|
+
continue;
|
|
652
|
+
const matchIndex = entry.name.toLowerCase().indexOf(queryLower);
|
|
653
|
+
if (matchIndex !== -1) {
|
|
654
|
+
results.push({
|
|
655
|
+
path: entryPath,
|
|
656
|
+
name: entry.name,
|
|
657
|
+
type: entry.isDirectory() ? "dir" : "file",
|
|
658
|
+
matchScore: matchIndex,
|
|
659
|
+
generated,
|
|
660
|
+
depth: directory.depth,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
if (entry.isDirectory() && directory.depth < maxDepth)
|
|
664
|
+
directories.push({ path: entryPath, depth: directory.depth + 1 });
|
|
637
665
|
}
|
|
638
|
-
if (entry.isDirectory())
|
|
639
|
-
await searchDir(entryPath, currentDepth + 1);
|
|
640
666
|
}
|
|
641
667
|
}
|
|
642
668
|
await searchDir(resolvedCwd, 0);
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
669
|
+
// Complete the project-file pass before dependencies can consume the bounded result budget.
|
|
670
|
+
for (const directory of deferred) {
|
|
671
|
+
if (visitedEntries >= maxVisitedEntries)
|
|
672
|
+
break;
|
|
673
|
+
const matchScore = directory.name.toLowerCase().indexOf(queryLower);
|
|
674
|
+
if (matchScore !== -1)
|
|
675
|
+
results.push({ path: directory.path, name: directory.name, type: "dir", matchScore, generated: true, depth: directory.depth - 1 });
|
|
676
|
+
await searchDir(directory.path, directory.depth, true);
|
|
677
|
+
}
|
|
678
|
+
results.sort((a, b) => Number(a.generated) - Number(b.generated)
|
|
679
|
+
|| Number(b.name.toLowerCase() === queryLower) - Number(a.name.toLowerCase() === queryLower)
|
|
680
|
+
|| a.depth - b.depth || a.matchScore - b.matchScore || a.name.localeCompare(b.name));
|
|
681
|
+
res.json({ results: results.slice(0, maxResults), query, cwd: resolvedCwd,
|
|
682
|
+
truncated: results.length > maxResults || visitedEntries >= maxVisitedEntries });
|
|
647
683
|
}
|
|
648
684
|
catch (error) {
|
|
649
685
|
sendRouteError(res, error, "搜索失败。可能原因:路径不存在或权限不足。");
|
|
@@ -51,6 +51,18 @@ export function registerLocalModelRoutes(app, deps) {
|
|
|
51
51
|
next(error);
|
|
52
52
|
}
|
|
53
53
|
});
|
|
54
|
+
app.patch("/api/local-models/speech/settings", deps.requireAdmin, async (req, res, next) => {
|
|
55
|
+
try {
|
|
56
|
+
if (!req.body || Array.isArray(req.body) || Object.keys(req.body).length !== 1 || typeof req.body.enabled !== "boolean") {
|
|
57
|
+
throw new DecisionError("INVALID_REQUEST", "仅允许修改语音启用状态。");
|
|
58
|
+
}
|
|
59
|
+
await deps.models.setSpeechEnabled(req.body.enabled);
|
|
60
|
+
res.status(202).json(await deps.models.status());
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
next(error);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
54
66
|
const errors = (error, _req, res, _next) => {
|
|
55
67
|
if (error instanceof DecisionError || error instanceof SpeechError) {
|
|
56
68
|
res.status(error.status).json({ error: error.message, code: error.code });
|
|
@@ -3,6 +3,7 @@ import { ProcessManager } from "./process-manager.js";
|
|
|
3
3
|
import { StructuredSessionManager } from "./structured-session-manager.js";
|
|
4
4
|
import { WandStorage } from "./storage.js";
|
|
5
5
|
import { ExecutionMode, ProcessEvent, SessionSnapshot, SessionSource, WandConfig } from "./types.js";
|
|
6
|
+
import type { OpenRouterFreeModelsService } from "./openrouter-free-models.js";
|
|
6
7
|
import { toSessionListItemDTO } from "./session-transport.js";
|
|
7
8
|
import { type ProviderHistoryDeleter, SessionRegistry } from "./session-registry.js";
|
|
8
9
|
import { type PiRecommendationRuntime } from "./server-pi-recommendation-routes.js";
|
|
@@ -46,6 +47,6 @@ export interface SessionDirectoryTreeResponse {
|
|
|
46
47
|
}
|
|
47
48
|
export declare function buildSessionListPage(sessions: SessionSnapshot[], offset: number, limit: number): SessionListPage;
|
|
48
49
|
export declare function buildSessionDirectoryTree(sessions: SessionSnapshot[], customNames?: ReadonlyMap<string, string>): SessionDirectoryTreeResponse;
|
|
49
|
-
export declare function registerSessionRoutes(app: Express, processes: ProcessManager, structured: StructuredSessionManager, storage: WandStorage, defaultMode: ExecutionMode, config: WandConfig, sessions: SessionRegistry, onSessionCreated?: (cwd: string | undefined | null) => void, onCompletionViewed?: (event: ProcessEvent) => void, decisions?: PiRecommendationRuntime): void;
|
|
50
|
+
export declare function registerSessionRoutes(app: Express, processes: ProcessManager, structured: StructuredSessionManager, storage: WandStorage, defaultMode: ExecutionMode, config: WandConfig, sessions: SessionRegistry, onSessionCreated?: (cwd: string | undefined | null) => void, onCompletionViewed?: (event: ProcessEvent) => void, decisions?: PiRecommendationRuntime, free?: Pick<OpenRouterFreeModelsService, "resolveForCall">): void;
|
|
50
51
|
export declare function registerClaudeHistoryRoutes(app: Express, processes: ProcessManager, storage: WandStorage): void;
|
|
51
52
|
export {};
|
|
@@ -11,7 +11,7 @@ import { resolveSessionCwd } from "./session-cwd.js";
|
|
|
11
11
|
import { resolveSystemAiContext } from "./session-ai-context.js";
|
|
12
12
|
import { getGitStatusAsync, QuickCommitError, runQuickCommitWithFallback, runTagHead, runPush, generateCommitMessageOnly, } from "./git-quick-commit.js";
|
|
13
13
|
import { getErrorMessage } from "./error-utils.js";
|
|
14
|
-
import { archiveCommitTasks } from "./commit-task-archive.js";
|
|
14
|
+
import { archiveCommitTasks, archiveCommitStandaloneSessions } from "./commit-task-archive.js";
|
|
15
15
|
import { buildIterationCommitContext, COMMIT_CONTEXT_MODE_PREF_KEY, isCommitContextMode, readCommitContextMode, resolveCommitContextInput, } from "./iteration-log.js";
|
|
16
16
|
import { inferProviderFromCommand, isSessionProvider, providerCliCommand, SESSION_PROVIDERS } from "./session-provider.js";
|
|
17
17
|
import { selectEmployeeCandidate } from "./silicon-employee-dispatch.js";
|
|
@@ -366,7 +366,7 @@ function canMergeSession(snapshot) {
|
|
|
366
366
|
function isMergeActionAllowed(snapshot) {
|
|
367
367
|
return snapshot.status !== "running";
|
|
368
368
|
}
|
|
369
|
-
export function registerSessionRoutes(app, processes, structured, storage, defaultMode, config, sessions, onSessionCreated, onCompletionViewed, decisions) {
|
|
369
|
+
export function registerSessionRoutes(app, processes, structured, storage, defaultMode, config, sessions, onSessionCreated, onCompletionViewed, decisions, free) {
|
|
370
370
|
registerPiRecommendationRoute(app, { structured, sessions, config, decisions });
|
|
371
371
|
const wantsCompactTools = (req) => req.get("X-Wand-Tool-Projection") === "compact" || req.query.compactTools === "1";
|
|
372
372
|
const sessionResponseDTO = (snapshot, req) => {
|
|
@@ -497,9 +497,9 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
497
497
|
res.status(400).json({ error: "请选择有效的执行引擎。" });
|
|
498
498
|
return;
|
|
499
499
|
}
|
|
500
|
-
const requestedEngine =
|
|
501
|
-
?
|
|
502
|
-
: rawEngine;
|
|
500
|
+
const requestedEngine = employee && !requestedProvider
|
|
501
|
+
? employeeAgent?.engine ?? "cli"
|
|
502
|
+
: rawEngine ? rawEngine : undefined;
|
|
503
503
|
if (requestedEngine === "sdk" && provider !== "pi") {
|
|
504
504
|
res.status(400).json({ error: "Wand Agent 只支持 Pi 结构化会话。" });
|
|
505
505
|
return;
|
|
@@ -1100,7 +1100,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
1100
1100
|
}
|
|
1101
1101
|
const body = (req.body ?? {});
|
|
1102
1102
|
try {
|
|
1103
|
-
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee());
|
|
1103
|
+
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee(), free);
|
|
1104
1104
|
// 自动 message + 迭代模式:拿本轮提示词当输入;提交成功后标记已用掉。
|
|
1105
1105
|
const context = await iterationCommitInput(storage, snapshot, body);
|
|
1106
1106
|
const result = await runQuickCommitWithFallback({
|
|
@@ -1120,6 +1120,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
1120
1120
|
? storage.markIterationPromptsConsumed(context.entryIds, result.commit.hash)
|
|
1121
1121
|
: 0;
|
|
1122
1122
|
let archivedTaskIds = [];
|
|
1123
|
+
const archivedSessionIds = [];
|
|
1123
1124
|
let archiveError;
|
|
1124
1125
|
if (body.archiveRelatedTasks === true && result.ok && result.commit?.hash) {
|
|
1125
1126
|
try {
|
|
@@ -1129,15 +1130,17 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
1129
1130
|
archivedTaskIds = archiveCommitTasks(storage, snapshot, context.entryIds, {
|
|
1130
1131
|
workspaceTaskId: openedTaskId || null,
|
|
1131
1132
|
});
|
|
1133
|
+
archiveCommitStandaloneSessions(storage, sessions, snapshot, archivedSessionIds);
|
|
1132
1134
|
}
|
|
1133
1135
|
catch (error) {
|
|
1134
|
-
archiveError = getErrorMessage(error, "
|
|
1136
|
+
archiveError = getErrorMessage(error, "归档任务与会话失败。");
|
|
1135
1137
|
console.error("[QuickCommit] Failed to archive related tasks:", archiveError);
|
|
1136
1138
|
}
|
|
1137
1139
|
}
|
|
1138
1140
|
res.json({
|
|
1139
1141
|
...result,
|
|
1140
1142
|
archivedTaskIds,
|
|
1143
|
+
archivedSessionIds,
|
|
1141
1144
|
...(archiveError ? { archiveError } : {}),
|
|
1142
1145
|
commitContext: {
|
|
1143
1146
|
source: commitMessageFromIteration(body) && context.digest ? "iteration" : "diff",
|
|
@@ -1199,7 +1202,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
1199
1202
|
}
|
|
1200
1203
|
const body = (req.body ?? {});
|
|
1201
1204
|
try {
|
|
1202
|
-
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee());
|
|
1205
|
+
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee(), free);
|
|
1203
1206
|
const context = await iterationCommitInput(storage, snapshot, body);
|
|
1204
1207
|
const result = await generateCommitMessageOnly(snapshot.cwd, config.language ?? "", {
|
|
1205
1208
|
...ai,
|
|
@@ -1236,7 +1239,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
1236
1239
|
}
|
|
1237
1240
|
const body = (req.body ?? {});
|
|
1238
1241
|
try {
|
|
1239
|
-
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee());
|
|
1242
|
+
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee(), free);
|
|
1240
1243
|
const result = await runTagHead({
|
|
1241
1244
|
cwd: snapshot.cwd,
|
|
1242
1245
|
language: config.language ?? "",
|
|
@@ -1733,7 +1736,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
1733
1736
|
sendRouteError(res, error, "无法停止会话。");
|
|
1734
1737
|
}
|
|
1735
1738
|
});
|
|
1736
|
-
//
|
|
1739
|
+
// 归档只隐藏会话,保留运行进程、Wand 会话 ID 与 provider session id。
|
|
1737
1740
|
app.post("/api/sessions/:id/archive", (req, res) => {
|
|
1738
1741
|
try {
|
|
1739
1742
|
const updated = sessions.setArchived(req.params.id, true);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type Express, type RequestHandler } from "express";
|
|
2
2
|
import { type SpeechService } from "./speech-service.js";
|
|
3
|
+
import type { SpeechPolisherService } from "./speech-polisher-service.js";
|
|
3
4
|
/** Mounted behind /api authentication. Clients may transcribe, only admins configure/download. */
|
|
4
5
|
export declare function registerSpeechRoutes(app: Express, deps: {
|
|
5
6
|
speech: SpeechService;
|
|
6
7
|
requireSessions: RequestHandler;
|
|
7
8
|
requireAdmin: RequestHandler;
|
|
9
|
+
polisher?: Pick<SpeechPolisherService, "polish">;
|
|
8
10
|
}): void;
|
|
@@ -53,6 +53,7 @@ export function registerSpeechRoutes(app, deps) {
|
|
|
53
53
|
next();
|
|
54
54
|
};
|
|
55
55
|
app.post("/api/speech/transcribe", admission, express.raw({ type: "audio/wav", limit: SPEECH_MAX_WAV_BYTES }), async (req, res, next) => {
|
|
56
|
+
const started = Date.now();
|
|
56
57
|
const abort = new AbortController();
|
|
57
58
|
const disconnected = () => { if (!res.writableEnded)
|
|
58
59
|
abort.abort(); };
|
|
@@ -61,8 +62,13 @@ export function registerSpeechRoutes(app, deps) {
|
|
|
61
62
|
if (!Buffer.isBuffer(req.body))
|
|
62
63
|
throw new SpeechError("INVALID_AUDIO", "未收到 WAV 音频。");
|
|
63
64
|
const result = await deps.speech.transcribe(req.body, abort.signal);
|
|
65
|
+
const polished = deps.polisher ? await deps.polisher.polish(result.text, abort.signal, Math.min(45_000, 123_000 - (Date.now() - started))).catch(() => {
|
|
66
|
+
abort.signal.throwIfAborted();
|
|
67
|
+
return { text: result.text, originalText: result.text, optimized: false,
|
|
68
|
+
optimizationError: "口述整理暂不可用,已保留原始转写。" };
|
|
69
|
+
}) : null;
|
|
64
70
|
if (!res.destroyed)
|
|
65
|
-
res.json(result);
|
|
71
|
+
res.json({ ...result, ...(polished ?? {}) });
|
|
66
72
|
}
|
|
67
73
|
catch (error) {
|
|
68
74
|
if (!res.destroyed)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Express } from "express";
|
|
2
2
|
import { type WandStorage } from "./storage.js";
|
|
3
3
|
import { generateWandTaskTitle } from "./task-title.js";
|
|
4
|
+
import type { OpenRouterFreeModelsService } from "./openrouter-free-models.js";
|
|
4
5
|
import type { SessionRegistry } from "./session-registry.js";
|
|
5
6
|
import type { StructuredSessionManager } from "./structured-session-manager.js";
|
|
6
7
|
import type { ProcessManager } from "./process-manager.js";
|
|
@@ -17,6 +18,7 @@ export interface TaskRouteDependencies {
|
|
|
17
18
|
/** PTY 会话创建入口;派发 `kind: "pty"` 的任务时使用。测试里可换成同步桩。 */
|
|
18
19
|
processes?: ProcessManager;
|
|
19
20
|
config?: WandConfig;
|
|
21
|
+
free?: Pick<OpenRouterFreeModelsService, "resolveForCall">;
|
|
20
22
|
aiTeams?: AiTeamRunner;
|
|
21
23
|
/** 可注入的任务标题生成器;测试里换成同步桩,避免真的起 CLI。 */
|
|
22
24
|
generateTitle?: typeof generateWandTaskTitle;
|
|
@@ -33,6 +35,7 @@ export declare function parseTaskAgent(value: unknown, fallbackMode?: WandTaskAg
|
|
|
33
35
|
export declare function whenWandTaskTitlesSettled(): Promise<void>;
|
|
34
36
|
export interface AutoTaskTitleOptions {
|
|
35
37
|
config?: WandConfig;
|
|
38
|
+
free?: Pick<OpenRouterFreeModelsService, "resolveForCall">;
|
|
36
39
|
/** 可注入的标题生成器;测试里换成同步桩,避免真的起 CLI。 */
|
|
37
40
|
generateTitle?: typeof generateWandTaskTitle;
|
|
38
41
|
/** 未从任务所属工作区解析到目录时的兜底 cwd。 */
|
|
@@ -41,6 +41,7 @@ export function writeTaskBoardLastAgent(storage, agent) {
|
|
|
41
41
|
thinkingEffort: agent.thinkingEffort,
|
|
42
42
|
mode: agent.mode,
|
|
43
43
|
kind: agent.kind,
|
|
44
|
+
...(agent.engine ? { engine: agent.engine } : {}),
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
function parseExecutionSubject(storage, value) {
|
|
@@ -250,7 +251,7 @@ function scheduleWandTaskTitleGeneration(storage, taskId, source, signature, opt
|
|
|
250
251
|
const cwd = options.cwd || options.config?.defaultCwd || process.cwd();
|
|
251
252
|
const run = async () => {
|
|
252
253
|
try {
|
|
253
|
-
const title = await generateTitle(source, cwd, options.config?.language ?? "", taskTitleAiOptions(options.config, storage));
|
|
254
|
+
const title = await generateTitle(source, cwd, options.config?.language ?? "", taskTitleAiOptions(options.config, storage, options.free));
|
|
254
255
|
const current = storage.getWandTask(taskId);
|
|
255
256
|
// 用户已经自己写了标题(原生端 / 面板编辑)就不要覆盖。
|
|
256
257
|
if (!current || current.titleSource !== "auto")
|
|
@@ -304,11 +305,12 @@ export function refreshAutoBoardTaskTitles(storage, options = {}) {
|
|
|
304
305
|
cwd,
|
|
305
306
|
config: options.config,
|
|
306
307
|
generateTitle: options.generateTitle,
|
|
308
|
+
free: options.free,
|
|
307
309
|
});
|
|
308
310
|
}
|
|
309
311
|
}
|
|
310
312
|
/** 自动生成标题没有会话上下文,按「默认 provider + 默认模型」解析,与提示词优化一致。 */
|
|
311
|
-
function taskTitleAiOptions(config, storage) {
|
|
313
|
+
function taskTitleAiOptions(config, storage, free) {
|
|
312
314
|
if (!config)
|
|
313
315
|
return {};
|
|
314
316
|
const provider = config.defaultProvider ?? "claude";
|
|
@@ -320,8 +322,7 @@ function taskTitleAiOptions(config, storage) {
|
|
|
320
322
|
selectedModel: null,
|
|
321
323
|
thinkingEffort: config.defaultThinkingEffort,
|
|
322
324
|
};
|
|
323
|
-
|
|
324
|
-
return resolveSystemAiContext(defaultSession, config, storage?.getSystemSiliconEmployee() ?? null);
|
|
325
|
+
return resolveSystemAiContext(defaultSession, config, storage?.getSystemSiliconEmployee() ?? null, free);
|
|
325
326
|
}
|
|
326
327
|
export function registerTaskRoutes(app, deps) {
|
|
327
328
|
const { storage, sessions, structured, processes, config, aiTeams } = deps;
|
|
@@ -329,7 +330,7 @@ export function registerTaskRoutes(app, deps) {
|
|
|
329
330
|
app.get("/api/wand-tasks", (req, res) => {
|
|
330
331
|
try {
|
|
331
332
|
// 侧栏建的任务 / 后续新增的会话都会在这里补上自动标题。
|
|
332
|
-
refreshAutoBoardTaskTitles(storage, { config, generateTitle: deps.generateTitle });
|
|
333
|
+
refreshAutoBoardTaskTitles(storage, { config, generateTitle: deps.generateTitle, free: deps.free });
|
|
333
334
|
}
|
|
334
335
|
catch (error) {
|
|
335
336
|
console.error("[WandTask] Failed to refresh auto task titles:", getErrorMessage(error));
|
|
@@ -483,13 +484,15 @@ export function registerTaskRoutes(app, deps) {
|
|
|
483
484
|
agent: assignedAgent,
|
|
484
485
|
executionSubject,
|
|
485
486
|
});
|
|
486
|
-
|
|
487
|
+
// Task execution settings stay local unless the caller explicitly saves future defaults.
|
|
488
|
+
if (body.rememberAgentDefaults === true && agent)
|
|
487
489
|
writeTaskBoardLastAgent(storage, agent);
|
|
488
490
|
if (titleSource === "auto") {
|
|
489
491
|
refreshAutoBoardTaskTitles(storage, {
|
|
490
492
|
cwd: workspaceId ? storage.getWorkspace(workspaceId)?.cwd : undefined,
|
|
491
493
|
config,
|
|
492
494
|
generateTitle: deps.generateTitle,
|
|
495
|
+
free: deps.free,
|
|
493
496
|
});
|
|
494
497
|
}
|
|
495
498
|
res.status(201).json(dto(storage.getWandTask(task.id) ?? task));
|
|
@@ -583,7 +586,7 @@ export function registerTaskRoutes(app, deps) {
|
|
|
583
586
|
res.status(404).json({ error: "未找到该任务。" });
|
|
584
587
|
return;
|
|
585
588
|
}
|
|
586
|
-
if (body.
|
|
589
|
+
if (body.rememberAgentDefaults === true && patch.agent)
|
|
587
590
|
writeTaskBoardLastAgent(storage, patch.agent);
|
|
588
591
|
res.json(dto(storage.getWandTask(task.id) ?? task));
|
|
589
592
|
}
|
|
@@ -741,12 +744,12 @@ export function registerTaskRoutes(app, deps) {
|
|
|
741
744
|
if (subject?.type === "employee" && (!employee || employee.archivedAt)) {
|
|
742
745
|
throw new Error("硅基员工不存在或已归档。");
|
|
743
746
|
}
|
|
744
|
-
const selected = employee ? selectEmployeeCandidate(employee
|
|
747
|
+
const selected = employee ? selectEmployeeCandidate(employee) : null;
|
|
745
748
|
const cliDefault = subject?.type === "cli"
|
|
746
749
|
? parseTaskAgent({ ...cliAgentForSubject(subject, task.agent), ...(body.kind === "pty" ? { kind: "pty" } : {}) })
|
|
747
750
|
: null;
|
|
748
751
|
const fallbackCandidate = !subject && body.agent === undefined && !task.agent
|
|
749
|
-
&& !body.provider ? selectEmployeeCandidate(defaultRoleForCli(storage, config.defaultProvider)
|
|
752
|
+
&& !body.provider ? selectEmployeeCandidate(defaultRoleForCli(storage, config.defaultProvider)).agent : null;
|
|
750
753
|
const fallbackAgent = fallbackCandidate && body.kind === "pty"
|
|
751
754
|
? { ...fallbackCandidate, kind: "pty" } : fallbackCandidate;
|
|
752
755
|
const agent = selected?.agent ?? (body.agent === undefined
|
|
@@ -780,7 +783,7 @@ export function registerTaskRoutes(app, deps) {
|
|
|
780
783
|
executionSubject: usedSubject,
|
|
781
784
|
status: task.status === "todo" ? "doing" : task.status,
|
|
782
785
|
});
|
|
783
|
-
if (!employee)
|
|
786
|
+
if (body.rememberAgentDefaults === true && !employee)
|
|
784
787
|
writeTaskBoardLastAgent(storage, agent);
|
|
785
788
|
// 派发也算一轮迭代里的改动意图:直接把任务的标题 / 描述记进迭代记录。
|
|
786
789
|
recordIterationPromptForTask(storage, {
|
package/dist/server.js
CHANGED
|
@@ -29,6 +29,8 @@ import { recordRecentPath, registerFileRoutes } from "./server-file-routes.js";
|
|
|
29
29
|
import { registerLocalPreviewRoutes } from "./server-local-preview-routes.js";
|
|
30
30
|
import { registerSettingsRoutes } from "./server-settings-routes.js";
|
|
31
31
|
import { registerSpeechRoutes } from "./server-speech-routes.js";
|
|
32
|
+
import { SpeechPolisherService } from "./speech-polisher-service.js";
|
|
33
|
+
import { SPEECH_POLISHER_KEY } from "./speech-polisher-identity.js";
|
|
32
34
|
import { registerLocalModelRoutes } from "./server-local-model-routes.js";
|
|
33
35
|
import { LocalModelSetupService } from "./local-model-setup.js";
|
|
34
36
|
import { DecisionExpertService } from "./decision-expert-service.js";
|
|
@@ -79,7 +81,7 @@ import { DistributionManager } from "./distribution-manager.js";
|
|
|
79
81
|
import { isLogBusActive, wandTuiLog } from "./tui/log-bus.js";
|
|
80
82
|
import { EMBEDDED_WEB_ASSETS } from "./web-ui/embedded-assets.js";
|
|
81
83
|
import { renderApp } from "./web-ui/index.js";
|
|
82
|
-
import { getAiTeamsChunk, getScriptAsset } from "./web-ui/scripts.js";
|
|
84
|
+
import { getAiTeamsChunk, getScriptAsset, getThemePreloadAsset } from "./web-ui/scripts.js";
|
|
83
85
|
import { getStylesAsset } from "./web-ui/styles.js";
|
|
84
86
|
import { WsBroadcastManager } from "./ws-broadcast.js";
|
|
85
87
|
import { TerminalDaemonClient } from "./terminal-daemon-client.js";
|
|
@@ -325,6 +327,8 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
325
327
|
const storage = new WandStorage(resolveDatabasePath(configPath));
|
|
326
328
|
const runtimeConfig = new RuntimeConfigState(config);
|
|
327
329
|
const openRouter = new OpenRouterFreeModelsService(storage, options.openRouterFetch);
|
|
330
|
+
const speechPolisher = new SpeechPolisherService({ config, free: openRouter,
|
|
331
|
+
employee: () => storage.getSystemSiliconEmployee(SPEECH_POLISHER_KEY) });
|
|
328
332
|
const authService = new AuthService(storage);
|
|
329
333
|
const settingsAccess = new SettingsWebAccess();
|
|
330
334
|
const decisions = new DecisionService(config.localDecision);
|
|
@@ -398,7 +402,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
398
402
|
const terminalHost = daemonAdmission.terminal(ptyHosts.host);
|
|
399
403
|
let decisionRuntime = null;
|
|
400
404
|
let autoAssignEvaluate;
|
|
401
|
-
const processes = new ProcessManager(config, storage, configDir, terminalHost, { resolveDecisionEvaluate: () => autoAssignEvaluate });
|
|
405
|
+
const processes = new ProcessManager(config, storage, configDir, terminalHost, { resolveDecisionEvaluate: () => autoAssignEvaluate, free: openRouter });
|
|
402
406
|
const structuredLogger = new SessionLogger(configDir, config.shortcutLogMaxBytes);
|
|
403
407
|
// Production startup provides a daemon-backed host for structured CLI runs
|
|
404
408
|
// even when Render owns every PTY. In-process hosts are for test injection.
|
|
@@ -550,6 +554,11 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
550
554
|
setAssetCache(req, res, asset.hash, "public");
|
|
551
555
|
res.type("text/css").send(asset.content);
|
|
552
556
|
});
|
|
557
|
+
app.get("/assets/theme.js", (req, res) => {
|
|
558
|
+
const asset = getThemePreloadAsset();
|
|
559
|
+
setAssetCache(req, res, asset.hash, "public");
|
|
560
|
+
res.type("application/javascript").send(asset.content);
|
|
561
|
+
});
|
|
553
562
|
app.get("/assets/app.js", (req, res) => {
|
|
554
563
|
const asset = getScriptAsset(configPath, requestedHash(req));
|
|
555
564
|
setAssetCache(req, res, asset.hash, "private");
|
|
@@ -764,7 +773,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
764
773
|
app.use("/api/silicon-employees/:employeeId/knowledge", requireSessions);
|
|
765
774
|
app.use(userMemoryOperationLog(storage));
|
|
766
775
|
const userMemory = new UserMemoryService({
|
|
767
|
-
storage, config,
|
|
776
|
+
storage, config, free: openRouter,
|
|
768
777
|
notifyChanged: (employeeId) => notifyAiTeamRun({ kind: "silicon-employee-definition", employeeId }),
|
|
769
778
|
});
|
|
770
779
|
registerUserMemoryRoutes(app, {
|
|
@@ -841,7 +850,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
841
850
|
});
|
|
842
851
|
registerGithubRoutes(app, { storage, requireAdmin, sessions: sessionRegistry });
|
|
843
852
|
// 任务管理与 Missions 一样只需登录:原生 connected-app 也要能列/建/派发。
|
|
844
|
-
registerTaskRoutes(app, { storage, sessions: sessionRegistry, structured: structuredSessions, processes, config, aiTeams });
|
|
853
|
+
registerTaskRoutes(app, { storage, sessions: sessionRegistry, structured: structuredSessions, processes, config, aiTeams, free: openRouter });
|
|
845
854
|
registerAiTeamRoutes(app, {
|
|
846
855
|
storage,
|
|
847
856
|
runner: aiTeams,
|
|
@@ -852,6 +861,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
852
861
|
registerSiliconEmployeeRoutes(app, {
|
|
853
862
|
storage,
|
|
854
863
|
config,
|
|
864
|
+
free: openRouter,
|
|
855
865
|
notifyEmployeeChanged: (employeeId) => notifyAiTeamRun({ kind: "silicon-employee-definition", employeeId }),
|
|
856
866
|
});
|
|
857
867
|
registerAttentionRoutes(app, { sessions: sessionRegistry, runner: aiTeams });
|
|
@@ -882,12 +892,12 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
882
892
|
}
|
|
883
893
|
registerSessionRoutes(app, processes, structuredSessions, storage, config.defaultMode, config, sessionRegistry, (cwd) => {
|
|
884
894
|
recordRecentPath(storage, cwd);
|
|
885
|
-
}, (event) => wsManager.emitEvent(event), decisionExpert);
|
|
895
|
+
}, (event) => wsManager.emitEvent(event), decisionExpert, openRouter);
|
|
886
896
|
registerClaudeHistoryRoutes(app, processes, storage);
|
|
887
897
|
registerWorkspaceRoutes(app, storage, sessionRegistry, { config });
|
|
888
898
|
registerMissionRoutes(app, missions);
|
|
889
899
|
registerUploadRoutes(app, sessionRegistry);
|
|
890
|
-
registerSpeechRoutes(app, { speech, requireSessions, requireAdmin });
|
|
900
|
+
registerSpeechRoutes(app, { speech, requireSessions, requireAdmin, polisher: speechPolisher });
|
|
891
901
|
registerLocalModelRoutes(app, { models: localModels, requireSessions, requireAdmin });
|
|
892
902
|
app.post("/api/optimize-prompt", asyncRoute(async (req, res) => {
|
|
893
903
|
const body = (req.body ?? {});
|
|
@@ -899,7 +909,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
899
909
|
if (snapshot?.cwd)
|
|
900
910
|
cwd = snapshot.cwd;
|
|
901
911
|
if (snapshot)
|
|
902
|
-
ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee());
|
|
912
|
+
ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee(), openRouter);
|
|
903
913
|
}
|
|
904
914
|
if (!ai) {
|
|
905
915
|
const defaultSession = {
|
|
@@ -910,7 +920,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
910
920
|
selectedModel: null,
|
|
911
921
|
thinkingEffort: config.defaultThinkingEffort,
|
|
912
922
|
};
|
|
913
|
-
ai = resolveSystemAiContext(defaultSession, config, storage.getSystemSiliconEmployee());
|
|
923
|
+
ai = resolveSystemAiContext(defaultSession, config, storage.getSystemSiliconEmployee(), openRouter);
|
|
914
924
|
}
|
|
915
925
|
try {
|
|
916
926
|
const optimized = await optimizePrompt(text, config.language ?? "", cwd, ai);
|
|
@@ -1180,6 +1190,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1180
1190
|
conversationService.dispose();
|
|
1181
1191
|
localModels.dispose();
|
|
1182
1192
|
speech.dispose();
|
|
1193
|
+
speechPolisher.dispose();
|
|
1183
1194
|
try {
|
|
1184
1195
|
structuredSessions.dispose();
|
|
1185
1196
|
}
|
|
@@ -1500,6 +1511,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1500
1511
|
conversationService.dispose();
|
|
1501
1512
|
localModels.dispose();
|
|
1502
1513
|
speech.dispose();
|
|
1514
|
+
speechPolisher.dispose();
|
|
1503
1515
|
try {
|
|
1504
1516
|
structuredSessions.dispose();
|
|
1505
1517
|
}
|