@co0ontty/wand 4.89.1 → 4.89.2-beta.gbb4a633
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-employee-binding.d.ts +1 -0
- package/dist/ai-team-employee-binding.js +1 -1
- package/dist/ai-team-types.d.ts +2 -2
- package/dist/build-info.json +4 -4
- package/dist/daemon-maintenance-targets.js +1 -1
- package/dist/default-employee.d.ts +5 -1
- package/dist/default-employee.js +17 -5
- package/dist/fixed-employee-avatar.d.ts +19 -0
- package/dist/fixed-employee-avatar.js +13 -0
- package/dist/plush-avatar.d.ts +100 -0
- package/dist/plush-avatar.js +96 -0
- package/dist/process-manager.js +4 -1
- package/dist/server-employee-routes.d.ts +4 -2
- package/dist/server-employee-routes.js +37 -9
- package/dist/server-task-routes.js +6 -0
- package/dist/server.js +6 -1
- package/dist/speech-service.js +1 -1
- package/dist/storage.d.ts +11 -4
- package/dist/storage.js +66 -31
- package/dist/system-employee.d.ts +2 -2
- package/dist/system-employee.js +7 -7
- package/dist/wand-task-sync.js +13 -2
- package/dist/web-ui/content/ai-teams.js +12 -12
- package/dist/web-ui/content/plush-avatar.js +4220 -0
- package/dist/web-ui/content/scripts.js +450 -199
- package/dist/web-ui/content/tailwind.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -0
- package/dist/web-ui/embedded-assets.js +4 -3
- package/dist/web-ui/scripts.d.ts +5 -0
- package/dist/web-ui/scripts.js +25 -5
- package/package.json +3 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type AiTeam, type AiTeamMember, type SiliconEmployee } from "./ai-team-types.js";
|
|
2
2
|
export interface AiTeamEmployeeSource {
|
|
3
3
|
getSiliconEmployee(id: string): SiliconEmployee | null;
|
|
4
|
+
getSiliconEmployeeDefinition?(id: string): SiliconEmployee | null;
|
|
4
5
|
}
|
|
5
6
|
export declare function teamMemberEmployee(member: AiTeamMember): SiliconEmployee | undefined;
|
|
6
7
|
/** Only storage restores this private field; HTTP parsing deliberately ignores it. */
|
|
@@ -31,7 +31,7 @@ export function requireTeamEmployee(source, id) {
|
|
|
31
31
|
}
|
|
32
32
|
// The default partner's read projection includes short-term habits; they are not team snapshot data.
|
|
33
33
|
return isDefaultSiliconEmployee(employee)
|
|
34
|
-
? defaultEmployeeDefinition(employee.agents, employee.updatedAt, employee, null)
|
|
34
|
+
? defaultEmployeeDefinition(employee.agents, employee.updatedAt, source.getSiliconEmployeeDefinition?.(id) ?? employee, null)
|
|
35
35
|
: employee;
|
|
36
36
|
}
|
|
37
37
|
/** Resolve once per new run. Team duty/role/leadership remain local; no knowledge is read. */
|
package/dist/ai-team-types.d.ts
CHANGED
|
@@ -71,8 +71,8 @@ export interface SiliconEmployee {
|
|
|
71
71
|
/** 内置标签按 systemKey 固定;普通员工的标签由用户维护。旧客户端可不传。 */
|
|
72
72
|
tags?: string[];
|
|
73
73
|
/**
|
|
74
|
-
* 非空 = Wand
|
|
75
|
-
*
|
|
74
|
+
* 非空 = Wand 内置身份、标签,不可归档、不可删除。
|
|
75
|
+
* 两个固定猫头像员工可编辑资料/人设/候选;其他内置员工保留既有资料锁定策略。
|
|
76
76
|
*/
|
|
77
77
|
systemKey?: string;
|
|
78
78
|
archivedAt?: string;
|
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-10-
|
|
4
|
-
"version": "4.89.
|
|
5
|
-
"channel": "
|
|
2
|
+
"commit": "bb4a633daf2c603b6bd7054dc0f9fe2b7bcbd088",
|
|
3
|
+
"builtAt": "2026-10-10T03:53:15.356Z",
|
|
4
|
+
"version": "4.89.2-beta.gbb4a633",
|
|
5
|
+
"channel": "beta"
|
|
6
6
|
}
|
|
@@ -173,7 +173,7 @@ export async function createDaemonMaintenanceTargets(configPath, pty, structured
|
|
|
173
173
|
const runs = await legacy.request("structuredList");
|
|
174
174
|
await Promise.all([
|
|
175
175
|
...sessions.filter(session => session.status === "running")
|
|
176
|
-
.map(session => legacy.request("kill", { sessionId: session.sessionId, signal: "
|
|
176
|
+
.map(session => legacy.request("kill", { sessionId: session.sessionId, signal: "SIGHUP" })),
|
|
177
177
|
...runs.filter(run => run.status === "running")
|
|
178
178
|
.map(run => legacy.request("structuredKill", { runId: run.runId, signal: "SIGTERM" })),
|
|
179
179
|
]);
|
|
@@ -5,7 +5,11 @@ import type { SessionProvider, SessionSnapshot } from "./types.js";
|
|
|
5
5
|
import type { UserMemoryProfile } from "./user-memory-types.js";
|
|
6
6
|
export declare const DEFAULT_EMPLOYEE_DUTY = "\u9ED8\u8BA4\u4EFB\u52A1\u4F19\u4F34\uFF1A\u7406\u89E3\u4F60\u7684\u9700\u6C42\uFF0C\u6309\u8BC1\u636E\u63A8\u8FDB\u5DE5\u4F5C\uFF0C\u5E76\u9010\u6B65\u9002\u5E94\u4F60\u7684\u6C9F\u901A\u4E0E\u4EA4\u4ED8\u4E60\u60EF\u3002";
|
|
7
7
|
export declare const DEFAULT_EMPLOYEE_PROMPT: string;
|
|
8
|
-
/**
|
|
8
|
+
/** Old releases persisted generated habits after this exact base; keep the column intact,
|
|
9
|
+
* but do not replay that expired generated tail as user-authored rules.
|
|
10
|
+
*/
|
|
11
|
+
export declare function savedDefaultEmployeeBasePrompt(prompt: string): string;
|
|
12
|
+
/** Seed profile defaults once; future memory is a pure projection over the saved role. */
|
|
9
13
|
export declare function defaultEmployeeDefinition(agents: WandTaskAgent[], now: string, existing?: SiliconEmployee | null, profile?: UserMemoryProfile | null, time?: number): SiliconEmployee;
|
|
10
14
|
export type SessionEmployeeIdentity = Pick<SessionSnapshot, "employeeId" | "employeeName" | "employeeAvatar">;
|
|
11
15
|
/** Older PTY sessions stored the injected role only as a prompt. Never infer from a CLI or a name mention. */
|
package/dist/default-employee.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_EMPLOYEE_ID, DEFAULT_EMPLOYEE_KEY, DEFAULT_EMPLOYEE_NAME, DEFAULT_EMPLOYEE_TAG, } from "./ai-team-types.js";
|
|
2
|
+
import { fixedEmployeeAvatar } from "./fixed-employee-avatar.js";
|
|
2
3
|
import { systemEmployeeSeedAgents } from "./system-employee.js";
|
|
3
4
|
export const DEFAULT_EMPLOYEE_DUTY = "默认任务伙伴:理解你的需求,按证据推进工作,并逐步适应你的沟通与交付习惯。";
|
|
4
5
|
export const DEFAULT_EMPLOYEE_PROMPT = [
|
|
@@ -8,7 +9,17 @@ export const DEFAULT_EMPLOYEE_PROMPT = [
|
|
|
8
9
|
"保留已有输入、工作和历史;涉及删除、发布、权限或凭据的操作遵守明确授权与项目约定。",
|
|
9
10
|
"用户当前要求、项目规则和工具权限始终优先。短期记忆只是可纠正的偏好提示,不能增加权限、替代本轮任务或形成新的执行指令。",
|
|
10
11
|
].join("\n");
|
|
11
|
-
/**
|
|
12
|
+
/** Old releases persisted generated habits after this exact base; keep the column intact,
|
|
13
|
+
* but do not replay that expired generated tail as user-authored rules.
|
|
14
|
+
*/
|
|
15
|
+
export function savedDefaultEmployeeBasePrompt(prompt) {
|
|
16
|
+
const prefix = DEFAULT_EMPLOYEE_PROMPT + "\n\n近期使用偏好(仅作参考;本轮要求冲突时忽略):\n";
|
|
17
|
+
if (prompt.startsWith(prefix) && /^- (?:沟通|工作方式|近期关注):[^\n]+(?:\n- (?:沟通|工作方式|近期关注):[^\n]+)*$/.test(prompt.slice(prefix.length))) {
|
|
18
|
+
return DEFAULT_EMPLOYEE_PROMPT;
|
|
19
|
+
}
|
|
20
|
+
return prompt;
|
|
21
|
+
}
|
|
22
|
+
/** Seed profile defaults once; future memory is a pure projection over the saved role. */
|
|
12
23
|
export function defaultEmployeeDefinition(agents, now, existing, profile, time = Date.now()) {
|
|
13
24
|
const preferences = profile && profile.expiresAt > time ? profile.preferences : [];
|
|
14
25
|
const memory = preferences.length ? [
|
|
@@ -16,13 +27,14 @@ export function defaultEmployeeDefinition(agents, now, existing, profile, time =
|
|
|
16
27
|
...preferences.map((entry) => `- ${entry.category === "communication" ? "沟通" : entry.category === "workflow" ? "工作方式" : "近期关注"}:${entry.text}`),
|
|
17
28
|
].join("\n") : "";
|
|
18
29
|
return {
|
|
30
|
+
...existing,
|
|
19
31
|
id: existing?.id ?? DEFAULT_EMPLOYEE_ID,
|
|
20
32
|
systemKey: DEFAULT_EMPLOYEE_KEY,
|
|
21
33
|
tags: [DEFAULT_EMPLOYEE_TAG],
|
|
22
|
-
name: DEFAULT_EMPLOYEE_NAME,
|
|
23
|
-
duty: DEFAULT_EMPLOYEE_DUTY,
|
|
24
|
-
prompt: DEFAULT_EMPLOYEE_PROMPT + memory,
|
|
25
|
-
avatar:
|
|
34
|
+
name: existing?.name ?? DEFAULT_EMPLOYEE_NAME,
|
|
35
|
+
duty: existing?.duty ?? DEFAULT_EMPLOYEE_DUTY,
|
|
36
|
+
prompt: savedDefaultEmployeeBasePrompt(existing?.prompt ?? DEFAULT_EMPLOYEE_PROMPT) + memory,
|
|
37
|
+
avatar: existing?.avatar ?? fixedEmployeeAvatar({ id: DEFAULT_EMPLOYEE_ID, systemKey: DEFAULT_EMPLOYEE_KEY }),
|
|
26
38
|
agents: agents.length ? agents : systemEmployeeSeedAgents(),
|
|
27
39
|
createdAt: existing?.createdAt ?? now,
|
|
28
40
|
updatedAt: now,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const FIXED_EMPLOYEE_AVATARS: readonly [{
|
|
2
|
+
readonly id: "e_wand_default";
|
|
3
|
+
readonly systemKey: "wand-default";
|
|
4
|
+
readonly avatar: "plush-cat:v1:silver";
|
|
5
|
+
}, {
|
|
6
|
+
readonly id: "e_wand_ops";
|
|
7
|
+
readonly systemKey: "wand-ops";
|
|
8
|
+
readonly avatar: "plush-cat:v1:orange";
|
|
9
|
+
}];
|
|
10
|
+
/** Stable identity pairs only: names and source employees never determine policy. */
|
|
11
|
+
export declare function fixedEmployeeAvatar(identity: {
|
|
12
|
+
id?: string | null;
|
|
13
|
+
systemKey?: string | null;
|
|
14
|
+
}): string | null;
|
|
15
|
+
/** The avatar exception does not change any other built-in employee's edit policy. */
|
|
16
|
+
export declare function isFixedAvatarEmployee(identity: {
|
|
17
|
+
id?: string | null;
|
|
18
|
+
systemKey?: string | null;
|
|
19
|
+
}): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DEFAULT_EMPLOYEE_ID, DEFAULT_EMPLOYEE_KEY, SYSTEM_EMPLOYEE_ID, SYSTEM_EMPLOYEE_KEY } from "./ai-team-types.js";
|
|
2
|
+
export const FIXED_EMPLOYEE_AVATARS = [
|
|
3
|
+
{ id: DEFAULT_EMPLOYEE_ID, systemKey: DEFAULT_EMPLOYEE_KEY, avatar: "plush-cat:v1:silver" },
|
|
4
|
+
{ id: SYSTEM_EMPLOYEE_ID, systemKey: SYSTEM_EMPLOYEE_KEY, avatar: "plush-cat:v1:orange" },
|
|
5
|
+
];
|
|
6
|
+
/** Stable identity pairs only: names and source employees never determine policy. */
|
|
7
|
+
export function fixedEmployeeAvatar(identity) {
|
|
8
|
+
return FIXED_EMPLOYEE_AVATARS.find((entry) => entry.id === identity.id && entry.systemKey === identity.systemKey)?.avatar ?? null;
|
|
9
|
+
}
|
|
10
|
+
/** The avatar exception does not change any other built-in employee's edit policy. */
|
|
11
|
+
export function isFixedAvatarEmployee(identity) {
|
|
12
|
+
return fixedEmployeeAvatar(identity) !== null;
|
|
13
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** Shared, versioned employee avatar contract. Keep v1 option IDs and seed rules stable across clients. */
|
|
2
|
+
export declare const PLUSH_SHAPES: readonly [{
|
|
3
|
+
readonly id: "heart";
|
|
4
|
+
readonly label: "心形";
|
|
5
|
+
}, {
|
|
6
|
+
readonly id: "triangle";
|
|
7
|
+
readonly label: "圆角三角";
|
|
8
|
+
}, {
|
|
9
|
+
readonly id: "diamond";
|
|
10
|
+
readonly label: "菱形";
|
|
11
|
+
}, {
|
|
12
|
+
readonly id: "round";
|
|
13
|
+
readonly label: "圆形";
|
|
14
|
+
}, {
|
|
15
|
+
readonly id: "square";
|
|
16
|
+
readonly label: "圆角方形";
|
|
17
|
+
}, {
|
|
18
|
+
readonly id: "capsule";
|
|
19
|
+
readonly label: "胶囊";
|
|
20
|
+
}];
|
|
21
|
+
export declare const PLUSH_COLORS: readonly [{
|
|
22
|
+
readonly id: "coral";
|
|
23
|
+
readonly label: "珊瑚";
|
|
24
|
+
readonly color: "#E99589";
|
|
25
|
+
}, {
|
|
26
|
+
readonly id: "sage";
|
|
27
|
+
readonly label: "灰绿";
|
|
28
|
+
readonly color: "#A9BDA7";
|
|
29
|
+
}, {
|
|
30
|
+
readonly id: "lavender";
|
|
31
|
+
readonly label: "淡紫";
|
|
32
|
+
readonly color: "#BFB0D9";
|
|
33
|
+
}, {
|
|
34
|
+
readonly id: "cream";
|
|
35
|
+
readonly label: "奶油";
|
|
36
|
+
readonly color: "#E8D8B8";
|
|
37
|
+
}, {
|
|
38
|
+
readonly id: "blue";
|
|
39
|
+
readonly label: "雾蓝";
|
|
40
|
+
readonly color: "#A9C6D5";
|
|
41
|
+
}, {
|
|
42
|
+
readonly id: "ochre";
|
|
43
|
+
readonly label: "赭黄";
|
|
44
|
+
readonly color: "#D4AE65";
|
|
45
|
+
}];
|
|
46
|
+
export declare const PLUSH_GLASSES: readonly [{
|
|
47
|
+
readonly id: "none";
|
|
48
|
+
readonly label: "无眼镜";
|
|
49
|
+
}, {
|
|
50
|
+
readonly id: "gold";
|
|
51
|
+
readonly label: "金框眼镜";
|
|
52
|
+
}, {
|
|
53
|
+
readonly id: "ink";
|
|
54
|
+
readonly label: "黑框眼镜";
|
|
55
|
+
}];
|
|
56
|
+
export declare const PLUSH_HATS: readonly [{
|
|
57
|
+
readonly id: "none";
|
|
58
|
+
readonly label: "无帽子";
|
|
59
|
+
}, {
|
|
60
|
+
readonly id: "beanie";
|
|
61
|
+
readonly label: "针织帽";
|
|
62
|
+
}, {
|
|
63
|
+
readonly id: "beret";
|
|
64
|
+
readonly label: "贝雷帽";
|
|
65
|
+
}];
|
|
66
|
+
export type PlushShape = typeof PLUSH_SHAPES[number]["id"];
|
|
67
|
+
export type PlushColor = typeof PLUSH_COLORS[number]["id"];
|
|
68
|
+
export type PlushGlasses = typeof PLUSH_GLASSES[number]["id"];
|
|
69
|
+
export type PlushHat = typeof PLUSH_HATS[number]["id"];
|
|
70
|
+
export interface PlushAvatarConfig {
|
|
71
|
+
version: 1;
|
|
72
|
+
shape: PlushShape;
|
|
73
|
+
color: PlushColor;
|
|
74
|
+
glasses: PlushGlasses;
|
|
75
|
+
hat: PlushHat;
|
|
76
|
+
}
|
|
77
|
+
/** Two explicit source-derived cat heads; geometric defaults and legacy pixels stay unchanged. */
|
|
78
|
+
export interface PlushCatAvatarConfig {
|
|
79
|
+
version: 1;
|
|
80
|
+
kind: "cat";
|
|
81
|
+
coat: "silver" | "orange";
|
|
82
|
+
}
|
|
83
|
+
export type PlushRenderConfig = PlushAvatarConfig | PlushCatAvatarConfig;
|
|
84
|
+
export declare function isPlushCatAvatar(config: PlushRenderConfig): config is PlushCatAvatarConfig;
|
|
85
|
+
export declare function parsePlushCatAvatar(value: string): PlushCatAvatarConfig | null;
|
|
86
|
+
export declare function encodePlushCatAvatar(config: PlushCatAvatarConfig): string;
|
|
87
|
+
/** Common identity projection; only an explicit cat value opts into this exception. */
|
|
88
|
+
export declare function resolveEmployeeAvatar(identity: PlushAvatarIdentity): PlushRenderConfig | null;
|
|
89
|
+
export interface PlushAvatarIdentity {
|
|
90
|
+
id?: string | null;
|
|
91
|
+
name?: string | null;
|
|
92
|
+
avatar?: string | null;
|
|
93
|
+
}
|
|
94
|
+
/** Only the exact known v1 contract is accepted for explicit configuration; no URL or future-version guessing. */
|
|
95
|
+
export declare function parsePlushAvatar(value: string): PlushAvatarConfig | null;
|
|
96
|
+
export declare function encodePlushAvatar(config: PlushAvatarConfig): string;
|
|
97
|
+
/** Pure presentation default; never persist this from a GET, a render, or a preview. */
|
|
98
|
+
export declare function defaultPlushAvatar({ id, name }: Pick<PlushAvatarIdentity, "id" | "name">): PlushAvatarConfig;
|
|
99
|
+
/** Uploaded photos and explicit legacy cats keep their renderer; corrupt old values fall back without migration. */
|
|
100
|
+
export declare function resolvePlushAvatar(identity: PlushAvatarIdentity): PlushAvatarConfig | null;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** Shared, versioned employee avatar contract. Keep v1 option IDs and seed rules stable across clients. */
|
|
2
|
+
export const PLUSH_SHAPES = [
|
|
3
|
+
{ id: "heart", label: "心形" },
|
|
4
|
+
{ id: "triangle", label: "圆角三角" },
|
|
5
|
+
{ id: "diamond", label: "菱形" },
|
|
6
|
+
{ id: "round", label: "圆形" },
|
|
7
|
+
{ id: "square", label: "圆角方形" },
|
|
8
|
+
{ id: "capsule", label: "胶囊" },
|
|
9
|
+
];
|
|
10
|
+
export const PLUSH_COLORS = [
|
|
11
|
+
{ id: "coral", label: "珊瑚", color: "#E99589" },
|
|
12
|
+
{ id: "sage", label: "灰绿", color: "#A9BDA7" },
|
|
13
|
+
{ id: "lavender", label: "淡紫", color: "#BFB0D9" },
|
|
14
|
+
{ id: "cream", label: "奶油", color: "#E8D8B8" },
|
|
15
|
+
{ id: "blue", label: "雾蓝", color: "#A9C6D5" },
|
|
16
|
+
{ id: "ochre", label: "赭黄", color: "#D4AE65" },
|
|
17
|
+
];
|
|
18
|
+
export const PLUSH_GLASSES = [
|
|
19
|
+
{ id: "none", label: "无眼镜" },
|
|
20
|
+
{ id: "gold", label: "金框眼镜" },
|
|
21
|
+
{ id: "ink", label: "黑框眼镜" },
|
|
22
|
+
];
|
|
23
|
+
export const PLUSH_HATS = [
|
|
24
|
+
{ id: "none", label: "无帽子" },
|
|
25
|
+
{ id: "beanie", label: "针织帽" },
|
|
26
|
+
{ id: "beret", label: "贝雷帽" },
|
|
27
|
+
];
|
|
28
|
+
export function isPlushCatAvatar(config) {
|
|
29
|
+
return "kind" in config && config.kind === "cat";
|
|
30
|
+
}
|
|
31
|
+
export function parsePlushCatAvatar(value) {
|
|
32
|
+
if (value === "plush-cat:v1:silver")
|
|
33
|
+
return { version: 1, kind: "cat", coat: "silver" };
|
|
34
|
+
if (value === "plush-cat:v1:orange")
|
|
35
|
+
return { version: 1, kind: "cat", coat: "orange" };
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
export function encodePlushCatAvatar(config) {
|
|
39
|
+
const value = `plush-cat:v${config.version}:${config.coat}`;
|
|
40
|
+
if (config.kind !== "cat" || !parsePlushCatAvatar(value))
|
|
41
|
+
throw new Error("毛绒猫头像配置无效。");
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
/** Common identity projection; only an explicit cat value opts into this exception. */
|
|
45
|
+
export function resolveEmployeeAvatar(identity) {
|
|
46
|
+
return parsePlushCatAvatar(identity.avatar ?? "") ?? resolvePlushAvatar(identity);
|
|
47
|
+
}
|
|
48
|
+
function hasId(options, id) {
|
|
49
|
+
return options.some(option => option.id === id);
|
|
50
|
+
}
|
|
51
|
+
/** Only the exact known v1 contract is accepted for explicit configuration; no URL or future-version guessing. */
|
|
52
|
+
export function parsePlushAvatar(value) {
|
|
53
|
+
const parts = value.split(":");
|
|
54
|
+
if (parts.length !== 6 || parts[0] !== "plush" || parts[1] !== "v1")
|
|
55
|
+
return null;
|
|
56
|
+
const [, , shape, color, glasses, hat] = parts;
|
|
57
|
+
if (!hasId(PLUSH_SHAPES, shape) || !hasId(PLUSH_COLORS, color)
|
|
58
|
+
|| !hasId(PLUSH_GLASSES, glasses) || !hasId(PLUSH_HATS, hat))
|
|
59
|
+
return null;
|
|
60
|
+
return { version: 1, shape, color, glasses, hat };
|
|
61
|
+
}
|
|
62
|
+
export function encodePlushAvatar(config) {
|
|
63
|
+
const value = `plush:v${config.version}:${config.shape}:${config.color}:${config.glasses}:${config.hat}`;
|
|
64
|
+
if (!parsePlushAvatar(value))
|
|
65
|
+
throw new Error("毛绒头像配置无效。");
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
/** FNV-1a followed by an avalanche: fixed 32-bit integer math, shared by Node and browsers. */
|
|
69
|
+
function seedHash(seed) {
|
|
70
|
+
let hash = 0x811c9dc5;
|
|
71
|
+
for (let at = 0; at < seed.length; at += 1) {
|
|
72
|
+
hash = Math.imul(hash ^ seed.charCodeAt(at), 0x01000193);
|
|
73
|
+
}
|
|
74
|
+
hash = Math.imul(hash ^ (hash >>> 16), 0x85ebca6b);
|
|
75
|
+
hash = Math.imul(hash ^ (hash >>> 13), 0xc2b2ae35);
|
|
76
|
+
return (hash ^ (hash >>> 16)) >>> 0;
|
|
77
|
+
}
|
|
78
|
+
/** Pure presentation default; never persist this from a GET, a render, or a preview. */
|
|
79
|
+
export function defaultPlushAvatar({ id, name }) {
|
|
80
|
+
const identity = (id ?? "").trim() || (name ?? "").trim() || "member";
|
|
81
|
+
const pick = (field, options) => options[seedHash(`plush:v1:${identity}:${field}`) % options.length].id;
|
|
82
|
+
return {
|
|
83
|
+
version: 1,
|
|
84
|
+
shape: pick("shape", PLUSH_SHAPES),
|
|
85
|
+
color: pick("color", PLUSH_COLORS),
|
|
86
|
+
glasses: pick("glasses", PLUSH_GLASSES),
|
|
87
|
+
hat: pick("hat", PLUSH_HATS),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Uploaded photos and explicit legacy cats keep their renderer; corrupt old values fall back without migration. */
|
|
91
|
+
export function resolvePlushAvatar(identity) {
|
|
92
|
+
const avatar = identity.avatar ?? "";
|
|
93
|
+
if (avatar.startsWith("data:image/") || /^cat:\d+$/.test(avatar))
|
|
94
|
+
return null;
|
|
95
|
+
return parsePlushAvatar(avatar) ?? defaultPlushAvatar(identity);
|
|
96
|
+
}
|
package/dist/process-manager.js
CHANGED
|
@@ -26,7 +26,7 @@ import { recordIterationPrompt } from "./iteration-log.js";
|
|
|
26
26
|
import { describePtySpawnFailure } from "./ensure-node-pty-helper.js";
|
|
27
27
|
import { resolveSessionProvider, resolveSystemAiContext } from "./session-ai-context.js";
|
|
28
28
|
import { resolveSessionCwd } from "./session-cwd.js";
|
|
29
|
-
import { inferProviderFromCommand, providerCliCommand } from "./session-provider.js";
|
|
29
|
+
import { inferProviderFromCommand, isSessionProvider, providerCliCommand } from "./session-provider.js";
|
|
30
30
|
import { RETENTION_IDLE_MS } from "./retention.js";
|
|
31
31
|
import { PtyTerminalState } from "./pty-terminal-state.js";
|
|
32
32
|
import { isPtySubmitInput } from "./pty-turn-activity.js";
|
|
@@ -2338,6 +2338,9 @@ export class ProcessManager extends EventEmitter {
|
|
|
2338
2338
|
return;
|
|
2339
2339
|
// 迭代提示词记录:和会话标题共用同一套「有没有信息量」判断,用户不用多做一步。
|
|
2340
2340
|
recordIterationPrompt(this.storage, record, prompt, "session");
|
|
2341
|
+
// 空白终端只执行 Shell 命令,不生成会话标题或调用系统员工总结。
|
|
2342
|
+
if (!isSessionProvider(record.provider))
|
|
2343
|
+
return;
|
|
2341
2344
|
const blockedTitles = sessionTopicBlocklistForSnapshot(record, this.storage);
|
|
2342
2345
|
// CLI 自己起了名字就归它,模型标题与输入首行都不再覆盖;取不到原生标题才走系统硅基员工。
|
|
2343
2346
|
const hasNativeTitle = this.nativeTitles.has(id);
|
|
@@ -9,10 +9,12 @@ export interface EmployeeDraftRequest {
|
|
|
9
9
|
existingNames: string[];
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* 其他内置员工只接受 agents;固定猫头员工另外接受资料的部分更新。
|
|
13
|
+
* 锁定字段即使由旧客户端或直接 API 发来,也必须一致;不一致明确拒绝。
|
|
14
14
|
*/
|
|
15
15
|
export declare function parseSystemEmployeeAgents(value: unknown, existing: SiliconEmployee): WandTaskAgent[];
|
|
16
|
+
/** Partial edits preserve omitted fields and the raw saved prompt, not a generated read projection. */
|
|
17
|
+
export declare function parseFixedAvatarEmployeeInput(value: unknown, existing: SiliconEmployee, now: string): SiliconEmployee;
|
|
16
18
|
export declare function parseSiliconEmployeeInput(value: unknown, existing: SiliconEmployee | null, now: string): SiliconEmployee;
|
|
17
19
|
export declare function registerSiliconEmployeeRoutes(app: Express, deps: {
|
|
18
20
|
storage: WandStorage;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isFixedAvatarEmployee } from "./fixed-employee-avatar.js";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { AI_TEAM_MAX_CANDIDATES, SILICON_EMPLOYEE_AVATAR_MAX_CHARS, agentKey, isBuiltinSiliconEmployee, parseSiliconEmployeeTags, siliconEmployeeTags, } from "./ai-team-types.js";
|
|
3
4
|
import { getErrorMessage } from "./error-utils.js";
|
|
@@ -9,6 +10,7 @@ import { employeeCliAvailable } from "./silicon-employee-dispatch.js";
|
|
|
9
10
|
import { EMPLOYEE_CREATION_DEFAULTS_PREF, resolveSiliconEmployeeDefaults, } from "./silicon-employee-defaults.js";
|
|
10
11
|
import { availableEmployeeProviders, generateSiliconEmployeeDraft, SiliconEmployeeDraftError, validateStructuredEmployeeDraft, } from "./silicon-employee-draft.js";
|
|
11
12
|
import { SESSION_PROVIDERS } from "./session-provider.js";
|
|
13
|
+
import { parsePlushAvatar, parsePlushCatAvatar } from "./plush-avatar.js";
|
|
12
14
|
import { DEFAULT_WAND_TASK_AGENT_KIND } from "./task-types.js";
|
|
13
15
|
import { EMPLOYEE_KNOWLEDGE_MAX_ENTRIES } from "./employee-knowledge-types.js";
|
|
14
16
|
/** 员工起草没有会话上下文,按「默认 provider」解析,与任务标题 / 提示词优化一致。 */
|
|
@@ -57,9 +59,14 @@ function boundedText(value, label, min, max) {
|
|
|
57
59
|
return result;
|
|
58
60
|
}
|
|
59
61
|
function parseAvatar(value, name) {
|
|
62
|
+
if (value !== undefined && value !== null && typeof value !== "string") {
|
|
63
|
+
throw new Error(`员工「${name}」的头像格式无效。`);
|
|
64
|
+
}
|
|
60
65
|
const avatar = text(value);
|
|
61
66
|
if (!avatar || /^cat:\d{1,2}$/.test(avatar))
|
|
62
67
|
return avatar;
|
|
68
|
+
if (parsePlushAvatar(avatar) || parsePlushCatAvatar(avatar))
|
|
69
|
+
return avatar;
|
|
63
70
|
if (!/^data:image\/(png|jpeg|webp);base64,[A-Za-z0-9+/=]+$/.test(avatar)) {
|
|
64
71
|
throw new Error(`员工「${name}」的头像格式无效。`);
|
|
65
72
|
}
|
|
@@ -68,7 +75,7 @@ function parseAvatar(value, name) {
|
|
|
68
75
|
}
|
|
69
76
|
return avatar;
|
|
70
77
|
}
|
|
71
|
-
/**
|
|
78
|
+
/** Other built-ins keep profile locks; the two stable cat identities lock only avatar. */
|
|
72
79
|
const SYSTEM_EMPLOYEE_LOCKED_FIELDS = [
|
|
73
80
|
{ key: "name", label: "名字" },
|
|
74
81
|
{ key: "duty", label: "职责" },
|
|
@@ -76,14 +83,14 @@ const SYSTEM_EMPLOYEE_LOCKED_FIELDS = [
|
|
|
76
83
|
{ key: "avatar", label: "头像" },
|
|
77
84
|
];
|
|
78
85
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
86
|
+
* 其他内置员工只接受 agents;固定猫头员工另外接受资料的部分更新。
|
|
87
|
+
* 锁定字段即使由旧客户端或直接 API 发来,也必须一致;不一致明确拒绝。
|
|
81
88
|
*/
|
|
82
89
|
export function parseSystemEmployeeAgents(value, existing) {
|
|
83
90
|
const body = bodyObject(value);
|
|
84
|
-
for (const field of SYSTEM_EMPLOYEE_LOCKED_FIELDS) {
|
|
91
|
+
for (const field of SYSTEM_EMPLOYEE_LOCKED_FIELDS.filter((field) => !isFixedAvatarEmployee(existing) || field.key === "avatar")) {
|
|
85
92
|
const incoming = body[field.key];
|
|
86
|
-
if (incoming === undefined || incoming === null)
|
|
93
|
+
if (incoming === undefined || (incoming === null && !isFixedAvatarEmployee(existing)))
|
|
87
94
|
continue;
|
|
88
95
|
const current = existing[field.key];
|
|
89
96
|
const isSame = typeof incoming === "string" && incoming === current;
|
|
@@ -94,10 +101,25 @@ export function parseSystemEmployeeAgents(value, existing) {
|
|
|
94
101
|
if (body.tags !== undefined && JSON.stringify(body.tags) !== JSON.stringify(siliconEmployeeTags(existing))) {
|
|
95
102
|
throw new Error("该员工是内置的,标签不可修改。");
|
|
96
103
|
}
|
|
104
|
+
if (isFixedAvatarEmployee(existing) && body.id !== undefined && body.id !== existing.id)
|
|
105
|
+
throw new Error("该员工是内置的,身份不可修改。");
|
|
97
106
|
if (body.systemKey !== undefined && body.systemKey !== existing.systemKey) {
|
|
98
107
|
throw new Error("该员工是内置的,身份不可修改。");
|
|
99
108
|
}
|
|
100
|
-
return parseEmployeeAgents(body.agents);
|
|
109
|
+
return isFixedAvatarEmployee(existing) && body.agents === undefined ? existing.agents : parseEmployeeAgents(body.agents);
|
|
110
|
+
}
|
|
111
|
+
/** Partial edits preserve omitted fields and the raw saved prompt, not a generated read projection. */
|
|
112
|
+
export function parseFixedAvatarEmployeeInput(value, existing, now) {
|
|
113
|
+
const body = bodyObject(value);
|
|
114
|
+
const agents = parseSystemEmployeeAgents(body, existing);
|
|
115
|
+
const name = body.name === undefined ? existing.name : boundedText(body.name, "员工名字", 1, 40);
|
|
116
|
+
const duty = body.duty === undefined ? existing.duty : text(body.duty);
|
|
117
|
+
const prompt = body.prompt === undefined ? existing.prompt : text(body.prompt);
|
|
118
|
+
if (duty.length > 2000)
|
|
119
|
+
throw new Error("员工职责不能超过 2000 个字符。");
|
|
120
|
+
if (prompt.length > 20_000)
|
|
121
|
+
throw new Error("员工设定 Prompt 不能超过 20000 个字符。");
|
|
122
|
+
return { ...existing, name, duty, prompt, agents, updatedAt: now };
|
|
101
123
|
}
|
|
102
124
|
/** One validation owner for every employee candidate list, including legacy single agents. */
|
|
103
125
|
function parseEmployeeAgents(value) {
|
|
@@ -328,9 +350,15 @@ export function registerSiliconEmployeeRoutes(app, deps) {
|
|
|
328
350
|
if (!existing)
|
|
329
351
|
throw new Error(`硅基员工「${req.params.id}」不存在。`);
|
|
330
352
|
const now = new Date().toISOString();
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
353
|
+
const definition = storage.getSiliconEmployeeDefinition(existing.id);
|
|
354
|
+
const body = bodyObject(req.body);
|
|
355
|
+
// An unchanged projected prompt is an echo, not a request to persist generated preferences.
|
|
356
|
+
const input = body.prompt === existing.prompt ? { ...body, prompt: definition.prompt } : body;
|
|
357
|
+
const employee = isFixedAvatarEmployee(definition)
|
|
358
|
+
? parseFixedAvatarEmployeeInput(input, definition, now)
|
|
359
|
+
: isBuiltinSiliconEmployee(existing)
|
|
360
|
+
? { ...existing, agents: parseSystemEmployeeAgents(input, existing), updatedAt: now }
|
|
361
|
+
: parseSiliconEmployeeInput(input, existing, now);
|
|
334
362
|
storage.saveSiliconEmployee(employee);
|
|
335
363
|
notifyEmployee(employee.id);
|
|
336
364
|
res.json(employee);
|
|
@@ -251,6 +251,10 @@ function scheduleWandTaskTitleGeneration(storage, taskId, source, signature, opt
|
|
|
251
251
|
const cwd = options.cwd || options.config?.defaultCwd || process.cwd();
|
|
252
252
|
const run = async () => {
|
|
253
253
|
try {
|
|
254
|
+
const pending = storage.getWandTask(taskId);
|
|
255
|
+
// 排队期间可能移走 Agent,只剩空白终端;此时也不应再启动总结。
|
|
256
|
+
if (!pending || !taskAutoNameSourceText(storage, pending))
|
|
257
|
+
return;
|
|
254
258
|
const title = await generateTitle(source, cwd, options.config?.language ?? "", taskTitleAiOptions(options.config, storage, options.free));
|
|
255
259
|
const current = storage.getWandTask(taskId);
|
|
256
260
|
// 用户已经自己写了标题(原生端 / 面板编辑)就不要覆盖。
|
|
@@ -259,6 +263,8 @@ function scheduleWandTaskTitleGeneration(storage, taskId, source, signature, opt
|
|
|
259
263
|
// 命名输入已经变了(新增会话 / 会话内容更新):这次是过期结果,交给下一轮。
|
|
260
264
|
if (current.autoTitleSignature !== signature)
|
|
261
265
|
return;
|
|
266
|
+
if (!taskAutoNameSourceText(storage, current))
|
|
267
|
+
return;
|
|
262
268
|
const clipped = title.slice(0, TASK_TITLE_MAX_LENGTH);
|
|
263
269
|
if (!clipped || clipped === current.title)
|
|
264
270
|
return;
|
package/dist/server.js
CHANGED
|
@@ -81,7 +81,7 @@ import { DistributionManager } from "./distribution-manager.js";
|
|
|
81
81
|
import { isLogBusActive, wandTuiLog } from "./tui/log-bus.js";
|
|
82
82
|
import { EMBEDDED_WEB_ASSETS } from "./web-ui/embedded-assets.js";
|
|
83
83
|
import { renderApp } from "./web-ui/index.js";
|
|
84
|
-
import { getAiTeamsChunk, getScriptAsset, getThemePreloadAsset } from "./web-ui/scripts.js";
|
|
84
|
+
import { getAiTeamsChunk, getPlushAvatarChunk, getScriptAsset, getThemePreloadAsset } from "./web-ui/scripts.js";
|
|
85
85
|
import { getStylesAsset } from "./web-ui/styles.js";
|
|
86
86
|
import { WsBroadcastManager } from "./ws-broadcast.js";
|
|
87
87
|
import { TerminalDaemonClient } from "./terminal-daemon-client.js";
|
|
@@ -569,6 +569,11 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
569
569
|
setAssetCache(req, res, chunk.hash, "public");
|
|
570
570
|
res.type("application/javascript").send(chunk.content);
|
|
571
571
|
});
|
|
572
|
+
app.get("/assets/plush-avatar.js", (req, res) => {
|
|
573
|
+
const chunk = getPlushAvatarChunk(requestedHash(req));
|
|
574
|
+
setAssetCache(req, res, chunk.hash, "public");
|
|
575
|
+
res.type("application/javascript").send(chunk.content);
|
|
576
|
+
});
|
|
572
577
|
// ── Web UI endpoints ──
|
|
573
578
|
app.get(["/", "/settings"], (req, res) => {
|
|
574
579
|
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
package/dist/speech-service.js
CHANGED
|
@@ -207,10 +207,10 @@ export class SpeechService {
|
|
|
207
207
|
return { executable: null, backend: backend };
|
|
208
208
|
}
|
|
209
209
|
async status() {
|
|
210
|
-
const settings = this.settings();
|
|
211
210
|
const runtime = await this.runtime();
|
|
212
211
|
const models = await Promise.all(this.models.map(async (model) => ({ id: model.id, label: model.label,
|
|
213
212
|
description: model.description, size: model.size, downloaded: await this.modelReady(model) })));
|
|
213
|
+
const settings = this.settings();
|
|
214
214
|
const reason = this.disposed ? "语音服务已关闭。" : this.maintenance ? "语音运行时正在初始化,请稍后重试。" : !settings.enabled ? "服务端识别未启用,请在管理设置中启用。"
|
|
215
215
|
: !runtime.executable ? "服务端尚未安装 whisper.cpp,请在服务器运行语音运行时安装脚本。"
|
|
216
216
|
: !models.find((model) => model.id === settings.model)?.downloaded ? "服务端模型尚未下载或校验失败,请在管理设置中下载。"
|
package/dist/storage.d.ts
CHANGED
|
@@ -385,22 +385,29 @@ export declare class WandStorage {
|
|
|
385
385
|
includeArchived?: boolean;
|
|
386
386
|
}): SiliconEmployee[];
|
|
387
387
|
private projectDefaultEmployee;
|
|
388
|
+
/** Saved definition without generated short-term preferences. */
|
|
389
|
+
getSiliconEmployeeDefinition(id: string): SiliconEmployee | null;
|
|
388
390
|
getSiliconEmployee(id: string): SiliconEmployee | null;
|
|
389
391
|
getDefaultSiliconEmployee(): SiliconEmployee | null;
|
|
390
392
|
/** Seed once, preserve execution choices, and never reset learned preferences on restart. */
|
|
391
393
|
ensureDefaultSiliconEmployee(provider?: SessionProvider): SiliconEmployee;
|
|
392
394
|
/** 内置「系统运维」员工;未创建(或被删除过)时返回 null。 */
|
|
393
395
|
getSystemSiliconEmployee(key?: string): SiliconEmployee | null;
|
|
394
|
-
/**
|
|
395
|
-
* 幂等地保证内置员工存在。已存在时只补齐锁定字段(防止旧的同名定义漂移),
|
|
396
|
-
* 候选保留用户当下的设置;不存在时按 seed 建首条候选。
|
|
397
|
-
*/
|
|
396
|
+
/** Seed only when missing; restart never overwrites user profile, candidates, or legacy avatars. */
|
|
398
397
|
ensureSystemSiliconEmployee(seed?: SystemEmployeeSeed): SiliconEmployee;
|
|
399
398
|
/** Stable built-in decision employee; seed once and retain the user's ordered call chain. */
|
|
400
399
|
ensureDecisionExpertEmployee(): SiliconEmployee;
|
|
401
400
|
/** Seed the SDK free group once, then preserve independent user choices. */
|
|
402
401
|
ensureSpeechPolisherEmployee(): SiliconEmployee;
|
|
403
402
|
saveSiliconEmployee(employee: SiliconEmployee): void;
|
|
403
|
+
/** Explicit, atomic migration; not called from GET, config load, seeds, or ordinary saves.
|
|
404
|
+
* Compare all expected avatar values first; change only avatar, including preserving timestamps.
|
|
405
|
+
*/
|
|
406
|
+
migrateFixedEmployeeAvatars(expected: ReadonlyArray<{
|
|
407
|
+
id: string;
|
|
408
|
+
systemKey: string;
|
|
409
|
+
avatar: string;
|
|
410
|
+
}>): number;
|
|
404
411
|
archiveSiliconEmployee(id: string, archivedAt?: string): void;
|
|
405
412
|
unarchiveSiliconEmployee(id: string): void;
|
|
406
413
|
deleteSiliconEmployee(id: string): void;
|