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
@@ -14,13 +14,24 @@ export declare class PluginCore {
14
14
  private workdir;
15
15
  constructor(workdir?: string);
16
16
  /**
17
- * Installs a plugin from a marketplace
17
+ * Installs a plugin from a marketplace and, when a scope is given, enables it
18
+ * there — the install record is filed under that same scope (spec plugin A-015).
18
19
  */
19
20
  installPlugin(pluginId: string, scope?: Scope): Promise<InstalledPlugin>;
20
21
  /**
21
- * Uninstalls a plugin and removes it from all configuration scopes
22
+ * Managed plugins are the ones the organization pushed through remote managed
23
+ * settings: a member cannot uninstall or disable them (spec plugin A-024).
24
+ * The check reads the managed layer only — a plugin the admin never mentioned
25
+ * stays the user's to manage, and dropping the entry restores that at once.
22
26
  */
23
- uninstallPlugin(pluginId: string): Promise<void>;
27
+ private assertNotManaged;
28
+ /**
29
+ * Uninstalls a plugin from a single scope (spec plugin A-015):只清除该作用域的
30
+ * 启用记录与该作用域的安装记录,其它作用域(含其它项目的项目/本地作用域)不受
31
+ * 影响;本机产物仅在该插件再无安装记录时删除。未指定作用域时按
32
+ * `local` > `project` > `user` 探测当前生效作用域(与 enable/disable 一致)。
33
+ */
34
+ uninstallPlugin(pluginId: string, scope?: Scope): Promise<Scope>;
24
35
  /**
25
36
  * Enables a plugin in the specified scope. If no scope is provided, it tries to find
26
37
  * the scope where the plugin is already configured, or defaults to "user".
@@ -32,20 +43,32 @@ export declare class PluginCore {
32
43
  */
33
44
  disablePlugin(pluginId: string, scope?: Scope): Promise<Scope>;
34
45
  /**
35
- * Updates an installed plugin to the latest version from its marketplace
46
+ * Moves an installed plugin to another installation scope: clears its
47
+ * enabledPlugins record from every scope, then enables it in the target one
48
+ * (spec plugin「设置页插件市场」场景 11:更换作用域后旧作用域不再保留该插件)。
49
+ * The install record follows the plugin, otherwise uninstalling it at the new
50
+ * scope would find no record there (spec plugin A-015)。
36
51
  */
37
- updatePlugin(pluginId: string): Promise<InstalledPlugin>;
52
+ setPluginScope(pluginId: string, scope: Scope): Promise<Scope>;
38
53
  /**
39
- * Toggles auto-update for a marketplace
54
+ * Updates an installed plugin to the latest version from its marketplace
40
55
  */
41
- toggleAutoUpdate(name: string, enabled: boolean): Promise<void>;
56
+ updatePlugin(pluginId: string): Promise<InstalledPlugin>;
42
57
  /**
43
- * Lists all plugins from all registered marketplaces with their installation and enabled status
58
+ * Lists all plugins from all registered marketplaces with their installation and enabled status.
44
59
  */
45
60
  listPlugins(): Promise<{
46
61
  plugins: MarketplacePluginStatus[];
47
62
  mergedEnabled: Record<string, boolean>;
48
63
  }>;
64
+ /**
65
+ * Reads the version declared by a marketplace plugin's own manifest inside the
66
+ * marketplace checkout — the version a fresh install would get. Sources that
67
+ * live outside the checkout (Git URLs and both object shapes, which always point
68
+ * at an external repository) have no local copy until install, so they yield
69
+ * undefined (spec plugin A-010 / A-021).
70
+ */
71
+ private readLatestVersion;
49
72
  /**
50
73
  * Adds a new marketplace
51
74
  */
@@ -55,9 +78,30 @@ export declare class PluginCore {
55
78
  */
56
79
  removeMarketplace(name: string, scope?: Scope): Promise<void>;
57
80
  /**
58
- * Updates a specific marketplace or all marketplaces
81
+ * Updates a specific marketplace or all marketplaces: refreshes the checkout
82
+ * from its upstream and reinstalls any plugins that are already installed from
83
+ * it (mirrors Claude Code's refresh-and-bump).
84
+ *
85
+ * 无界面可依赖的入口(非交互命令 `wave plugin marketplace update`)走这里;
86
+ * 交互界面的「批量更新插件」走 [updateMarketplacePlugins](不拉检出)。
87
+ *
88
+ * @returns 实际发生版本变化的插件数量(0 = 全部已是最新),供 GUI 宿主区分提示。
89
+ */
90
+ updateMarketplace(name?: string): Promise<number>;
91
+ /**
92
+ * 批量升级某市场内已安装的插件,且不拉取检出——交互界面的「批量更新插件」
93
+ * (GUI 三端按钮与 CLI 插件管理器的同名项)。清单的新鲜度由打开这些界面时的
94
+ * 后台刷新保证(spec 插件市场 A-013、场景 14)。
95
+ *
96
+ * @returns 实际发生版本变化的插件数量(0 = 该市场已是最新),供宿主区分提示。
97
+ */
98
+ updateMarketplacePlugins(name?: string): Promise<number>;
99
+ /**
100
+ * Refreshes the checkout of every registered marketplace, without touching
101
+ * installed plugins. Called by hosts when the user opens a plugin
102
+ * marketplace surface (spec 插件市场 A-013 场景 5/13).
59
103
  */
60
- updateMarketplace(name?: string): Promise<void>;
104
+ refreshMarketplaces(): Promise<void>;
61
105
  /**
62
106
  * Lists all registered marketplaces
63
107
  */
@@ -1,8 +1,13 @@
1
1
  import { Container } from "../utils/container.js";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
2
4
  import { PluginManager } from "../managers/pluginManager.js";
3
5
  import { PluginScopeManager } from "../managers/pluginScopeManager.js";
4
6
  import { MarketplaceService } from "../services/MarketplaceService.js";
5
7
  import { ConfigurationService } from "../services/configurationService.js";
8
+ import { remoteSettingsService } from "../services/remoteSettingsService.js";
9
+ import { parsePluginSource } from "../utils/pluginSource.js";
10
+ import { logger } from "../utils/globalLogger.js";
6
11
  /**
7
12
  * PluginCore
8
13
  *
@@ -12,6 +17,11 @@ import { ConfigurationService } from "../services/configurationService.js";
12
17
  export class PluginCore {
13
18
  constructor(workdir = process.cwd()) {
14
19
  this.workdir = workdir;
20
+ // 托管层(spec plugin A-024)读的是远端设置的磁盘缓存,而 `wave plugin
21
+ // uninstall` 这类独立命令不经过 Agent 初始化,没人调过 initialize() ⇒
22
+ // 缓存为空、托管限制在这些入口上形同虚设。这里补上:initialize() 只是
23
+ // 同步读一次 `~/.wave/remote-settings.json`(幂等),已加载过的进程不受影响。
24
+ remoteSettingsService.initialize();
15
25
  this.container = new Container();
16
26
  this.configurationService = new ConfigurationService();
17
27
  this.marketplaceService = new MarketplaceService(this.workdir, this.configurationService);
@@ -27,21 +37,45 @@ export class PluginCore {
27
37
  });
28
38
  }
29
39
  /**
30
- * Installs a plugin from a marketplace
40
+ * Installs a plugin from a marketplace and, when a scope is given, enables it
41
+ * there — the install record is filed under that same scope (spec plugin A-015).
31
42
  */
32
43
  async installPlugin(pluginId, scope) {
33
- const installedPlugin = await this.marketplaceService.installPlugin(pluginId);
44
+ const installedPlugin = await this.marketplaceService.installPlugin(pluginId, scope ? this.pluginScopeManager.getInstallLocation(scope) : undefined);
34
45
  if (scope) {
35
46
  await this.enablePlugin(pluginId, scope);
36
47
  }
37
48
  return installedPlugin;
38
49
  }
39
50
  /**
40
- * Uninstalls a plugin and removes it from all configuration scopes
51
+ * Managed plugins are the ones the organization pushed through remote managed
52
+ * settings: a member cannot uninstall or disable them (spec plugin A-024).
53
+ * The check reads the managed layer only — a plugin the admin never mentioned
54
+ * stays the user's to manage, and dropping the entry restores that at once.
41
55
  */
42
- async uninstallPlugin(pluginId) {
43
- await this.marketplaceService.uninstallPlugin(pluginId);
44
- await this.pluginScopeManager.removePluginFromAllScopes(pluginId);
56
+ assertNotManaged(pluginId, action) {
57
+ const managed = this.configurationService.getManagedEnabledPlugins();
58
+ if (!managed || !Object.prototype.hasOwnProperty.call(managed, pluginId)) {
59
+ return;
60
+ }
61
+ const verb = action === "uninstall" ? "uninstalled" : "disabled";
62
+ throw new Error(`Plugin ${pluginId} is managed by your organization and cannot be ${verb}. Contact your admin to change the managed plugin configuration.`);
63
+ }
64
+ /**
65
+ * Uninstalls a plugin from a single scope (spec plugin A-015):只清除该作用域的
66
+ * 启用记录与该作用域的安装记录,其它作用域(含其它项目的项目/本地作用域)不受
67
+ * 影响;本机产物仅在该插件再无安装记录时删除。未指定作用域时按
68
+ * `local` > `project` > `user` 探测当前生效作用域(与 enable/disable 一致)。
69
+ */
70
+ async uninstallPlugin(pluginId, scope) {
71
+ this.assertNotManaged(pluginId, "uninstall");
72
+ const targetScope = scope ?? this.findPluginScope(pluginId);
73
+ if (!targetScope) {
74
+ throw new Error(`Plugin ${pluginId} is not enabled in any scope for ${this.workdir}. Use --scope to pick the scope to uninstall from.`);
75
+ }
76
+ await this.marketplaceService.uninstallPlugin(pluginId, this.pluginScopeManager.getInstallLocation(targetScope));
77
+ await this.pluginScopeManager.removePluginFromScope(targetScope, pluginId);
78
+ return targetScope;
45
79
  }
46
80
  /**
47
81
  * Enables a plugin in the specified scope. If no scope is provided, it tries to find
@@ -57,49 +91,77 @@ export class PluginCore {
57
91
  * the scope where the plugin is already configured, or defaults to "user".
58
92
  */
59
93
  async disablePlugin(pluginId, scope) {
94
+ this.assertNotManaged(pluginId, "disable");
60
95
  const targetScope = scope || this.findPluginScope(pluginId) || "user";
61
96
  await this.pluginScopeManager.disablePlugin(targetScope, pluginId);
62
97
  return targetScope;
63
98
  }
64
99
  /**
65
- * Updates an installed plugin to the latest version from its marketplace
100
+ * Moves an installed plugin to another installation scope: clears its
101
+ * enabledPlugins record from every scope, then enables it in the target one
102
+ * (spec plugin「设置页插件市场」场景 11:更换作用域后旧作用域不再保留该插件)。
103
+ * The install record follows the plugin, otherwise uninstalling it at the new
104
+ * scope would find no record there (spec plugin A-015)。
66
105
  */
67
- async updatePlugin(pluginId) {
68
- return await this.marketplaceService.updatePlugin(pluginId);
106
+ async setPluginScope(pluginId, scope) {
107
+ const previousScope = this.findPluginScope(pluginId);
108
+ await this.pluginScopeManager.removePluginFromAllScopes(pluginId);
109
+ await this.pluginScopeManager.enablePlugin(scope, pluginId);
110
+ await this.marketplaceService.relocatePlugin(pluginId, previousScope
111
+ ? this.pluginScopeManager.getInstallLocation(previousScope)
112
+ : undefined, this.pluginScopeManager.getInstallLocation(scope));
113
+ return scope;
69
114
  }
70
115
  /**
71
- * Toggles auto-update for a marketplace
116
+ * Updates an installed plugin to the latest version from its marketplace
72
117
  */
73
- async toggleAutoUpdate(name, enabled) {
74
- await this.marketplaceService.toggleAutoUpdate(name, enabled);
118
+ async updatePlugin(pluginId) {
119
+ return await this.marketplaceService.updatePlugin(pluginId);
75
120
  }
76
121
  /**
77
- * Lists all plugins from all registered marketplaces with their installation and enabled status
122
+ * Lists all plugins from all registered marketplaces with their installation and enabled status.
78
123
  */
79
124
  async listPlugins() {
80
125
  const installedPlugins = await this.marketplaceService.getInstalledPlugins();
81
126
  const marketplaces = await this.marketplaceService.listMarketplaces();
82
127
  const mergedEnabled = this.configurationService.getMergedEnabledPlugins(this.workdir);
128
+ const managedEnabled = this.configurationService.getManagedEnabledPlugins() ?? {};
83
129
  const allMarketplacePlugins = [];
84
130
  for (const m of marketplaces) {
85
131
  try {
86
- const manifest = await this.marketplaceService.loadMarketplaceManifest(this.marketplaceService.getMarketplacePath(m.source));
87
- manifest.plugins.forEach((p) => {
132
+ const marketplacePath = this.marketplaceService.getMarketplacePath(m.source);
133
+ const manifest = await this.marketplaceService.loadMarketplaceManifest(marketplacePath);
134
+ for (const p of manifest.plugins) {
88
135
  const pluginId = `${p.name}@${m.name}`;
89
- const installed = installedPlugins.plugins.find((ip) => ip.name === p.name && ip.marketplace === m.name);
136
+ const scope = this.pluginScopeManager.findPluginScope(pluginId) ?? undefined;
137
+ const installedEntry = installedPlugins.plugins.find((ip) => ip.name === p.name && ip.marketplace === m.name);
138
+ // 安装状态以当前工作目录为准(spec plugin A-012):配置链里有启用记录、
139
+ // 且本机已有安装产物,才视为「已安装」。安装产物单独存在只说明该插件曾被
140
+ // 下载过(例如以项目/本地作用域装在别的项目里),若据此判为已安装,会在
141
+ // 当前目录渲染出「已安装 + 作用域未知」。作用域标签与安装态同源,未安装时
142
+ // 不回传作用域。
143
+ // 托管插件由组织下发(spec plugin A-024):它在任何工程都算已安装,
144
+ // 成员本机没有可卸载的记录,界面据此把卸载入口判为不可用。
145
+ const managed = Object.prototype.hasOwnProperty.call(managedEnabled, pluginId);
146
+ const installed = managed || scope ? installedEntry : undefined;
90
147
  allMarketplacePlugins.push({
91
148
  ...p,
92
149
  marketplace: m.name,
93
- installed: !!installed,
150
+ installed: managed || !!installed,
94
151
  version: installed?.version,
152
+ latestVersion: await this.readLatestVersion(marketplacePath, p.source),
95
153
  cachePath: installed?.cachePath,
96
154
  projectPath: installed?.projectPath,
97
- scope: this.pluginScopeManager.findPluginScope(pluginId) || undefined,
155
+ scope: installed ? scope : undefined,
156
+ managed: managed || undefined,
98
157
  });
99
- });
158
+ }
100
159
  }
101
- catch {
102
- // Skip marketplaces that fail to load
160
+ catch (error) {
161
+ // A marketplace that fails to load must be diagnosable: swallowing this
162
+ // silently is what made an unparsable entry look like an empty market
163
+ // (spec plugin「兼容 Claude Code 生态的市场清单与插件」场景 5).
164
+ logger.warn(`Failed to load marketplace ${m.name}: ${error instanceof Error ? error.message : String(error)}`);
103
165
  }
104
166
  }
105
167
  return {
@@ -107,6 +169,31 @@ export class PluginCore {
107
169
  mergedEnabled,
108
170
  };
109
171
  }
172
+ /**
173
+ * Reads the version declared by a marketplace plugin's own manifest inside the
174
+ * marketplace checkout — the version a fresh install would get. Sources that
175
+ * live outside the checkout (Git URLs and both object shapes, which always point
176
+ * at an external repository) have no local copy until install, so they yield
177
+ * undefined (spec plugin A-010 / A-021).
178
+ */
179
+ async readLatestVersion(marketplacePath, source) {
180
+ const parsed = parsePluginSource(source);
181
+ if (!parsed || parsed.kind !== "local")
182
+ return undefined;
183
+ const pluginPath = path.resolve(marketplacePath, parsed.path);
184
+ for (const dir of [".wave-plugin", ".claude-plugin"]) {
185
+ try {
186
+ const raw = await fs.readFile(path.join(pluginPath, dir, "plugin.json"), "utf-8");
187
+ const version = JSON.parse(raw).version;
188
+ if (version)
189
+ return version;
190
+ }
191
+ catch {
192
+ // Try the next manifest location
193
+ }
194
+ }
195
+ return undefined;
196
+ }
110
197
  /**
111
198
  * Adds a new marketplace
112
199
  */
@@ -120,10 +207,37 @@ export class PluginCore {
120
207
  await this.marketplaceService.removeMarketplace(name, scope);
121
208
  }
122
209
  /**
123
- * Updates a specific marketplace or all marketplaces
210
+ * Updates a specific marketplace or all marketplaces: refreshes the checkout
211
+ * from its upstream and reinstalls any plugins that are already installed from
212
+ * it (mirrors Claude Code's refresh-and-bump).
213
+ *
214
+ * 无界面可依赖的入口(非交互命令 `wave plugin marketplace update`)走这里;
215
+ * 交互界面的「批量更新插件」走 [updateMarketplacePlugins](不拉检出)。
216
+ *
217
+ * @returns 实际发生版本变化的插件数量(0 = 全部已是最新),供 GUI 宿主区分提示。
124
218
  */
125
219
  async updateMarketplace(name) {
126
- await this.marketplaceService.updateMarketplace(name);
220
+ return await this.marketplaceService.updateMarketplace(name, {
221
+ updatePlugins: true,
222
+ });
223
+ }
224
+ /**
225
+ * 批量升级某市场内已安装的插件,且不拉取检出——交互界面的「批量更新插件」
226
+ * (GUI 三端按钮与 CLI 插件管理器的同名项)。清单的新鲜度由打开这些界面时的
227
+ * 后台刷新保证(spec 插件市场 A-013、场景 14)。
228
+ *
229
+ * @returns 实际发生版本变化的插件数量(0 = 该市场已是最新),供宿主区分提示。
230
+ */
231
+ async updateMarketplacePlugins(name) {
232
+ return await this.marketplaceService.updateMarketplacePlugins(name);
233
+ }
234
+ /**
235
+ * Refreshes the checkout of every registered marketplace, without touching
236
+ * installed plugins. Called by hosts when the user opens a plugin
237
+ * marketplace surface (spec 插件市场 A-013 场景 5/13).
238
+ */
239
+ async refreshMarketplaces() {
240
+ await this.marketplaceService.refreshMarketplaces();
127
241
  }
128
242
  /**
129
243
  * Lists all registered marketplaces
@@ -1,2 +1,2 @@
1
- export { listSessions, listAllSessions, truncateContent, loadSessionFromJsonl, loadFullMessageThread, deleteSession, } from "../services/session.js";
1
+ export { listSessions, listAllSessions, setSessionCustomTitle, truncateContent, loadSessionFromJsonl, loadFullMessageThread, deleteSession, } from "../services/session.js";
2
2
  export type { SessionMetadata, SessionData } from "../services/session.js";
@@ -1 +1 @@
1
- export { listSessions, listAllSessions, truncateContent, loadSessionFromJsonl, loadFullMessageThread, deleteSession, } from "../services/session.js";
1
+ export { listSessions, listAllSessions, setSessionCustomTitle, truncateContent, loadSessionFromJsonl, loadFullMessageThread, deleteSession, } from "../services/session.js";
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The Exec catalog: which MCP tools exist, how they are rendered, how that
3
+ * rendering is budgeted, and the shape of the sandbox's `search` entry point.
4
+ *
5
+ * "Rendered" means into the catalog announcement (`exec/catalogAnnouncement.ts`),
6
+ * not into `Exec`'s tool description: the description has to stay a
7
+ * pool-independent constant, or every server that connects rewrites the cached
8
+ * prefix. This module produces the entries; the announcement wraps them.
9
+ *
10
+ * This module is the single source of truth for "what the sandbox may reach".
11
+ * The hard constraint is that the pool must equal the tools the agent could
12
+ * already call directly — if a tool were reachable from the sandbox but hidden
13
+ * from the agent, Exec would be a permission-escalation channel.
14
+ *
15
+ * The same reasoning applies to `search`: its call form is written once, as a
16
+ * schema, and both the model-visible prose and the host-side validation derive
17
+ * from that one object. Teaching a form in prose while accepting a different one
18
+ * in code is a bug with no diff to review.
19
+ */
20
+ import type { McpManager } from "../managers/mcpManager.js";
21
+ import type { PermissionManager } from "../managers/permissionManager.js";
22
+ export interface ExecPoolEntry {
23
+ /** Flattened `mcp__<server>__<tool>` name — the key on the sandbox `tools` object. */
24
+ name: string;
25
+ description?: string;
26
+ /** Raw MCP input schema (JSON Schema), used only to render a compact signature. */
27
+ inputSchema?: Record<string, unknown>;
28
+ /**
29
+ * The schema the server declared for this tool's output, rendered as the
30
+ * signature's return type. Absent for most servers today; a tool without one
31
+ * still gets a return type (`Promise<unknown>`), because leaving it out would
32
+ * read as "this call returns nothing".
33
+ */
34
+ outputSchema?: Record<string, unknown>;
35
+ }
36
+ /**
37
+ * Every MCP tool the current agent may call, in catalog form.
38
+ *
39
+ * Derived from `getMcpToolsConfig()` — the exact source that produces the flat
40
+ * `tools[]` declarations when the pool is not collapsed, and the one whose
41
+ * connected/reconnecting filtering already keeps a transient disconnect from
42
+ * churning the tool list. So "the catalog equals what would have been declared"
43
+ * holds structurally, not by parallel construction.
44
+ */
45
+ export declare function buildExecPool(mcpManager: McpManager, permissionManager?: PermissionManager): ExecPoolEntry[];
46
+ /**
47
+ * The callable signature for one tool: parameters and return type. The catalog and
48
+ * search results share it, so the model can copy either one verbatim.
49
+ *
50
+ * The return type comes from the schema the server declared for its output, and a
51
+ * tool that declared none still gets `Promise<unknown>` rather than no return type
52
+ * at all: the sandbox resolves every call to the tool's output (its structured
53
+ * content, else its text, else `null`), and a signature ending at the parameters
54
+ * would read as "calls this, get nothing". Rendering it from the same rule the
55
+ * runtime applies is what keeps the catalog from teaching a shape the host will
56
+ * not deliver.
57
+ */
58
+ export declare function renderToolSignature(entry: ExecPoolEntry): string;
59
+ /**
60
+ * The callable signature of search, rendered by the very function that renders
61
+ * every catalog entry, so its shape cannot drift from what the host accepts or
62
+ * returns. Multi-line, for the sandbox API blurb where there is room to show the
63
+ * field docs.
64
+ */
65
+ export declare function renderSearchSignature(): string;
66
+ /**
67
+ * The one-line call form of search: the path plus a placeholder per field, all
68
+ * derived from `SEARCH_INPUT_SCHEMA`. For prose that cannot afford the multi-line
69
+ * block — the catalog's `PARTIAL` notice and error messages, which must still name
70
+ * a shape the host accepts.
71
+ */
72
+ export declare function renderSearchCallForm(): string;
73
+ /**
74
+ * Validate a search call's arguments against `SEARCH_INPUT_SCHEMA` and return the
75
+ * query to match on.
76
+ *
77
+ * The sandbox only guarantees a plain object (see `callHost`); it knows nothing
78
+ * about this schema, so a call naming a field the schema does not have must fail
79
+ * loudly here. Treating `{ q: "..." }` as an empty query would answer "here is the
80
+ * whole pool" and dress a typo up as a successful search.
81
+ */
82
+ export declare function resolveSearchQuery(args: Record<string, unknown>): string;
83
+ /**
84
+ * A single catalog entry: its signature (possibly several lines) followed by the
85
+ * first line of its description.
86
+ */
87
+ export declare function renderCatalogEntry(entry: ExecPoolEntry): string;
88
+ export interface RenderedCatalog {
89
+ text: string;
90
+ /** How many entries actually appear in `text`. */
91
+ shown: number;
92
+ total: number;
93
+ /** True when the budget forced entries out. Never silent. */
94
+ truncated: boolean;
95
+ /**
96
+ * Per-server tool counts, in pool order. The rendered entries cannot tell a later
97
+ * turn what moved — the announcement diff is count-level — so the snapshot is
98
+ * handed back here rather than recovered by parsing `text`.
99
+ */
100
+ namespaces: Array<{
101
+ name: string;
102
+ count: number;
103
+ shown: number;
104
+ }>;
105
+ }
106
+ /**
107
+ * The one-line index of a server: `- mcp__github (40 tools, 13 shown)`. The tail
108
+ * is dropped when the server is fully shown, and reads `none shown` when it got
109
+ * no seat at all.
110
+ *
111
+ * The prefix is the flattened name's, not the routing key alone, so the line is a
112
+ * substring of every tool name it covers (and of what search matches on). For a
113
+ * server whose name itself contains `__` the key is only the first segment — the
114
+ * same pre-existing ambiguity the grouping key has (see `execNamespace`).
115
+ *
116
+ * Exported for the announcement's per-namespace delta lines: the same renderer for
117
+ * the same counts, so a summary line and a delta line cannot drift apart.
118
+ */
119
+ export declare function summarizeNamespace(name: string, count: number, shownCount: number): string;
120
+ /**
121
+ * Render the catalog within an estimated-token budget.
122
+ *
123
+ * Selection round-robins across servers: one entry per server per round, so
124
+ * every server gets a seat before any server gets its second. A plain first-N
125
+ * cut would drop whole servers that happen to be connected later, and the model
126
+ * reads a missing server as "those tools do not exist" — the failure mode this
127
+ * whole feature has to avoid.
128
+ *
129
+ * An entry is a whole multi-line block and is budgeted and placed atomically: a
130
+ * server either gets the block this round or sits the round out, never half a
131
+ * signature.
132
+ *
133
+ * A truncated catalog additionally prints one summary line per server (see
134
+ * `summarizeNamespace`), before that server's blocks and outside the budget.
135
+ *
136
+ * The truncation notice deliberately carries no budget number: the budget is a
137
+ * tuning knob, and rendering it would make an unchanged tool pool produce
138
+ * different model-visible text after a config change.
139
+ */
140
+ export declare function renderCatalog(entries: ExecPoolEntry[], budgetTokens: number): RenderedCatalog;