wave-agent-sdk 1.1.5 → 1.3.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 (210) hide show
  1. package/dist/agent.d.ts +128 -28
  2. package/dist/agent.js +201 -49
  3. package/dist/builtin/index.js +2 -0
  4. package/dist/builtin/plugins.js +11 -20
  5. package/dist/builtin/skills/settings.js +7 -20
  6. package/dist/builtin/skills/wave-daemon.d.ts +1 -0
  7. package/dist/builtin/skills/wave-daemon.js +194 -0
  8. package/dist/constants/images.d.ts +26 -0
  9. package/dist/constants/images.js +26 -0
  10. package/dist/constants/index.d.ts +16 -0
  11. package/dist/constants/index.js +16 -0
  12. package/dist/constants/memory.d.ts +26 -0
  13. package/dist/constants/memory.js +34 -0
  14. package/dist/constants/messages.d.ts +11 -0
  15. package/dist/constants/messages.js +11 -0
  16. package/dist/constants/plugins.d.ts +8 -0
  17. package/dist/constants/plugins.js +8 -0
  18. package/dist/constants/tools.d.ts +1 -0
  19. package/dist/constants/tools.js +1 -0
  20. package/dist/core/plugin.d.ts +54 -10
  21. package/dist/core/plugin.js +137 -23
  22. package/dist/core/session.d.ts +1 -1
  23. package/dist/core/session.js +1 -1
  24. package/dist/exec/catalog.d.ts +140 -0
  25. package/dist/exec/catalog.js +470 -0
  26. package/dist/exec/catalogAnnouncement.d.ts +89 -0
  27. package/dist/exec/catalogAnnouncement.js +293 -0
  28. package/dist/exec/constants.d.ts +51 -0
  29. package/dist/exec/constants.js +51 -0
  30. package/dist/exec/execRuntime.d.ts +55 -0
  31. package/dist/exec/execRuntime.js +217 -0
  32. package/dist/exec/workerSource.d.ts +28 -0
  33. package/dist/exec/workerSource.js +299 -0
  34. package/dist/host/index.d.ts +23 -0
  35. package/dist/host/index.js +23 -0
  36. package/dist/index.d.ts +7 -1
  37. package/dist/index.js +8 -1
  38. package/dist/managers/MemoryRuleManager.d.ts +6 -0
  39. package/dist/managers/MemoryRuleManager.js +12 -0
  40. package/dist/managers/aiManager.d.ts +35 -25
  41. package/dist/managers/aiManager.js +204 -202
  42. package/dist/managers/backgroundTaskManager.js +14 -0
  43. package/dist/managers/bashModeManager.d.ts +33 -0
  44. package/dist/managers/bashModeManager.js +110 -0
  45. package/dist/managers/hookManager.d.ts +18 -0
  46. package/dist/managers/hookManager.js +37 -3
  47. package/dist/managers/liveConfigManager.d.ts +33 -0
  48. package/dist/managers/liveConfigManager.js +106 -11
  49. package/dist/managers/lspManager.d.ts +9 -0
  50. package/dist/managers/lspManager.js +47 -18
  51. package/dist/managers/mcpManager.d.ts +68 -10
  52. package/dist/managers/mcpManager.js +265 -15
  53. package/dist/managers/messageManager.d.ts +60 -18
  54. package/dist/managers/messageManager.js +170 -81
  55. package/dist/managers/permissionManager.d.ts +69 -0
  56. package/dist/managers/permissionManager.js +221 -78
  57. package/dist/managers/planManager.d.ts +9 -0
  58. package/dist/managers/planManager.js +19 -1
  59. package/dist/managers/pluginManager.d.ts +46 -2
  60. package/dist/managers/pluginManager.js +117 -11
  61. package/dist/managers/pluginScopeManager.d.ts +15 -2
  62. package/dist/managers/pluginScopeManager.js +20 -1
  63. package/dist/managers/skillManager.d.ts +50 -0
  64. package/dist/managers/skillManager.js +166 -12
  65. package/dist/managers/slashCommandManager.d.ts +10 -0
  66. package/dist/managers/slashCommandManager.js +44 -31
  67. package/dist/managers/subagentManager.d.ts +15 -0
  68. package/dist/managers/subagentManager.js +81 -9
  69. package/dist/managers/toolManager.d.ts +29 -3
  70. package/dist/managers/toolManager.js +87 -13
  71. package/dist/managers/workflowManager.js +6 -0
  72. package/dist/prompts/autoMemory.d.ts +9 -0
  73. package/dist/prompts/autoMemory.js +30 -31
  74. package/dist/prompts/autoMemoryExtraction.d.ts +4 -0
  75. package/dist/prompts/autoMemoryExtraction.js +8 -111
  76. package/dist/prompts/index.d.ts +0 -1
  77. package/dist/prompts/index.js +0 -4
  78. package/dist/prompts/memoryTypes.d.ts +63 -0
  79. package/dist/prompts/memoryTypes.js +191 -0
  80. package/dist/services/GitService.d.ts +7 -0
  81. package/dist/services/GitService.js +23 -0
  82. package/dist/services/MarketplaceService.d.ts +101 -17
  83. package/dist/services/MarketplaceService.js +323 -102
  84. package/dist/services/artifactContent.d.ts +84 -0
  85. package/dist/services/artifactContent.js +204 -0
  86. package/dist/services/artifactSession.d.ts +6 -0
  87. package/dist/services/artifactSession.js +17 -0
  88. package/dist/services/autoMemoryService.js +5 -13
  89. package/dist/services/configurationService.d.ts +92 -9
  90. package/dist/services/configurationService.js +246 -64
  91. package/dist/services/contentSummarizer.d.ts +15 -0
  92. package/dist/services/contentSummarizer.js +45 -0
  93. package/dist/services/execAvailability.d.ts +9 -0
  94. package/dist/services/execAvailability.js +32 -0
  95. package/dist/services/fileWatcher.js +61 -6
  96. package/dist/services/initializationService.js +21 -17
  97. package/dist/services/interactionService.d.ts +9 -1
  98. package/dist/services/interactionService.js +28 -8
  99. package/dist/services/jsonlHandler.d.ts +98 -0
  100. package/dist/services/jsonlHandler.js +250 -12
  101. package/dist/services/memory.d.ts +17 -1
  102. package/dist/services/memory.js +44 -7
  103. package/dist/services/officialMarketplaceMirror.d.ts +85 -0
  104. package/dist/services/officialMarketplaceMirror.js +290 -0
  105. package/dist/services/pluginLoader.d.ts +12 -4
  106. package/dist/services/pluginLoader.js +38 -7
  107. package/dist/services/remoteSettingsService.js +20 -6
  108. package/dist/services/session.d.ts +74 -0
  109. package/dist/services/session.js +174 -16
  110. package/dist/services/sessionEntries.d.ts +2 -0
  111. package/dist/services/sessionEntries.js +20 -0
  112. package/dist/services/worktreeHooks.js +6 -1
  113. package/dist/stdio/index.d.ts +12 -0
  114. package/dist/stdio/index.js +12 -0
  115. package/dist/stdio/notificationRouter.d.ts +38 -0
  116. package/dist/stdio/notificationRouter.js +97 -0
  117. package/dist/stdio/rpcClient.d.ts +18 -0
  118. package/dist/stdio/rpcClient.js +10 -0
  119. package/dist/stdio/stdioAgent.d.ts +229 -0
  120. package/dist/stdio/stdioAgent.js +360 -0
  121. package/dist/tools/artifactTool.js +406 -273
  122. package/dist/tools/bashTool.js +10 -6
  123. package/dist/tools/editTool.js +6 -3
  124. package/dist/tools/execTool.d.ts +2 -0
  125. package/dist/tools/execTool.js +165 -0
  126. package/dist/tools/exitPlanMode.js +10 -2
  127. package/dist/tools/grepTool.js +7 -1
  128. package/dist/tools/readTool.js +30 -2
  129. package/dist/tools/types.d.ts +34 -8
  130. package/dist/tools/webFetchTool.js +15 -166
  131. package/dist/tools/workflowTool.js +40 -8
  132. package/dist/tools/writeTool.js +6 -3
  133. package/dist/types/agent.d.ts +24 -1
  134. package/dist/types/commands.d.ts +7 -0
  135. package/dist/types/configuration.d.ts +45 -2
  136. package/dist/types/hooks.d.ts +1 -0
  137. package/dist/types/hooks.js +19 -0
  138. package/dist/types/marketplace.d.ts +40 -2
  139. package/dist/types/mcp.d.ts +42 -0
  140. package/dist/types/messaging.d.ts +1 -8
  141. package/dist/types/permissions.d.ts +22 -0
  142. package/dist/types/permissions.js +17 -0
  143. package/dist/types/plugins.d.ts +26 -2
  144. package/dist/types/skills.d.ts +26 -0
  145. package/dist/utils/bashParser.d.ts +17 -0
  146. package/dist/utils/bashParser.js +72 -0
  147. package/dist/utils/bashStructure/bashLexer.d.ts +96 -0
  148. package/dist/utils/bashStructure/bashLexer.js +676 -0
  149. package/dist/utils/bashStructure/bashParser.d.ts +144 -0
  150. package/dist/utils/bashStructure/bashParser.js +606 -0
  151. package/dist/utils/bashStructure/bashSemantics.d.ts +70 -0
  152. package/dist/utils/bashStructure/bashSemantics.js +477 -0
  153. package/dist/utils/bashStructure/index.d.ts +26 -0
  154. package/dist/utils/bashStructure/index.js +27 -0
  155. package/dist/utils/bashStructure/types.d.ts +62 -0
  156. package/dist/utils/bashStructure/types.js +47 -0
  157. package/dist/utils/constants.d.ts +10 -0
  158. package/dist/utils/constants.js +10 -0
  159. package/dist/utils/containerSetup.js +48 -6
  160. package/dist/utils/convertMessagesForAPI.d.ts +7 -1
  161. package/dist/utils/convertMessagesForAPI.js +64 -14
  162. package/dist/utils/fileChangeReminder.d.ts +20 -0
  163. package/dist/utils/fileChangeReminder.js +153 -0
  164. package/dist/utils/fileSearch.js +4 -3
  165. package/dist/utils/fileUtils.d.ts +44 -0
  166. package/dist/utils/fileUtils.js +118 -0
  167. package/dist/utils/frontmatterYaml.d.ts +33 -0
  168. package/dist/utils/frontmatterYaml.js +192 -0
  169. package/dist/utils/imageBudget.d.ts +85 -0
  170. package/dist/utils/imageBudget.js +109 -0
  171. package/dist/utils/imageDimensions.d.ts +83 -0
  172. package/dist/utils/imageDimensions.js +232 -0
  173. package/dist/utils/imageProcessor.d.ts +66 -0
  174. package/dist/utils/imageProcessor.js +84 -0
  175. package/dist/utils/imageRewrite.d.ts +29 -0
  176. package/dist/utils/imageRewrite.js +251 -0
  177. package/dist/utils/markdownParser.d.ts +5 -1
  178. package/dist/utils/markdownParser.js +9 -51
  179. package/dist/utils/mcpInstructions.d.ts +61 -0
  180. package/dist/utils/mcpInstructions.js +126 -0
  181. package/dist/utils/mcpUtils.d.ts +7 -0
  182. package/dist/utils/mcpUtils.js +11 -2
  183. package/dist/utils/memoryAge.d.ts +32 -0
  184. package/dist/utils/memoryAge.js +47 -0
  185. package/dist/utils/memoryEntrypoint.d.ts +20 -0
  186. package/dist/utils/memoryEntrypoint.js +49 -0
  187. package/dist/utils/memoryIndex.d.ts +30 -0
  188. package/dist/utils/memoryIndex.js +76 -0
  189. package/dist/utils/messageOperations.d.ts +6 -20
  190. package/dist/utils/messageOperations.js +40 -91
  191. package/dist/utils/nestedMemory.d.ts +22 -0
  192. package/dist/utils/nestedMemory.js +61 -0
  193. package/dist/utils/npmTarball.d.ts +19 -0
  194. package/dist/utils/npmTarball.js +92 -0
  195. package/dist/utils/pluginSource.d.ts +37 -0
  196. package/dist/utils/pluginSource.js +73 -0
  197. package/dist/utils/ripgrep.d.ts +18 -4
  198. package/dist/utils/ripgrep.js +56 -4
  199. package/dist/utils/runtimeDeps.d.ts +35 -0
  200. package/dist/utils/runtimeDeps.js +426 -0
  201. package/dist/utils/skillParser.js +22 -52
  202. package/dist/utils/subagentParser.js +48 -45
  203. package/dist/utils/tokenCalculation.js +0 -8
  204. package/dist/utils/userSettings.d.ts +90 -0
  205. package/dist/utils/userSettings.js +291 -0
  206. package/dist/utils/worktreeUtils.d.ts +2 -1
  207. package/dist/utils/worktreeUtils.js +64 -34
  208. package/package.json +12 -4
  209. package/dist/managers/bangManager.d.ts +0 -26
  210. package/dist/managers/bangManager.js +0 -78
@@ -0,0 +1,90 @@
1
+ /**
2
+ * 用户偏好设置(三端设置页读写的子集)的读写工具。
3
+ *
4
+ * 落点唯一 = 用户级 `~/.wave/settings.json`(`getUserConfigPaths()[0]`):
5
+ * 三端(VS Code 扩展 / JetBrains 插件 / 桌面端)设置页保存的 AI 回复语言、
6
+ * 上下文长度、自动记忆开关/轮次、服务端地址都写这个文件,由 SDK 的实时重载
7
+ * (LiveConfigManager)在各会话下一轮开始时生效——不经 `AgentOptions`
8
+ * 覆盖层下发(覆盖层优先级高于 settings.json,会永久遮蔽实时配置,
9
+ * 见 docs/specs/core/agent-config.md「设置实时重载」「分层职责」)。
10
+ *
11
+ * 上下文长度的键 = `env.WAVE_MAX_INPUT_TOKENS`(全局默认,K×1000 落盘、
12
+ * 回读按同一换算),UI 与线协议都传 K 值。
13
+ *
14
+ * 服务端地址的键 = `env.WAVE_SERVER_URL`(SDK 既有键,解析链
15
+ * `options.serverUrl > process.env.WAVE_SERVER_URL > DEFAULT_SERVER_URL`;
16
+ * settings `env` 的值经 `setEnvironmentVars` 特例镜像到 `process.env`,
17
+ * 见 docs/specs/core/agent-config.md 边界说明「服务端地址的落点」)。
18
+ *
19
+ * 写入是「读-改-写」:只覆盖这些键,其余顶层键与 `env` 下其它键原样保留。
20
+ *
21
+ * `readUserPreferenceView` 额外回答**当前生效值来自哪一层**:用户级文件是用户
22
+ * 偏好的**落点**,但企业下发的 remote-settings(或机器上的环境变量)可以盖过它,
23
+ * 此时只读用户文件会得到「显示值 ≠ 生效值」(spec core/agent-config.md
24
+ * 边界说明「用户偏好的层与来源」)。设置页据此对「由组织配置管理」的键置灰。
25
+ */
26
+ import type { UserPreferenceKey, UserPreferenceSettings, UserPreferenceSource, WaveConfiguration } from "../types/configuration.js";
27
+ export type { UserPreferenceSettings };
28
+ /** 上下文长度在 settings.json 中的落点(全局默认,spec 边界说明「上下文长度的落点」)。 */
29
+ export declare const MAX_INPUT_TOKENS_ENV_KEY = "WAVE_MAX_INPUT_TOKENS";
30
+ /**
31
+ * 自动记忆的 env 键,与 `ConfigurationService.resolveAutoMemoryEnabledFrom` /
32
+ * `resolveAutoMemoryFrequency` 读取的键名一致(本视图只按同一优先级归因,
33
+ * 不改变解析行为)。
34
+ */
35
+ export declare const DISABLE_AUTO_MEMORY_ENV_KEY = "WAVE_DISABLE_AUTO_MEMORY";
36
+ export declare const AUTO_MEMORY_FREQUENCY_ENV_KEY = "WAVE_AUTO_MEMORY_FREQUENCY";
37
+ /**
38
+ * 服务端地址的 env 键(SDK 既有键,见文件头与
39
+ * `ConfigurationService.setEnvironmentVars` 的镜像特例)。
40
+ */
41
+ export declare const SERVER_URL_ENV_KEY = "WAVE_SERVER_URL";
42
+ /** 用户偏好键(设置页控件)。 */
43
+ export type { UserPreferenceKey, UserPreferenceSource };
44
+ /** 生效偏好 + 每个键的来源。 */
45
+ export interface UserPreferenceView {
46
+ /** 生效值;键缺失 = 该偏好没有任何提供者(落 SDK 默认/未设置态)。 */
47
+ values: UserPreferenceSettings;
48
+ /** 每个键恒有来源(缺省即 `default`)。 */
49
+ sources: Record<UserPreferenceKey, UserPreferenceSource>;
50
+ }
51
+ /** UI 的 K 值 → settings.json 的 token 数(K×1000)。 */
52
+ export declare function contextLengthToMaxInputTokens(contextLengthK: number): number;
53
+ /** settings.json 的 token 数 → UI 的 K 值(回读按同一换算)。 */
54
+ export declare function maxInputTokensToContextLength(maxInputTokens: string | undefined): number | undefined;
55
+ /** 用户级 settings.json 路径(`~/.wave/settings.json`)。 */
56
+ export declare function userSettingsFilePath(): string;
57
+ /**
58
+ * 读取当前用户偏好。文件缺失或损坏时返回空对象(与
59
+ * `loadWaveConfigFromFile` 的容错一致:损坏只影响展示,不阻塞设置页渲染);
60
+ * 写入路径则会明确报错而不是覆盖用户文件。
61
+ */
62
+ export declare function readUserPreferenceSettings(filePath?: string): UserPreferenceSettings;
63
+ /**
64
+ * 读取**生效**用户偏好及其来源层。
65
+ *
66
+ * 优先级与各 resolve 链一致(只按**进程级可达**的层归因):Remote 组织下发
67
+ * (顶层键,以及其 `env` 的 `WAVE_MAX_INPUT_TOKENS` / `WAVE_DISABLE_AUTO_MEMORY` /
68
+ * `WAVE_AUTO_MEMORY_FREQUENCY`)> 用户级文件(同样两种落点)> 机器环境变量
69
+ * (`process.env`)> 默认。Remote 胜出与 `mergeRemoteSettings` 的「标量远程赢、
70
+ * env 逐键远程赢」一致(remoteSettingsService.ts)。
71
+ *
72
+ * `values` 的语义**与只读用户文件时完全一致**:只有真的有提供者才会出现该键,
73
+ * 没有任何层提供时键缺失(设置页据此显示「未设置」态、按 SDK 默认值展示)。
74
+ * 新增信息只在 `sources`:每个键恒有来源(缺省 `default`)。
75
+ *
76
+ * 注意归因范围:`project`(`.wave/settings.json`)/ `local`(`.wave/settings.local.json`)
77
+ * 需要 workdir,而本 RPC 是刻意的无会话全局请求;`override` / `options` 是会话级
78
+ * 语义。三者都不在本视图的归因范围内——被它们覆盖时这里仍按进程级各层给出
79
+ * 「本层值」,见 spec core/agent-config.md 边界说明「用户偏好的层与来源」。
80
+ *
81
+ * @param remote - Remote 组织下发配置(默认取进程内缓存)
82
+ * @param filePath - 用户级 settings.json 路径
83
+ * @param env - 机器环境变量(默认 `process.env`,便于测试注入)
84
+ */
85
+ export declare function readUserPreferenceView(remote?: WaveConfiguration | null, filePath?: string, env?: Record<string, string | undefined>): UserPreferenceView;
86
+ /**
87
+ * 把 `patch` 里的键合并进用户级 settings.json,返回写入后的完整用户偏好。
88
+ * 未提供的键保持文件中现值;`patch` 为空时只回读、不落盘(无差异保存)。
89
+ */
90
+ export declare function updateUserPreferenceSettings(patch: UserPreferenceSettings, filePath?: string): Promise<UserPreferenceSettings>;
@@ -0,0 +1,291 @@
1
+ /**
2
+ * 用户偏好设置(三端设置页读写的子集)的读写工具。
3
+ *
4
+ * 落点唯一 = 用户级 `~/.wave/settings.json`(`getUserConfigPaths()[0]`):
5
+ * 三端(VS Code 扩展 / JetBrains 插件 / 桌面端)设置页保存的 AI 回复语言、
6
+ * 上下文长度、自动记忆开关/轮次、服务端地址都写这个文件,由 SDK 的实时重载
7
+ * (LiveConfigManager)在各会话下一轮开始时生效——不经 `AgentOptions`
8
+ * 覆盖层下发(覆盖层优先级高于 settings.json,会永久遮蔽实时配置,
9
+ * 见 docs/specs/core/agent-config.md「设置实时重载」「分层职责」)。
10
+ *
11
+ * 上下文长度的键 = `env.WAVE_MAX_INPUT_TOKENS`(全局默认,K×1000 落盘、
12
+ * 回读按同一换算),UI 与线协议都传 K 值。
13
+ *
14
+ * 服务端地址的键 = `env.WAVE_SERVER_URL`(SDK 既有键,解析链
15
+ * `options.serverUrl > process.env.WAVE_SERVER_URL > DEFAULT_SERVER_URL`;
16
+ * settings `env` 的值经 `setEnvironmentVars` 特例镜像到 `process.env`,
17
+ * 见 docs/specs/core/agent-config.md 边界说明「服务端地址的落点」)。
18
+ *
19
+ * 写入是「读-改-写」:只覆盖这些键,其余顶层键与 `env` 下其它键原样保留。
20
+ *
21
+ * `readUserPreferenceView` 额外回答**当前生效值来自哪一层**:用户级文件是用户
22
+ * 偏好的**落点**,但企业下发的 remote-settings(或机器上的环境变量)可以盖过它,
23
+ * 此时只读用户文件会得到「显示值 ≠ 生效值」(spec core/agent-config.md
24
+ * 边界说明「用户偏好的层与来源」)。设置页据此对「由组织配置管理」的键置灰。
25
+ */
26
+ import { existsSync, mkdirSync, readFileSync } from "fs";
27
+ import { dirname } from "path";
28
+ import { atomicWriteFile } from "./atomicWrite.js";
29
+ import { getUserConfigPaths } from "./configPaths.js";
30
+ import { getRemoteSettingsSync } from "../services/remoteSettingsService.js";
31
+ /** 上下文长度在 settings.json 中的落点(全局默认,spec 边界说明「上下文长度的落点」)。 */
32
+ export const MAX_INPUT_TOKENS_ENV_KEY = "WAVE_MAX_INPUT_TOKENS";
33
+ /**
34
+ * 自动记忆的 env 键,与 `ConfigurationService.resolveAutoMemoryEnabledFrom` /
35
+ * `resolveAutoMemoryFrequency` 读取的键名一致(本视图只按同一优先级归因,
36
+ * 不改变解析行为)。
37
+ */
38
+ export const DISABLE_AUTO_MEMORY_ENV_KEY = "WAVE_DISABLE_AUTO_MEMORY";
39
+ export const AUTO_MEMORY_FREQUENCY_ENV_KEY = "WAVE_AUTO_MEMORY_FREQUENCY";
40
+ /**
41
+ * 服务端地址的 env 键(SDK 既有键,见文件头与
42
+ * `ConfigurationService.setEnvironmentVars` 的镜像特例)。
43
+ */
44
+ export const SERVER_URL_ENV_KEY = "WAVE_SERVER_URL";
45
+ /**
46
+ * 上下文长度的 env 值(token 字符串)→ UI 的 K 值;非数字/非正数视作未提供。
47
+ */
48
+ function envContextLength(raw) {
49
+ return typeof raw === "string"
50
+ ? maxInputTokensToContextLength(raw)
51
+ : undefined;
52
+ }
53
+ /** `WAVE_DISABLE_AUTO_MEMORY` → 关闭(false) / 未提供(undefined)。 */
54
+ function envDisableAutoMemory(raw) {
55
+ return raw === "1" || raw === "true" ? false : undefined;
56
+ }
57
+ /** `WAVE_AUTO_MEMORY_FREQUENCY` → 正数轮次 / 未提供。 */
58
+ function envAutoMemoryFrequency(raw) {
59
+ const parsed = Number.parseInt(typeof raw === "string" ? raw : "", 10);
60
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
61
+ }
62
+ /** 服务端地址:非空字符串 / 未提供(空串视作未提供,不编造默认值)。 */
63
+ function envNonEmptyString(raw) {
64
+ return typeof raw === "string" && raw.trim() !== "" ? raw : undefined;
65
+ }
66
+ /** 按优先级取第一个提供了值的层。 */
67
+ function selectPreference(candidates) {
68
+ for (const [source, value] of candidates) {
69
+ if (value !== undefined)
70
+ return { value, source };
71
+ }
72
+ return undefined;
73
+ }
74
+ /** UI 的 K 值 → settings.json 的 token 数(K×1000)。 */
75
+ export function contextLengthToMaxInputTokens(contextLengthK) {
76
+ return Math.round(contextLengthK * 1000);
77
+ }
78
+ /** settings.json 的 token 数 → UI 的 K 值(回读按同一换算)。 */
79
+ export function maxInputTokensToContextLength(maxInputTokens) {
80
+ if (maxInputTokens === undefined)
81
+ return undefined;
82
+ const parsed = Number.parseInt(maxInputTokens, 10);
83
+ if (!Number.isFinite(parsed) || parsed <= 0)
84
+ return undefined;
85
+ return Math.round(parsed / 1000);
86
+ }
87
+ /** 用户级 settings.json 路径(`~/.wave/settings.json`)。 */
88
+ export function userSettingsFilePath() {
89
+ return getUserConfigPaths()[0];
90
+ }
91
+ function parseSettingsFile(filePath) {
92
+ if (!existsSync(filePath))
93
+ return {};
94
+ const raw = readFileSync(filePath, "utf-8");
95
+ if (raw.trim() === "")
96
+ return {};
97
+ const parsed = JSON.parse(raw);
98
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
99
+ throw new Error(`${filePath} 的顶层必须是 JSON 对象`);
100
+ }
101
+ return parsed;
102
+ }
103
+ /** settings.json 的 `env`(用户手写文件,须容忍非对象值)。 */
104
+ function asEnvBlock(value) {
105
+ return value !== null && typeof value === "object" && !Array.isArray(value)
106
+ ? value
107
+ : {};
108
+ }
109
+ function toSettings(data) {
110
+ const env = asEnvBlock(data.env);
111
+ const rawMax = env[MAX_INPUT_TOKENS_ENV_KEY];
112
+ const settings = {};
113
+ if (typeof data.language === "string")
114
+ settings.language = data.language;
115
+ const contextLength = maxInputTokensToContextLength(typeof rawMax === "string" ? rawMax : undefined);
116
+ if (contextLength !== undefined)
117
+ settings.contextLength = contextLength;
118
+ if (typeof data.autoMemoryEnabled === "boolean") {
119
+ settings.autoMemoryEnabled = data.autoMemoryEnabled;
120
+ }
121
+ if (typeof data.autoMemoryFrequency === "number") {
122
+ settings.autoMemoryFrequency = data.autoMemoryFrequency;
123
+ }
124
+ const serverUrl = envNonEmptyString(env[SERVER_URL_ENV_KEY]);
125
+ if (serverUrl !== undefined)
126
+ settings.serverUrl = serverUrl;
127
+ return settings;
128
+ }
129
+ /** 容忍损坏的读取:文件缺失或损坏时视作没有设置(只影响展示,不阻塞设置页)。 */
130
+ function readSettingsFileTolerant(filePath) {
131
+ try {
132
+ return parseSettingsFile(filePath);
133
+ }
134
+ catch {
135
+ return {};
136
+ }
137
+ }
138
+ /**
139
+ * 读取当前用户偏好。文件缺失或损坏时返回空对象(与
140
+ * `loadWaveConfigFromFile` 的容错一致:损坏只影响展示,不阻塞设置页渲染);
141
+ * 写入路径则会明确报错而不是覆盖用户文件。
142
+ */
143
+ export function readUserPreferenceSettings(filePath = userSettingsFilePath()) {
144
+ return toSettings(readSettingsFileTolerant(filePath));
145
+ }
146
+ /**
147
+ * 读取**生效**用户偏好及其来源层。
148
+ *
149
+ * 优先级与各 resolve 链一致(只按**进程级可达**的层归因):Remote 组织下发
150
+ * (顶层键,以及其 `env` 的 `WAVE_MAX_INPUT_TOKENS` / `WAVE_DISABLE_AUTO_MEMORY` /
151
+ * `WAVE_AUTO_MEMORY_FREQUENCY`)> 用户级文件(同样两种落点)> 机器环境变量
152
+ * (`process.env`)> 默认。Remote 胜出与 `mergeRemoteSettings` 的「标量远程赢、
153
+ * env 逐键远程赢」一致(remoteSettingsService.ts)。
154
+ *
155
+ * `values` 的语义**与只读用户文件时完全一致**:只有真的有提供者才会出现该键,
156
+ * 没有任何层提供时键缺失(设置页据此显示「未设置」态、按 SDK 默认值展示)。
157
+ * 新增信息只在 `sources`:每个键恒有来源(缺省 `default`)。
158
+ *
159
+ * 注意归因范围:`project`(`.wave/settings.json`)/ `local`(`.wave/settings.local.json`)
160
+ * 需要 workdir,而本 RPC 是刻意的无会话全局请求;`override` / `options` 是会话级
161
+ * 语义。三者都不在本视图的归因范围内——被它们覆盖时这里仍按进程级各层给出
162
+ * 「本层值」,见 spec core/agent-config.md 边界说明「用户偏好的层与来源」。
163
+ *
164
+ * @param remote - Remote 组织下发配置(默认取进程内缓存)
165
+ * @param filePath - 用户级 settings.json 路径
166
+ * @param env - 机器环境变量(默认 `process.env`,便于测试注入)
167
+ */
168
+ export function readUserPreferenceView(remote = getRemoteSettingsSync(), filePath = userSettingsFilePath(), env = process.env) {
169
+ const fileData = readSettingsFileTolerant(filePath);
170
+ const user = toSettings(fileData);
171
+ const userEnv = asEnvBlock(fileData.env);
172
+ const remoteEnv = asEnvBlock(remote?.env);
173
+ const values = {};
174
+ const sources = {
175
+ language: "default",
176
+ contextLength: "default",
177
+ autoMemoryEnabled: "default",
178
+ autoMemoryFrequency: "default",
179
+ serverUrl: "default",
180
+ };
181
+ // language 没有 env 落点(`resolveLanguage` 只读合并后的顶层键)。
182
+ const language = selectPreference([
183
+ ["remote", remote?.language],
184
+ ["user", user.language],
185
+ ]);
186
+ if (language) {
187
+ values.language = language.value;
188
+ sources.language = language.source;
189
+ }
190
+ // 上下文长度的归因层 = `env.WAVE_MAX_INPUT_TOKENS` 这个 env key 所在的层
191
+ // (不是「模型配置」那层:模型自带上限时以模型配置为准是 SDK 既有行为,
192
+ // 设置页那一行可见说明已交代)。
193
+ const contextLength = selectPreference([
194
+ ["remote", envContextLength(remoteEnv[MAX_INPUT_TOKENS_ENV_KEY])],
195
+ ["user", user.contextLength],
196
+ ["env", envContextLength(env[MAX_INPUT_TOKENS_ENV_KEY])],
197
+ ]);
198
+ if (contextLength) {
199
+ values.contextLength = contextLength.value;
200
+ sources.contextLength = contextLength.source;
201
+ }
202
+ // 自动记忆开关每层有两条路径:顶层标量,以及 `env.WAVE_DISABLE_AUTO_MEMORY`。
203
+ // 顶层标量整层优先于 env(`resolveAutoMemoryEnabledFrom` 先看 config 再看 env)。
204
+ // 没有任何层提供时**不编造**值(与其余三个键一致:键缺失 = 无提供者),
205
+ // 设置页按 SDK 默认显示为「开」,与生效值一致。
206
+ const autoMemoryEnabled = selectPreference([
207
+ ["remote", remote?.autoMemoryEnabled],
208
+ ["user", user.autoMemoryEnabled],
209
+ ["remote", envDisableAutoMemory(remoteEnv[DISABLE_AUTO_MEMORY_ENV_KEY])],
210
+ ["user", envDisableAutoMemory(userEnv[DISABLE_AUTO_MEMORY_ENV_KEY])],
211
+ ["env", envDisableAutoMemory(env[DISABLE_AUTO_MEMORY_ENV_KEY])],
212
+ ]);
213
+ if (autoMemoryEnabled) {
214
+ values.autoMemoryEnabled = autoMemoryEnabled.value;
215
+ sources.autoMemoryEnabled = autoMemoryEnabled.source;
216
+ }
217
+ const autoMemoryFrequency = selectPreference([
218
+ ["remote", remote?.autoMemoryFrequency],
219
+ ["user", user.autoMemoryFrequency],
220
+ [
221
+ "remote",
222
+ envAutoMemoryFrequency(remoteEnv[AUTO_MEMORY_FREQUENCY_ENV_KEY]),
223
+ ],
224
+ ["user", envAutoMemoryFrequency(userEnv[AUTO_MEMORY_FREQUENCY_ENV_KEY])],
225
+ ["env", envAutoMemoryFrequency(env[AUTO_MEMORY_FREQUENCY_ENV_KEY])],
226
+ ]);
227
+ if (autoMemoryFrequency) {
228
+ values.autoMemoryFrequency = autoMemoryFrequency.value;
229
+ sources.autoMemoryFrequency = autoMemoryFrequency.source;
230
+ }
231
+ // 服务端地址的落点 = `env.WAVE_SERVER_URL`,来源只可能是 user / env / default——
232
+ // 不归因 remote(远端托管配置本身取自该地址,不存在「组织下发服务端地址」的闭环,
233
+ // 见 spec core/agent-config.md 边界说明「服务端地址的落点」)。
234
+ const serverUrl = selectPreference([
235
+ ["user", user.serverUrl],
236
+ ["env", envNonEmptyString(env[SERVER_URL_ENV_KEY])],
237
+ ]);
238
+ if (serverUrl) {
239
+ values.serverUrl = serverUrl.value;
240
+ sources.serverUrl = serverUrl.source;
241
+ }
242
+ return { values, sources };
243
+ }
244
+ /**
245
+ * 把 `patch` 里的键合并进用户级 settings.json,返回写入后的完整用户偏好。
246
+ * 未提供的键保持文件中现值;`patch` 为空时只回读、不落盘(无差异保存)。
247
+ */
248
+ export async function updateUserPreferenceSettings(patch, filePath = userSettingsFilePath()) {
249
+ const data = parseSettingsFile(filePath);
250
+ const hasLanguage = patch.language !== undefined;
251
+ const hasContextLength = patch.contextLength !== undefined;
252
+ const hasEnabled = patch.autoMemoryEnabled !== undefined;
253
+ const hasFrequency = patch.autoMemoryFrequency !== undefined;
254
+ // 空串视作未提供(与回读侧 `envNonEmptyString` 同一判据):写进去会在
255
+ // `process.env` 镜像里遮蔽 OS 环境变量(解析链用 `||` 回落到默认值),而回读侧
256
+ // 又按「未设置」展示 ⇒ 显示值与生效值分叉。省略键 = 不改该键。
257
+ const serverUrl = envNonEmptyString(patch.serverUrl);
258
+ if (!hasLanguage &&
259
+ !hasContextLength &&
260
+ !hasEnabled &&
261
+ !hasFrequency &&
262
+ serverUrl === undefined) {
263
+ return toSettings(data);
264
+ }
265
+ if (hasLanguage)
266
+ data.language = patch.language;
267
+ if (typeof patch.contextLength === "number" &&
268
+ Number.isFinite(patch.contextLength)) {
269
+ const env = asEnvBlock(data.env);
270
+ env[MAX_INPUT_TOKENS_ENV_KEY] = String(contextLengthToMaxInputTokens(patch.contextLength));
271
+ data.env = env;
272
+ }
273
+ if (hasEnabled)
274
+ data.autoMemoryEnabled = patch.autoMemoryEnabled;
275
+ if (typeof patch.autoMemoryFrequency === "number" &&
276
+ Number.isFinite(patch.autoMemoryFrequency)) {
277
+ data.autoMemoryFrequency = patch.autoMemoryFrequency;
278
+ }
279
+ // 服务端地址落 `env.WAVE_SERVER_URL`(既有键):原样写入,不做归一/清理
280
+ // (格式校验在设置页,见 spec 边界说明「服务端地址的落点」)。
281
+ if (serverUrl !== undefined) {
282
+ const env = asEnvBlock(data.env);
283
+ env[SERVER_URL_ENV_KEY] = serverUrl;
284
+ data.env = env;
285
+ }
286
+ // 首次保存时 `~/.wave` 可能尚不存在(会话启动时文件不存在,场景 7),
287
+ // 原子写需要同目录可写,先确保目录存在。
288
+ mkdirSync(dirname(filePath), { recursive: true });
289
+ await atomicWriteFile(filePath, `${JSON.stringify(data, null, 2)}\n`);
290
+ return toSettings(data);
291
+ }
@@ -47,7 +47,8 @@ export declare function performPostCreationSetup(worktreePath: string, repoRoot:
47
47
  * is MAX_PATH-limited — deep paths (e.g. node_modules) can fail with "Filename
48
48
  * too long", leaving an orphan directory. When git fails we fall back to
49
49
  * fs.rmSync with an extended-length path (bypasses MAX_PATH) and prune stale
50
- * metadata. Failures are logged but never block branch deletion.
50
+ * metadata. Failures are logged; when the directory survives, its branch is
51
+ * deliberately kept so the leftover checkout stays reachable through git.
51
52
  */
52
53
  export declare function removeWorktree(info: WorktreeInfo): void;
53
54
  /**
@@ -517,6 +517,36 @@ function toExtendedLengthPath(worktreePath) {
517
517
  }
518
518
  return `\\\\?\\${absolute}`;
519
519
  }
520
+ /**
521
+ * Describe what is still on disk after a failed removal. Without this a log
522
+ * line cannot distinguish "directory already gone" from "the whole checkout is
523
+ * still sitting there".
524
+ */
525
+ function probeResidue(worktreePath) {
526
+ try {
527
+ if (!fs.existsSync(worktreePath))
528
+ return "none";
529
+ const entries = fs.readdirSync(worktreePath);
530
+ const head = entries.slice(0, 5).join(",");
531
+ return `${entries.length}[${head}${entries.length > 5 ? ",…" : ""}]`;
532
+ }
533
+ catch (error) {
534
+ return `unknown(${error.code ?? "error"})`;
535
+ }
536
+ }
537
+ /** Delete the worktree directory with fs.rmSync; returns the error, if any. */
538
+ function rmWorktreeDirWithFs(worktreePath) {
539
+ try {
540
+ fs.rmSync(toExtendedLengthPath(worktreePath), {
541
+ recursive: true,
542
+ force: true,
543
+ });
544
+ return null;
545
+ }
546
+ catch (error) {
547
+ return error;
548
+ }
549
+ }
520
550
  /**
521
551
  * Remove a git worktree and its branch.
522
552
  *
@@ -525,28 +555,36 @@ function toExtendedLengthPath(worktreePath) {
525
555
  * is MAX_PATH-limited — deep paths (e.g. node_modules) can fail with "Filename
526
556
  * too long", leaving an orphan directory. When git fails we fall back to
527
557
  * fs.rmSync with an extended-length path (bypasses MAX_PATH) and prune stale
528
- * metadata. Failures are logged but never block branch deletion.
558
+ * metadata. Failures are logged; when the directory survives, its branch is
559
+ * deliberately kept so the leftover checkout stays reachable through git.
529
560
  */
530
561
  export function removeWorktree(info) {
531
562
  const repoRoot = info.repoRoot;
532
- // Get current branch in worktree before removing
533
- let currentBranch;
534
- try {
535
- currentBranch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
536
- cwd: info.path,
537
- encoding: "utf8",
538
- stdio: ["ignore", "pipe", "ignore"],
539
- }).trim();
540
- }
541
- catch {
542
- // Ignore errors
543
- }
544
563
  // Remove worktree
545
564
  try {
546
565
  execFileSync("git", ["worktree", "remove", "--force", info.path], {
547
566
  cwd: repoRoot,
548
567
  stdio: ["ignore", "pipe", "pipe"],
549
568
  });
569
+ // git exits 0 even when it did not delete the directory: on Windows it
570
+ // cannot remove the directory symlinks/junctions that pnpm's node_modules
571
+ // is made of, so it deletes the files, leaves the skeleton behind and still
572
+ // reports success. Verify on disk instead of trusting the exit status.
573
+ if (fs.existsSync(info.path)) {
574
+ logger.warn("git worktree remove reported success but the directory survived, falling back to fs.rmSync:", {
575
+ worktreePath: info.path,
576
+ residue: probeResidue(info.path),
577
+ });
578
+ const rmError = rmWorktreeDirWithFs(info.path);
579
+ if (rmError !== null) {
580
+ logger.error("Failed to remove worktree or branch:", {
581
+ worktreePath: info.path,
582
+ stage: "fs(after git success)",
583
+ error: rmError instanceof Error ? rmError.message : String(rmError),
584
+ residue: probeResidue(info.path),
585
+ });
586
+ }
587
+ }
550
588
  }
551
589
  catch (error) {
552
590
  logger.warn("git worktree remove failed, falling back to fs.rmSync:", {
@@ -577,7 +615,19 @@ export function removeWorktree(info) {
577
615
  // Ignore errors
578
616
  }
579
617
  }
580
- // Delete worktree branch
618
+ // The directory, not any exit code, decides whether the removal happened. A
619
+ // surviving directory means its checkout (and any uncommitted work in it) is
620
+ // still on disk, and the branch is the only ref still leading back to it.
621
+ if (fs.existsSync(info.path)) {
622
+ logger.warn(`Worktree directory survived removal — keeping ${info.branch} so the leftover checkout stays reachable:`, {
623
+ worktreePath: info.path,
624
+ residue: probeResidue(info.path),
625
+ });
626
+ return;
627
+ }
628
+ // Delete the worktree's own branch. Branches the user checked out inside the
629
+ // worktree are never touched — they may hold commits that are not reachable
630
+ // from anywhere else (aligned with Claude Code).
581
631
  try {
582
632
  execFileSync("git", ["branch", "-D", info.branch], {
583
633
  cwd: repoRoot,
@@ -587,26 +637,6 @@ export function removeWorktree(info) {
587
637
  catch {
588
638
  // Ignore errors
589
639
  }
590
- // Delete current branch if different and not protected
591
- if (currentBranch &&
592
- currentBranch !== info.branch &&
593
- currentBranch !== "HEAD") {
594
- const defaultRemoteBranch = getDefaultRemoteBranch(repoRoot);
595
- const defaultBranchName = defaultRemoteBranch.split("/").pop();
596
- if (currentBranch !== defaultBranchName &&
597
- currentBranch !== "main" &&
598
- currentBranch !== "master") {
599
- try {
600
- execFileSync("git", ["branch", "-D", currentBranch], {
601
- cwd: repoRoot,
602
- stdio: ["ignore", "pipe", "pipe"],
603
- });
604
- }
605
- catch {
606
- // Ignore errors
607
- }
608
- }
609
- }
610
640
  }
611
641
  /**
612
642
  * Validate that a worktree path is safe to remove before running git removal.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "1.1.5",
3
+ "version": "1.3.0",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -13,6 +13,7 @@
13
13
  "main": "dist/index.js",
14
14
  "types": "dist/index.d.ts",
15
15
  "type": "module",
16
+ "sideEffects": false,
16
17
  "exports": {
17
18
  ".": {
18
19
  "types": "./dist/index.d.ts",
@@ -23,8 +24,12 @@
23
24
  "default": "./dist/types/index.js"
24
25
  },
25
26
  "./constants": {
26
- "types": "./dist/constants/tools.d.ts",
27
- "default": "./dist/constants/tools.js"
27
+ "types": "./dist/constants/index.d.ts",
28
+ "default": "./dist/constants/index.js"
29
+ },
30
+ "./host": {
31
+ "types": "./dist/host/index.d.ts",
32
+ "default": "./dist/host/index.js"
28
33
  },
29
34
  "./package.json": "./package.json",
30
35
  "./*": "./*"
@@ -50,15 +55,19 @@
50
55
  "@vscode/ripgrep": "^1.18.0",
51
56
  "chokidar": "^4.0.3",
52
57
  "cron-parser": "^5.5.0",
58
+ "fflate": "^0.8.3",
53
59
  "fuzzysort": "^3.1.0",
54
60
  "glob": "^13.0.0",
55
61
  "lru-cache": "^11.3.5",
56
62
  "marked": "^17.0.2",
57
63
  "minimatch": "^10.0.3",
58
64
  "openai": "^5.12.2",
65
+ "semver": "^7.7.4",
66
+ "sharp": "^0.35.4",
59
67
  "turndown": "^7.2.2"
60
68
  },
61
69
  "devDependencies": {
70
+ "@types/semver": "^7.7.1",
62
71
  "@types/turndown": "^5.0.6",
63
72
  "@vitest/coverage-v8": "^4.1.7",
64
73
  "rimraf": "^6.1.2",
@@ -71,7 +80,6 @@
71
80
  "scripts": {
72
81
  "build": "rimraf dist && tsc -p tsconfig.build.json",
73
82
  "type-check": "tsc --noEmit --incremental",
74
- "watch": "tsc -p tsconfig.build.json --watch",
75
83
  "test": "vitest run --reporter=dot",
76
84
  "test:coverage": "vitest run --coverage --reporter=dot",
77
85
  "test:unit": "vitest run --reporter=dot --exclude 'tests/integration/**' --exclude '**/*.integration.test.ts'",
@@ -1,26 +0,0 @@
1
- import { Container } from "../utils/container.js";
2
- export interface BangManagerOptions {
3
- workdir: string;
4
- }
5
- export interface CommandExecutionResult {
6
- exitCode: number;
7
- output: string;
8
- }
9
- export declare class BangManager {
10
- private container;
11
- private workdir;
12
- isCommandRunning: boolean;
13
- private currentProcess;
14
- onCommandRunningChange?: (running: boolean) => void;
15
- constructor(container: Container, options: BangManagerOptions);
16
- private get messageManager();
17
- /**
18
- * Merged env (OS env overlaid with this session's settings snapshot) for
19
- * bang-command subprocesses, so settings `env` vars reach them without
20
- * polluting other sessions in one `wave --stdio` process.
21
- */
22
- private get sessionEnv();
23
- private setCommandRunning;
24
- executeCommand(command: string): Promise<number>;
25
- abortCommand(): void;
26
- }