@co0ontty/wand 4.79.0 → 4.80.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/README.md +18 -6
- package/dist/agent-dispatch.d.ts +3 -0
- package/dist/agent-dispatch.js +5 -0
- package/dist/ai-team-runner.js +4 -2
- package/dist/ai-team-types.d.ts +34 -0
- package/dist/ai-team-types.js +12 -1
- package/dist/build-info.json +3 -3
- package/dist/config.d.ts +4 -3
- package/dist/config.js +25 -0
- package/dist/git-quick-commit.d.ts +9 -8
- package/dist/git-quick-commit.js +123 -8
- package/dist/models.d.ts +3 -0
- package/dist/models.js +24 -2
- package/dist/process-manager.js +16 -3
- package/dist/provider-cli-updater.d.ts +1 -1
- package/dist/provider-cli-updater.js +12 -1
- package/dist/server-ai-team-routes.js +4 -1
- package/dist/server-employee-routes.d.ts +22 -0
- package/dist/server-employee-routes.js +277 -0
- package/dist/server-session-routes.js +38 -13
- package/dist/server-settings-routes.js +5 -0
- package/dist/server-task-routes.d.ts +2 -0
- package/dist/server-task-routes.js +174 -22
- package/dist/server-update-routes.js +1 -1
- package/dist/server-workspace-routes.js +3 -0
- package/dist/server.js +16 -3
- package/dist/session-ai-context.d.ts +7 -2
- package/dist/session-ai-context.js +36 -9
- package/dist/session-provider.d.ts +6 -1
- package/dist/session-provider.js +27 -1
- package/dist/session-transport.js +4 -0
- package/dist/silicon-employee-dispatch.d.ts +9 -0
- package/dist/silicon-employee-dispatch.js +13 -0
- package/dist/silicon-employee-draft.d.ts +36 -0
- package/dist/silicon-employee-draft.js +146 -0
- package/dist/storage.d.ts +19 -2
- package/dist/storage.js +158 -7
- package/dist/structured-gemini-adapter.d.ts +40 -0
- package/dist/structured-gemini-adapter.js +198 -0
- package/dist/structured-provider-common.d.ts +2 -1
- package/dist/structured-provider-common.js +7 -2
- package/dist/structured-session-manager.d.ts +10 -0
- package/dist/structured-session-manager.js +106 -14
- package/dist/system-ai.js +1 -1
- package/dist/system-employee.d.ts +35 -0
- package/dist/system-employee.js +81 -0
- package/dist/task-types.d.ts +8 -1
- package/dist/task-types.js +7 -0
- package/dist/types.d.ts +23 -4
- package/dist/web-ui/content/ai-teams.js +13 -13
- package/dist/web-ui/content/scripts.js +64 -64
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/content/tailwind.css +3 -0
- package/dist/web-ui/embedded-assets.js +3 -3
- package/dist/web-ui/provider-identity.d.ts +2 -1
- package/dist/web-ui/provider-identity.js +10 -1
- package/dist/web-ui/session-activity.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dispatchAgentForTask } from "./agent-dispatch.js";
|
|
1
|
+
import { dispatchAgentForTask, resolveTaskDispatchTarget } from "./agent-dispatch.js";
|
|
2
2
|
import { asyncRoute } from "./express-async.js";
|
|
3
3
|
import { getErrorMessage } from "./error-utils.js";
|
|
4
4
|
import { bodyObject, sendRouteError, text } from "./server-request.js";
|
|
@@ -7,6 +7,8 @@ import { defaultMilestoneIdForWrite, resolvedMilestoneFields, scopedMilestoneId
|
|
|
7
7
|
import { generateWandTaskTitle, provisionalTaskTitleFromDescription, TASK_TITLE_MAX_LENGTH } from "./task-title.js";
|
|
8
8
|
import { recordIterationPromptForTask } from "./iteration-log.js";
|
|
9
9
|
import { resolveSystemAiContext } from "./session-ai-context.js";
|
|
10
|
+
import { memberAgents } from "./ai-team-types.js";
|
|
11
|
+
import { selectEmployeeCandidate } from "./silicon-employee-dispatch.js";
|
|
10
12
|
import { DEFAULT_WAND_TASK_AGENT_KIND, DEFAULT_WAND_TASK_AGENT_MODE, DEFAULT_WAND_TASK_PRIORITY, isWandTaskAgentKind, isWandTaskAgentMode, normalizeWandTaskAgentMode, WAND_MILESTONE_NAME_MAX_LENGTH } from "./task-types.js";
|
|
11
13
|
import { isAutoNameableBoardTask, taskAutoNameSignature, taskAutoNameSourceText } from "./wand-task-sync.js";
|
|
12
14
|
import { isSessionProvider } from "./session-provider.js";
|
|
@@ -38,6 +40,48 @@ export function writeTaskBoardLastAgent(storage, agent) {
|
|
|
38
40
|
kind: agent.kind,
|
|
39
41
|
});
|
|
40
42
|
}
|
|
43
|
+
function parseExecutionSubject(storage, value) {
|
|
44
|
+
if (value === null)
|
|
45
|
+
return null;
|
|
46
|
+
const body = bodyObject(value);
|
|
47
|
+
const type = text(body.type);
|
|
48
|
+
const id = text(body.id);
|
|
49
|
+
if (!id)
|
|
50
|
+
throw new Error("请选择执行对象。");
|
|
51
|
+
if (type === "employee") {
|
|
52
|
+
const employee = storage.getSiliconEmployee(id);
|
|
53
|
+
if (!employee || employee.archivedAt)
|
|
54
|
+
throw new Error("硅基员工不存在或已归档。");
|
|
55
|
+
return { type, id };
|
|
56
|
+
}
|
|
57
|
+
if (type === "team") {
|
|
58
|
+
if (!storage.getAiTeam(id))
|
|
59
|
+
throw new Error("AI 团队不存在。");
|
|
60
|
+
return { type, id };
|
|
61
|
+
}
|
|
62
|
+
if (type === "cli") {
|
|
63
|
+
if (!isSessionProvider(id))
|
|
64
|
+
throw new Error("请选择有效的 CLI 工具。");
|
|
65
|
+
return { type, id };
|
|
66
|
+
}
|
|
67
|
+
throw new Error("执行对象类型无效。");
|
|
68
|
+
}
|
|
69
|
+
function preferredAgentForSubject(storage, subject) {
|
|
70
|
+
if (subject.type === "employee")
|
|
71
|
+
return storage.getSiliconEmployee(subject.id)?.agents[0] ?? null;
|
|
72
|
+
if (subject.type === "team") {
|
|
73
|
+
const team = storage.getAiTeam(subject.id);
|
|
74
|
+
const member = team?.members.find((entry) => entry.isLeader) ?? team?.members[0];
|
|
75
|
+
return member ? memberAgents(member)[0] ?? null : null;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function cliAgentForSubject(subject, current) {
|
|
80
|
+
return parseTaskAgent({
|
|
81
|
+
...(current?.provider === subject.id ? current : defaultTaskBoardAgent()),
|
|
82
|
+
provider: subject.id,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
41
85
|
function dateValue(value) {
|
|
42
86
|
if (value === undefined)
|
|
43
87
|
return undefined;
|
|
@@ -178,6 +222,9 @@ function taskSessionSummary(session) {
|
|
|
178
222
|
thinkingEffort: session.thinkingEffort || "off",
|
|
179
223
|
// 会话实际跑的执行模式;旧会话没有该字段时留空,前端回落到任务上的配置。
|
|
180
224
|
mode: session.mode || "",
|
|
225
|
+
employeeId: session.employeeId,
|
|
226
|
+
employeeName: session.employeeName,
|
|
227
|
+
employeeAvatar: session.employeeAvatar,
|
|
181
228
|
};
|
|
182
229
|
}
|
|
183
230
|
/**
|
|
@@ -191,7 +238,7 @@ function scheduleWandTaskTitleGeneration(storage, taskId, source, signature, opt
|
|
|
191
238
|
const cwd = options.cwd || options.config?.defaultCwd || process.cwd();
|
|
192
239
|
const run = async () => {
|
|
193
240
|
try {
|
|
194
|
-
const title = await generateTitle(source, cwd, options.config?.language ?? "", taskTitleAiOptions(options.config));
|
|
241
|
+
const title = await generateTitle(source, cwd, options.config?.language ?? "", taskTitleAiOptions(options.config, storage));
|
|
195
242
|
const current = storage.getWandTask(taskId);
|
|
196
243
|
// 用户已经自己写了标题(原生端 / 面板编辑)就不要覆盖。
|
|
197
244
|
if (!current || current.titleSource !== "auto")
|
|
@@ -249,7 +296,7 @@ export function refreshAutoBoardTaskTitles(storage, options = {}) {
|
|
|
249
296
|
}
|
|
250
297
|
}
|
|
251
298
|
/** 自动生成标题没有会话上下文,按「默认 provider + 默认模型」解析,与提示词优化一致。 */
|
|
252
|
-
function taskTitleAiOptions(config) {
|
|
299
|
+
function taskTitleAiOptions(config, storage) {
|
|
253
300
|
if (!config)
|
|
254
301
|
return {};
|
|
255
302
|
const provider = config.defaultProvider ?? "claude";
|
|
@@ -261,11 +308,11 @@ function taskTitleAiOptions(config) {
|
|
|
261
308
|
selectedModel: null,
|
|
262
309
|
thinkingEffort: config.defaultThinkingEffort,
|
|
263
310
|
};
|
|
264
|
-
// resolveSystemAiContext 会在直连 API 可用且已启用时优先走 API
|
|
265
|
-
return resolveSystemAiContext(defaultSession, config);
|
|
311
|
+
// resolveSystemAiContext 会在直连 API 可用且已启用时优先走 API,否则走系统运维员工的 CLI 候选链。
|
|
312
|
+
return resolveSystemAiContext(defaultSession, config, storage?.getSystemSiliconEmployee() ?? null);
|
|
266
313
|
}
|
|
267
314
|
export function registerTaskRoutes(app, deps) {
|
|
268
|
-
const { storage, sessions, structured, processes, config } = deps;
|
|
315
|
+
const { storage, sessions, structured, processes, config, aiTeams } = deps;
|
|
269
316
|
const dto = (task) => taskDto(deps, task);
|
|
270
317
|
app.get("/api/wand-tasks", (req, res) => {
|
|
271
318
|
try {
|
|
@@ -392,8 +439,18 @@ export function registerTaskRoutes(app, deps) {
|
|
|
392
439
|
const status = STATUSES.has(body.status) ? body.status : "todo";
|
|
393
440
|
const priority = PRIORITIES.has(body.priority) ? body.priority : DEFAULT_WAND_TASK_PRIORITY;
|
|
394
441
|
const agent = body.agent === undefined ? null : parseTaskAgent(body.agent);
|
|
395
|
-
|
|
396
|
-
|
|
442
|
+
const executionSubject = body.executionSubject === undefined
|
|
443
|
+
? agent ? { type: "cli", id: agent.provider } : null
|
|
444
|
+
: parseExecutionSubject(storage, body.executionSubject);
|
|
445
|
+
if (executionSubject?.type === "cli" && agent && agent.provider !== executionSubject.id) {
|
|
446
|
+
throw new Error("CLI 执行对象与工具配置不一致。");
|
|
447
|
+
}
|
|
448
|
+
if (executionSubject?.type !== "cli" && executionSubject && agent?.kind === "pty") {
|
|
449
|
+
throw new Error("PTY 只能选择 CLI 工具。");
|
|
450
|
+
}
|
|
451
|
+
const assignedAgent = executionSubject && executionSubject.type !== "cli"
|
|
452
|
+
? preferredAgentForSubject(storage, executionSubject)
|
|
453
|
+
: executionSubject?.type === "cli" ? agent ?? cliAgentForSubject(executionSubject) : agent;
|
|
397
454
|
const labels = labelsFrom(body.labels);
|
|
398
455
|
const dueDate = dateValue(body.dueDate) ?? null;
|
|
399
456
|
// 没选迭代就落到默认迭代:每个任务都属于一个迭代。
|
|
@@ -411,8 +468,11 @@ export function registerTaskRoutes(app, deps) {
|
|
|
411
468
|
labels,
|
|
412
469
|
dueDate,
|
|
413
470
|
milestoneId,
|
|
414
|
-
agent,
|
|
471
|
+
agent: assignedAgent,
|
|
472
|
+
executionSubject,
|
|
415
473
|
});
|
|
474
|
+
if (agent)
|
|
475
|
+
writeTaskBoardLastAgent(storage, agent);
|
|
416
476
|
if (titleSource === "auto") {
|
|
417
477
|
refreshAutoBoardTaskTitles(storage, {
|
|
418
478
|
cwd: workspaceId ? storage.getWorkspace(workspaceId)?.cwd : undefined,
|
|
@@ -484,8 +544,24 @@ export function registerTaskRoutes(app, deps) {
|
|
|
484
544
|
if (body.agent !== undefined) {
|
|
485
545
|
// 老客户端不传 mode / kind:沿用任务当前值,而不是复位成标准 / 结构化。
|
|
486
546
|
patch.agent = parseTaskAgent(body.agent, currentTask.agent?.mode, currentTask.agent?.kind);
|
|
487
|
-
if (patch.agent)
|
|
488
|
-
|
|
547
|
+
if (body.executionSubject === undefined && patch.agent) {
|
|
548
|
+
patch.executionSubject = { type: "cli", id: patch.agent.provider };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (body.executionSubject !== undefined) {
|
|
552
|
+
patch.executionSubject = parseExecutionSubject(storage, body.executionSubject);
|
|
553
|
+
if (patch.executionSubject?.type === "cli" && patch.agent
|
|
554
|
+
&& patch.agent.provider !== patch.executionSubject.id) {
|
|
555
|
+
throw new Error("CLI 执行对象与工具配置不一致。");
|
|
556
|
+
}
|
|
557
|
+
if (patch.executionSubject?.type === "cli" && !patch.agent) {
|
|
558
|
+
patch.agent = cliAgentForSubject(patch.executionSubject, currentTask.agent);
|
|
559
|
+
}
|
|
560
|
+
if (patch.executionSubject && patch.executionSubject.type !== "cli") {
|
|
561
|
+
if (patch.agent?.kind === "pty")
|
|
562
|
+
throw new Error("PTY 只能选择 CLI 工具。");
|
|
563
|
+
patch.agent = preferredAgentForSubject(storage, patch.executionSubject);
|
|
564
|
+
}
|
|
489
565
|
}
|
|
490
566
|
if (body.sortOrder !== undefined && Number.isFinite(Number(body.sortOrder))) {
|
|
491
567
|
patch.sortOrder = Math.floor(Number(body.sortOrder));
|
|
@@ -495,6 +571,8 @@ export function registerTaskRoutes(app, deps) {
|
|
|
495
571
|
res.status(404).json({ error: "未找到该任务。" });
|
|
496
572
|
return;
|
|
497
573
|
}
|
|
574
|
+
if (body.agent !== undefined && patch.agent)
|
|
575
|
+
writeTaskBoardLastAgent(storage, patch.agent);
|
|
498
576
|
res.json(dto(storage.getWandTask(task.id) ?? task));
|
|
499
577
|
}
|
|
500
578
|
catch (error) {
|
|
@@ -556,16 +634,81 @@ export function registerTaskRoutes(app, deps) {
|
|
|
556
634
|
if (!config)
|
|
557
635
|
throw new Error("当前服务未启用派发,无法派发 Agent。");
|
|
558
636
|
const body = bodyObject(req.body);
|
|
559
|
-
const
|
|
560
|
-
?
|
|
561
|
-
:
|
|
637
|
+
const subject = body.subject === undefined
|
|
638
|
+
? body.agent !== undefined ? null : task.executionSubject ?? null
|
|
639
|
+
: parseExecutionSubject(storage, body.subject);
|
|
640
|
+
if ((subject?.type === "employee" || subject?.type === "team")
|
|
641
|
+
&& (body.kind === "pty" || (body.agent && bodyObject(body.agent).kind === "pty"))) {
|
|
642
|
+
throw new Error("PTY 只能选择 CLI 工具。");
|
|
643
|
+
}
|
|
644
|
+
const requestedPrompt = text(body.prompt);
|
|
645
|
+
const existingSessions = storage.listWandTaskSessionIds(task.id).length;
|
|
646
|
+
if (existingSessions > 0 && !requestedPrompt)
|
|
647
|
+
throw new Error("请输入提示词。");
|
|
648
|
+
const prompt = requestedPrompt || task.description.trim() || task.title || "执行此任务";
|
|
649
|
+
if (subject?.type === "team") {
|
|
650
|
+
if (!aiTeams)
|
|
651
|
+
throw new Error("当前服务未启用 AI 团队。");
|
|
652
|
+
const team = storage.getAiTeam(subject.id);
|
|
653
|
+
if (!team)
|
|
654
|
+
throw new Error("AI 团队不存在。");
|
|
655
|
+
if (body.workspaceId !== undefined) {
|
|
656
|
+
const workspaceId = body.workspaceId === null ? null : text(body.workspaceId) || null;
|
|
657
|
+
if (workspaceId && !storage.getWorkspace(workspaceId))
|
|
658
|
+
throw new Error("项目不存在。");
|
|
659
|
+
parentTaskIdFrom(storage, task.parentTaskId, workspaceId, task.id, task.parentTaskId);
|
|
660
|
+
if (workspaceId !== task.workspaceId)
|
|
661
|
+
assertTaskWorkspaceMove(storage, task.id, workspaceId);
|
|
662
|
+
task = storage.updateWandTask(task.id, { workspaceId }) ?? task;
|
|
663
|
+
}
|
|
664
|
+
const target = resolveTaskDispatchTarget({ storage, config }, task);
|
|
665
|
+
const detail = await aiTeams.start({ teamId: team.id, taskId: task.id, note: prompt });
|
|
666
|
+
const preferredMember = team.members.find((member) => member.isLeader) ?? team.members[0];
|
|
667
|
+
const preferredAgent = preferredMember ? memberAgents(preferredMember)[0] ?? null : null;
|
|
668
|
+
storage.updateWandTask(task.id, {
|
|
669
|
+
executionSubject: subject,
|
|
670
|
+
agent: preferredAgent,
|
|
671
|
+
status: task.status === "todo" ? "doing" : task.status,
|
|
672
|
+
});
|
|
673
|
+
recordIterationPromptForTask(storage, {
|
|
674
|
+
sessionId: detail.run.chatSessionId ?? undefined,
|
|
675
|
+
cwd: target.cwd,
|
|
676
|
+
workspaceId: target.workspaceId,
|
|
677
|
+
milestoneId: task.milestoneId,
|
|
678
|
+
taskId: task.id,
|
|
679
|
+
title: task.title,
|
|
680
|
+
detail: task.description || prompt,
|
|
681
|
+
source: "dispatch",
|
|
682
|
+
});
|
|
683
|
+
res.status(202).json({
|
|
684
|
+
ok: true,
|
|
685
|
+
taskId: task.id,
|
|
686
|
+
subject,
|
|
687
|
+
teamRun: detail,
|
|
688
|
+
session: detail.run.chatSessionId ? { id: detail.run.chatSessionId, sessionKind: "structured" } : null,
|
|
689
|
+
});
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const employee = subject?.type === "employee" ? storage.getSiliconEmployee(subject.id) : null;
|
|
693
|
+
if (subject?.type === "employee" && (!employee || employee.archivedAt)) {
|
|
694
|
+
throw new Error("硅基员工不存在或已归档。");
|
|
695
|
+
}
|
|
696
|
+
const selected = employee ? selectEmployeeCandidate(employee) : null;
|
|
697
|
+
const cliDefault = subject?.type === "cli"
|
|
698
|
+
? parseTaskAgent({ ...cliAgentForSubject(subject, task.agent), ...(body.kind === "pty" ? { kind: "pty" } : {}) })
|
|
699
|
+
: null;
|
|
700
|
+
const agent = selected?.agent ?? (body.agent === undefined
|
|
701
|
+
? cliDefault ?? task.agent ?? parseWandTaskAgent(JSON.stringify(body))
|
|
702
|
+
: parseTaskAgent(body.agent, task.agent?.mode, task.agent?.kind));
|
|
562
703
|
if (!agent)
|
|
563
704
|
throw new Error("请先为该任务选择 CLI 工具。");
|
|
705
|
+
if (subject?.type === "cli" && agent.provider !== subject.id) {
|
|
706
|
+
throw new Error("CLI 执行对象与工具配置不一致。");
|
|
707
|
+
}
|
|
564
708
|
if (agent.kind === "structured" && !structured)
|
|
565
709
|
throw new Error("当前服务未启用结构化会话,无法派发 Agent。");
|
|
566
710
|
if (agent.kind === "pty" && !processes)
|
|
567
711
|
throw new Error("当前服务未启用终端会话,无法派发 Agent。");
|
|
568
|
-
writeTaskBoardLastAgent(storage, agent);
|
|
569
712
|
if (body.workspaceId !== undefined) {
|
|
570
713
|
const workspaceId = body.workspaceId === null ? null : text(body.workspaceId) || null;
|
|
571
714
|
if (workspaceId && !storage.getWorkspace(workspaceId))
|
|
@@ -575,14 +718,18 @@ export function registerTaskRoutes(app, deps) {
|
|
|
575
718
|
assertTaskWorkspaceMove(storage, task.id, workspaceId);
|
|
576
719
|
task = storage.updateWandTask(task.id, { workspaceId }) ?? task;
|
|
577
720
|
}
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
throw new Error("请输入提示词。");
|
|
582
|
-
const prompt = requestedPrompt || task.description.trim() || task.title || "执行此任务";
|
|
583
|
-
const { session, cwd, workspaceId } = await dispatchAgentForTask({ storage, config, structured: structured ?? null, processes: processes ?? null }, { task, agent, prompt, automationId: `wand-task:${task.id}` });
|
|
721
|
+
const { session, cwd, workspaceId } = await dispatchAgentForTask({ storage, config, structured: structured ?? null, processes: processes ?? null }, { task, agent, prompt, automationId: `wand-task:${task.id}`,
|
|
722
|
+
systemPrompt: employee?.prompt, employee: employee ?? undefined,
|
|
723
|
+
employeeCandidateIndex: selected?.index });
|
|
584
724
|
task = storage.getWandTask(task.id);
|
|
585
|
-
|
|
725
|
+
const usedSubject = subject ?? { type: "cli", id: agent.provider };
|
|
726
|
+
storage.updateWandTask(task.id, {
|
|
727
|
+
agent,
|
|
728
|
+
executionSubject: usedSubject,
|
|
729
|
+
status: task.status === "todo" ? "doing" : task.status,
|
|
730
|
+
});
|
|
731
|
+
if (!employee)
|
|
732
|
+
writeTaskBoardLastAgent(storage, agent);
|
|
586
733
|
// 派发也算一轮迭代里的改动意图:直接把任务的标题 / 描述记进迭代记录。
|
|
587
734
|
recordIterationPromptForTask(storage, {
|
|
588
735
|
sessionId: session.id,
|
|
@@ -597,6 +744,7 @@ export function registerTaskRoutes(app, deps) {
|
|
|
597
744
|
res.status(202).json({
|
|
598
745
|
ok: true,
|
|
599
746
|
taskId: task.id,
|
|
747
|
+
subject: usedSubject,
|
|
600
748
|
session: {
|
|
601
749
|
id: session.id,
|
|
602
750
|
provider: session.provider,
|
|
@@ -605,6 +753,10 @@ export function registerTaskRoutes(app, deps) {
|
|
|
605
753
|
thinkingEffort: session.thinkingEffort,
|
|
606
754
|
mode: session.mode,
|
|
607
755
|
cwd: session.cwd,
|
|
756
|
+
employeeId: session.employeeId,
|
|
757
|
+
employeeName: session.employeeName,
|
|
758
|
+
employeeAvatar: session.employeeAvatar,
|
|
759
|
+
employeeCandidateIndex: session.employeeCandidateIndex,
|
|
608
760
|
},
|
|
609
761
|
});
|
|
610
762
|
}
|
|
@@ -405,7 +405,7 @@ export function registerAdminUpdateRoutes(app, deps) {
|
|
|
405
405
|
return;
|
|
406
406
|
}
|
|
407
407
|
const rawIds = Array.isArray(req.body?.ids) ? req.body.ids : [];
|
|
408
|
-
const ids = rawIds.filter((value) => value === "claude" || value === "codex" || value === "opencode" || value === "grok" || value === "qoder" || value === "pi");
|
|
408
|
+
const ids = rawIds.filter((value) => value === "claude" || value === "codex" || value === "opencode" || value === "grok" || value === "qoder" || value === "pi" || value === "gemini");
|
|
409
409
|
state.providerCliUpdateInFlight = true;
|
|
410
410
|
try {
|
|
411
411
|
const before = await refreshProviderCliUpdateState(state, config);
|
|
@@ -40,6 +40,9 @@ function workspaceSessionSummary(session, names = {}, registry, team = {}) {
|
|
|
40
40
|
inFlight: live.structuredState?.inFlight === true,
|
|
41
41
|
cwd: live.cwd,
|
|
42
42
|
startedAt: live.startedAt,
|
|
43
|
+
employeeId: live.employeeId,
|
|
44
|
+
employeeName: live.employeeName,
|
|
45
|
+
employeeAvatar: live.employeeAvatar,
|
|
43
46
|
...(team.chat ? { teamChat: team.chat } : {}),
|
|
44
47
|
...(team.step ? { teamStep: team.step } : {}),
|
|
45
48
|
};
|
package/dist/server.js
CHANGED
|
@@ -33,6 +33,7 @@ import { registerMissionRoutes } from "./server-mission-routes.js";
|
|
|
33
33
|
import { Missions } from "./missions.js";
|
|
34
34
|
import { createAiTeamRunner } from "./ai-team-runner.js";
|
|
35
35
|
import { registerAiTeamRoutes } from "./server-ai-team-routes.js";
|
|
36
|
+
import { registerSiliconEmployeeRoutes } from "./server-employee-routes.js";
|
|
36
37
|
import { registerAttentionRoutes } from "./server-attention-routes.js";
|
|
37
38
|
import { refreshProviderCliUpdateState, registerAdminUpdateRoutes, registerPublicUpdateRoutes, ServerUpdateState, } from "./server-update-routes.js";
|
|
38
39
|
import { parseSessionCreationOrigin, registerClaudeHistoryRoutes, registerSessionRoutes } from "./server-session-routes.js";
|
|
@@ -298,6 +299,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
298
299
|
grok: storage.getPreference("pref:defaultGrokModel", config.defaultGrokModel ?? ""),
|
|
299
300
|
qoder: storage.getPreference("pref:defaultQoderModel", config.defaultQoderModel ?? ""),
|
|
300
301
|
pi: storage.getPreference("pref:defaultPiModel", config.defaultPiModel ?? ""),
|
|
302
|
+
gemini: storage.getPreference("pref:defaultGeminiModel", config.defaultGeminiModel ?? ""),
|
|
301
303
|
});
|
|
302
304
|
const getModelRefreshOptions = () => {
|
|
303
305
|
const injected = options.modelRefreshOptions?.() ?? {};
|
|
@@ -361,6 +363,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
361
363
|
grok: "grokModels",
|
|
362
364
|
qoder: "qoderModels",
|
|
363
365
|
pi: "piModels",
|
|
366
|
+
gemini: "geminiModels",
|
|
364
367
|
}[provider];
|
|
365
368
|
return cache[key].map((entry) => entry.id);
|
|
366
369
|
};
|
|
@@ -707,12 +710,17 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
707
710
|
});
|
|
708
711
|
registerGithubRoutes(app, { storage, requireAdmin, sessions: sessionRegistry });
|
|
709
712
|
// 任务管理与 Missions 一样只需登录:原生 connected-app 也要能列/建/派发。
|
|
710
|
-
registerTaskRoutes(app, { storage, sessions: sessionRegistry, structured: structuredSessions, processes, config });
|
|
713
|
+
registerTaskRoutes(app, { storage, sessions: sessionRegistry, structured: structuredSessions, processes, config, aiTeams });
|
|
711
714
|
registerAiTeamRoutes(app, {
|
|
712
715
|
storage,
|
|
713
716
|
runner: aiTeams,
|
|
714
717
|
notifyTeamChanged: (teamId) => notifyAiTeamRun({ kind: "ai-team-definition", teamId }),
|
|
715
718
|
});
|
|
719
|
+
registerSiliconEmployeeRoutes(app, {
|
|
720
|
+
storage,
|
|
721
|
+
config,
|
|
722
|
+
notifyEmployeeChanged: (employeeId) => notifyAiTeamRun({ kind: "silicon-employee-definition", employeeId }),
|
|
723
|
+
});
|
|
716
724
|
registerAttentionRoutes(app, { sessions: sessionRegistry, runner: aiTeams });
|
|
717
725
|
registerAdminUpdateRoutes(app, {
|
|
718
726
|
storage,
|
|
@@ -755,7 +763,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
755
763
|
if (snapshot?.cwd)
|
|
756
764
|
cwd = snapshot.cwd;
|
|
757
765
|
if (snapshot)
|
|
758
|
-
ai = resolveSystemAiContext(snapshot, config);
|
|
766
|
+
ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee());
|
|
759
767
|
}
|
|
760
768
|
if (!ai) {
|
|
761
769
|
const defaultSession = {
|
|
@@ -766,7 +774,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
766
774
|
selectedModel: null,
|
|
767
775
|
thinkingEffort: config.defaultThinkingEffort,
|
|
768
776
|
};
|
|
769
|
-
ai = resolveSystemAiContext(defaultSession, config);
|
|
777
|
+
ai = resolveSystemAiContext(defaultSession, config, storage.getSystemSiliconEmployee());
|
|
770
778
|
}
|
|
771
779
|
try {
|
|
772
780
|
const optimized = await optimizePrompt(text, config.language ?? "", cwd, ai);
|
|
@@ -785,6 +793,11 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
785
793
|
// ── Session control ──
|
|
786
794
|
app.post("/api/commands", asyncRoute(async (req, res) => {
|
|
787
795
|
const body = req.body;
|
|
796
|
+
if (body.employeeId !== undefined || body.teamId !== undefined
|
|
797
|
+
|| (body.subject !== undefined && body.subject?.type !== "cli")) {
|
|
798
|
+
res.status(400).json({ error: "PTY 只能选择 CLI 工具。" });
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
788
801
|
const interactiveShell = body.shell === true;
|
|
789
802
|
if (!interactiveShell && !body.command?.trim()) {
|
|
790
803
|
res.status(400).json({ error: "请输入要执行的命令。" });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { discoverCliSystemAiConfigs } from "./system-ai.js";
|
|
2
|
+
import type { SiliconEmployee } from "./ai-team-types.js";
|
|
2
3
|
import type { SessionProvider, SessionSnapshot, SystemAiConfig, WandConfig } from "./types.js";
|
|
3
4
|
export interface SessionAiContext {
|
|
4
5
|
provider: SessionProvider;
|
|
@@ -6,6 +7,10 @@ export interface SessionAiContext {
|
|
|
6
7
|
thinkingEffort: SessionSnapshot["thinkingEffort"];
|
|
7
8
|
inheritEnv?: boolean;
|
|
8
9
|
systemAi?: SystemAiConfig;
|
|
10
|
+
/** 内置「系统运维」员工的人设;作为系统提示前缀注入 Wand 自有 AI 调用。 */
|
|
11
|
+
opsPersona?: string;
|
|
12
|
+
/** CLI 降级链(按顺序)。为空时只用 provider/model 这一次调用。 */
|
|
13
|
+
cliCandidates?: import("./types.js").AiCliCandidate[];
|
|
9
14
|
}
|
|
10
15
|
/**
|
|
11
16
|
* Resolve the provider from every representation used by current and legacy
|
|
@@ -16,6 +21,6 @@ export declare function resolveSessionProvider(snapshot: Pick<SessionSnapshot, "
|
|
|
16
21
|
/** Build the provider-specific settings used by session-adjacent AI actions. */
|
|
17
22
|
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;
|
|
18
23
|
/** Build the source order for Wand-owned AI features such as titles. */
|
|
19
|
-
export declare function resolveSystemAiContext(snapshot: Parameters<typeof resolveSessionAiContext>[0], config: Parameters<typeof resolveSessionAiContext>[1] & Pick<WandConfig, "systemAi" | "systemAiCli" | "systemAiModel"
|
|
24
|
+
export declare function resolveSystemAiContext(snapshot: Parameters<typeof resolveSessionAiContext>[0], config: Parameters<typeof resolveSessionAiContext>[1] & Pick<WandConfig, "systemAi" | "systemAiCli" | "systemAiModel">, systemEmployee?: SiliconEmployee | null): SessionAiContext;
|
|
20
25
|
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
21
|
-
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">, discoverApis?: typeof discoverCliSystemAiConfigs): SessionAiContext;
|
|
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;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getDefaultModelForProvider } from "./config.js";
|
|
2
2
|
import { discoverCliSystemAiConfigs, mergeSystemAiConfigs, systemAiProfiles, } from "./system-ai.js";
|
|
3
|
-
import {
|
|
3
|
+
import { providerCliInstalled, isSessionProvider } from "./session-provider.js";
|
|
4
|
+
import { isSystemSiliconEmployee, systemEmployeeCliCandidates } from "./system-employee.js";
|
|
5
|
+
import { inferProviderFromCommand, inferProviderFromRunner } from "./session-provider.js";
|
|
4
6
|
/**
|
|
5
7
|
* Resolve the provider from every representation used by current and legacy
|
|
6
8
|
* sessions. Older persisted sessions may not have the top-level provider, but
|
|
@@ -37,18 +39,39 @@ export function resolveSessionAiContext(snapshot, config) {
|
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
/** Build the source order for Wand-owned AI features such as titles. */
|
|
40
|
-
export function resolveSystemAiContext(snapshot, config) {
|
|
42
|
+
export function resolveSystemAiContext(snapshot, config, systemEmployee) {
|
|
41
43
|
const sessionContext = resolveSessionAiContext(snapshot, config);
|
|
44
|
+
// 候选里的「跟随默认模型」在这里就换成具体的 Wand 默认模型:降级到下一个 provider
|
|
45
|
+
// 时不能拿上一个 provider 的模型,也不能把决定权交给 CLI 自己的默认值。
|
|
46
|
+
const chain = systemEmployeeCliCandidates(systemEmployee).map((candidate) => ({
|
|
47
|
+
...candidate,
|
|
48
|
+
model: candidate.model ?? normalizeModel(getDefaultModelForProvider(config, candidate.provider)),
|
|
49
|
+
}));
|
|
50
|
+
const owned = {
|
|
51
|
+
...sessionContext,
|
|
52
|
+
...(chain.length ? { cliCandidates: chain } : {}),
|
|
53
|
+
...(isSystemSiliconEmployee(systemEmployee) ? { opsPersona: systemEmployee.prompt } : {}),
|
|
54
|
+
};
|
|
55
|
+
// 内置员工可用时,Wand 自有调用一律按它的候选链:provider/model 取首个已安装的
|
|
56
|
+
// 候选(两条路都一致),整条链留给运行期降级。
|
|
57
|
+
if (chain.length) {
|
|
58
|
+
const preferred = chain.find((candidate) => providerCliInstalled(candidate.provider)) ?? chain[0];
|
|
59
|
+
owned.provider = preferred.provider;
|
|
60
|
+
owned.model = preferred.model ?? normalizeModel(getDefaultModelForProvider(config, preferred.provider));
|
|
61
|
+
owned.thinkingEffort = preferred.thinkingEffort ?? config.defaultThinkingEffort;
|
|
62
|
+
}
|
|
42
63
|
if (config.systemAi?.enabled) {
|
|
43
64
|
const directApi = usableSystemAi(config.systemAi);
|
|
44
|
-
return directApi ? { ...
|
|
65
|
+
return directApi ? { ...owned, systemAi: directApi } : owned;
|
|
45
66
|
}
|
|
67
|
+
if (chain.length)
|
|
68
|
+
return owned;
|
|
46
69
|
// Older installations without a system AI CLI preference still follow the
|
|
47
70
|
// current session. A chosen CLI must never inherit another provider's model.
|
|
48
71
|
if (!isSessionProvider(config.systemAiCli))
|
|
49
|
-
return
|
|
72
|
+
return owned;
|
|
50
73
|
return {
|
|
51
|
-
...
|
|
74
|
+
...owned,
|
|
52
75
|
provider: config.systemAiCli,
|
|
53
76
|
model: normalizeModel(config.systemAiModel)
|
|
54
77
|
?? normalizeModel(getDefaultModelForProvider(config, config.systemAiCli)),
|
|
@@ -56,10 +79,14 @@ export function resolveSystemAiContext(snapshot, config) {
|
|
|
56
79
|
};
|
|
57
80
|
}
|
|
58
81
|
/** Build the AI context for quick-commit actions from their global preferences. */
|
|
59
|
-
export function resolveCommitAiContext(snapshot, config, discoverApis = discoverCliSystemAiConfigs) {
|
|
60
|
-
const sessionContext =
|
|
61
|
-
if (config.commitAiSource !== "api")
|
|
62
|
-
|
|
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
|
+
}
|
|
63
90
|
const directApi = mergeSystemAiConfigs(config.systemAi, discoverApis(sessionContext.provider));
|
|
64
91
|
return {
|
|
65
92
|
...sessionContext,
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { SessionProvider } from "./types.js";
|
|
2
2
|
/** 全部受支持的会话 provider;顺序与 UI / 派发选择器一致。 */
|
|
3
|
-
export declare const SESSION_PROVIDERS: readonly ["claude", "codex", "opencode", "grok", "qoder", "pi"];
|
|
3
|
+
export declare const SESSION_PROVIDERS: readonly ["claude", "codex", "opencode", "grok", "qoder", "pi", "gemini"];
|
|
4
4
|
/** 运行时守卫:把配置、数据库、请求体里的任意值收敛成 SessionProvider。 */
|
|
5
5
|
export declare function isSessionProvider(value: unknown): value is SessionProvider;
|
|
6
6
|
/** provider 对应的 CLI 可执行文件名(Qoder 的 CLI 叫 qodercli)。 */
|
|
7
7
|
export declare function providerCliCommand(provider: SessionProvider): string;
|
|
8
|
+
/**
|
|
9
|
+
* 不启动 shell 也能判断某个 provider 的 CLI 是否在 PATH 上(与子进程 PATH 一致)。
|
|
10
|
+
* 用于多候选降级时跳过没安装的工具,避免白等一次 spawn 失败。
|
|
11
|
+
*/
|
|
12
|
+
export declare function providerCliInstalled(provider: SessionProvider, pathValue?: string): boolean;
|
|
8
13
|
/** 从 runner 名推断 provider;`pty` 等与 provider 无关的 runner 返回 undefined。 */
|
|
9
14
|
export declare function inferProviderFromRunner(runner: unknown): SessionProvider | undefined;
|
|
10
15
|
/**
|
package/dist/session-provider.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { constants, accessSync, statSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
/** 全部受支持的会话 provider;顺序与 UI / 派发选择器一致。 */
|
|
2
|
-
export const SESSION_PROVIDERS = ["claude", "codex", "opencode", "grok", "qoder", "pi"];
|
|
4
|
+
export const SESSION_PROVIDERS = ["claude", "codex", "opencode", "grok", "qoder", "pi", "gemini"];
|
|
3
5
|
const SESSION_PROVIDER_SET = new Set(SESSION_PROVIDERS);
|
|
4
6
|
/** 运行时守卫:把配置、数据库、请求体里的任意值收敛成 SessionProvider。 */
|
|
5
7
|
export function isSessionProvider(value) {
|
|
@@ -9,6 +11,25 @@ export function isSessionProvider(value) {
|
|
|
9
11
|
export function providerCliCommand(provider) {
|
|
10
12
|
return provider === "qoder" ? "qodercli" : provider;
|
|
11
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* 不启动 shell 也能判断某个 provider 的 CLI 是否在 PATH 上(与子进程 PATH 一致)。
|
|
16
|
+
* 用于多候选降级时跳过没安装的工具,避免白等一次 spawn 失败。
|
|
17
|
+
*/
|
|
18
|
+
export function providerCliInstalled(provider, pathValue = process.env.PATH ?? "") {
|
|
19
|
+
const command = providerCliCommand(provider);
|
|
20
|
+
for (const directory of pathValue.split(path.delimiter)) {
|
|
21
|
+
if (!directory)
|
|
22
|
+
continue;
|
|
23
|
+
try {
|
|
24
|
+
const executable = path.join(directory, command);
|
|
25
|
+
accessSync(executable, constants.X_OK);
|
|
26
|
+
if (statSync(executable).isFile())
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch { /* 检查下一个 PATH 目录 */ }
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
12
33
|
/** 从 runner 名推断 provider;`pty` 等与 provider 无关的 runner 返回 undefined。 */
|
|
13
34
|
export function inferProviderFromRunner(runner) {
|
|
14
35
|
if (runner === "claude-cli" || runner === "claude-cli-print" || runner === "claude-sdk")
|
|
@@ -23,6 +44,8 @@ export function inferProviderFromRunner(runner) {
|
|
|
23
44
|
return "qoder";
|
|
24
45
|
if (runner === "pi-cli-json")
|
|
25
46
|
return "pi";
|
|
47
|
+
if (runner === "gemini-cli-json")
|
|
48
|
+
return "gemini";
|
|
26
49
|
return undefined;
|
|
27
50
|
}
|
|
28
51
|
/**
|
|
@@ -45,5 +68,8 @@ export function inferProviderFromCommand(command) {
|
|
|
45
68
|
return "qoder";
|
|
46
69
|
if (/^pi\b/i.test(value))
|
|
47
70
|
return "pi";
|
|
71
|
+
// Gemini CLI 的可执行文件就叫 gemini;-p / --prompt 等参数不影响首 token 识别。
|
|
72
|
+
if (/^gemini\b/i.test(value))
|
|
73
|
+
return "gemini";
|
|
48
74
|
return undefined;
|
|
49
75
|
}
|
|
@@ -26,6 +26,10 @@ function sessionBase(snapshot) {
|
|
|
26
26
|
id: snapshot.id,
|
|
27
27
|
sessionSource: snapshot.sessionSource,
|
|
28
28
|
automationId: snapshot.automationId,
|
|
29
|
+
employeeId: snapshot.employeeId,
|
|
30
|
+
employeeName: snapshot.employeeName,
|
|
31
|
+
employeeAvatar: snapshot.employeeAvatar,
|
|
32
|
+
employeeCandidateIndex: snapshot.employeeCandidateIndex,
|
|
29
33
|
sessionKind: snapshot.sessionKind,
|
|
30
34
|
provider: snapshot.provider,
|
|
31
35
|
providerCliActive: snapshot.providerCliActive,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SiliconEmployee } from "./ai-team-types.js";
|
|
2
|
+
import type { WandTaskAgent } from "./task-types.js";
|
|
3
|
+
/** Match child-process PATH without starting a shell or trusting a client availability claim. */
|
|
4
|
+
export declare function employeeCliAvailable(agent: WandTaskAgent, pathValue?: string): boolean;
|
|
5
|
+
/** First installed candidate wins; when none is installed retain the preferred candidate for a recoverable failure. */
|
|
6
|
+
export declare function selectEmployeeCandidate(employee: Pick<SiliconEmployee, "agents">, available?: (agent: WandTaskAgent) => boolean): {
|
|
7
|
+
agent: WandTaskAgent;
|
|
8
|
+
index: number;
|
|
9
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { providerCliInstalled } from "./session-provider.js";
|
|
2
|
+
/** Match child-process PATH without starting a shell or trusting a client availability claim. */
|
|
3
|
+
export function employeeCliAvailable(agent, pathValue = process.env.PATH ?? "") {
|
|
4
|
+
return providerCliInstalled(agent.provider, pathValue);
|
|
5
|
+
}
|
|
6
|
+
/** First installed candidate wins; when none is installed retain the preferred candidate for a recoverable failure. */
|
|
7
|
+
export function selectEmployeeCandidate(employee, available = employeeCliAvailable) {
|
|
8
|
+
if (!employee.agents.length || employee.agents.some((agent) => agent.kind !== "structured")) {
|
|
9
|
+
throw new Error("硅基员工没有可用的结构化执行候选。");
|
|
10
|
+
}
|
|
11
|
+
const index = employee.agents.findIndex((agent) => available(agent));
|
|
12
|
+
return { agent: employee.agents[index < 0 ? 0 : index], index: index < 0 ? 0 : index };
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type QuickCommitAiOptions } from "./git-quick-commit.js";
|
|
2
|
+
import type { SiliconEmployeeDraft } from "./ai-team-types.js";
|
|
3
|
+
import type { AiTextRequest } from "./system-ai.js";
|
|
4
|
+
import type { WandTaskAgent } from "./task-types.js";
|
|
5
|
+
import type { SessionProvider } from "./types.js";
|
|
6
|
+
export declare class SiliconEmployeeDraftError extends Error {
|
|
7
|
+
readonly code: string;
|
|
8
|
+
constructor(message: string, code: string);
|
|
9
|
+
}
|
|
10
|
+
/** 硅基员工只能跑结构化会话;候选沿用看板的「跟随服务端默认」。 */
|
|
11
|
+
export declare function employeeAgentFor(provider: SessionProvider): WandTaskAgent;
|
|
12
|
+
/**
|
|
13
|
+
* 已安装的 CLI:默认 provider 优先,其余按全局顺序;
|
|
14
|
+
* 都没装时返回空数组,由调用方决定退回到哪一个首选。
|
|
15
|
+
*/
|
|
16
|
+
export declare function availableEmployeeProviders(preferred?: SessionProvider, isAvailable?: (agent: WandTaskAgent) => boolean): SessionProvider[];
|
|
17
|
+
/** 从模型输出里取第一个 JSON 对象,容忍前后夹带解释文字。 */
|
|
18
|
+
export declare function extractDraftJson(raw: string): Record<string, unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* 校验并收敛模型输出。名字 / 角色设定缺失即视为不可用;
|
|
21
|
+
* provider 不在已安装清单里时按候选顺序兜底,不让员工带着跑不起来的 CLI 落库。
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseSiliconEmployeeDraft(raw: string, options?: {
|
|
24
|
+
allowedProviders?: SessionProvider[];
|
|
25
|
+
fallbackProvider?: SessionProvider;
|
|
26
|
+
}): SiliconEmployeeDraft;
|
|
27
|
+
/** 规则走系统提示,用户的期望走用户消息;一次性调用沿用系统 AI 通道。 */
|
|
28
|
+
export declare function buildEmployeeDraftPrompt(expectation: string, providers: SessionProvider[], existingNames?: string[]): AiTextRequest;
|
|
29
|
+
/** 按自然语言期望生成员工配置;调用方负责随后落库。 */
|
|
30
|
+
export declare function generateSiliconEmployeeDraft(expectation: string, ai?: QuickCommitAiOptions, options?: {
|
|
31
|
+
cwd?: string;
|
|
32
|
+
language?: string;
|
|
33
|
+
existingNames?: string[];
|
|
34
|
+
preferredProvider?: SessionProvider;
|
|
35
|
+
isProviderAvailable?: (agent: WandTaskAgent) => boolean;
|
|
36
|
+
}): Promise<SiliconEmployeeDraft>;
|