@co0ontty/wand 4.80.0 → 4.80.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/ai-team-availability.js +0 -1
- package/dist/ai-team-live.d.ts +3 -1
- package/dist/ai-team-live.js +31 -2
- package/dist/ai-team-runner.d.ts +2 -1
- package/dist/ai-team-runner.js +17 -7
- package/dist/ai-team-types.d.ts +8 -0
- package/dist/ai-team-types.js +4 -0
- package/dist/build-info.json +3 -3
- package/dist/claude-pty-bridge.js +16 -1
- package/dist/cli.js +0 -9
- package/dist/config.d.ts +2 -10
- package/dist/config.js +1 -73
- package/dist/git-quick-commit.d.ts +3 -6
- package/dist/git-quick-commit.js +38 -75
- package/dist/language-prompt.d.ts +1 -1
- package/dist/language-prompt.js +1 -1
- package/dist/native-session-title.d.ts +17 -0
- package/dist/native-session-title.js +257 -0
- package/dist/process-manager.d.ts +3 -0
- package/dist/process-manager.js +37 -6
- package/dist/server-session-routes.js +4 -29
- package/dist/server-settings-routes.js +2 -177
- package/dist/server-task-routes.js +1 -1
- package/dist/server-workspace-routes.js +1 -1
- package/dist/server.js +1 -13
- package/dist/session-ai-context.d.ts +3 -7
- package/dist/session-ai-context.js +0 -25
- package/dist/session-topic.d.ts +19 -0
- package/dist/session-topic.js +53 -3
- package/dist/session-transport.js +0 -1
- package/dist/silicon-employee-draft.d.ts +1 -1
- package/dist/storage.js +12 -18
- package/dist/structured-claude-adapter.d.ts +0 -6
- package/dist/structured-claude-adapter.js +1 -5
- package/dist/structured-provider-common.d.ts +8 -4
- package/dist/structured-provider-common.js +14 -15
- package/dist/structured-session-manager.d.ts +4 -40
- package/dist/structured-session-manager.js +53 -773
- package/dist/types.d.ts +12 -27
- package/dist/web-ui/content/ai-teams.js +9 -9
- package/dist/web-ui/content/scripts.js +70 -70
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/dist/ws-broadcast.js +19 -7
- package/package.json +1 -2
- package/dist/claude-sdk-runner.d.ts +0 -59
- package/dist/claude-sdk-runner.js +0 -188
- package/dist/claude-skills.d.ts +0 -6
- package/dist/claude-skills.js +0 -53
- package/dist/system-ai.d.ts +0 -27
- package/dist/system-ai.js +0 -424
|
@@ -2,27 +2,16 @@ import { existsSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { buildInfoPayload } from "./build-info.js";
|
|
4
4
|
import { buildChildEnv } from "./env-utils.js";
|
|
5
|
-
import { getErrorMessage } from "./error-utils.js";
|
|
6
5
|
import { asyncRoute } from "./express-async.js";
|
|
7
6
|
import { sendRouteError } from "./server-request.js";
|
|
8
|
-
import { getProviderDefaultModels, PREFERENCE_KEYS, saveConfig,
|
|
7
|
+
import { getProviderDefaultModels, PREFERENCE_KEYS, saveConfig, writePreferenceToStorage, } from "./config.js";
|
|
9
8
|
import { withConfiguredDefaultModelLabels } from "./models.js";
|
|
10
9
|
import { DEPLOYMENT_CONFIG_KEYS } from "./runtime-config.js";
|
|
11
|
-
import { callSystemAiText, discoverCliSystemAiConfigs, mergeSystemAiConfigs, normalizeSystemAiConfig, } from "./system-ai.js";
|
|
12
|
-
function systemAiRouteIdentity(profile) {
|
|
13
|
-
return [
|
|
14
|
-
profile.protocol,
|
|
15
|
-
profile.baseUrl,
|
|
16
|
-
profile.model,
|
|
17
|
-
profile.authHeader ?? "bearer",
|
|
18
|
-
].join("\0");
|
|
19
|
-
}
|
|
20
10
|
function publicConfig(config) {
|
|
21
11
|
const { password: _password, appSecret: _appSecret, ...safe } = config;
|
|
22
12
|
const defaultModels = getProviderDefaultModels(config);
|
|
23
13
|
return {
|
|
24
14
|
...safe,
|
|
25
|
-
systemAi: publicSystemAi(safe.systemAi),
|
|
26
15
|
defaultModel: defaultModels.claude,
|
|
27
16
|
defaultCodexModel: defaultModels.codex,
|
|
28
17
|
defaultOpenCodeModel: defaultModels.opencode,
|
|
@@ -33,21 +22,6 @@ function publicConfig(config) {
|
|
|
33
22
|
defaultModels,
|
|
34
23
|
};
|
|
35
24
|
}
|
|
36
|
-
function publicSystemAi(systemAi) {
|
|
37
|
-
if (!systemAi)
|
|
38
|
-
return undefined;
|
|
39
|
-
return {
|
|
40
|
-
...systemAi,
|
|
41
|
-
apiKey: "",
|
|
42
|
-
hasApiKey: Boolean(systemAi.apiKey),
|
|
43
|
-
fallbacks: systemAi.fallbacks?.map((profile) => ({
|
|
44
|
-
...profile,
|
|
45
|
-
apiKey: "",
|
|
46
|
-
hasApiKey: Boolean(profile.apiKey),
|
|
47
|
-
fallbacks: undefined,
|
|
48
|
-
})),
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
25
|
function publicDistributionInfo(distribution) {
|
|
52
26
|
const androidApk = { ...distribution.androidApk };
|
|
53
27
|
const macosDmg = { ...distribution.macosDmg };
|
|
@@ -138,99 +112,6 @@ export function registerSettingsRoutes(app, deps) {
|
|
|
138
112
|
res.json({ inheritEnv, total: entries.length, reveal, entries });
|
|
139
113
|
}
|
|
140
114
|
});
|
|
141
|
-
app.post("/api/settings/system-ai/import", requireAdmin, (_req, res) => {
|
|
142
|
-
const imported = discoverCliSystemAiConfigs();
|
|
143
|
-
if (!imported.length) {
|
|
144
|
-
res.status(404).json({ error: "没有在已配置的 CLI 文件中找到可直连的 API 地址、密钥和模型。" });
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const candidate = runtimeConfig.createCandidate();
|
|
148
|
-
const remainingImported = [...imported];
|
|
149
|
-
const existingRoutes = candidate.systemAi
|
|
150
|
-
? [candidate.systemAi, ...(candidate.systemAi.fallbacks ?? [])]
|
|
151
|
-
.map((profile) => ({ ...profile, fallbacks: undefined }))
|
|
152
|
-
: [];
|
|
153
|
-
const refreshedExisting = existingRoutes.map((profile) => {
|
|
154
|
-
const importedIndex = remainingImported.findIndex((item) => systemAiRouteIdentity(item) === systemAiRouteIdentity(profile));
|
|
155
|
-
if (importedIndex < 0)
|
|
156
|
-
return profile;
|
|
157
|
-
const [matchingImport] = remainingImported.splice(importedIndex, 1);
|
|
158
|
-
return {
|
|
159
|
-
...profile,
|
|
160
|
-
apiKey: matchingImport.apiKey,
|
|
161
|
-
source: matchingImport.source,
|
|
162
|
-
};
|
|
163
|
-
});
|
|
164
|
-
const merged = mergeSystemAiConfigs(refreshedExisting, remainingImported);
|
|
165
|
-
if (!merged) {
|
|
166
|
-
res.status(404).json({ error: "没有找到可用的系统 AI API 配置。" });
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
writePreferenceToStorage(candidate, storage, "systemAi", {
|
|
170
|
-
...merged,
|
|
171
|
-
enabled: candidate.systemAi?.enabled === true,
|
|
172
|
-
});
|
|
173
|
-
runtimeConfig.commit(candidate, new Set(["systemAi"]));
|
|
174
|
-
res.json({ ok: true, count: imported.length, systemAi: (publicConfig(candidate).systemAi) });
|
|
175
|
-
});
|
|
176
|
-
app.post("/api/settings/system-ai/test", requireAdmin, asyncRoute(async (req, res) => {
|
|
177
|
-
const raw = req.body?.route;
|
|
178
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
179
|
-
res.status(400).json({ error: "请提供要测试的系统 AI 线路。" });
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
const submitted = raw;
|
|
183
|
-
const desired = runtimeConfig.desiredSnapshot().systemAi;
|
|
184
|
-
const storedRoutes = desired
|
|
185
|
-
? [desired, ...(desired.fallbacks ?? [])].map((profile) => ({ ...profile, fallbacks: undefined }))
|
|
186
|
-
: [];
|
|
187
|
-
const submittedId = typeof submitted.id === "string" ? submitted.id.trim() : "";
|
|
188
|
-
const stored = submittedId
|
|
189
|
-
? storedRoutes.find((profile) => profile.id === submittedId)
|
|
190
|
-
: undefined;
|
|
191
|
-
const apiKey = submitted.clearApiKey === true
|
|
192
|
-
? ""
|
|
193
|
-
: typeof submitted.apiKey === "string" && submitted.apiKey.trim()
|
|
194
|
-
? submitted.apiKey.trim()
|
|
195
|
-
: stored?.apiKey ?? "";
|
|
196
|
-
let route;
|
|
197
|
-
try {
|
|
198
|
-
route = normalizeSystemAiConfig({
|
|
199
|
-
...stored,
|
|
200
|
-
...submitted,
|
|
201
|
-
enabled: true,
|
|
202
|
-
apiKey,
|
|
203
|
-
fallbacks: undefined,
|
|
204
|
-
}, stored);
|
|
205
|
-
if (!route.baseUrl || !route.apiKey || !route.model) {
|
|
206
|
-
throw new Error("测试线路需要完整的 API 地址、API Key 和模型。");
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
catch (error) {
|
|
210
|
-
sendRouteError(res, error, "测试线路配置无效。");
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const startedAt = Date.now();
|
|
214
|
-
try {
|
|
215
|
-
const text = await callSystemAiText({ system: "你是 Wand 的系统 API 线路探针。", prompt: "这是 Wand 系统 API 线路验收。请只回复 WAND_API_OK。" }, route, 30_000);
|
|
216
|
-
if (!text.trim())
|
|
217
|
-
throw new Error("系统 AI API 返回了空结果。");
|
|
218
|
-
res.json({
|
|
219
|
-
ok: true,
|
|
220
|
-
source: route.source ?? "custom",
|
|
221
|
-
requestedModel: route.model,
|
|
222
|
-
reasoningEffort: route.protocol === "openai" ? "low" : "disabled",
|
|
223
|
-
latencyMs: Date.now() - startedAt,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
catch (error) {
|
|
227
|
-
res.status(502).json({
|
|
228
|
-
error: getErrorMessage(error, "系统 AI API 测试失败。"),
|
|
229
|
-
requestedModel: route.model,
|
|
230
|
-
latencyMs: Date.now() - startedAt,
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
}));
|
|
234
115
|
app.get("/api/app-connect-code", requireAdmin, (req, res) => {
|
|
235
116
|
res.json(deps.resolveAppConnectCode(req));
|
|
236
117
|
});
|
|
@@ -244,9 +125,7 @@ export function registerSettingsRoutes(app, deps) {
|
|
|
244
125
|
setPreference(key, value) { stagedPreferences.push({ key, value }); },
|
|
245
126
|
};
|
|
246
127
|
const stagePreference = (field, value) => {
|
|
247
|
-
writePreferenceToStorage(candidateConfig, stagingStorage, field, value
|
|
248
|
-
deferCommitAiValidation: true,
|
|
249
|
-
});
|
|
128
|
+
writePreferenceToStorage(candidateConfig, stagingStorage, field, value);
|
|
250
129
|
stagedPreferenceFields.add(field);
|
|
251
130
|
};
|
|
252
131
|
let touchedDeployField = false;
|
|
@@ -296,66 +175,12 @@ export function registerSettingsRoutes(app, deps) {
|
|
|
296
175
|
if (Object.hasOwn(body.defaultModels, "gemini"))
|
|
297
176
|
stagePreference("defaultGeminiModel", body.defaultModels.gemini);
|
|
298
177
|
}
|
|
299
|
-
if (body.systemAi !== undefined) {
|
|
300
|
-
if (!body.systemAi || typeof body.systemAi !== "object" || Array.isArray(body.systemAi)) {
|
|
301
|
-
throw new Error("systemAi 必须是对象。");
|
|
302
|
-
}
|
|
303
|
-
const previous = candidateConfig.systemAi;
|
|
304
|
-
const systemAiEnabled = body.systemAi.enabled === true;
|
|
305
|
-
const previousRoutes = previous ? [previous, ...(previous.fallbacks ?? [])] : [];
|
|
306
|
-
const previousById = new Map(previousRoutes.flatMap((profile) => profile.id ? [[profile.id, profile]] : []));
|
|
307
|
-
const rawFallbacks = Array.isArray(body.systemAi.fallbacks) ? body.systemAi.fallbacks : [];
|
|
308
|
-
const rawRoutes = [body.systemAi, ...rawFallbacks].map((item) => item && typeof item === "object" && !Array.isArray(item)
|
|
309
|
-
? item
|
|
310
|
-
: {});
|
|
311
|
-
const submittedIds = new Set();
|
|
312
|
-
const normalizedRoutes = rawRoutes.map((raw, index) => {
|
|
313
|
-
const submittedId = typeof raw.id === "string" ? raw.id.trim() : "";
|
|
314
|
-
if (submittedId) {
|
|
315
|
-
if (submittedIds.has(submittedId))
|
|
316
|
-
throw new Error("系统 AI 路由 ID 不能重复。");
|
|
317
|
-
submittedIds.add(submittedId);
|
|
318
|
-
}
|
|
319
|
-
// Older web clients did not submit route IDs. For those clients only,
|
|
320
|
-
// match a unique route by its non-secret identity; never fall back to
|
|
321
|
-
// the array index because routes may have been reordered.
|
|
322
|
-
const identityMatches = submittedId ? [] : previousRoutes.filter((profile) => profile.protocol === (raw.protocol === "anthropic" ? "anthropic" : "openai")
|
|
323
|
-
&& profile.baseUrl === (typeof raw.baseUrl === "string" ? raw.baseUrl.trim().replace(/\/+$/, "") : "")
|
|
324
|
-
&& profile.model === (typeof raw.model === "string" ? raw.model.trim() : "")
|
|
325
|
-
&& (profile.authHeader ?? "bearer") === (raw.authHeader === "x-api-key" ? "x-api-key" : "bearer"));
|
|
326
|
-
const prior = (submittedId ? previousById.get(submittedId) : undefined)
|
|
327
|
-
?? (identityMatches.length === 1 ? identityMatches[0] : undefined);
|
|
328
|
-
const apiKey = raw.clearApiKey === true
|
|
329
|
-
? ""
|
|
330
|
-
: typeof raw.apiKey === "string" && raw.apiKey.trim()
|
|
331
|
-
? raw.apiKey.trim()
|
|
332
|
-
: prior?.apiKey ?? "";
|
|
333
|
-
return normalizeSystemAiConfig({
|
|
334
|
-
...prior,
|
|
335
|
-
...raw,
|
|
336
|
-
enabled: index === 0 ? systemAiEnabled : true,
|
|
337
|
-
apiKey,
|
|
338
|
-
fallbacks: undefined,
|
|
339
|
-
}, prior);
|
|
340
|
-
});
|
|
341
|
-
const [primary, ...fallbacks] = normalizedRoutes;
|
|
342
|
-
if (!primary)
|
|
343
|
-
throw new Error("系统 AI 至少需要一个路由占位。");
|
|
344
|
-
stagePreference("systemAi", {
|
|
345
|
-
...primary,
|
|
346
|
-
enabled: systemAiEnabled,
|
|
347
|
-
...(fallbacks.length ? { fallbacks } : { fallbacks: [] }),
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
178
|
for (const field of PREFERENCE_KEYS) {
|
|
351
|
-
if (field === "systemAi")
|
|
352
|
-
continue;
|
|
353
179
|
const value = body[field];
|
|
354
180
|
if (!(field in body) || value === undefined)
|
|
355
181
|
continue;
|
|
356
182
|
stagePreference(field, value);
|
|
357
183
|
}
|
|
358
|
-
validateCommitAiConfig(candidateConfig);
|
|
359
184
|
}
|
|
360
185
|
catch (error) {
|
|
361
186
|
sendRouteError(res, error, "配置校验失败。");
|
|
@@ -308,7 +308,7 @@ function taskTitleAiOptions(config, storage) {
|
|
|
308
308
|
selectedModel: null,
|
|
309
309
|
thinkingEffort: config.defaultThinkingEffort,
|
|
310
310
|
};
|
|
311
|
-
// resolveSystemAiContext
|
|
311
|
+
// resolveSystemAiContext 按系统运维员工的 CLI 候选链解析(首个已安装的优先)。
|
|
312
312
|
return resolveSystemAiContext(defaultSession, config, storage?.getSystemSiliconEmployee() ?? null);
|
|
313
313
|
}
|
|
314
314
|
export function registerTaskRoutes(app, deps) {
|
|
@@ -33,7 +33,7 @@ function workspaceSessionSummary(session, names = {}, registry, team = {}) {
|
|
|
33
33
|
sessionKind: live.sessionKind,
|
|
34
34
|
runner: live.runner,
|
|
35
35
|
command: live.command,
|
|
36
|
-
title: workspaceSessionTitle(live, names),
|
|
36
|
+
title: team.chat?.chatTitle ?? workspaceSessionTitle(live, names),
|
|
37
37
|
status: live.status,
|
|
38
38
|
ptyBusy: live.ptyBusy === true,
|
|
39
39
|
providerCliActive: live.providerCliActive,
|
package/dist/server.js
CHANGED
|
@@ -11,7 +11,6 @@ import process from "node:process";
|
|
|
11
11
|
import { WebSocketServer } from "ws";
|
|
12
12
|
import { AuthService, BROWSER_ADMIN_PRINCIPAL, CONNECTED_APP_PRINCIPAL, principalHasScope, readSessionCookie, SESSION_COOKIE_HTTP, SESSION_COOKIE_HTTPS, SESSION_COOKIE_LEGACY, } from "./auth.js";
|
|
13
13
|
import { ensureCertificates } from "./cert.js";
|
|
14
|
-
import { listClaudeSkills } from "./claude-skills.js";
|
|
15
14
|
import { getDefaultModelForProvider, getProviderDefaultModels, isExecutionMode, resolveConfigDir, } from "./config.js";
|
|
16
15
|
import { ModelCatalogService } from "./models.js";
|
|
17
16
|
import { ProcessManager } from "./process-manager.js";
|
|
@@ -346,7 +345,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
346
345
|
const legacyStructuredHost = ptyHosts.legacyHost
|
|
347
346
|
?? (terminalHost instanceof TerminalDaemonClient ? terminalHost : null);
|
|
348
347
|
const structuredHosts = await createUpgradeAwareStructuredHost(configPath, legacyStructuredHost, config.structured?.processHost);
|
|
349
|
-
const structuredSessions = new StructuredSessionManager(storage, config, structuredLogger,
|
|
348
|
+
const structuredSessions = new StructuredSessionManager(storage, config, structuredLogger, {}, structuredHosts.host);
|
|
350
349
|
const sessionRegistry = new SessionRegistry(processes, structuredSessions, storage);
|
|
351
350
|
const missions = new Missions(storage, structuredSessions, sessionRegistry);
|
|
352
351
|
// wsManager 在后面才建:团队运行的变更通知经由这个转发口,接好之前静默丢弃。
|
|
@@ -598,7 +597,6 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
598
597
|
"/api/session-directories",
|
|
599
598
|
"/api/structured-sessions",
|
|
600
599
|
"/api/commands",
|
|
601
|
-
"/api/claude-skills",
|
|
602
600
|
"/api/claude-history",
|
|
603
601
|
"/api/codex-history",
|
|
604
602
|
"/api/opencode-history",
|
|
@@ -656,7 +654,6 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
656
654
|
defaultModels,
|
|
657
655
|
defaultThinkingEffort: config.defaultThinkingEffort ?? "off",
|
|
658
656
|
commandPresets: config.commandPresets,
|
|
659
|
-
structuredRunner: config.structuredRunner ?? "cli",
|
|
660
657
|
structuredRunners: [
|
|
661
658
|
{ label: "Claude Structured", runner: "claude-cli-print" },
|
|
662
659
|
{ label: "Codex Structured", runner: "codex-cli-exec" },
|
|
@@ -679,15 +676,6 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
679
676
|
});
|
|
680
677
|
}));
|
|
681
678
|
// ── Claude skills & browser extension vault ──
|
|
682
|
-
app.get("/api/claude-skills", (req, res) => {
|
|
683
|
-
try {
|
|
684
|
-
const cwd = resolveSessionCwd(typeof req.query.cwd === "string" ? req.query.cwd : undefined, config.defaultCwd);
|
|
685
|
-
res.json({ skills: listClaudeSkills(cwd) });
|
|
686
|
-
}
|
|
687
|
-
catch (error) {
|
|
688
|
-
sendRouteError(res, error, "无法读取 skills。");
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
679
|
registerVaultRoutes(app, { storage, config, useHttps });
|
|
692
680
|
// ── Settings endpoints ──
|
|
693
681
|
const getDistributionSettings = () => distributionManager.getSettings();
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { discoverCliSystemAiConfigs } from "./system-ai.js";
|
|
2
1
|
import type { SiliconEmployee } from "./ai-team-types.js";
|
|
3
|
-
import type { SessionProvider, SessionSnapshot,
|
|
2
|
+
import type { AiCliCandidate, SessionProvider, SessionSnapshot, WandConfig } from "./types.js";
|
|
4
3
|
export interface SessionAiContext {
|
|
5
4
|
provider: SessionProvider;
|
|
6
5
|
model?: string;
|
|
7
6
|
thinkingEffort: SessionSnapshot["thinkingEffort"];
|
|
8
7
|
inheritEnv?: boolean;
|
|
9
|
-
systemAi?: SystemAiConfig;
|
|
10
8
|
/** 内置「系统运维」员工的人设;作为系统提示前缀注入 Wand 自有 AI 调用。 */
|
|
11
9
|
opsPersona?: string;
|
|
12
10
|
/** CLI 降级链(按顺序)。为空时只用 provider/model 这一次调用。 */
|
|
13
|
-
cliCandidates?:
|
|
11
|
+
cliCandidates?: AiCliCandidate[];
|
|
14
12
|
}
|
|
15
13
|
/**
|
|
16
14
|
* Resolve the provider from every representation used by current and legacy
|
|
@@ -21,6 +19,4 @@ export declare function resolveSessionProvider(snapshot: Pick<SessionSnapshot, "
|
|
|
21
19
|
/** Build the provider-specific settings used by session-adjacent AI actions. */
|
|
22
20
|
export declare function resolveSessionAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultOpenCodeModel" | "defaultGrokModel" | "defaultQoderModel" | "defaultPiModel" | "defaultThinkingEffort" | "inheritEnv">): SessionAiContext;
|
|
23
21
|
/** Build the source order for Wand-owned AI features such as titles. */
|
|
24
|
-
export declare function resolveSystemAiContext(snapshot: Parameters<typeof resolveSessionAiContext>[0], config: Parameters<typeof resolveSessionAiContext>[1] & Pick<WandConfig, "
|
|
25
|
-
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
26
|
-
export declare function resolveCommitAiContext(snapshot: Pick<SessionSnapshot, "provider" | "structuredState" | "runner" | "command" | "selectedModel" | "thinkingEffort">, config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultOpenCodeModel" | "defaultGrokModel" | "defaultQoderModel" | "defaultPiModel" | "defaultThinkingEffort" | "inheritEnv" | "commitAiSource" | "systemAi" | "systemAiCli" | "systemAiModel">, discoverApis?: typeof discoverCliSystemAiConfigs, systemEmployee?: SiliconEmployee | null): SessionAiContext;
|
|
22
|
+
export declare function resolveSystemAiContext(snapshot: Parameters<typeof resolveSessionAiContext>[0], config: Parameters<typeof resolveSessionAiContext>[1] & Pick<WandConfig, "systemAiCli" | "systemAiModel">, systemEmployee?: SiliconEmployee | null): SessionAiContext;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getDefaultModelForProvider } from "./config.js";
|
|
2
|
-
import { discoverCliSystemAiConfigs, mergeSystemAiConfigs, systemAiProfiles, } from "./system-ai.js";
|
|
3
2
|
import { providerCliInstalled, isSessionProvider } from "./session-provider.js";
|
|
4
3
|
import { isSystemSiliconEmployee, systemEmployeeCliCandidates } from "./system-employee.js";
|
|
5
4
|
import { inferProviderFromCommand, inferProviderFromRunner } from "./session-provider.js";
|
|
@@ -21,11 +20,6 @@ function normalizeModel(value) {
|
|
|
21
20
|
const model = value?.trim();
|
|
22
21
|
return model && model !== "default" ? model : undefined;
|
|
23
22
|
}
|
|
24
|
-
function usableSystemAi(config) {
|
|
25
|
-
if (!systemAiProfiles(config, true).length)
|
|
26
|
-
return undefined;
|
|
27
|
-
return { ...config, enabled: true };
|
|
28
|
-
}
|
|
29
23
|
/** Build the provider-specific settings used by session-adjacent AI actions. */
|
|
30
24
|
export function resolveSessionAiContext(snapshot, config) {
|
|
31
25
|
const provider = resolveSessionProvider(snapshot);
|
|
@@ -60,10 +54,6 @@ export function resolveSystemAiContext(snapshot, config, systemEmployee) {
|
|
|
60
54
|
owned.model = preferred.model ?? normalizeModel(getDefaultModelForProvider(config, preferred.provider));
|
|
61
55
|
owned.thinkingEffort = preferred.thinkingEffort ?? config.defaultThinkingEffort;
|
|
62
56
|
}
|
|
63
|
-
if (config.systemAi?.enabled) {
|
|
64
|
-
const directApi = usableSystemAi(config.systemAi);
|
|
65
|
-
return directApi ? { ...owned, systemAi: directApi } : owned;
|
|
66
|
-
}
|
|
67
57
|
if (chain.length)
|
|
68
58
|
return owned;
|
|
69
59
|
// Older installations without a system AI CLI preference still follow the
|
|
@@ -78,18 +68,3 @@ export function resolveSystemAiContext(snapshot, config, systemEmployee) {
|
|
|
78
68
|
thinkingEffort: config.defaultThinkingEffort,
|
|
79
69
|
};
|
|
80
70
|
}
|
|
81
|
-
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
82
|
-
export function resolveCommitAiContext(snapshot, config, discoverApis = discoverCliSystemAiConfigs, systemEmployee) {
|
|
83
|
-
const sessionContext = resolveSystemAiContext(snapshot, config, systemEmployee);
|
|
84
|
-
if (config.commitAiSource !== "api") {
|
|
85
|
-
// 快捷提交的直连来源只看 commitAiSource;系统 AI 的 enabled 不能顺手把它打开。
|
|
86
|
-
const cliContext = { ...sessionContext };
|
|
87
|
-
delete cliContext.systemAi;
|
|
88
|
-
return cliContext;
|
|
89
|
-
}
|
|
90
|
-
const directApi = mergeSystemAiConfigs(config.systemAi, discoverApis(sessionContext.provider));
|
|
91
|
-
return {
|
|
92
|
-
...sessionContext,
|
|
93
|
-
...(directApi ? { systemAi: directApi } : {}),
|
|
94
|
-
};
|
|
95
|
-
}
|
package/dist/session-topic.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export declare function consumePtyInputForTopic(buffer: PtyTopicLineBuffer, inpu
|
|
|
20
20
|
export interface SessionTopic {
|
|
21
21
|
title: string;
|
|
22
22
|
description: string;
|
|
23
|
+
/** native = CLI 自己生成的标题;缺省表示由系统硅基员工总结出来的候选。 */
|
|
24
|
+
source?: "native";
|
|
23
25
|
}
|
|
24
26
|
export interface SessionTopicRequest {
|
|
25
27
|
messages?: readonly ConversationTurn[];
|
|
@@ -27,6 +29,10 @@ export interface SessionTopicRequest {
|
|
|
27
29
|
cwd?: string;
|
|
28
30
|
language?: string;
|
|
29
31
|
ai?: QuickCommitAiOptions;
|
|
32
|
+
/** provider 自己在运行期写下的标题;命中即优先使用,不再调用模型。 */
|
|
33
|
+
readNativeTitle?: () => Promise<string>;
|
|
34
|
+
/** 已确认过原生标题:本轮没有新名字时不要让模型覆盖它。 */
|
|
35
|
+
hasNativeTitle?: boolean;
|
|
30
36
|
onGenerating(generating: boolean): void;
|
|
31
37
|
onTopic(topic: SessionTopic): void;
|
|
32
38
|
onError(error: unknown): void;
|
|
@@ -68,6 +74,18 @@ export declare function generateSessionTopic(userMessages: readonly string[], cw
|
|
|
68
74
|
* in-flight result and is summarized together with every earlier user turn.
|
|
69
75
|
* Short inputs (single commands, choices) are dropped before any model call.
|
|
70
76
|
*/
|
|
77
|
+
/**
|
|
78
|
+
* 记住哪些会话的标题来自 provider 原生 CLI。原生标题优先于模型标题:
|
|
79
|
+
* 一旦记下,后续输入不再发起模型总结;provider 改名时直接覆盖。
|
|
80
|
+
*/
|
|
81
|
+
export declare class SessionNativeTitleTracker {
|
|
82
|
+
private readonly titles;
|
|
83
|
+
record(sessionId: string, title: string): void;
|
|
84
|
+
get(sessionId: string): string;
|
|
85
|
+
has(sessionId: string): boolean;
|
|
86
|
+
delete(sessionId: string): void;
|
|
87
|
+
clear(): void;
|
|
88
|
+
}
|
|
71
89
|
export declare class SessionTopicCoordinator {
|
|
72
90
|
private readonly generate;
|
|
73
91
|
private readonly states;
|
|
@@ -76,4 +94,5 @@ export declare class SessionTopicCoordinator {
|
|
|
76
94
|
request(sessionId: string, request: SessionTopicRequest): void;
|
|
77
95
|
clear(): void;
|
|
78
96
|
private run;
|
|
97
|
+
private readNativeTitle;
|
|
79
98
|
}
|
package/dist/session-topic.js
CHANGED
|
@@ -268,6 +268,30 @@ export async function generateSessionTopic(userMessages, cwd, language, ai = {})
|
|
|
268
268
|
* in-flight result and is summarized together with every earlier user turn.
|
|
269
269
|
* Short inputs (single commands, choices) are dropped before any model call.
|
|
270
270
|
*/
|
|
271
|
+
/**
|
|
272
|
+
* 记住哪些会话的标题来自 provider 原生 CLI。原生标题优先于模型标题:
|
|
273
|
+
* 一旦记下,后续输入不再发起模型总结;provider 改名时直接覆盖。
|
|
274
|
+
*/
|
|
275
|
+
export class SessionNativeTitleTracker {
|
|
276
|
+
titles = new Map();
|
|
277
|
+
record(sessionId, title) {
|
|
278
|
+
const cleaned = title.replace(/\s+/g, " ").trim();
|
|
279
|
+
if (cleaned)
|
|
280
|
+
this.titles.set(sessionId, cleaned);
|
|
281
|
+
}
|
|
282
|
+
get(sessionId) {
|
|
283
|
+
return this.titles.get(sessionId) ?? "";
|
|
284
|
+
}
|
|
285
|
+
has(sessionId) {
|
|
286
|
+
return this.titles.has(sessionId);
|
|
287
|
+
}
|
|
288
|
+
delete(sessionId) {
|
|
289
|
+
this.titles.delete(sessionId);
|
|
290
|
+
}
|
|
291
|
+
clear() {
|
|
292
|
+
this.titles.clear();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
271
295
|
export class SessionTopicCoordinator {
|
|
272
296
|
generate;
|
|
273
297
|
states = new Map();
|
|
@@ -311,9 +335,24 @@ export class SessionTopicCoordinator {
|
|
|
311
335
|
const request = state.request;
|
|
312
336
|
const userMessages = state.userMessages.slice();
|
|
313
337
|
try {
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
338
|
+
const pendingNativeTitle = this.readNativeTitle(request);
|
|
339
|
+
// 没有原生标题源时保持同步短路,不给普通会话增加一次等待。
|
|
340
|
+
const nativeTitle = typeof pendingNativeTitle === "string"
|
|
341
|
+
? pendingNativeTitle
|
|
342
|
+
: pendingNativeTitle ? await pendingNativeTitle : "";
|
|
343
|
+
if (nativeTitle) {
|
|
344
|
+
if (!this.disposed && this.states.get(sessionId) === state && state.revision === revision) {
|
|
345
|
+
request.onTopic({ title: nativeTitle, description: nativeTitle, source: "native" });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
else if (request.hasNativeTitle) {
|
|
349
|
+
// CLI 这次没读到名字(锁库、改名中),也不能让模型盖掉已确认的原生标题。
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
const topic = await this.generate(userMessages, request.cwd, request.language, request.ai);
|
|
353
|
+
if (!this.disposed && this.states.get(sessionId) === state && state.revision === revision) {
|
|
354
|
+
state.request.onTopic(topic);
|
|
355
|
+
}
|
|
317
356
|
}
|
|
318
357
|
}
|
|
319
358
|
catch (error) {
|
|
@@ -330,4 +369,15 @@ export class SessionTopicCoordinator {
|
|
|
330
369
|
state.running = false;
|
|
331
370
|
state.request.onGenerating(false);
|
|
332
371
|
}
|
|
372
|
+
readNativeTitle(request) {
|
|
373
|
+
if (!request.readNativeTitle)
|
|
374
|
+
return null;
|
|
375
|
+
try {
|
|
376
|
+
// 读取失败按「没有原生标题」处理,回退系统硅基员工。
|
|
377
|
+
return request.readNativeTitle().catch(() => "");
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return Promise.resolve("");
|
|
381
|
+
}
|
|
382
|
+
}
|
|
333
383
|
}
|
|
@@ -58,7 +58,6 @@ function sessionBase(snapshot) {
|
|
|
58
58
|
claudeSessionId: snapshot.claudeSessionId,
|
|
59
59
|
providerSessionId: snapshot.claudeSessionId,
|
|
60
60
|
queuedMessages: snapshot.queuedMessages,
|
|
61
|
-
queuedMessageSkills: snapshot.queuedMessageSkills,
|
|
62
61
|
structuredState: snapshot.structuredState,
|
|
63
62
|
resumedFromSessionId: snapshot.resumedFromSessionId,
|
|
64
63
|
autoRecovered: snapshot.autoRecovered,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type QuickCommitAiOptions } from "./git-quick-commit.js";
|
|
2
2
|
import type { SiliconEmployeeDraft } from "./ai-team-types.js";
|
|
3
|
-
import type { AiTextRequest } from "./
|
|
3
|
+
import type { AiTextRequest } from "./types.js";
|
|
4
4
|
import type { WandTaskAgent } from "./task-types.js";
|
|
5
5
|
import type { SessionProvider } from "./types.js";
|
|
6
6
|
export declare class SiliconEmployeeDraftError extends Error {
|
package/dist/storage.js
CHANGED
|
@@ -8,7 +8,7 @@ import { inferProviderFromCommand, inferProviderFromRunner, isSessionProvider, S
|
|
|
8
8
|
import { DEFAULT_ITERATION_NAME, DEFAULT_WAND_TASK_AGENT_KIND, DEFAULT_WAND_TASK_PRIORITY, isWandTaskAgentKind, normalizeWandTaskAgentMode } from "./task-types.js";
|
|
9
9
|
import { firstLayoutTabId } from "./layout-tree.js";
|
|
10
10
|
import { isUnnamedWorkspaceTaskName } from "./wand-task-sync.js";
|
|
11
|
-
import { AI_TEAM_DEFAULT_MAX_STEPS, AI_TEAM_TERMINAL_RUN_STATUSES, isTeamMemberRole, memberAgents } from "./ai-team-types.js";
|
|
11
|
+
import { AI_TEAM_DEFAULT_MAX_STEPS, AI_TEAM_TERMINAL_RUN_STATUSES, aiTeamChatTitle, isTeamMemberRole, memberAgents } from "./ai-team-types.js";
|
|
12
12
|
import { isTaskExecutionSubject } from "./task-types.js";
|
|
13
13
|
import { SYSTEM_EMPLOYEE_KEY, systemEmployeeDefinition, systemEmployeeSeedAgents, } from "./system-employee.js";
|
|
14
14
|
import { isThinkingEffort } from "./structured-provider-common.js";
|
|
@@ -267,17 +267,6 @@ function parseQueuedMessages(raw) {
|
|
|
267
267
|
const parsed = safeJsonParse(raw);
|
|
268
268
|
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : undefined;
|
|
269
269
|
}
|
|
270
|
-
function parseQueuedMessageSkills(raw, queueLength) {
|
|
271
|
-
const parsed = safeJsonParse(raw);
|
|
272
|
-
if (!Array.isArray(parsed))
|
|
273
|
-
return undefined;
|
|
274
|
-
return Array.from({ length: queueLength }, (_, index) => {
|
|
275
|
-
const skills = parsed[index];
|
|
276
|
-
return Array.isArray(skills)
|
|
277
|
-
? skills.filter((skill) => typeof skill === "string")
|
|
278
|
-
: [];
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
270
|
function parseWorktreeInfo(raw) {
|
|
282
271
|
const parsed = safeJsonParse(raw);
|
|
283
272
|
if (parsed && typeof parsed.branch === "string" && typeof parsed.path === "string") {
|
|
@@ -391,7 +380,6 @@ function sessionPersistAssignments() {
|
|
|
391
380
|
runner = excluded.runner,
|
|
392
381
|
messages = excluded.messages,
|
|
393
382
|
queued_messages = excluded.queued_messages,
|
|
394
|
-
queued_message_skills = excluded.queued_message_skills,
|
|
395
383
|
structured_state = excluded.structured_state,
|
|
396
384
|
resumed_from_session_id = excluded.resumed_from_session_id,
|
|
397
385
|
auto_recovered = excluded.auto_recovered,
|
|
@@ -410,7 +398,7 @@ function sessionRuntimeMetadataAssignments() {
|
|
|
410
398
|
command = ?, cwd = ?, mode = ?, status = ?, exit_code = ?,
|
|
411
399
|
started_at = ?, ended_at = ?,
|
|
412
400
|
archived = ?, archived_at = ?, claude_session_id = ?,
|
|
413
|
-
provider = ?, session_kind = ?, runner = ?, queued_messages = ?,
|
|
401
|
+
provider = ?, session_kind = ?, runner = ?, queued_messages = ?, structured_state = ?,
|
|
414
402
|
resumed_from_session_id = ?, auto_recovered = ?,
|
|
415
403
|
worktree_enabled = ?, worktree_info = ?, worktree_merge_status = ?, worktree_merge_info = ?,
|
|
416
404
|
title = ?, description = ?, session_options = ?`;
|
|
@@ -437,7 +425,8 @@ function sessionPersistValues(snapshot) {
|
|
|
437
425
|
snapshot.runner ?? null,
|
|
438
426
|
snapshot.messages ? JSON.stringify(snapshot.messages) : null,
|
|
439
427
|
snapshot.queuedMessages ? JSON.stringify(snapshot.queuedMessages) : null,
|
|
440
|
-
|
|
428
|
+
// queued_message_skills 列按「只加不删」保留,但 SDK skills 功能已移除,不再写入。
|
|
429
|
+
null,
|
|
441
430
|
snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null,
|
|
442
431
|
snapshot.resumedFromSessionId ?? null,
|
|
443
432
|
snapshot.autoRecovered ? 1 : 0,
|
|
@@ -470,7 +459,6 @@ function sessionRuntimeMetadataValues(snapshot) {
|
|
|
470
459
|
snapshot.sessionKind ?? "pty",
|
|
471
460
|
snapshot.runner ?? null,
|
|
472
461
|
snapshot.queuedMessages ? JSON.stringify(snapshot.queuedMessages) : null,
|
|
473
|
-
snapshot.queuedMessageSkills ? JSON.stringify(snapshot.queuedMessageSkills) : null,
|
|
474
462
|
snapshot.structuredState ? JSON.stringify(snapshot.structuredState) : null,
|
|
475
463
|
snapshot.resumedFromSessionId ?? null,
|
|
476
464
|
snapshot.autoRecovered ? 1 : 0,
|
|
@@ -511,7 +499,6 @@ function mapSessionCore(row) {
|
|
|
511
499
|
claudeSessionId: row.claude_session_id,
|
|
512
500
|
messages: safeJsonParse(row.messages),
|
|
513
501
|
queuedMessages,
|
|
514
|
-
queuedMessageSkills: parseQueuedMessageSkills(row.queued_message_skills, queuedMessages?.length ?? 0),
|
|
515
502
|
structuredState: safeJsonParse(row.structured_state),
|
|
516
503
|
resumedFromSessionId: row.resumed_from_session_id ?? undefined,
|
|
517
504
|
autoRecovered: Boolean(row.auto_recovered),
|
|
@@ -2430,8 +2417,14 @@ export class WandStorage {
|
|
|
2430
2417
|
* 一次查询建整张表,供 /api/tasks 每请求一次构建;不逐会话查询。
|
|
2431
2418
|
*/
|
|
2432
2419
|
listAiTeamRunChatMarkers() {
|
|
2433
|
-
const rows = this.db.prepare(`SELECT r.id, r.team_id, r.team_json, r.chat_session_id, t.name AS current_team_name
|
|
2420
|
+
const rows = this.db.prepare(`SELECT r.id, r.team_id, r.team_json, r.chat_session_id, t.name AS current_team_name,
|
|
2421
|
+
task.title AS task_title
|
|
2434
2422
|
FROM ai_team_runs r LEFT JOIN ai_teams t ON t.id = r.team_id
|
|
2423
|
+
LEFT JOIN command_sessions s ON s.id = r.chat_session_id
|
|
2424
|
+
LEFT JOIN wand_tasks task ON task.id = COALESCE((
|
|
2425
|
+
SELECT id FROM wand_tasks WHERE workspace_task_id = s.workspace_task_id
|
|
2426
|
+
ORDER BY updated_at DESC, rowid DESC LIMIT 1
|
|
2427
|
+
), r.task_id)
|
|
2435
2428
|
WHERE r.chat_session_id IS NOT NULL AND r.chat_session_id <> '' ORDER BY r.created_at DESC`).all();
|
|
2436
2429
|
const markers = new Map();
|
|
2437
2430
|
for (const row of rows) {
|
|
@@ -2443,6 +2436,7 @@ export class WandStorage {
|
|
|
2443
2436
|
runId: String(row.id),
|
|
2444
2437
|
teamId: String(row.team_id),
|
|
2445
2438
|
teamName: typeof row.current_team_name === "string" ? row.current_team_name : String(rawTeam?.name ?? ""),
|
|
2439
|
+
chatTitle: aiTeamChatTitle(typeof row.task_title === "string" ? row.task_title : ""),
|
|
2446
2440
|
memberCount: Array.isArray(rawTeam?.members) ? rawTeam.members.length : 0,
|
|
2447
2441
|
});
|
|
2448
2442
|
}
|
|
@@ -18,12 +18,6 @@ export interface ClaudeCliArgsOptions {
|
|
|
18
18
|
systemPromptParts?: string[];
|
|
19
19
|
}
|
|
20
20
|
export declare function buildClaudeCliArgs(session: SessionSnapshot, options: ClaudeCliArgsOptions): string[];
|
|
21
|
-
export declare function buildClaudeSdkThinking(effort: SessionSnapshot["thinkingEffort"]): {
|
|
22
|
-
type: "enabled";
|
|
23
|
-
budgetTokens: number;
|
|
24
|
-
} | {
|
|
25
|
-
type: "disabled";
|
|
26
|
-
};
|
|
27
21
|
export interface ClaudeCliRunnerOptions {
|
|
28
22
|
language?: () => string | undefined;
|
|
29
23
|
}
|
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { isRunningAsRoot } from "./env-utils.js";
|
|
5
5
|
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
6
6
|
import { buildLanguageDirective, buildManagedAutonomyDirective } from "./language-prompt.js";
|
|
7
|
-
import { thinkingEffortToClaudeCliEffort
|
|
7
|
+
import { thinkingEffortToClaudeCliEffort } from "./structured-provider-common.js";
|
|
8
8
|
import { ClaudeCliProtocolReducer } from "./structured-claude-protocol.js";
|
|
9
9
|
const ROOT_FALLBACK_ALLOWED_TOOLS = [
|
|
10
10
|
"Bash", "Edit", "Write", "Read", "Glob", "Grep", "NotebookEdit", "WebFetch", "WebSearch",
|
|
@@ -124,10 +124,6 @@ export function buildClaudeCliArgs(session, options) {
|
|
|
124
124
|
args.push("--resume", session.claudeSessionId);
|
|
125
125
|
return args;
|
|
126
126
|
}
|
|
127
|
-
export function buildClaudeSdkThinking(effort) {
|
|
128
|
-
const budgetTokens = thinkingEffortToSdkBudget(effort);
|
|
129
|
-
return budgetTokens > 0 ? { type: "enabled", budgetTokens } : { type: "disabled" };
|
|
130
|
-
}
|
|
131
127
|
/** Owns the Claude print-mode process and its stream-json protocol. */
|
|
132
128
|
export class ClaudeCliRunner {
|
|
133
129
|
options;
|