@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
|
@@ -12,7 +12,6 @@ export function isSessionGoneStatus(status) {
|
|
|
12
12
|
* CLI 缺失类文案(spawn-missing)。来源样例(只引用不改):
|
|
13
13
|
* - structured-session-manager.ts: codex exec ENOENT →「PATH 中找不到 … 可执行文件」
|
|
14
14
|
* - git-quick-commit.ts: error.code === "ENOENT" →「未找到 <cmd> CLI。」
|
|
15
|
-
* - claude-sdk-runner.ts:「Claude Code native binary not found」/ ENOENT →「未找到 claude CLI。」
|
|
16
15
|
*/
|
|
17
16
|
const SPAWN_MISSING_PATTERNS = [
|
|
18
17
|
/\bENOENT\b/i,
|
package/dist/ai-team-live.d.ts
CHANGED
|
@@ -10,7 +10,9 @@ export interface LiveStepText {
|
|
|
10
10
|
/**
|
|
11
11
|
* 渲染「成员此刻干到哪儿了」的紧凑文本,给固定尺寸、内部滚动的 live 卡片用。
|
|
12
12
|
* 口径:从最后一条 user turn(没有就取最后一条 assistant turn)渲染到末尾;
|
|
13
|
-
* text 原样保留,tool_use 压成一行 `▸ 名称[ · 描述]
|
|
13
|
+
* text 原样保留,tool_use 压成一行 `▸ 名称[ · 描述]`(描述优先用 adapter 填的 `description`,
|
|
14
|
+
* 没有就从 input 里的命令/路径/搜索词派生,见 `toolInputDescription`),
|
|
15
|
+
* tool_result 与 thinking 不渲染;
|
|
14
16
|
* 尾部保留最多 AI_TEAM_LIVE_TEXT_MAX_CHARS 字;渲染结果为空时回落原始 output 尾部
|
|
15
17
|
* (先按尾部截窗再剥 ANSI、同样截尾,见 `outputTailText`),保证 PTY 会话也有内容可显示。
|
|
16
18
|
* 纯函数、无 IO。
|
package/dist/ai-team-live.js
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import { AI_TEAM_LIVE_TEXT_MAX_CHARS } from "./ai-team-types.js";
|
|
2
2
|
/** tool_use 行的 description 单行截断长度。 */
|
|
3
3
|
const TOOL_DESCRIPTION_MAX_CHARS = 60;
|
|
4
|
+
/**
|
|
5
|
+
* 没有 `description` 时按序从这里挑一个 input 字段当描述。顺序 = 具体到笼统:
|
|
6
|
+
* 命令/路径/搜索词/网址这些一眼看出「在干什么」的排在前,待办与子代理的说明排在后。
|
|
7
|
+
* 只认字符串字段,不塞 JSON 尾巴:live 卡的目的是让人看懂进展,不是看参数。
|
|
8
|
+
*/
|
|
9
|
+
const TOOL_INPUT_DESCRIPTION_KEYS = [
|
|
10
|
+
"command", "cmd",
|
|
11
|
+
"file_path", "filePath", "path", "notebook_path", "move_path",
|
|
12
|
+
"pattern", "query", "url",
|
|
13
|
+
"subject", "title", "explanation", "description", "prompt",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* adapter 给的 `description` 只有 grok / opencode / codex 少数几种工具会填,pi / claude / qoder
|
|
17
|
+
* 基本没有,卡片就只剩一个工具名(「▸ Pi/todo」重复刷屏)。这里从 input 里派生一行摘要,
|
|
18
|
+
* 与客户端会话里的工具卡同一个思路(路径 / 命令 / 搜索词)。取不到就返回空串,仍是裸工具名。
|
|
19
|
+
*/
|
|
20
|
+
function toolInputDescription(input) {
|
|
21
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
22
|
+
return "";
|
|
23
|
+
const record = input;
|
|
24
|
+
for (const key of TOOL_INPUT_DESCRIPTION_KEYS) {
|
|
25
|
+
const value = record[key];
|
|
26
|
+
if (typeof value === "string" && value.trim())
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
4
31
|
/** CSI + OSC + 两字节转义,覆盖 PTY 输出里常见的控制序列。 */
|
|
5
32
|
const ANSI_PATTERN = /\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07\x1B]*(?:\x07|\x1B\\)|[ @-Z\\-_])/g;
|
|
6
33
|
function stripAnsi(text) {
|
|
@@ -84,7 +111,9 @@ function oneLine(text, max) {
|
|
|
84
111
|
/**
|
|
85
112
|
* 渲染「成员此刻干到哪儿了」的紧凑文本,给固定尺寸、内部滚动的 live 卡片用。
|
|
86
113
|
* 口径:从最后一条 user turn(没有就取最后一条 assistant turn)渲染到末尾;
|
|
87
|
-
* text 原样保留,tool_use 压成一行 `▸ 名称[ · 描述]
|
|
114
|
+
* text 原样保留,tool_use 压成一行 `▸ 名称[ · 描述]`(描述优先用 adapter 填的 `description`,
|
|
115
|
+
* 没有就从 input 里的命令/路径/搜索词派生,见 `toolInputDescription`),
|
|
116
|
+
* tool_result 与 thinking 不渲染;
|
|
88
117
|
* 尾部保留最多 AI_TEAM_LIVE_TEXT_MAX_CHARS 字;渲染结果为空时回落原始 output 尾部
|
|
89
118
|
* (先按尾部截窗再剥 ANSI、同样截尾,见 `outputTailText`),保证 PTY 会话也有内容可显示。
|
|
90
119
|
* 纯函数、无 IO。
|
|
@@ -121,7 +150,7 @@ export function renderLiveStepText(messages, output, options = {}) {
|
|
|
121
150
|
fragments.push(text);
|
|
122
151
|
}
|
|
123
152
|
else if (block.type === "tool_use") {
|
|
124
|
-
const description = block.description?.trim();
|
|
153
|
+
const description = block.description?.trim() || toolInputDescription(block.input);
|
|
125
154
|
fragments.push(description
|
|
126
155
|
? `▸ ${block.name} · ${oneLine(description, TOOL_DESCRIPTION_MAX_CHARS)}`
|
|
127
156
|
: `▸ ${block.name}`);
|
package/dist/ai-team-runner.d.ts
CHANGED
|
@@ -91,7 +91,7 @@ export declare const CHAT_INVITE_PER_LINE = 4;
|
|
|
91
91
|
* 每行最多 [CHAT_INVITE_PER_LINE] 人,第二行起用 `继续邀请 …`;除负责人外没人时给
|
|
92
92
|
* `还没有邀请其他成员入群`(仍两条,不伪造成员)。
|
|
93
93
|
*/
|
|
94
|
-
export declare function chatIntroLines(team: Pick<AiTeam, "
|
|
94
|
+
export declare function chatIntroLines(team: Pick<AiTeam, "members">, chatTitle: string): string[];
|
|
95
95
|
/** 开工发言里的一条依据(来自 `step.dependsOn` 解析出的上游步骤)。 */
|
|
96
96
|
export interface AiTeamStepBasis {
|
|
97
97
|
seq: number;
|
|
@@ -232,6 +232,7 @@ export declare class AiTeamRunner {
|
|
|
232
232
|
/**
|
|
233
233
|
* 开工发言(S4):成员**自己发的真实发言**(头像 + 名字 + 气泡),不是居中 notice;
|
|
234
234
|
* 文案由 [stepStartText] 拼(依据只来自 `step.dependsOn`,不引 `decision.message` / 报告正文 / 提示词文本)。
|
|
235
|
+
* 工作步骤与负责人步骤都走这里:负责人轮(制定计划 / 安排下一步 / 回应用户)同样要让人看见他开工了。
|
|
235
236
|
*/
|
|
236
237
|
private postStepStart;
|
|
237
238
|
/**
|
package/dist/ai-team-runner.js
CHANGED
|
@@ -6,7 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import { dispatchAgentForTask, resolveTaskDispatchTarget, sendToAgentSession, stopAgentSession, } from "./agent-dispatch.js";
|
|
7
7
|
import { AI_TEAM_REPORT_DIR, aiTeamChatHistoryPath, aiTeamHandoffPath, aiTeamReportPath, buildLeaderFollowupPrompt, buildLeaderFormatRetryPrompt, buildAiTeamObjective, buildLeaderKickoffPrompt, buildMemberPrompt, leaderOf, parseLeaderDecision, renderChatHistoryFile, renderHandoffFile, } from "./ai-team-prompts.js";
|
|
8
8
|
import { AI_TEAM_STARTUP_WINDOW_MS, classifyCandidateFailure, classifyStartupFailure, isDegradableWorkFailure, isModelUnknownBeforeDispatch, } from "./ai-team-availability.js";
|
|
9
|
-
import { AI_TEAM_ACTIVE_RUN_STATUSES, AI_TEAM_DETAIL_CHAT_TURNS, AI_TEAM_TERMINAL_RUN_STATUSES, agentKey, memberAgents, } from "./ai-team-types.js";
|
|
9
|
+
import { AI_TEAM_ACTIVE_RUN_STATUSES, AI_TEAM_DETAIL_CHAT_TURNS, AI_TEAM_TERMINAL_RUN_STATUSES, agentKey, aiTeamChatTitle, memberAgents, } from "./ai-team-types.js";
|
|
10
10
|
import { renderLiveStepText } from "./ai-team-live.js";
|
|
11
11
|
import { getErrorMessage } from "./error-utils.js";
|
|
12
12
|
import { activityState } from "./missions.js";
|
|
@@ -190,9 +190,9 @@ function chatStepLabel(seq, title) {
|
|
|
190
190
|
* 每行最多 [CHAT_INVITE_PER_LINE] 人,第二行起用 `继续邀请 …`;除负责人外没人时给
|
|
191
191
|
* `还没有邀请其他成员入群`(仍两条,不伪造成员)。
|
|
192
192
|
*/
|
|
193
|
-
export function chatIntroLines(team) {
|
|
194
|
-
const
|
|
195
|
-
const lines = [
|
|
193
|
+
export function chatIntroLines(team, chatTitle) {
|
|
194
|
+
const title = chatTitle.trim();
|
|
195
|
+
const lines = [title ? `创建了团队群聊「${title}」` : "创建了团队群聊"];
|
|
196
196
|
const invitees = team.members.filter((member) => !member.isLeader)
|
|
197
197
|
.map((member) => member.name.trim())
|
|
198
198
|
.filter(Boolean);
|
|
@@ -575,8 +575,14 @@ export class AiTeamRunner {
|
|
|
575
575
|
// 群聊回合只给最近这一段(§4.4);没有群聊会话的旧运行给空数组,前端不用判 null。
|
|
576
576
|
const messages = run.chatSessionId ? this.ops.snapshot(run.chatSessionId)?.messages ?? [] : [];
|
|
577
577
|
const currentTeam = this.storage.getAiTeam(run.teamId);
|
|
578
|
+
const binding = run.chatSessionId ? this.storage.getSessionWorkspace(run.chatSessionId) : null;
|
|
579
|
+
const task = (binding?.workspaceTaskId
|
|
580
|
+
? this.storage.getWandTaskByWorkspaceTaskId(binding.workspaceTaskId) : null)
|
|
581
|
+
?? this.storage.getWandTask(run.taskId);
|
|
578
582
|
return {
|
|
579
583
|
run,
|
|
584
|
+
chatTitle: aiTeamChatTitle(task?.title),
|
|
585
|
+
chatTitleUpdatedAt: task?.updatedAt ?? "",
|
|
580
586
|
steps,
|
|
581
587
|
memberStates,
|
|
582
588
|
// 别把展示字段写回 run.team:运行快照里的执行候选/职责由 runner 独立管理。
|
|
@@ -1073,6 +1079,7 @@ export class AiTeamRunner {
|
|
|
1073
1079
|
/**
|
|
1074
1080
|
* 开工发言(S4):成员**自己发的真实发言**(头像 + 名字 + 气泡),不是居中 notice;
|
|
1075
1081
|
* 文案由 [stepStartText] 拼(依据只来自 `step.dependsOn`,不引 `decision.message` / 报告正文 / 提示词文本)。
|
|
1082
|
+
* 工作步骤与负责人步骤都走这里:负责人轮(制定计划 / 安排下一步 / 回应用户)同样要让人看见他开工了。
|
|
1076
1083
|
*/
|
|
1077
1084
|
postStepStart(run, step, member, agent, reusable) {
|
|
1078
1085
|
this.postTurn(run, {
|
|
@@ -1241,7 +1248,10 @@ export class AiTeamRunner {
|
|
|
1241
1248
|
// 开工发言(S4):就发在这个位置(原来那条居中 notice 的地方),所以候选起不来的那次尝试
|
|
1242
1249
|
// 也会留一句「我正在开始工作」+ 随后的降级/失败出口;作者署名与报告同一套口径,
|
|
1243
1250
|
// 复用会话时带真实 sessionId,新建在 open 前未知;若 open 失败,失败报告也可能无 ID。
|
|
1244
|
-
|
|
1251
|
+
// 负责人步骤同样要发:负责人一轮可能要读代码好几分钟,中途只在群里留一条 live 卡,
|
|
1252
|
+
// 群聊看上去就是「工位上写着工作中、消息里什么都没有」,等他交出计划才突然出现一屏文字。
|
|
1253
|
+
// 设计 v2 的口径本来就是「步骤开工时该成员自己发一条」,工作步骤那条门是从旧 notice 继承来的。
|
|
1254
|
+
if (member)
|
|
1245
1255
|
this.postStepStart(run, running, member, agent, reusable);
|
|
1246
1256
|
try {
|
|
1247
1257
|
if (!member)
|
|
@@ -1545,7 +1555,7 @@ export class AiTeamRunner {
|
|
|
1545
1555
|
const firstChat = !run.chatSessionId;
|
|
1546
1556
|
try {
|
|
1547
1557
|
if (firstChat) {
|
|
1548
|
-
run.chatSessionId = this.chat.open({ task, run, title:
|
|
1558
|
+
run.chatSessionId = this.chat.open({ task, run, title: aiTeamChatTitle(task.title) });
|
|
1549
1559
|
}
|
|
1550
1560
|
}
|
|
1551
1561
|
catch (error) {
|
|
@@ -1564,7 +1574,7 @@ export class AiTeamRunner {
|
|
|
1564
1574
|
// 首次建群:负责人先入群,再按名单邀请其余成员(2–3 条系统行,作者都是负责人)。
|
|
1565
1575
|
const leader = leaderOf(run.team);
|
|
1566
1576
|
const leaderAgent = leader ? memberAgents(leader)[0] ?? leader.agent : undefined;
|
|
1567
|
-
for (const line of chatIntroLines(run.team)) {
|
|
1577
|
+
for (const line of chatIntroLines(run.team, aiTeamChatTitle(task.title))) {
|
|
1568
1578
|
this.postNotice(run, line, run.chatSessionId, leader, leaderAgent);
|
|
1569
1579
|
}
|
|
1570
1580
|
}
|
package/dist/ai-team-types.d.ts
CHANGED
|
@@ -129,6 +129,8 @@ export interface AiTeamRun {
|
|
|
129
129
|
createdAt: string;
|
|
130
130
|
updatedAt: string;
|
|
131
131
|
}
|
|
132
|
+
/** 群聊名从任务标题派生;不修改团队定义,也不回写历史消息。 */
|
|
133
|
+
export declare function aiTeamChatTitle(taskTitle?: string | null): string;
|
|
132
134
|
/**
|
|
133
135
|
* 会话列表用的群聊标记:chat_session_id → 最近一次运行(`AiTeamRunChatMarker` 索引的 value)。
|
|
134
136
|
* 只带列表徽标和点开 IM 视图所需的入口信息,不暴露运行细节。
|
|
@@ -137,6 +139,8 @@ export interface AiTeamRunChatMarker {
|
|
|
137
139
|
runId: string;
|
|
138
140
|
teamId: string;
|
|
139
141
|
teamName: string;
|
|
142
|
+
/** 当前任务名派生的群名;旧服务端可能没有。 */
|
|
143
|
+
chatTitle?: string;
|
|
140
144
|
memberCount: number;
|
|
141
145
|
}
|
|
142
146
|
/**
|
|
@@ -226,6 +230,10 @@ export interface AiTeamLiveUpdate {
|
|
|
226
230
|
}
|
|
227
231
|
export interface AiTeamRunDetail {
|
|
228
232
|
run: AiTeamRun;
|
|
233
|
+
/** 仅供展示的群名,读取当前任务标题,不以团队名代替。 */
|
|
234
|
+
chatTitle?: string;
|
|
235
|
+
/** 任务标题的版本;任务改名不改变 run.updatedAt。 */
|
|
236
|
+
chatTitleUpdatedAt?: string;
|
|
229
237
|
steps: AiTeamStep[];
|
|
230
238
|
/** 运行中步骤所在会话的实时状态(等待授权 / 等待回答等)。 */
|
|
231
239
|
memberStates: Record<string, AgentActivityState>;
|
package/dist/ai-team-types.js
CHANGED
|
@@ -41,6 +41,10 @@ export function isSystemSiliconEmployee(employee) {
|
|
|
41
41
|
export const AI_TEAM_ACTIVE_RUN_STATUSES = ["running", "awaiting_approval", "waiting_user"];
|
|
42
42
|
/** 终态:不会再有 live 变化,runner 据此清掉该 run 的推送指纹,避免长跑服务按 run 数攒字符串。 */
|
|
43
43
|
export const AI_TEAM_TERMINAL_RUN_STATUSES = ["done", "failed", "stopped"];
|
|
44
|
+
/** 群聊名从任务标题派生;不修改团队定义,也不回写历史消息。 */
|
|
45
|
+
export function aiTeamChatTitle(taskTitle) {
|
|
46
|
+
return `${taskTitle?.trim() ?? ""}任务处理群`;
|
|
47
|
+
}
|
|
44
48
|
/** detail() 里群聊回合的截尾条数(§4.4):面板只给最近这一段,完整会话走「打开群聊」。 */
|
|
45
49
|
export const AI_TEAM_DETAIL_CHAT_TURNS = 200;
|
|
46
50
|
/** live 卡片文本的尾部保留上限(§4.9),超出部分回报 omittedChars 给前端显示「已省略前面 N 字」。 */
|
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-09-
|
|
4
|
-
"version": "4.80.
|
|
2
|
+
"commit": "1fbbefb6858936b5ae6af8fc19a86c66ad2a997c",
|
|
3
|
+
"builtAt": "2026-09-30T13:28:55.310Z",
|
|
4
|
+
"version": "4.80.1",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
|
@@ -151,6 +151,10 @@ export class ClaudePtyBridge extends EventEmitter {
|
|
|
151
151
|
if (!this.isRealChatInput(input)) {
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
|
+
if (this.chatEmitTimer) {
|
|
155
|
+
clearTimeout(this.chatEmitTimer);
|
|
156
|
+
this.chatEmitTimer = null;
|
|
157
|
+
}
|
|
154
158
|
const cleanInput = input.replace(/[\r\n]+$/, "").trim();
|
|
155
159
|
const now = new Date().toISOString();
|
|
156
160
|
// Add user message
|
|
@@ -754,8 +758,14 @@ export class ClaudePtyBridge extends EventEmitter {
|
|
|
754
758
|
const now = Date.now();
|
|
755
759
|
if (now - this.lastChatEmitAt < ClaudePtyBridge.CHAT_THROTTLE_MS) {
|
|
756
760
|
if (!this.chatEmitTimer) {
|
|
757
|
-
|
|
761
|
+
const streamedIndex = idx;
|
|
762
|
+
const timer = setTimeout(() => {
|
|
763
|
+
if (this.chatEmitTimer !== timer)
|
|
764
|
+
return;
|
|
758
765
|
this.chatEmitTimer = null;
|
|
766
|
+
if (this._exited || this.chatState.phase !== "responding"
|
|
767
|
+
|| this.chatState.assistantIndex !== streamedIndex)
|
|
768
|
+
return;
|
|
759
769
|
this.lastChatEmitAt = Date.now();
|
|
760
770
|
this.emitEvent({
|
|
761
771
|
type: "output.chat",
|
|
@@ -768,6 +778,7 @@ export class ClaudePtyBridge extends EventEmitter {
|
|
|
768
778
|
},
|
|
769
779
|
});
|
|
770
780
|
}, ClaudePtyBridge.CHAT_THROTTLE_MS);
|
|
781
|
+
this.chatEmitTimer = timer;
|
|
771
782
|
}
|
|
772
783
|
return;
|
|
773
784
|
}
|
|
@@ -784,6 +795,10 @@ export class ClaudePtyBridge extends EventEmitter {
|
|
|
784
795
|
});
|
|
785
796
|
}
|
|
786
797
|
finalizeResponse() {
|
|
798
|
+
if (this.chatEmitTimer) {
|
|
799
|
+
clearTimeout(this.chatEmitTimer);
|
|
800
|
+
this.chatEmitTimer = null;
|
|
801
|
+
}
|
|
787
802
|
const idx = this.chatState.assistantIndex;
|
|
788
803
|
if (idx !== null) {
|
|
789
804
|
const text = this.cleanForChat(this.chatState.buffer);
|
package/dist/cli.js
CHANGED
|
@@ -115,15 +115,6 @@ async function main() {
|
|
|
115
115
|
...config,
|
|
116
116
|
password: config.password === "change-me" ? "change-me" : "<set>",
|
|
117
117
|
appSecret: config.appSecret ? "<set>" : "",
|
|
118
|
-
systemAi: config.systemAi ? {
|
|
119
|
-
...config.systemAi,
|
|
120
|
-
apiKey: config.systemAi.apiKey ? "<set>" : "",
|
|
121
|
-
fallbacks: config.systemAi.fallbacks?.map((profile) => ({
|
|
122
|
-
...profile,
|
|
123
|
-
apiKey: profile.apiKey ? "<set>" : "",
|
|
124
|
-
fallbacks: undefined,
|
|
125
|
-
})),
|
|
126
|
-
} : undefined,
|
|
127
118
|
};
|
|
128
119
|
process.stdout.write(`${JSON.stringify(display, null, 2)}\n`);
|
|
129
120
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { WandStorage } from "./storage.js";
|
|
|
7
7
|
* 升级路径:老 JSON 里仍存有这些字段时,首次启动会被搬到 DB(见 migrateLegacyPreferencesToDb),
|
|
8
8
|
* 然后下一次 saveConfig 写回 JSON 时它们会被剥离(见 stripPreferenceFields)。
|
|
9
9
|
*/
|
|
10
|
-
export declare const PREFERENCE_KEYS: readonly ["defaultProvider", "defaultSessionKind", "defaultTaskWorktree", "defaultMode", "defaultCwd", "defaultModel", "defaultCodexModel", "defaultOpenCodeModel", "defaultGrokModel", "defaultQoderModel", "defaultPiModel", "defaultGeminiModel", "commitCli", "commitModel", "
|
|
10
|
+
export declare const PREFERENCE_KEYS: readonly ["defaultProvider", "defaultSessionKind", "defaultTaskWorktree", "defaultMode", "defaultCwd", "defaultModel", "defaultCodexModel", "defaultOpenCodeModel", "defaultGrokModel", "defaultQoderModel", "defaultPiModel", "defaultGeminiModel", "commitCli", "commitModel", "systemAiCli", "systemAiModel", "defaultThinkingEffort", "language", "cardDefaults", "inheritEnv"];
|
|
11
11
|
export type PreferenceKey = (typeof PREFERENCE_KEYS)[number];
|
|
12
12
|
export declare function isPreferenceKey(key: string): key is PreferenceKey;
|
|
13
13
|
/**
|
|
@@ -37,15 +37,7 @@ export declare function loadConfigWithStorage(configPath: string, storage: WandS
|
|
|
37
37
|
*/
|
|
38
38
|
export declare function applyStoragePreferences(config: WandConfig, storage: WandStorage): WandConfig;
|
|
39
39
|
/** Write a single preference value to DB and (in-place) update the live config object. */
|
|
40
|
-
export declare function writePreferenceToStorage(config: WandConfig, storage: WandStorage, key: PreferenceKey, value: unknown
|
|
41
|
-
deferCommitAiValidation?: boolean;
|
|
42
|
-
}): void;
|
|
43
|
-
/**
|
|
44
|
-
* Kept as a compatibility seam for callers that validate preference batches.
|
|
45
|
-
* Commit API profiles are discovered from the configured tools at request time,
|
|
46
|
-
* so selecting API is valid even when no manual systemAi profile is stored.
|
|
47
|
-
*/
|
|
48
|
-
export declare function validateCommitAiConfig(_config: Pick<WandConfig, "commitAiSource" | "systemAi">): void;
|
|
40
|
+
export declare function writePreferenceToStorage(config: WandConfig, storage: WandStorage, key: PreferenceKey, value: unknown): void;
|
|
49
41
|
export declare function isExecutionMode(value: unknown): value is ExecutionMode;
|
|
50
42
|
export declare function getProviderDefaultModels(config: Pick<WandConfig, "defaultModel" | "defaultCodexModel" | "defaultOpenCodeModel" | "defaultGrokModel" | "defaultQoderModel" | "defaultPiModel" | "defaultGeminiModel">): {
|
|
51
43
|
claude: string;
|
package/dist/config.js
CHANGED
|
@@ -5,7 +5,6 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import { isRunningAsRoot } from "./env-utils.js";
|
|
8
|
-
import { normalizeSystemAiConfig, systemAiProfiles } from "./system-ai.js";
|
|
9
8
|
import { isSessionProvider } from "./session-provider.js";
|
|
10
9
|
import { isThinkingEffort } from "./structured-provider-common.js";
|
|
11
10
|
const DEFAULT_CONFIG_DIR = ".wand";
|
|
@@ -32,12 +31,9 @@ export const PREFERENCE_KEYS = [
|
|
|
32
31
|
"defaultGeminiModel",
|
|
33
32
|
"commitCli",
|
|
34
33
|
"commitModel",
|
|
35
|
-
"commitAiSource",
|
|
36
|
-
"systemAi",
|
|
37
34
|
"systemAiCli",
|
|
38
35
|
"systemAiModel",
|
|
39
36
|
"defaultThinkingEffort",
|
|
40
|
-
"structuredRunner",
|
|
41
37
|
"language",
|
|
42
38
|
"cardDefaults",
|
|
43
39
|
"inheritEnv",
|
|
@@ -106,20 +102,8 @@ export const defaultConfig = () => ({
|
|
|
106
102
|
defaultGeminiModel: "",
|
|
107
103
|
commitCli: "claude",
|
|
108
104
|
commitModel: "",
|
|
109
|
-
commitAiSource: "cli",
|
|
110
|
-
systemAi: {
|
|
111
|
-
id: crypto.randomUUID(),
|
|
112
|
-
enabled: false,
|
|
113
|
-
protocol: "openai",
|
|
114
|
-
baseUrl: "",
|
|
115
|
-
apiKey: "",
|
|
116
|
-
model: "",
|
|
117
|
-
authHeader: "bearer",
|
|
118
|
-
source: "custom",
|
|
119
|
-
},
|
|
120
105
|
systemAiModel: "",
|
|
121
106
|
defaultThinkingEffort: "off",
|
|
122
|
-
structuredRunner: "cli",
|
|
123
107
|
inheritEnv: true,
|
|
124
108
|
commandPresets: [
|
|
125
109
|
{
|
|
@@ -391,15 +375,6 @@ export function applyStoragePreferences(config, storage) {
|
|
|
391
375
|
if (typeof v === "string")
|
|
392
376
|
config.commitModel = v.trim();
|
|
393
377
|
}
|
|
394
|
-
if (storage.hasPreference(preferenceStorageKey("commitAiSource"))) {
|
|
395
|
-
const v = storage.getPreference(preferenceStorageKey("commitAiSource"), defaults.commitAiSource ?? "cli");
|
|
396
|
-
if (v === "cli" || v === "api")
|
|
397
|
-
config.commitAiSource = v;
|
|
398
|
-
}
|
|
399
|
-
if (storage.hasPreference(preferenceStorageKey("systemAi"))) {
|
|
400
|
-
const v = storage.getPreference(preferenceStorageKey("systemAi"), defaults.systemAi);
|
|
401
|
-
config.systemAi = normalizeSystemAiConfig(v, defaults.systemAi);
|
|
402
|
-
}
|
|
403
378
|
if (storage.hasPreference(preferenceStorageKey("systemAiCli"))) {
|
|
404
379
|
const v = storage.getPreference(preferenceStorageKey("systemAiCli"), defaults.systemAiCli);
|
|
405
380
|
if (isSessionProvider(v))
|
|
@@ -415,11 +390,6 @@ export function applyStoragePreferences(config, storage) {
|
|
|
415
390
|
if (isThinkingEffort(v))
|
|
416
391
|
config.defaultThinkingEffort = v;
|
|
417
392
|
}
|
|
418
|
-
if (storage.hasPreference(preferenceStorageKey("structuredRunner"))) {
|
|
419
|
-
const v = storage.getPreference(preferenceStorageKey("structuredRunner"), defaults.structuredRunner ?? "cli");
|
|
420
|
-
if (v === "cli" || v === "sdk")
|
|
421
|
-
config.structuredRunner = v;
|
|
422
|
-
}
|
|
423
393
|
if (storage.hasPreference(preferenceStorageKey("language"))) {
|
|
424
394
|
const v = storage.getPreference(preferenceStorageKey("language"), defaults.language ?? "");
|
|
425
395
|
if (typeof v === "string")
|
|
@@ -433,11 +403,10 @@ export function applyStoragePreferences(config, storage) {
|
|
|
433
403
|
const v = storage.getPreference(preferenceStorageKey("inheritEnv"), defaults.inheritEnv ?? true);
|
|
434
404
|
config.inheritEnv = v === false ? false : true;
|
|
435
405
|
}
|
|
436
|
-
validateCommitAiConfig(config);
|
|
437
406
|
return config;
|
|
438
407
|
}
|
|
439
408
|
/** Write a single preference value to DB and (in-place) update the live config object. */
|
|
440
|
-
export function writePreferenceToStorage(config, storage, key, value
|
|
409
|
+
export function writePreferenceToStorage(config, storage, key, value) {
|
|
441
410
|
const dbKey = preferenceStorageKey(key);
|
|
442
411
|
switch (key) {
|
|
443
412
|
case "defaultProvider": {
|
|
@@ -529,31 +498,6 @@ export function writePreferenceToStorage(config, storage, key, value, options =
|
|
|
529
498
|
config.commitModel = v;
|
|
530
499
|
break;
|
|
531
500
|
}
|
|
532
|
-
case "commitAiSource": {
|
|
533
|
-
if (value !== "cli" && value !== "api")
|
|
534
|
-
throw new Error(`无效 commit AI 来源: ${String(value)}`);
|
|
535
|
-
if (!options.deferCommitAiValidation) {
|
|
536
|
-
validateCommitAiConfig({ ...config, commitAiSource: value });
|
|
537
|
-
}
|
|
538
|
-
storage.setPreference(dbKey, value);
|
|
539
|
-
config.commitAiSource = value;
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
case "systemAi": {
|
|
543
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
544
|
-
throw new Error("systemAi 必须是对象。");
|
|
545
|
-
}
|
|
546
|
-
const normalized = normalizeSystemAiConfig(value, config.systemAi ?? defaultConfig().systemAi);
|
|
547
|
-
if (normalized.enabled && !systemAiProfiles(normalized, true).length) {
|
|
548
|
-
throw new Error("启用系统 AI API 时,至少需要一条地址、API Key 和模型完整的路由。");
|
|
549
|
-
}
|
|
550
|
-
if (!options.deferCommitAiValidation) {
|
|
551
|
-
validateCommitAiConfig({ ...config, systemAi: normalized });
|
|
552
|
-
}
|
|
553
|
-
storage.setPreference(dbKey, normalized);
|
|
554
|
-
config.systemAi = normalized;
|
|
555
|
-
break;
|
|
556
|
-
}
|
|
557
501
|
case "systemAiCli": {
|
|
558
502
|
if (!isSessionProvider(value))
|
|
559
503
|
throw new Error(`无效系统 AI CLI: ${String(value)}`);
|
|
@@ -575,14 +519,6 @@ export function writePreferenceToStorage(config, storage, key, value, options =
|
|
|
575
519
|
config.defaultThinkingEffort = v;
|
|
576
520
|
break;
|
|
577
521
|
}
|
|
578
|
-
case "structuredRunner": {
|
|
579
|
-
if (value !== "cli" && value !== "sdk")
|
|
580
|
-
throw new Error(`无效 structured runner: ${String(value)}`);
|
|
581
|
-
const v = value;
|
|
582
|
-
storage.setPreference(dbKey, v);
|
|
583
|
-
config.structuredRunner = v;
|
|
584
|
-
break;
|
|
585
|
-
}
|
|
586
522
|
case "language": {
|
|
587
523
|
const v = typeof value === "string" ? value.trim() : "";
|
|
588
524
|
storage.setPreference(dbKey, v);
|
|
@@ -605,12 +541,6 @@ export function writePreferenceToStorage(config, storage, key, value, options =
|
|
|
605
541
|
}
|
|
606
542
|
}
|
|
607
543
|
}
|
|
608
|
-
/**
|
|
609
|
-
* Kept as a compatibility seam for callers that validate preference batches.
|
|
610
|
-
* Commit API profiles are discovered from the configured tools at request time,
|
|
611
|
-
* so selecting API is valid even when no manual systemAi profile is stored.
|
|
612
|
-
*/
|
|
613
|
-
export function validateCommitAiConfig(_config) { }
|
|
614
544
|
function defaultCardExpandDefaults() {
|
|
615
545
|
return {
|
|
616
546
|
editCards: false,
|
|
@@ -815,11 +745,9 @@ function mergeWithDefaults(input) {
|
|
|
815
745
|
defaultGeminiModel: typeof input.defaultGeminiModel === "string" ? input.defaultGeminiModel.trim() : defaults.defaultGeminiModel,
|
|
816
746
|
commitCli: input.commitCli === "codex" || input.commitCli === "opencode" ? input.commitCli : "claude",
|
|
817
747
|
commitModel: typeof input.commitModel === "string" ? input.commitModel.trim() : defaults.commitModel,
|
|
818
|
-
commitAiSource: input.commitAiSource === "api" ? "api" : "cli",
|
|
819
748
|
systemAiCli: isSessionProvider(input.systemAiCli) ? input.systemAiCli : undefined,
|
|
820
749
|
systemAiModel: typeof input.systemAiModel === "string" ? input.systemAiModel.trim() : defaults.systemAiModel,
|
|
821
750
|
defaultThinkingEffort: isThinkingEffort(input.defaultThinkingEffort) ? input.defaultThinkingEffort : "off",
|
|
822
|
-
structuredRunner: (input.structuredRunner === "sdk" || input.structuredRunner === "cli") ? input.structuredRunner : defaults.structuredRunner,
|
|
823
751
|
inheritEnv: typeof input.inheritEnv === "boolean" ? input.inheritEnv : (defaults.inheritEnv ?? true),
|
|
824
752
|
};
|
|
825
753
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AiTextRequest } from "./types.js";
|
|
2
2
|
import { GitStatusResult, PushResult, QuickCommitResult, SessionProvider, SessionSnapshot, TagHeadResult } from "./types.js";
|
|
3
3
|
export type QuickCommitErrorCode = "CWD_MISSING" | "NO_CWD" | "NOT_A_GIT_REPO" | "NO_COMMIT" | "NOTHING_TO_COMMIT" | "NOTHING_TO_PUSH" | "EMPTY_MESSAGE" | "EMPTY_TAG" | "EMPTY_AI_MESSAGE" | "AI_FALLBACK_FAILED" | "INVALID_AI_TAG" | "TAG_EXISTS" | "GIT_ADD_FAILED" | "GIT_DIFF_FAILED" | "GIT_COMMIT_FAILED" | "GIT_TAG_FAILED" | "CLAUDE_CLI_MISSING" | "CLAUDE_CLI_FAILED" | "CLAUDE_TIMEOUT";
|
|
4
4
|
/** Non-blocking status implementation used by HTTP routes. */
|
|
@@ -23,7 +23,6 @@ export interface QuickCommitAiOptions {
|
|
|
23
23
|
model?: string | null;
|
|
24
24
|
thinkingEffort?: SessionSnapshot["thinkingEffort"];
|
|
25
25
|
inheritEnv?: boolean;
|
|
26
|
-
systemAi?: import("./types.js").SystemAiConfig;
|
|
27
26
|
/** 内置「系统运维」员工的角色设定:作为系统提示前缀注入。 */
|
|
28
27
|
opsPersona?: string;
|
|
29
28
|
/** CLI 降级链(按顺序);未设置时只用 provider/model 这一次调用。 */
|
|
@@ -46,9 +45,8 @@ export interface CommitInputOptions {
|
|
|
46
45
|
/** 角色设定走系统提示前缀;任务自己的输出格式与约束必须排在后面。 */
|
|
47
46
|
export declare function withOpsPersona(request: AiTextRequest, persona?: string): AiTextRequest;
|
|
48
47
|
/**
|
|
49
|
-
* Run a lightweight AI request through the
|
|
50
|
-
*
|
|
51
|
-
* CLI chain. CLI mode walks that chain in order.
|
|
48
|
+
* Run a lightweight AI request through the system employee's CLI chain.
|
|
49
|
+
* 所有 Wand 自有文本调用都走本机已安装的 CLI,没有任何直连 API 分支。
|
|
52
50
|
*/
|
|
53
51
|
export declare function callConfiguredAiText(request: AiTextRequest, cwd: string, language: string, opts: QuickCommitAiOptions): Promise<string>;
|
|
54
52
|
export interface GenerateCommitMessageResult {
|
|
@@ -64,7 +62,6 @@ interface TagHeadOptions {
|
|
|
64
62
|
model?: string | null;
|
|
65
63
|
thinkingEffort?: SessionSnapshot["thinkingEffort"];
|
|
66
64
|
inheritEnv?: boolean;
|
|
67
|
-
systemAi?: import("./types.js").SystemAiConfig;
|
|
68
65
|
/** Explicit tag name. If empty and `autoTag` is true, ask the session provider to generate one. */
|
|
69
66
|
tag?: string;
|
|
70
67
|
autoTag?: boolean;
|