@co0ontty/wand 4.97.0 → 4.98.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.
Files changed (160) hide show
  1. package/README.md +0 -4
  2. package/dist/agent-dispatch.d.ts +1 -0
  3. package/dist/agent-dispatch.js +13 -2
  4. package/dist/ai-team-types.d.ts +1 -1
  5. package/dist/ai-team-types.js +1 -1
  6. package/dist/build-info.json +3 -3
  7. package/dist/cli.js +2 -0
  8. package/dist/config.d.ts +1 -1
  9. package/dist/config.js +2 -2
  10. package/dist/core-runner.d.ts +0 -4
  11. package/dist/core-runner.js +41 -3
  12. package/dist/decision-expert-employee.js +1 -0
  13. package/dist/distribution-manager.d.ts +29 -4
  14. package/dist/distribution-manager.js +147 -57
  15. package/dist/employee-types.d.ts +2 -1
  16. package/dist/employee-types.js +2 -4
  17. package/dist/feedback-feature-map.d.ts +0 -1
  18. package/dist/feedback-feature-map.js +0 -1
  19. package/dist/feedback.d.ts +0 -1
  20. package/dist/feedback.js +1 -1
  21. package/dist/git-quick-commit.js +13 -52
  22. package/dist/git-tag-generation.d.ts +11 -0
  23. package/dist/git-tag-generation.js +47 -0
  24. package/dist/hr-employee.js +1 -0
  25. package/dist/local-model-setup.d.ts +1 -9
  26. package/dist/local-model-setup.js +52 -166
  27. package/dist/local-model-types.d.ts +2 -5
  28. package/dist/managed-temp.d.ts +1 -1
  29. package/dist/managed-temp.js +1 -1
  30. package/dist/middleware/api-scope.js +2 -4
  31. package/dist/migration-backup-retention.d.ts +17 -0
  32. package/dist/migration-backup-retention.js +91 -0
  33. package/dist/pi-tmux-host.d.ts +28 -0
  34. package/dist/pi-tmux-host.js +189 -0
  35. package/dist/server-employee-routes.d.ts +1 -0
  36. package/dist/server-employee-routes.js +17 -4
  37. package/dist/server-local-model-routes.js +2 -15
  38. package/dist/server-session-input-routes.js +3 -3
  39. package/dist/server-session-lifecycle-routes.js +94 -18
  40. package/dist/server-task-routes.js +18 -13
  41. package/dist/server-update-routes.d.ts +11 -1
  42. package/dist/server-update-routes.js +25 -12
  43. package/dist/server-workspace-routes.js +11 -5
  44. package/dist/server.js +29 -129
  45. package/dist/storage/conversations-repository.d.ts +2 -0
  46. package/dist/storage/conversations-repository.js +4 -0
  47. package/dist/storage/employees-repository.d.ts +2 -2
  48. package/dist/storage/employees-repository.js +12 -15
  49. package/dist/storage/internal.js +2 -2
  50. package/dist/storage/migrations.js +20 -0
  51. package/dist/storage/readonly.js +3 -0
  52. package/dist/storage/sessions-repository.d.ts +2 -0
  53. package/dist/storage/sessions-repository.js +13 -0
  54. package/dist/storage/tasks-repository.d.ts +22 -0
  55. package/dist/storage/tasks-repository.js +52 -3
  56. package/dist/storage/workspaces-repository.d.ts +1 -0
  57. package/dist/storage/workspaces-repository.js +3 -1
  58. package/dist/storage-cleanup.d.ts +37 -2
  59. package/dist/storage-cleanup.js +62 -2
  60. package/dist/storage-schema.js +9 -0
  61. package/dist/structured-claude-adapter.js +5 -1
  62. package/dist/structured-claude-protocol.d.ts +1 -1
  63. package/dist/structured-claude-protocol.js +12 -1
  64. package/dist/structured-runner.d.ts +2 -0
  65. package/dist/structured-session-manager.d.ts +14 -2
  66. package/dist/structured-session-manager.js +87 -14
  67. package/dist/system-ai-call.d.ts +5 -1
  68. package/dist/system-ai-call.js +32 -1
  69. package/dist/system-ai-session-recorder.d.ts +2 -0
  70. package/dist/system-ai-session-recorder.js +35 -3
  71. package/dist/system-cli.js +6 -1
  72. package/dist/system-employee.js +1 -0
  73. package/dist/task-comment-dispatch.d.ts +18 -0
  74. package/dist/task-comment-dispatch.js +121 -0
  75. package/dist/task-types.d.ts +14 -0
  76. package/dist/version-utils.d.ts +31 -0
  77. package/dist/version-utils.js +35 -0
  78. package/dist/web-ui/content/ai-teams.js +10 -10
  79. package/dist/web-ui/content/module-manifest.json +1 -1
  80. package/dist/web-ui/content/modules/02b1ec89c69dc0dd/host.js +30 -0
  81. package/dist/web-ui/content/modules/{e8f0cda6b9ac8a70 → 0434a45abd2c1d15}/chunk.js +1 -1
  82. package/dist/web-ui/content/modules/{e9a01a25ec573294 → 0afa04591224bc23}/library-bridge.js +1 -1
  83. package/dist/web-ui/content/modules/{b33c567946044d83 → 0bf4a78c8be1bdc9}/host.js +1 -1
  84. package/dist/web-ui/content/modules/17f72e0fb014a302/chat-render.js +75 -0
  85. package/dist/web-ui/content/modules/{5903104a2e0e3888 → 183db4802a7d99ed}/host.js +1 -1
  86. package/dist/web-ui/content/modules/23d2006fcfcf3c0c/host-bridge.js +1 -0
  87. package/dist/web-ui/content/modules/{0c96341a022aa360 → 32f151da1a006041}/host.js +1 -1
  88. package/dist/web-ui/content/modules/{3172fe0b46d51f8d → 34a7674efc329383}/chunk.js +1 -1
  89. package/dist/web-ui/content/modules/3541ba4cb2886e29/chunk.js +1 -0
  90. package/dist/web-ui/content/modules/{801e7f686ce7eb28 → 395257bd20cd0098}/chunk.js +1 -1
  91. package/dist/web-ui/content/modules/{3ddcbd8505bfcdf4 → 3fd75af0a6891549}/chunk.js +1 -1
  92. package/dist/web-ui/content/modules/{c41d8ffa27816766 → 428352e422932c40}/host.js +1 -1
  93. package/dist/web-ui/content/modules/485a72b2c83e7680/workspace-window.js +1 -0
  94. package/dist/web-ui/content/modules/489dfe1f7441ef2c/markdown.js +1 -0
  95. package/dist/web-ui/content/modules/4b42b4336458b7b5/runtime.js +550 -0
  96. package/dist/web-ui/content/modules/{de00ab5329b5e05a → 528c5d97e8f9d582}/chunk.js +1 -1
  97. package/dist/web-ui/content/modules/{3086df78444998b3 → 535aad05d34079d2}/host.js +1 -1
  98. package/dist/web-ui/content/modules/62028b9bbf8ffc88/page.js +6 -0
  99. package/dist/web-ui/content/modules/6868f75a5981ad08/host.js +69 -0
  100. package/dist/web-ui/content/modules/6c72c8c166e34de7/host.js +1 -0
  101. package/dist/web-ui/content/modules/6ff6ce370ef26753/host.js +1 -0
  102. package/dist/web-ui/content/modules/{87379801875d1921 → 72bef6889fdafefe}/host.js +1 -1
  103. package/dist/web-ui/content/modules/{5e5eabd9a9366a56 → 763c79f5527d762a}/panel.js +1 -1
  104. package/dist/web-ui/content/modules/{d30ac9af6dc7d555 → 77ea205020273767}/chunk.js +1 -1
  105. package/dist/web-ui/content/modules/{e01e1e25b7fe37e9 → 7fb02c44fd6f877b}/host.js +1 -1
  106. package/dist/web-ui/content/modules/{349ee129eef5a182 → 818d87c1651fc6a1}/chunk.js +1 -1
  107. package/dist/web-ui/content/modules/{66f36cd2c38cfb07 → 8cb5d84a2731789e}/chunk.js +1 -1
  108. package/dist/web-ui/content/modules/{f5b3fb6fedd9b05d → 96e78c7f2cd9acd5}/chunk.js +1 -1
  109. package/dist/web-ui/content/modules/{64c1b0217f5377aa → a60a07ba1abcdc3f}/chunk.js +1 -1
  110. package/dist/web-ui/content/modules/{ee12d3c4421a5c12 → b4bcb9bf5cfb0b3a}/host.js +1 -1
  111. package/dist/web-ui/content/modules/b71cda413318d71f/employee-card.js +1 -0
  112. package/dist/web-ui/content/modules/bb0c299cf006e157/task-board-host.js +17 -0
  113. package/dist/web-ui/content/modules/{42ae495da5903b07 → c8c6adee81d4bd98}/sha256.js +1 -1
  114. package/dist/web-ui/content/modules/{854b0f6dc887923b → cd0cec0ba1887ffd}/chunk.js +1 -1
  115. package/dist/web-ui/content/modules/{3338b55c7767bacb → d673a76becc7d1f5}/chunk.js +1 -1
  116. package/dist/web-ui/content/modules/{2bce1137f402bd70 → d99ea7e921488348}/chunk.js +1 -1
  117. package/dist/web-ui/content/modules/{a9f15bd03bc590e7 → e862ccabc908eb0c}/host.js +1 -1
  118. package/dist/web-ui/content/modules/{9984f6bb05ac55bb → eb46bf62b1ae1bea}/host.js +1 -1
  119. package/dist/web-ui/content/plush-avatar.js +166 -166
  120. package/dist/web-ui/content/styles.css +1 -1
  121. package/dist/web-ui/embedded-assets.d.ts +177 -177
  122. package/dist/web-ui/embedded-assets.js +6 -6
  123. package/dist/web-ui/page-route.d.ts +24 -0
  124. package/dist/web-ui/page-route.js +130 -0
  125. package/dist/web-ui/pty-input-state.d.ts +2 -0
  126. package/dist/web-ui/pty-input-state.js +2 -2
  127. package/dist/web-ui/react/workspaces/types.d.ts +5 -0
  128. package/package.json +1 -3
  129. package/dist/server-speech-routes.d.ts +0 -10
  130. package/dist/server-speech-routes.js +0 -93
  131. package/dist/speech-models.d.ts +0 -24
  132. package/dist/speech-models.js +0 -16
  133. package/dist/speech-polisher-employee.d.ts +0 -6
  134. package/dist/speech-polisher-employee.js +0 -23
  135. package/dist/speech-polisher-identity.d.ts +0 -4
  136. package/dist/speech-polisher-identity.js +0 -4
  137. package/dist/speech-polisher-service.d.ts +0 -33
  138. package/dist/speech-polisher-service.js +0 -110
  139. package/dist/speech-service.d.ts +0 -59
  140. package/dist/speech-service.js +0 -420
  141. package/dist/speech-support.d.ts +0 -6
  142. package/dist/speech-support.js +0 -24
  143. package/dist/speech-types.d.ts +0 -52
  144. package/dist/speech-types.js +0 -3
  145. package/dist/web-ui/content/modules/15e32e85e086f6b4/page.js +0 -6
  146. package/dist/web-ui/content/modules/1e43259bddbc6805/markdown.js +0 -1
  147. package/dist/web-ui/content/modules/271d488d262909c2/host.js +0 -30
  148. package/dist/web-ui/content/modules/43746e7237d61da9/task-board-host.js +0 -17
  149. package/dist/web-ui/content/modules/4485e6c1f0459aa8/chunk.js +0 -1
  150. package/dist/web-ui/content/modules/5b541e0f296a3592/employee-card.js +0 -1
  151. package/dist/web-ui/content/modules/6ed52e6604661678/chat-render.js +0 -75
  152. package/dist/web-ui/content/modules/89888da0972e5033/host.js +0 -50
  153. package/dist/web-ui/content/modules/c11165f843d818f4/host-bridge.js +0 -1
  154. package/dist/web-ui/content/modules/c1829693f91fd25c/host.js +0 -1
  155. package/dist/web-ui/content/modules/cdb882ddab12e325/host.js +0 -1
  156. package/dist/web-ui/content/modules/ea67fd4e96c9f45a/workspace-window.js +0 -1
  157. package/dist/web-ui/content/modules/fd767282c29173b5/runtime.js +0 -539
  158. package/dist/web-ui/speech-audio.d.ts +0 -2
  159. package/dist/web-ui/speech-audio.js +0 -41
  160. package/scripts/install-speech-runtime.js +0 -82
package/README.md CHANGED
@@ -319,10 +319,6 @@ wand config:set port 9443
319
319
  - SQLite 升级前会生成一致性备份,迁移保留既有数据;遇到高于当前版本所支持的 schema 会拒绝启动,不会猜测兼容性并继续写入。降级需使用匹配的数据备份,不能让旧版本直接打开已经迁移的库。保险库和连接器的新密文使用数据库目录内独立的 `wand-vault.key`(权限 `0600`),不再只依赖同一数据库里的 `appSecret`。备份/迁移实例时须同时妥善保存数据库和密钥文件;只有数据库备份不能恢复新密文。缺失或损坏密钥会明确失败,不会生成新密钥覆盖现有密文。
320
320
  - 独立密钥防止仅泄露数据库时直接解密,但不防御获得整个数据目录或当前用户权限的攻击者。管理员密码的现有 CLI 查看/连接码兼容契约仍保留;不能据此宣称已全面完成密码哈希迁移或按设备令牌改造。
321
321
 
322
- ### 语音识别
323
-
324
- Web、Android、iOS 可在语音输入设置中选择「服务端识别」或「客户端本地识别」。服务端使用按需安装的 whisper.cpp 多语言模型,支持无 GPU CPU 服务器、Mac mini Metal 与可选 CUDA;模型不随普通构建下载。部署、模型大小、鉴权和隐私边界见 [服务端语音识别](docs/server-speech.md)。设置内下载/初始化 LAYA 与语音模型,以及「石一」(决策专家)的本机性能检查和默认本地/免费调用链,见 [本地模型管理](docs/local-model-setup.md)。
325
-
326
322
  ### 可选本地决策(实验性)
327
323
 
328
324
  Apple Silicon 服务端可共享一份离线 Laya-MLX,为结构化 CLI 提供通用选择、评分和是非判断。它不是聊天模型,也不负责授权或自动派工。
@@ -40,6 +40,7 @@ export declare function dispatchAgentForTask(deps: AgentDispatchDeps, input: {
40
40
  systemPrompt?: string;
41
41
  employee?: SiliconEmployee;
42
42
  employeeCandidateIndex?: number;
43
+ sessionCreated?: (session: SessionSnapshot) => void;
43
44
  teamRequestStarted?: (sessionId: string, requestId: string) => void;
44
45
  }): Promise<AgentDispatchResult>;
45
46
  /**
@@ -49,6 +49,16 @@ export async function dispatchAgentForTask(deps, input) {
49
49
  if (isLocalDecisionModel(resolvedModel)) {
50
50
  throw new AgentDispatchPreflightError("LAYA 仅支持有界决策,不能执行聊天或任务;该候选尚未接受输入。");
51
51
  }
52
+ // This resolution is a pure pre-session capability check: rejection here cannot have delivered input.
53
+ let sdkEngine = null;
54
+ if (agent.kind === "structured" && agent.engine === "sdk") {
55
+ try {
56
+ sdkEngine = await structured.resolveNewSessionPiEngine("sdk");
57
+ }
58
+ catch (cause) {
59
+ throw new AgentDispatchPreflightError("当前 Wand Agent 引擎不可用;未创建会话或发送输入,请核对执行工具配置。", { cause });
60
+ }
61
+ }
52
62
  const session = agent.kind === "pty"
53
63
  ? await processes.start(providerCliCommand(provider), cwd, agent.mode, input.prompt, {
54
64
  provider,
@@ -69,8 +79,8 @@ export async function dispatchAgentForTask(deps, input) {
69
79
  thinkingEffort: agent.thinkingEffort,
70
80
  // 直接派发 Wand Agent(不进员工候选链):先裁决进程内引擎是否可用,
71
81
  // 不可用就让这次派发明确失败,不静默退回 CLI 冒充。
72
- ...(agent.engine === "sdk"
73
- ? { engine: (await structured.resolveNewSessionPiEngine("sdk")).engine }
82
+ ...(sdkEngine
83
+ ? { engine: sdkEngine.engine }
74
84
  : {}),
75
85
  worktreeEnabled: false,
76
86
  sessionSource: "automation",
@@ -83,6 +93,7 @@ export async function dispatchAgentForTask(deps, input) {
83
93
  workspaceTaskId: group.workspaceTaskId,
84
94
  });
85
95
  storage.bindWandTaskSession(input.task.id, session.id);
96
+ input.sessionCreated?.(session);
86
97
  if (agent.kind !== "pty") {
87
98
  const completion = input.teamRequestStarted
88
99
  ? structured.sendMessage(session.id, input.prompt, { teamRequestStarted: input.teamRequestStarted })
@@ -3,7 +3,7 @@ import type { AiTeamDeliverySummary } from "./ai-team-delivery-types.js";
3
3
  import type { WandTaskAgent } from "./task-types.js";
4
4
  import type { ConversationTurn } from "./types.js";
5
5
  export type { SiliconEmployee, SiliconEmployeeDraft } from "./employee-types.js";
6
- export { DEFAULT_EMPLOYEE_ID, DEFAULT_EMPLOYEE_KEY, DEFAULT_EMPLOYEE_NAME, DEFAULT_EMPLOYEE_TAG, DECISION_EXPERT_KEY, HR_EMPLOYEE_KEY, isBuiltinSiliconEmployee, isDecisionExpertSiliconEmployee, isDefaultSiliconEmployee, isSystemSiliconEmployee, parseSiliconEmployeeTagInput, parseSiliconEmployeeTags, SILICON_EMPLOYEE_MAX_TAGS, SILICON_EMPLOYEE_TAG_MAX_CHARS, siliconEmployeeTags, SPEECH_POLISHER_KEY, SYSTEM_EMPLOYEE_ID, SYSTEM_EMPLOYEE_KEY, SYSTEM_EMPLOYEE_NAME, SYSTEM_EMPLOYEE_TAG, } from "./employee-types.js";
6
+ export { DEFAULT_EMPLOYEE_ID, DEFAULT_EMPLOYEE_KEY, DEFAULT_EMPLOYEE_NAME, DEFAULT_EMPLOYEE_TAG, DECISION_EXPERT_KEY, HR_EMPLOYEE_KEY, isBuiltinSiliconEmployee, isDecisionExpertSiliconEmployee, isDefaultSiliconEmployee, isSystemSiliconEmployee, parseSiliconEmployeeTagInput, parseSiliconEmployeeTags, SILICON_EMPLOYEE_MAX_TAGS, SILICON_EMPLOYEE_TAG_MAX_CHARS, siliconEmployeeTags, SYSTEM_EMPLOYEE_ID, SYSTEM_EMPLOYEE_KEY, SYSTEM_EMPLOYEE_NAME, SYSTEM_EMPLOYEE_TAG, } from "./employee-types.js";
7
7
  export declare const AI_TEAM_MIN_MEMBERS = 2;
8
8
  export declare const AI_TEAM_MAX_MEMBERS = 8;
9
9
  export declare const AI_TEAM_DEFAULT_MAX_STEPS = 30;
@@ -1,4 +1,4 @@
1
- export { DEFAULT_EMPLOYEE_ID, DEFAULT_EMPLOYEE_KEY, DEFAULT_EMPLOYEE_NAME, DEFAULT_EMPLOYEE_TAG, DECISION_EXPERT_KEY, HR_EMPLOYEE_KEY, isBuiltinSiliconEmployee, isDecisionExpertSiliconEmployee, isDefaultSiliconEmployee, isSystemSiliconEmployee, parseSiliconEmployeeTagInput, parseSiliconEmployeeTags, SILICON_EMPLOYEE_MAX_TAGS, SILICON_EMPLOYEE_TAG_MAX_CHARS, siliconEmployeeTags, SPEECH_POLISHER_KEY, SYSTEM_EMPLOYEE_ID, SYSTEM_EMPLOYEE_KEY, SYSTEM_EMPLOYEE_NAME, SYSTEM_EMPLOYEE_TAG, } from "./employee-types.js";
1
+ export { DEFAULT_EMPLOYEE_ID, DEFAULT_EMPLOYEE_KEY, DEFAULT_EMPLOYEE_NAME, DEFAULT_EMPLOYEE_TAG, DECISION_EXPERT_KEY, HR_EMPLOYEE_KEY, isBuiltinSiliconEmployee, isDecisionExpertSiliconEmployee, isDefaultSiliconEmployee, isSystemSiliconEmployee, parseSiliconEmployeeTagInput, parseSiliconEmployeeTags, SILICON_EMPLOYEE_MAX_TAGS, SILICON_EMPLOYEE_TAG_MAX_CHARS, siliconEmployeeTags, SYSTEM_EMPLOYEE_ID, SYSTEM_EMPLOYEE_KEY, SYSTEM_EMPLOYEE_NAME, SYSTEM_EMPLOYEE_TAG, } from "./employee-types.js";
2
2
  export const AI_TEAM_MIN_MEMBERS = 2;
3
3
  export const AI_TEAM_MAX_MEMBERS = 8;
4
4
  export const AI_TEAM_DEFAULT_MAX_STEPS = 30;
@@ -1,6 +1,6 @@
1
1
  {
2
- "commit": "a09542822526896c8dcc2c5af5862441fdb58282",
3
- "builtAt": "2026-10-17T13:42:08.826Z",
4
- "version": "4.97.0",
2
+ "commit": "2d092b1bc52286f63ff106cafb86f9c49ceb6a57",
3
+ "builtAt": "2026-10-19T03:45:08.103Z",
4
+ "version": "4.98.0",
5
5
  "channel": "stable"
6
6
  }
package/dist/cli.js CHANGED
@@ -204,6 +204,7 @@ async function main() {
204
204
  case "session:create":
205
205
  case "session:resume":
206
206
  case "schedule:list":
207
+ case "task:context":
207
208
  case "schedule:read":
208
209
  case "schedule:create":
209
210
  case "schedule:update": {
@@ -364,6 +365,7 @@ Agent runtime:
364
365
  wand session:create --stdin Create an employee session from JSON
365
366
  wand session:resume <id> --stdin Continue the exact structured session with JSON input
366
367
  wand schedule:list [--include-cancelled]
368
+ wand task:context <task-id> [comment-id] Read task facts and source reference
367
369
  wand schedule:read <id> Read schedule and execution history
368
370
  wand schedule:create --stdin Create a schedule from JSON (requestId required)
369
371
  wand schedule:update <id> --stdin Edit/pause/cancel (revision required)
package/dist/config.d.ts CHANGED
@@ -45,7 +45,7 @@ export declare function saveConfig(configPath: string, config: WandConfig): Prom
45
45
  export declare function loadConfigWithStorage(configPath: string, storage: WandStorage): Promise<WandConfig>;
46
46
  /**
47
47
  * Idempotent seeding of built-in employees (system ops, default partner, decision
48
- * expert, speech polisher, HR). Runs once per process start from loadConfigWithStorage;
48
+ * expert, HR). Runs once per process start from loadConfigWithStorage;
49
49
  * never rewrites user-maintained candidates.
50
50
  */
51
51
  export declare function bootstrapBuiltinEmployees(config: WandConfig, storage: WandStorage): void;
package/dist/config.js CHANGED
@@ -196,7 +196,7 @@ export async function loadConfigWithStorage(configPath, storage) {
196
196
  }
197
197
  /**
198
198
  * Idempotent seeding of built-in employees (system ops, default partner, decision
199
- * expert, speech polisher, HR). Runs once per process start from loadConfigWithStorage;
199
+ * expert, HR). Runs once per process start from loadConfigWithStorage;
200
200
  * never rewrites user-maintained candidates.
201
201
  */
202
202
  export function bootstrapBuiltinEmployees(config, storage) {
@@ -211,7 +211,7 @@ export function bootstrapBuiltinEmployees(config, storage) {
211
211
  // 默认任务伙伴独立于系统运维;重启保留短期偏好与用户维护的候选。
212
212
  storage.ensureDefaultSiliconEmployee(config.defaultProvider);
213
213
  storage.ensureDecisionExpertEmployee();
214
- storage.ensureSpeechPolisherEmployee();
214
+ storage.retireLegacySpeechPolisherEmployee();
215
215
  storage.ensureHrEmployee(storage.getSystemSiliconEmployee()?.agents);
216
216
  }
217
217
  /**
@@ -20,10 +20,6 @@ export declare function coreMessagesFromTurns(session: SessionSnapshot, options?
20
20
  until?: number;
21
21
  summary?: string;
22
22
  }): AgentMessage[];
23
- /**
24
- * core agent 事件 → Wand 回合状态。与 pi CLI 适配器共用同一套 blocks 语义,
25
- * 但参数与结果是上游真值(不再从 provider 文本里反推)。返回本轮的 primaryError。
26
- */
27
23
  export declare function applyCoreAgentEvent(state: StructuredRunnerTurnState, event: Record<string, unknown>): string | null;
28
24
  export interface CoreRunnerOptions {
29
25
  openRouter?: OpenRouterFreeModelsService;
@@ -195,9 +195,13 @@ function applyAssistantMessage(state, message) {
195
195
  * core agent 事件 → Wand 回合状态。与 pi CLI 适配器共用同一套 blocks 语义,
196
196
  * 但参数与结果是上游真值(不再从 provider 文本里反推)。返回本轮的 primaryError。
197
197
  */
198
+ const assistantMessageOffsets = new WeakMap();
198
199
  export function applyCoreAgentEvent(state, event) {
199
200
  capturePiExecutionEvent(state.blocks, event);
200
201
  const type = typeof event.type === "string" ? event.type : "";
202
+ if (type === "message_start" && event.message?.role === "assistant") {
203
+ assistantMessageOffsets.set(state, { blocks: state.blocks.length, result: state.result.length });
204
+ }
201
205
  if (type === "agent_start" || type === "turn_start" || type === "message_start") {
202
206
  state.phase = "responding";
203
207
  return null;
@@ -211,7 +215,7 @@ export function applyCoreAgentEvent(state, event) {
211
215
  return null;
212
216
  if (update?.type === "text_delta") {
213
217
  const last = state.blocks.at(-1);
214
- if (last?.type === "text")
218
+ if (last?.type === "text" && state.blocks.length > (assistantMessageOffsets.get(state)?.blocks ?? 0))
215
219
  last.text += delta;
216
220
  else
217
221
  state.blocks.push({ type: "text", text: delta });
@@ -219,7 +223,7 @@ export function applyCoreAgentEvent(state, event) {
219
223
  }
220
224
  else if (update?.type === "thinking_delta") {
221
225
  const last = state.blocks.at(-1);
222
- if (last?.type === "thinking")
226
+ if (last?.type === "thinking" && state.blocks.length > (assistantMessageOffsets.get(state)?.blocks ?? 0))
223
227
  last.thinking += delta;
224
228
  else
225
229
  state.blocks.push({ type: "thinking", thinking: delta });
@@ -230,7 +234,19 @@ export function applyCoreAgentEvent(state, event) {
230
234
  const message = event.message;
231
235
  if (message?.role !== "assistant")
232
236
  return null;
233
- applyAssistantMessage(state, message);
237
+ const offset = assistantMessageOffsets.get(state);
238
+ if (offset) {
239
+ const segment = { ...state, blocks: state.blocks.slice(offset.blocks), result: state.result.slice(offset.result) };
240
+ applyAssistantMessage(segment, message);
241
+ state.blocks = [...state.blocks.slice(0, offset.blocks), ...segment.blocks];
242
+ state.result = state.result.slice(0, offset.result) + segment.result;
243
+ state.model = segment.model;
244
+ state.usage = segment.usage;
245
+ assistantMessageOffsets.delete(state);
246
+ }
247
+ else {
248
+ applyAssistantMessage(state, message);
249
+ }
234
250
  if (message.stopReason === "error") {
235
251
  return typeof message.errorMessage === "string" && message.errorMessage ? message.errorMessage : "core 引擎回合失败";
236
252
  }
@@ -457,6 +473,28 @@ export class CoreRunner {
457
473
  },
458
474
  });
459
475
  }
476
+ // Agent owns getSteeringMessages internally. Stage only at its awaited finishTurn
477
+ // boundary, then let the native one-at-a-time queue drain before the next request.
478
+ // Install after AgentSession so extension boundary hooks finish first.
479
+ agent.steeringMode = "one-at-a-time";
480
+ const finishTurn = agent.finishTurn;
481
+ agent.finishTurn = async (turn, signal) => {
482
+ const decision = await finishTurn?.(turn, signal);
483
+ if (decision?.action === "end" || controller.signal.aborted || signal?.aborted
484
+ || !observer.isActive() || turn.message.stopReason === "error" || turn.message.stopReason === "aborted"
485
+ || agent.hasQueuedMessages())
486
+ return decision ?? undefined;
487
+ const prompt = context.takeSteering?.(state);
488
+ if (prompt !== null && prompt !== undefined) {
489
+ agent.steer({ role: "user", content: [{ type: "text", text: prompt }], timestamp: Date.now() });
490
+ // The preceding segment is now durable. Subsequent streaming must append
491
+ // after its new user turn rather than upsert the entire old assistant text.
492
+ state.blocks = [];
493
+ state.result = "";
494
+ state.usage = undefined;
495
+ }
496
+ return decision ?? undefined;
497
+ };
460
498
  if (extensionHost)
461
499
  register(agent, () => extensionHost.interrupt());
462
500
  const handleEvent = (event) => {
@@ -17,6 +17,7 @@ export function decisionExpertSeedAgents() {
17
17
  export function decisionExpertDefinition(now, existing) {
18
18
  return { id: existing?.id ?? DECISION_EXPERT_ID, systemKey: DECISION_EXPERT_KEY, name: DECISION_EXPERT_NAME,
19
19
  duty: DECISION_EXPERT_DUTY, prompt: DECISION_EXPERT_PROMPT, avatar: "", tags: [SYSTEM_EMPLOYEE_TAG],
20
+ systemSessionLimit: existing?.systemSessionLimit,
20
21
  agents: existing?.agents.length ? existing.agents : decisionExpertSeedAgents(),
21
22
  createdAt: existing?.createdAt ?? now, updatedAt: now };
22
23
  }
@@ -38,6 +38,11 @@ export interface DistributionManagerOptions {
38
38
  fetch?: typeof fetch;
39
39
  now?: () => number;
40
40
  }
41
+ /** 分发目录清理结果:删了哪些文件名、释放多少字节。 */
42
+ export interface PruneReport {
43
+ deleted: string[];
44
+ freedBytes: number;
45
+ }
41
46
  /**
42
47
  * GitHub Release 正文还包含 Android/macOS/iOS 的安装指引;它们属于发布页,
43
48
  * 不该出现在 Android 的更新弹窗。保留分隔线前的变更摘要,并兼容旧版正文。
@@ -78,10 +83,30 @@ export declare class DistributionManager {
78
83
  * compareLocalAssetCandidates)。返回删除的文件名与释放字节数;目录不存在、
79
84
  * 只剩一个包等情形都是空操作。单个文件删除失败不阻断其余清理。
80
85
  */
81
- pruneAndroidApkDirectory(keepFileName?: string): Promise<{
82
- deleted: string[];
83
- freedBytes: number;
84
- }>;
86
+ pruneAndroidApkDirectory(keepFileName?: string): Promise<PruneReport>;
87
+ /**
88
+ * 清理 macOS 分发目录。macOS 的构建脚本每个 debug 构建都产出独立时间戳的
89
+ * `.zip` + `.dmg` + `.update.json`,跨版本、同版本多次构建都会堆积。
90
+ *
91
+ * 保留:最新版本的全部文件(同版本的 zip/dmg/update.json 是一套,必须整体
92
+ * 保留——更新检查选 zip、`/macos/download` 可能选 dmg、update.json 是它们的
93
+ * 校验边车)、`currentDmgFile` 固定指向的包及其边车、本次正在下发的包及其边车。
94
+ * 其余版本的文件才会删除。
95
+ */
96
+ pruneMacosBetaDirectory(keepFileName?: string): Promise<PruneReport>;
97
+ /**
98
+ * 清理 iOS 分发目录。`ios/build.sh` 每次构建产出 `wand-v<version>.ipa`,
99
+ * debug 版本号带时间戳,因此同样会按构建次数堆积。
100
+ */
101
+ pruneIosIpaDirectory(keepFileName?: string): Promise<PruneReport>;
102
+ /**
103
+ * 分发目录清理的统一实现。三个平台只在「哪些文件算包」「怎么比版本」上不同,
104
+ * 保留规则和删除安全性必须共用,避免各自演化出不同答案。
105
+ *
106
+ * 排序与更新检查共用 `compareLocalAssetCandidates`,所以清理永远保留更新
107
+ * 检查会选中的那个文件。`.update.json` 这类边车不参与排序,只跟随它的包。
108
+ */
109
+ private pruneDistributionDirectory;
85
110
  /** Beta 仅从当前服务端的 macOS 分发目录选择,绝不回退 GitHub。 */
86
111
  resolveLatestMacosBeta(): Promise<ResolvedDistributionAsset | null>;
87
112
  /** 固定下载文件名,避免检查与下载之间新增包后下载到另一个版本。 */
@@ -2,9 +2,31 @@ import { createHash } from "node:crypto";
2
2
  import { createReadStream } from "node:fs";
3
3
  import { lstat, mkdir, readdir, readFile, stat, unlink } from "node:fs/promises";
4
4
  import path from "node:path";
5
- import { compareApkInstallOrder, compareSemver, compareWandInstallOrder, extractSemver } from "./version-utils.js";
5
+ import { compareApkInstallOrder, compareDistributionFiles, compareMacosFormatTieBreak, compareSemver, compareWandInstallOrder, extractSemver } from "./version-utils.js";
6
6
  const GITHUB_CACHE_TTL_MS = 10 * 60 * 1000;
7
7
  const MACOS_UPDATE_FILE_PATTERN = /^wand-v\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?(?:\+[A-Za-z0-9.-]+)?\.(?:zip|dmg)$/i;
8
+ /** macOS 构建脚本写在包旁边的校验边车(`wand-v<version>.update.json`)。 */
9
+ const MACOS_UPDATE_SIDECAR_PATTERN = /^wand-v\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?(?:\+[A-Za-z0-9.-]+)?\.update\.json$/i;
10
+ function emptyPruneReport() {
11
+ return { deleted: [], freedBytes: 0 };
12
+ }
13
+ /** 从 `wand-v1.2.3.update.json` 得到 `wand-v1.2.3`,非边车返回 null。 */
14
+ function macosSidecarBase(name) {
15
+ if (!MACOS_UPDATE_SIDECAR_PATTERN.test(name))
16
+ return null;
17
+ return name.slice(0, -"update.json".length - 1);
18
+ }
19
+ /** 尽力删除:删不掉(如下载流仍持有)就留给下次触发,不计入结果。 */
20
+ async function removeQuietly(filePath, report) {
21
+ try {
22
+ await unlink(filePath);
23
+ report.deleted.push(path.basename(filePath));
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
8
30
  /**
9
31
  * GitHub Release 正文还包含 Android/macOS/iOS 的安装指引;它们属于发布页,
10
32
  * 不该出现在 Android 的更新弹窗。保留分隔线前的变更摘要,并兼容旧版正文。
@@ -50,25 +72,11 @@ function extractArtifactVersion(fileName, extension) {
50
72
  return extractSemver(fileName.replace(new RegExp(`\\${extension}$`, "i"), ""));
51
73
  }
52
74
  /**
53
- * 本地分发候选的统一排序:版本新者优先(调用方提供比较器),解析不出版本的
54
- * 文件排在有版本的后面,同版本按修改时间新者优先。resolveLocalAsset 的挑选
55
- * 和 pruneAndroidApkDirectory 的「保最新」判定共用这一份规则。
75
+ * 本地分发候选的统一排序:直接复用 version-utils 的实现,保证「更新检查选谁」
76
+ * 与「构建包清理保谁」永远同一个答案。
56
77
  */
57
78
  function compareLocalAssetCandidates(a, b, extension, compareVersions) {
58
- const aVersion = extractArtifactVersion(a.name, extension);
59
- const bVersion = extractArtifactVersion(b.name, extension);
60
- if (aVersion && bVersion) {
61
- const comparison = compareVersions(bVersion, aVersion);
62
- if (comparison !== 0)
63
- return comparison;
64
- }
65
- else if (aVersion) {
66
- return -1;
67
- }
68
- else if (bVersion) {
69
- return 1;
70
- }
71
- return b.mtimeMs - a.mtimeMs;
79
+ return compareDistributionFiles({ ...a, version: extractArtifactVersion(a.name, extension) }, { ...b, version: extractArtifactVersion(b.name, extension) }, compareVersions);
72
80
  }
73
81
  function isPrerelease(version) {
74
82
  return !!version && version.includes("-");
@@ -172,45 +180,136 @@ export class DistributionManager {
172
180
  await this.refreshConfig();
173
181
  const { config, configDir } = this.options;
174
182
  if (config.android?.enabled !== true)
175
- return { deleted: [], freedBytes: 0 };
176
- const directory = resolveConfiguredDir(configDir, config.android.apkDir, "android");
183
+ return emptyPruneReport();
184
+ return this.pruneDistributionDirectory({
185
+ directory: resolveConfiguredDir(configDir, config.android.apkDir, "android"),
186
+ matches: (name) => name.toLowerCase().endsWith(".apk"),
187
+ extension: (name) => ".apk",
188
+ compareVersions: compareApkInstallOrder,
189
+ keepNames: [config.android.currentApkFile, keepFileName],
190
+ });
191
+ }
192
+ /**
193
+ * 清理 macOS 分发目录。macOS 的构建脚本每个 debug 构建都产出独立时间戳的
194
+ * `.zip` + `.dmg` + `.update.json`,跨版本、同版本多次构建都会堆积。
195
+ *
196
+ * 保留:最新版本的全部文件(同版本的 zip/dmg/update.json 是一套,必须整体
197
+ * 保留——更新检查选 zip、`/macos/download` 可能选 dmg、update.json 是它们的
198
+ * 校验边车)、`currentDmgFile` 固定指向的包及其边车、本次正在下发的包及其边车。
199
+ * 其余版本的文件才会删除。
200
+ */
201
+ async pruneMacosBetaDirectory(keepFileName) {
202
+ await this.refreshConfig();
203
+ const { config, configDir } = this.options;
204
+ if (config.macos?.enabled !== true)
205
+ return emptyPruneReport();
206
+ return this.pruneDistributionDirectory({
207
+ directory: resolveConfiguredDir(configDir, config.macos.dmgDir, "macos"),
208
+ matches: (name) => MACOS_UPDATE_FILE_PATTERN.test(name) || MACOS_UPDATE_SIDECAR_PATTERN.test(name),
209
+ extension: (name) => (name.toLowerCase().endsWith(".zip") ? ".zip" : ".dmg"),
210
+ // 与 resolveMacosBetaAsset 完全同一份规则:同版本 ZIP 优先,其次修改时间。
211
+ compareVersions: compareWandInstallOrder,
212
+ tieBreak: compareMacosFormatTieBreak,
213
+ keepNames: [config.macos.currentDmgFile, keepFileName],
214
+ sidecarOf: macosSidecarBase,
215
+ });
216
+ }
217
+ /**
218
+ * 清理 iOS 分发目录。`ios/build.sh` 每次构建产出 `wand-v<version>.ipa`,
219
+ * debug 版本号带时间戳,因此同样会按构建次数堆积。
220
+ */
221
+ async pruneIosIpaDirectory(keepFileName) {
222
+ await this.refreshConfig();
223
+ const { config, configDir } = this.options;
224
+ if (config.ios?.enabled !== true)
225
+ return emptyPruneReport();
226
+ return this.pruneDistributionDirectory({
227
+ directory: resolveConfiguredDir(configDir, config.ios.ipaDir, "ios"),
228
+ matches: (name) => name.toLowerCase().endsWith(".ipa"),
229
+ extension: () => ".ipa",
230
+ compareVersions: compareWandInstallOrder,
231
+ keepNames: [config.ios.currentIpaFile, keepFileName],
232
+ });
233
+ }
234
+ /**
235
+ * 分发目录清理的统一实现。三个平台只在「哪些文件算包」「怎么比版本」上不同,
236
+ * 保留规则和删除安全性必须共用,避免各自演化出不同答案。
237
+ *
238
+ * 排序与更新检查共用 `compareLocalAssetCandidates`,所以清理永远保留更新
239
+ * 检查会选中的那个文件。`.update.json` 这类边车不参与排序,只跟随它的包。
240
+ */
241
+ async pruneDistributionDirectory(options) {
242
+ const report = emptyPruneReport();
177
243
  let entries;
178
244
  try {
179
- entries = await readdir(directory, { withFileTypes: true });
245
+ entries = await readdir(options.directory, { withFileTypes: true });
180
246
  }
181
247
  catch {
182
- return { deleted: [], freedBytes: 0 };
248
+ return report;
183
249
  }
184
- const apks = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".apk"));
185
- if (apks.length === 0)
186
- return { deleted: [], freedBytes: 0 };
187
- const candidates = await Promise.all(apks.map(async (entry) => ({
188
- name: entry.name,
189
- filePath: path.join(directory, entry.name),
190
- fileStat: await stat(path.join(directory, entry.name)),
191
- })));
192
- const sorted = [...candidates].sort((a, b) => compareLocalAssetCandidates({ name: a.name, mtimeMs: a.fileStat.mtimeMs }, { name: b.name, mtimeMs: b.fileStat.mtimeMs }, ".apk", compareApkInstallOrder));
193
- const keepNames = new Set([sorted[0].name]);
194
- const pinned = config.android.currentApkFile?.trim();
195
- if (pinned)
196
- keepNames.add(path.basename(pinned));
197
- if (keepFileName)
198
- keepNames.add(keepFileName);
199
- const deleted = [];
200
- let freedBytes = 0;
201
- for (const candidate of sorted) {
202
- if (keepNames.has(candidate.name))
203
- continue;
250
+ const matched = entries.filter((entry) => entry.isFile() && options.matches(entry.name));
251
+ if (matched.length === 0)
252
+ return report;
253
+ const candidates = await Promise.all(matched.map(async (entry) => {
254
+ const filePath = path.join(options.directory, entry.name);
204
255
  try {
205
- await unlink(candidate.filePath);
206
- deleted.push(candidate.name);
207
- freedBytes += candidate.fileStat.size;
256
+ const fileStat = await stat(filePath);
257
+ const sidecar = options.sidecarOf?.(entry.name) ?? null;
258
+ return {
259
+ name: entry.name,
260
+ filePath,
261
+ fileStat,
262
+ version: sidecar ? null : extractArtifactVersion(entry.name, options.extension(entry.name)),
263
+ sidecar,
264
+ };
208
265
  }
209
266
  catch {
210
- // 清理是尽力而为;删不掉(如正被另一个下载流读取)留到下次触发。
267
+ return null;
211
268
  }
269
+ }));
270
+ const packages = candidates.filter((candidate) => candidate !== null && candidate.version !== null);
271
+ const sidecars = candidates.filter((candidate) => candidate !== null && candidate.version === null);
272
+ if (packages.length === 0)
273
+ return report;
274
+ const sorted = [...packages].sort((a, b) => compareDistributionFiles({ name: a.name, mtimeMs: a.fileStat.mtimeMs, version: a.version }, { name: b.name, mtimeMs: b.fileStat.mtimeMs, version: b.version }, options.compareVersions, options.tieBreak));
275
+ const keepNames = new Set();
276
+ const newest = sorted[0];
277
+ // 最新版本保留整套:同版本的所有格式(zip/dmg)都留。
278
+ // debug 构建的版本号里已带构建时间戳(`4.97.1-debug.10181725`),所以同版本
279
+ // 多次构建是不同 version,自然只有最靠前的那一次被保留。
280
+ for (const candidate of packages)
281
+ if (candidate.version === newest.version)
282
+ keepNames.add(candidate.name);
283
+ for (const name of options.keepNames) {
284
+ const base = name?.trim();
285
+ if (!base)
286
+ continue;
287
+ const fileName = path.basename(base);
288
+ keepNames.add(fileName);
289
+ const version = extractArtifactVersion(fileName, options.extension(fileName));
290
+ // 固定指向某个版本时,该版本整套产物一起保留,避免留下半套。
291
+ if (version)
292
+ for (const candidate of packages)
293
+ if (candidate.version === version)
294
+ keepNames.add(candidate.name);
212
295
  }
213
- return { deleted, freedBytes };
296
+ for (const candidate of packages) {
297
+ if (keepNames.has(candidate.name))
298
+ continue;
299
+ if (await removeQuietly(candidate.filePath, report))
300
+ report.freedBytes += candidate.fileStat.size;
301
+ }
302
+ // 边车跟随它的包:保留集合里的包所对应的 `wand-v<version>` 基名。
303
+ const keepBases = new Set();
304
+ for (const name of keepNames)
305
+ keepBases.add(name.slice(0, name.length - path.extname(name).length));
306
+ for (const sidecar of sidecars) {
307
+ if (sidecar.sidecar && keepBases.has(sidecar.sidecar))
308
+ continue;
309
+ if (await removeQuietly(sidecar.filePath, report))
310
+ report.freedBytes += sidecar.fileStat.size;
311
+ }
312
+ return report;
214
313
  }
215
314
  /** Beta 仅从当前服务端的 macOS 分发目录选择,绝不回退 GitHub。 */
216
315
  async resolveLatestMacosBeta() {
@@ -266,16 +365,7 @@ export class DistributionManager {
266
365
  filePath: path.join(directory, entry.name),
267
366
  fileStat: await stat(path.join(directory, entry.name)),
268
367
  })));
269
- candidates.sort((a, b) => {
270
- const aVersion = extractArtifactVersion(a.name, path.extname(a.name));
271
- const bVersion = extractArtifactVersion(b.name, path.extname(b.name));
272
- const order = compareWandInstallOrder(aVersion ?? "", bVersion ?? "");
273
- if (order !== 0)
274
- return -order;
275
- // 同版本优先 ZIP,避免 DMG 挂载;同格式多构建按最新修改时间挑选。
276
- const format = Number(b.name.toLowerCase().endsWith(".zip")) - Number(a.name.toLowerCase().endsWith(".zip"));
277
- return format || b.fileStat.mtimeMs - a.fileStat.mtimeMs;
278
- });
368
+ candidates.sort((a, b) => compareDistributionFiles({ name: a.name, mtimeMs: a.fileStat.mtimeMs, version: extractArtifactVersion(a.name, path.extname(a.name)) }, { name: b.name, mtimeMs: b.fileStat.mtimeMs, version: extractArtifactVersion(b.name, path.extname(b.name)) }, compareWandInstallOrder, compareMacosFormatTieBreak));
279
369
  const selected = candidates[0];
280
370
  if (!selected)
281
371
  return null;
@@ -1,7 +1,6 @@
1
1
  import type { WandTaskAgent } from "./task-types.js";
2
2
  export { DECISION_EXPERT_KEY } from "./decision-expert-identity.js";
3
3
  export { HR_EMPLOYEE_KEY } from "./hr-employee-identity.js";
4
- export { SPEECH_POLISHER_KEY } from "./speech-polisher-identity.js";
5
4
  /** Employee contact entry; identity and execution choices are persisted independently of teams. */
6
5
  export interface SiliconEmployee {
7
6
  id: string;
@@ -12,6 +11,8 @@ export interface SiliconEmployee {
12
11
  agents: WandTaskAgent[];
13
12
  /** Built-in tags are fixed by systemKey; ordinary employees' tags are user-maintained. */
14
13
  tags?: string[];
14
+ /** Finished system auxiliary history only, excluding agent decisions/dispatch; null/omitted = unlimited, 0 = none. */
15
+ systemSessionLimit?: number | null;
15
16
  /** Non-empty = Wand built-in identity; it cannot be archived or deleted. */
16
17
  systemKey?: string;
17
18
  archivedAt?: string;
@@ -1,9 +1,7 @@
1
1
  import { DECISION_EXPERT_KEY } from "./decision-expert-identity.js";
2
2
  export { DECISION_EXPERT_KEY } from "./decision-expert-identity.js";
3
- import { SPEECH_POLISHER_KEY } from "./speech-polisher-identity.js";
4
3
  import { HR_EMPLOYEE_KEY } from "./hr-employee-identity.js";
5
4
  export { HR_EMPLOYEE_KEY } from "./hr-employee-identity.js";
6
- export { SPEECH_POLISHER_KEY } from "./speech-polisher-identity.js";
7
5
  export const SYSTEM_EMPLOYEE_KEY = "wand-ops";
8
6
  export const SYSTEM_EMPLOYEE_ID = "e_wand_ops";
9
7
  export const SYSTEM_EMPLOYEE_NAME = "勤劳的初二";
@@ -24,14 +22,14 @@ export function isDecisionExpertSiliconEmployee(employee) {
24
22
  export function isBuiltinSiliconEmployee(employee) {
25
23
  const key = employee?.systemKey;
26
24
  return key === SYSTEM_EMPLOYEE_KEY || key === DEFAULT_EMPLOYEE_KEY || key === DECISION_EXPERT_KEY
27
- || key === SPEECH_POLISHER_KEY || key === HR_EMPLOYEE_KEY;
25
+ || key === HR_EMPLOYEE_KEY;
28
26
  }
29
27
  export const SILICON_EMPLOYEE_MAX_TAGS = 8;
30
28
  export const SILICON_EMPLOYEE_TAG_MAX_CHARS = 20;
31
29
  /** Built-in labels can only be projected from the corresponding stable employee identity. */
32
30
  export function siliconEmployeeTags(employee) {
33
31
  if (isSystemSiliconEmployee(employee) || isDecisionExpertSiliconEmployee(employee)
34
- || employee.systemKey === SPEECH_POLISHER_KEY || employee.systemKey === HR_EMPLOYEE_KEY) {
32
+ || employee.systemKey === HR_EMPLOYEE_KEY) {
35
33
  return [SYSTEM_EMPLOYEE_TAG];
36
34
  }
37
35
  if (isDefaultSiliconEmployee(employee))
@@ -23,7 +23,6 @@ export declare const FEEDBACK_API_FEATURES: {
23
23
  readonly "file-read": "files";
24
24
  readonly "file-write": "files";
25
25
  readonly "recent-paths": "files";
26
- readonly speech: "speech";
27
26
  readonly settings: "settings";
28
27
  readonly config: "settings";
29
28
  readonly "user-profile": "settings";
@@ -4,7 +4,6 @@ export const FEEDBACK_API_FEATURES = {
4
4
  conversations: "conversations",
5
5
  "wand-tasks": "tasks", "wand-task-agent-defaults": "tasks", tasks: "tasks", "workspace-tasks": "tasks", workspaces: "tasks", "wand-milestones": "tasks", missions: "tasks", "scheduled-tasks": "tasks",
6
6
  files: "files", upload: "files", directory: "files", "session-files": "files", "file-preview": "files", "file-read": "files", "file-write": "files", "recent-paths": "files",
7
- speech: "speech",
8
7
  settings: "settings", config: "settings", "user-profile": "settings", "auto-update": "settings", "update-channel": "settings",
9
8
  models: "models", "model-groups": "models", "local-models": "models",
10
9
  login: "authentication", logout: "authentication", "app-login": "authentication", "session-check": "authentication", "set-password": "authentication",
@@ -16,7 +16,6 @@ export declare const FEEDBACK_FEATURE_LABELS: {
16
16
  readonly conversations: "对话";
17
17
  readonly tasks: "任务";
18
18
  readonly files: "文件";
19
- readonly speech: "语音";
20
19
  readonly settings: "设置";
21
20
  readonly models: "模型";
22
21
  readonly authentication: "登录认证";
package/dist/feedback.js CHANGED
@@ -17,7 +17,7 @@ export const FEEDBACK_CLIENT_SOURCES = ["web", "macos", "android", "ios"];
17
17
  /** Stable semantic owners, not error-text classification or user-controlled URLs. */
18
18
  export const FEEDBACK_FEATURE_LABELS = {
19
19
  app_update: "应用更新", server_update: "服务端更新", provider_update: "CLI 工具更新", daemon_update: "底层组件更新",
20
- api: "接口请求", sessions: "会话", conversations: "对话", tasks: "任务", files: "文件", speech: "语音",
20
+ api: "接口请求", sessions: "会话", conversations: "对话", tasks: "任务", files: "文件",
21
21
  settings: "设置", models: "模型", authentication: "登录认证", realtime: "实时连接", runtime: "系统运行",
22
22
  startup: "服务启动", storage: "存储", repository: "代码仓库",
23
23
  };