wave-agent-sdk 0.14.2 → 0.14.3

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.
@@ -93,7 +93,7 @@ For detailed guidance on creating subagents, see [SUBAGENTS.md](${WAVE_SKILL_DIR
93
93
 
94
94
  ### 9. Plugins
95
95
 
96
- Plugins bundle skills, hooks, MCP servers, LSP servers, and commands into a reusable package. You can install plugins locally or from a marketplace.
96
+ Plugins bundle skills, hooks, MCP servers, LSP servers, commands, and subagents into a reusable package. You can install plugins locally or from a marketplace.
97
97
  For detailed guidance on creating plugins and marketplaces, see [PLUGINS.md](${WAVE_SKILL_DIR}/PLUGINS.md).
98
98
 
99
99
  ### 10. Other Settings
@@ -39,12 +39,31 @@ You are a specialized subagent for a specific task. Your goal is to:
39
39
 
40
40
  ## Subagent Locations
41
41
 
42
- Wave looks for subagents in two locations:
42
+ Wave looks for subagents in three locations:
43
43
 
44
44
  1. **User Subagents**: `~/.wave/agents/` (Available in all projects)
45
45
  2. **Project Subagents**: `.wave/agents/` (Specific to the current project)
46
+ 3. **Plugin Agents**: `agents/` within an installed plugin directory (Scoped to the plugin)
46
47
 
47
- Project subagents take precedence over user subagents with the same name.
48
+ Project subagents take precedence over user subagents with the same name. Plugin agents are namespaced with the plugin name (e.g., `pluginName:agentName`) to avoid collisions.
49
+
50
+ ## Plugin Agents
51
+
52
+ Plugins can define their own subagents in an `agents/` directory within the plugin. These agents can reference their parent plugin's directory using the `${WAVE_PLUGIN_ROOT}` template variable, which is substituted at load time.
53
+
54
+ For example, a plugin at `/path/to/my-plugin/` with `agents/researcher.md`:
55
+
56
+ ```markdown
57
+ ---
58
+ name: researcher
59
+ description: A research agent that uses the plugin's knowledge base
60
+ tools: ["Read", "Glob"]
61
+ ---
62
+
63
+ You are a research assistant. Access plugin resources at ${WAVE_PLUGIN_ROOT}/data.
64
+ ```
65
+
66
+ After loading, `${WAVE_PLUGIN_ROOT}` is replaced with `/path/to/my-plugin/`, and the agent is registered as `my-plugin:researcher`.
48
67
 
49
68
  ## Delegating to Subagents
50
69
 
@@ -16,6 +16,7 @@ export declare class PluginManager {
16
16
  private get mcpManager();
17
17
  private get slashCommandManager();
18
18
  private get configurationService();
19
+ private get subagentManager();
19
20
  /**
20
21
  * Update enabled plugins configuration
21
22
  */
@@ -1 +1 @@
1
- {"version":3,"file":"pluginManager.d.ts","sourceRoot":"","sources":["../../src/managers/pluginManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAUzD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,qBAAa,aAAa;IAMtB,OAAO,CAAC,SAAS;IALnB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAA0B;gBAGtC,SAAS,EAAE,SAAS,EAC5B,OAAO,EAAE,oBAAoB;IAM/B,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,mBAAmB,GAE9B;IAED,OAAO,KAAK,oBAAoB,GAE/B;IAED;;OAEG;IACH,oBAAoB,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAInE;;OAEG;YACW,oBAAoB;IA0ElC;;OAEG;YACW,gBAAgB;IA2D9B;;;OAGG;IACG,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBzD;;OAEG;IACH,UAAU,IAAI,MAAM,EAAE;IAItB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAG5C"}
1
+ {"version":3,"file":"pluginManager.d.ts","sourceRoot":"","sources":["../../src/managers/pluginManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAWzD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,qBAAa,aAAa;IAMtB,OAAO,CAAC,SAAS;IALnB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAA0B;gBAGtC,SAAS,EAAE,SAAS,EAC5B,OAAO,EAAE,oBAAoB;IAM/B,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,KAAK,mBAAmB,GAE9B;IAED,OAAO,KAAK,oBAAoB,GAE/B;IAED,OAAO,KAAK,eAAe,GAE1B;IAED;;OAEG;IACH,oBAAoB,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAInE;;OAEG;YACW,oBAAoB;IA0ElC;;OAEG;YACW,gBAAgB;IAgE9B;;;OAGG;IACG,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBzD;;OAEG;IACH,UAAU,IAAI,MAAM,EAAE;IAItB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAG5C"}
@@ -27,6 +27,9 @@ export class PluginManager {
27
27
  get configurationService() {
28
28
  return this.container.get("ConfigurationService");
29
29
  }
30
+ get subagentManager() {
31
+ return this.container.get("SubagentManager");
32
+ }
30
33
  /**
31
34
  * Update enabled plugins configuration
32
35
  */
@@ -106,6 +109,7 @@ export class PluginManager {
106
109
  path: absolutePath,
107
110
  commands: PluginLoader.loadCommands(absolutePath),
108
111
  skills: await PluginLoader.loadSkills(absolutePath),
112
+ agents: await PluginLoader.loadAgents(absolutePath),
109
113
  lspConfig: await PluginLoader.loadLspConfig(absolutePath),
110
114
  mcpConfig: await PluginLoader.loadMcpConfig(absolutePath),
111
115
  hooksConfig: await PluginLoader.loadHooksConfig(absolutePath),
@@ -132,6 +136,9 @@ export class PluginManager {
132
136
  if (this.hookManager && plugin.hooksConfig) {
133
137
  this.hookManager.registerPluginHooks(plugin.path, plugin.hooksConfig);
134
138
  }
139
+ if (this.subagentManager && plugin.agents.length > 0) {
140
+ this.subagentManager.registerPluginAgents(plugin.name, plugin.agents);
141
+ }
135
142
  this.plugins.set(manifest.name, plugin);
136
143
  logger?.info(`Loaded plugin: ${manifest.name} v${manifest.version}`);
137
144
  }
@@ -77,6 +77,11 @@ export declare class SubagentManager {
77
77
  * Find subagent by exact name match
78
78
  */
79
79
  findSubagent(name: string): Promise<SubagentConfiguration | null>;
80
+ /**
81
+ * Register plugin agents into the cached configurations.
82
+ * Names each agent as `pluginName:agentName` to avoid collisions.
83
+ */
84
+ registerPluginAgents(pluginName: string, agents: SubagentConfiguration[]): void;
80
85
  /**
81
86
  * Create a new subagent instance with isolated managers
82
87
  */
@@ -1 +1 @@
1
- {"version":3,"file":"subagentManager.d.ts","sourceRoot":"","sources":["../../src/managers/subagentManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subagentManager.d.ts","sourceRoot":"","sources":["../../src/managers/subagentManager.ts"],"names":[],"mappings":"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"}
@@ -88,9 +88,44 @@ export class SubagentManager {
88
88
  * Find subagent by exact name match
89
89
  */
90
90
  async findSubagent(name) {
91
+ // Check cached configurations first (includes plugin agents)
92
+ if (this.cachedConfigurations !== null) {
93
+ const cached = this.cachedConfigurations.find((config) => config.name === name);
94
+ if (cached)
95
+ return cached;
96
+ }
97
+ // Fall back to filesystem scan for non-plugin agents
91
98
  const { findSubagentByName } = await import("../utils/subagentParser.js");
92
99
  return findSubagentByName(name, this.workdir);
93
100
  }
101
+ /**
102
+ * Register plugin agents into the cached configurations.
103
+ * Names each agent as `pluginName:agentName` to avoid collisions.
104
+ */
105
+ registerPluginAgents(pluginName, agents) {
106
+ if (this.cachedConfigurations === null) {
107
+ // Should not happen if initialization order is correct
108
+ this.cachedConfigurations = [];
109
+ }
110
+ // Remove any previously registered agents for this plugin (by name prefix)
111
+ this.cachedConfigurations = this.cachedConfigurations.filter((config) => !config.name.startsWith(`${pluginName}:`));
112
+ for (const agent of agents) {
113
+ const namespacedName = `${pluginName}:${agent.name}`;
114
+ const namespacedAgent = {
115
+ ...agent,
116
+ name: namespacedName,
117
+ // Safety net: substitute any remaining ${WAVE_PLUGIN_ROOT} placeholders
118
+ systemPrompt: agent.systemPrompt.replace(/\$\{WAVE_PLUGIN_ROOT\}/g, agent.pluginRoot ?? ""),
119
+ };
120
+ this.cachedConfigurations.push(namespacedAgent);
121
+ }
122
+ // Re-sort by priority then name
123
+ this.cachedConfigurations.sort((a, b) => {
124
+ if (a.priority !== b.priority)
125
+ return a.priority - b.priority;
126
+ return a.name.localeCompare(b.name);
127
+ });
128
+ }
94
129
  /**
95
130
  * Create a new subagent instance with isolated managers
96
131
  */
@@ -1,4 +1,5 @@
1
1
  import { PluginManifest, CustomSlashCommand, Skill, LspConfig, McpConfig, PartialHookConfiguration } from "../types/index.js";
2
+ import { type SubagentConfiguration } from "../utils/subagentParser.js";
2
3
  export declare class PluginLoader {
3
4
  /**
4
5
  * Load and validate a plugin manifest from a directory
@@ -27,6 +28,10 @@ export declare class PluginLoader {
27
28
  * Load hooks configuration from a plugin
28
29
  */
29
30
  static loadHooksConfig(pluginPath: string): Promise<PartialHookConfiguration | undefined>;
31
+ /**
32
+ * Load agent configurations from a plugin's agents directory
33
+ */
34
+ static loadAgents(pluginPath: string): Promise<SubagentConfiguration[]>;
30
35
  /**
31
36
  * Validate the plugin manifest structure
32
37
  */
@@ -1 +1 @@
1
- {"version":3,"file":"pluginLoader.d.ts","sourceRoot":"","sources":["../../src/services/pluginLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,SAAS,EACT,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAK3B,qBAAa,YAAY;IACvB;;;OAGG;WACU,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA6CtE;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAK7D;;;OAGG;WACU,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAuC7D;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAUjC;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAUjC;;OAEG;WACU,eAAe,CAC1B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAgBhD;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;CAgBhC"}
1
+ {"version":3,"file":"pluginLoader.d.ts","sourceRoot":"","sources":["../../src/services/pluginLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,SAAS,EACT,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,4BAA4B,CAAC;AAIpC,qBAAa,YAAY;IACvB;;;OAGG;WACU,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA6CtE;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAK7D;;;OAGG;WACU,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAuC7D;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAUjC;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAUjC;;OAEG;WACU,eAAe,CAC1B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAgBhD;;OAEG;WACU,UAAU,CACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,qBAAqB,EAAE,CAAC;IA2BnC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;CAgBhC"}
@@ -2,7 +2,9 @@ import * as fs from "fs/promises";
2
2
  import * as path from "path";
3
3
  import { scanCommandsDirectory } from "../utils/customCommands.js";
4
4
  import { parseSkillFile } from "../utils/skillParser.js";
5
+ import { parseAgentFile, } from "../utils/subagentParser.js";
5
6
  import { resolveMcpConfig } from "../managers/mcpManager.js";
7
+ import { logger } from "../utils/globalLogger.js";
6
8
  export class PluginLoader {
7
9
  /**
8
10
  * Load and validate a plugin manifest from a directory
@@ -135,6 +137,33 @@ export class PluginLoader {
135
137
  return undefined;
136
138
  }
137
139
  }
140
+ /**
141
+ * Load agent configurations from a plugin's agents directory
142
+ */
143
+ static async loadAgents(pluginPath) {
144
+ const agentsPath = path.join(pluginPath, "agents");
145
+ const agents = [];
146
+ try {
147
+ const entries = await fs.readdir(agentsPath, { withFileTypes: true });
148
+ for (const entry of entries) {
149
+ if (entry.isFile() && entry.name.endsWith(".md")) {
150
+ const agentFilePath = path.join(agentsPath, entry.name);
151
+ try {
152
+ const config = parseAgentFile(agentFilePath, "plugin", pluginPath);
153
+ agents.push(config);
154
+ }
155
+ catch (parseError) {
156
+ // Log error but continue with other files
157
+ logger?.warn(`Warning: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
158
+ }
159
+ }
160
+ }
161
+ }
162
+ catch {
163
+ // agents directory might not exist
164
+ }
165
+ return agents;
166
+ }
138
167
  /**
139
168
  * Validate the plugin manifest structure
140
169
  */
@@ -3,6 +3,7 @@ import { Skill } from "./skills.js";
3
3
  import { LspConfig } from "./lsp.js";
4
4
  import { McpConfig } from "./mcp.js";
5
5
  import { PartialHookConfiguration } from "./configuration.js";
6
+ import { SubagentConfiguration } from "../utils/subagentParser.js";
6
7
  /**
7
8
  * Plugin manifest structure (.wave-plugin/plugin.json)
8
9
  */
@@ -28,6 +29,7 @@ export interface Plugin extends PluginManifest {
28
29
  path: string;
29
30
  commands: CustomSlashCommand[];
30
31
  skills: Skill[];
32
+ agents: SubagentConfiguration[];
31
33
  lspConfig?: LspConfig;
32
34
  mcpConfig?: McpConfig;
33
35
  hooksConfig?: PartialHookConfiguration;
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/types/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,MAAO,SAAQ,cAAc;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC"}
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/types/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,MAAO,SAAQ,cAAc;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"convertMessagesForAPI.d.ts","sourceRoot":"","sources":["../../src/utils/convertMessagesForAPI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,OAAO,EAEL,0BAA0B,EAC3B,MAAM,qBAAqB,CAAC;AA0B7B;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,OAAO,EAAE,GAClB,0BAA0B,EAAE,CAuO9B"}
1
+ {"version":3,"file":"convertMessagesForAPI.d.ts","sourceRoot":"","sources":["../../src/utils/convertMessagesForAPI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,OAAO,EAEL,0BAA0B,EAC3B,MAAM,qBAAqB,CAAC;AA0B7B;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,OAAO,EAAE,GAClB,0BAA0B,EAAE,CAsP9B"}
@@ -114,6 +114,16 @@ export function convertMessagesForAPI(messages) {
114
114
  .map((block) => (block.type === "text" ? block.content : ""))
115
115
  .join("\n");
116
116
  }
117
+ // Extract reasoning content from reasoning blocks
118
+ const reasoningBlocks = message.blocks.filter((block) => block.type === "reasoning" &&
119
+ block.content &&
120
+ block.content.trim().length > 0);
121
+ let reasoning_content;
122
+ if (reasoningBlocks.length > 0) {
123
+ reasoning_content = reasoningBlocks
124
+ .map((block) => (block.type === "reasoning" ? block.content : ""))
125
+ .join("\n");
126
+ }
117
127
  // Construct tool calls from tool blocks
118
128
  if (toolBlocks.length > 0) {
119
129
  tool_calls = toolBlocks
@@ -138,6 +148,7 @@ export function convertMessagesForAPI(messages) {
138
148
  role: "assistant",
139
149
  content: hasContent ? content : undefined,
140
150
  tool_calls,
151
+ ...(reasoning_content ? { reasoning_content } : {}),
141
152
  ...(message.additionalFields ? { ...message.additionalFields } : {}),
142
153
  };
143
154
  recentMessages.unshift(assistantMessage);
@@ -5,9 +5,16 @@ export interface SubagentConfiguration {
5
5
  model?: string;
6
6
  systemPrompt: string;
7
7
  filePath: string;
8
- scope: "project" | "user" | "builtin";
8
+ scope: "project" | "user" | "builtin" | "plugin";
9
9
  priority: number;
10
+ /** Plugin root directory path, set when scope is "plugin" */
11
+ pluginRoot?: string;
10
12
  }
13
+ /**
14
+ * Parse a plugin agent markdown file.
15
+ * Exposed as a public API for PluginLoader to use.
16
+ */
17
+ export declare function parseAgentFile(filePath: string, scope: "plugin", pluginRoot: string): SubagentConfiguration;
11
18
  /**
12
19
  * Load all subagent configurations from project and user directories, plus built-in subagents
13
20
  */
@@ -1 +1 @@
1
- {"version":3,"file":"subagentParser.d.ts","sourceRoot":"","sources":["../../src/utils/subagentParser.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAwLD;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAsBlC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAGvC"}
1
+ {"version":3,"file":"subagentParser.d.ts","sourceRoot":"","sources":["../../src/utils/subagentParser.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkKD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,QAAQ,EACf,UAAU,EAAE,MAAM,GACjB,qBAAqB,CAEvB;AAqCD;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAsBlC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAGvC"}
@@ -80,9 +80,9 @@ function validateConfiguration(config, filePath) {
80
80
  }
81
81
  }
82
82
  /**
83
- * Parse a single subagent markdown file
83
+ * Parse a single subagent markdown file with optional pluginRoot support
84
84
  */
85
- function parseSubagentFile(filePath, scope) {
85
+ function parseSubagentFile(filePath, scope, pluginRoot) {
86
86
  try {
87
87
  const content = readFileSync(filePath, "utf-8");
88
88
  const { frontmatter, body } = parseFrontmatter(content);
@@ -99,21 +99,36 @@ function parseSubagentFile(filePath, scope) {
99
99
  priority = 2;
100
100
  if (scope === "builtin")
101
101
  priority = 3;
102
+ if (scope === "plugin")
103
+ priority = 2; // Same priority as user-level
104
+ let systemPrompt = body;
105
+ // Substitute ${WAVE_PLUGIN_ROOT} for plugin scope at parse time
106
+ if (scope === "plugin" && pluginRoot) {
107
+ systemPrompt = systemPrompt.replace(/\$\{WAVE_PLUGIN_ROOT\}/g, pluginRoot);
108
+ }
102
109
  return {
103
110
  name: frontmatter.name,
104
111
  description: frontmatter.description,
105
112
  tools: frontmatter.tools,
106
113
  model: frontmatter.model,
107
- systemPrompt: body,
114
+ systemPrompt,
108
115
  filePath,
109
116
  scope,
110
117
  priority,
118
+ pluginRoot: scope === "plugin" ? pluginRoot : undefined,
111
119
  };
112
120
  }
113
121
  catch (error) {
114
122
  throw new Error(`Failed to parse subagent file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
115
123
  }
116
124
  }
125
+ /**
126
+ * Parse a plugin agent markdown file.
127
+ * Exposed as a public API for PluginLoader to use.
128
+ */
129
+ export function parseAgentFile(filePath, scope, pluginRoot) {
130
+ return parseSubagentFile(filePath, scope, pluginRoot);
131
+ }
117
132
  /**
118
133
  * Scan directory for subagent files
119
134
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -7,6 +7,7 @@ import { HookManager } from "./hookManager.js";
7
7
  import { LspManager } from "./lspManager.js";
8
8
  import { McpManager } from "./mcpManager.js";
9
9
  import { SlashCommandManager } from "./slashCommandManager.js";
10
+ import { SubagentManager } from "./subagentManager.js";
10
11
  import { MarketplaceService } from "../services/MarketplaceService.js";
11
12
  import { ConfigurationService } from "../services/configurationService.js";
12
13
  import { Container } from "../utils/container.js";
@@ -53,6 +54,10 @@ export class PluginManager {
53
54
  return this.container.get<ConfigurationService>("ConfigurationService");
54
55
  }
55
56
 
57
+ private get subagentManager(): SubagentManager | undefined {
58
+ return this.container.get<SubagentManager>("SubagentManager");
59
+ }
60
+
56
61
  /**
57
62
  * Update enabled plugins configuration
58
63
  */
@@ -155,6 +160,7 @@ export class PluginManager {
155
160
  path: absolutePath,
156
161
  commands: PluginLoader.loadCommands(absolutePath),
157
162
  skills: await PluginLoader.loadSkills(absolutePath),
163
+ agents: await PluginLoader.loadAgents(absolutePath),
158
164
  lspConfig: await PluginLoader.loadLspConfig(absolutePath),
159
165
  mcpConfig: await PluginLoader.loadMcpConfig(absolutePath),
160
166
  hooksConfig: await PluginLoader.loadHooksConfig(absolutePath),
@@ -192,6 +198,10 @@ export class PluginManager {
192
198
  this.hookManager.registerPluginHooks(plugin.path, plugin.hooksConfig);
193
199
  }
194
200
 
201
+ if (this.subagentManager && plugin.agents.length > 0) {
202
+ this.subagentManager.registerPluginAgents(plugin.name, plugin.agents);
203
+ }
204
+
195
205
  this.plugins.set(manifest.name, plugin);
196
206
  logger?.info(`Loaded plugin: ${manifest.name} v${manifest.version}`);
197
207
  } catch (error) {
@@ -187,10 +187,57 @@ export class SubagentManager {
187
187
  * Find subagent by exact name match
188
188
  */
189
189
  async findSubagent(name: string) {
190
+ // Check cached configurations first (includes plugin agents)
191
+ if (this.cachedConfigurations !== null) {
192
+ const cached = this.cachedConfigurations.find(
193
+ (config) => config.name === name,
194
+ );
195
+ if (cached) return cached;
196
+ }
197
+ // Fall back to filesystem scan for non-plugin agents
190
198
  const { findSubagentByName } = await import("../utils/subagentParser.js");
191
199
  return findSubagentByName(name, this.workdir);
192
200
  }
193
201
 
202
+ /**
203
+ * Register plugin agents into the cached configurations.
204
+ * Names each agent as `pluginName:agentName` to avoid collisions.
205
+ */
206
+ registerPluginAgents(
207
+ pluginName: string,
208
+ agents: SubagentConfiguration[],
209
+ ): void {
210
+ if (this.cachedConfigurations === null) {
211
+ // Should not happen if initialization order is correct
212
+ this.cachedConfigurations = [];
213
+ }
214
+
215
+ // Remove any previously registered agents for this plugin (by name prefix)
216
+ this.cachedConfigurations = this.cachedConfigurations.filter(
217
+ (config) => !config.name.startsWith(`${pluginName}:`),
218
+ );
219
+
220
+ for (const agent of agents) {
221
+ const namespacedName = `${pluginName}:${agent.name}`;
222
+ const namespacedAgent: SubagentConfiguration = {
223
+ ...agent,
224
+ name: namespacedName,
225
+ // Safety net: substitute any remaining ${WAVE_PLUGIN_ROOT} placeholders
226
+ systemPrompt: agent.systemPrompt.replace(
227
+ /\$\{WAVE_PLUGIN_ROOT\}/g,
228
+ agent.pluginRoot ?? "",
229
+ ),
230
+ };
231
+ this.cachedConfigurations!.push(namespacedAgent);
232
+ }
233
+
234
+ // Re-sort by priority then name
235
+ this.cachedConfigurations!.sort((a, b) => {
236
+ if (a.priority !== b.priority) return a.priority - b.priority;
237
+ return a.name.localeCompare(b.name);
238
+ });
239
+ }
240
+
194
241
  /**
195
242
  * Create a new subagent instance with isolated managers
196
243
  */
@@ -10,7 +10,12 @@ import {
10
10
  } from "../types/index.js";
11
11
  import { scanCommandsDirectory } from "../utils/customCommands.js";
12
12
  import { parseSkillFile } from "../utils/skillParser.js";
13
+ import {
14
+ parseAgentFile,
15
+ type SubagentConfiguration,
16
+ } from "../utils/subagentParser.js";
13
17
  import { resolveMcpConfig } from "../managers/mcpManager.js";
18
+ import { logger } from "../utils/globalLogger.js";
14
19
 
15
20
  export class PluginLoader {
16
21
  /**
@@ -165,6 +170,38 @@ export class PluginLoader {
165
170
  }
166
171
  }
167
172
 
173
+ /**
174
+ * Load agent configurations from a plugin's agents directory
175
+ */
176
+ static async loadAgents(
177
+ pluginPath: string,
178
+ ): Promise<SubagentConfiguration[]> {
179
+ const agentsPath = path.join(pluginPath, "agents");
180
+ const agents: SubagentConfiguration[] = [];
181
+
182
+ try {
183
+ const entries = await fs.readdir(agentsPath, { withFileTypes: true });
184
+ for (const entry of entries) {
185
+ if (entry.isFile() && entry.name.endsWith(".md")) {
186
+ const agentFilePath = path.join(agentsPath, entry.name);
187
+ try {
188
+ const config = parseAgentFile(agentFilePath, "plugin", pluginPath);
189
+ agents.push(config);
190
+ } catch (parseError) {
191
+ // Log error but continue with other files
192
+ logger?.warn(
193
+ `Warning: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
194
+ );
195
+ }
196
+ }
197
+ }
198
+ } catch {
199
+ // agents directory might not exist
200
+ }
201
+
202
+ return agents;
203
+ }
204
+
168
205
  /**
169
206
  * Validate the plugin manifest structure
170
207
  */
@@ -3,6 +3,7 @@ import { Skill } from "./skills.js";
3
3
  import { LspConfig } from "./lsp.js";
4
4
  import { McpConfig } from "./mcp.js";
5
5
  import { PartialHookConfiguration } from "./configuration.js";
6
+ import { SubagentConfiguration } from "../utils/subagentParser.js";
6
7
 
7
8
  /**
8
9
  * Plugin manifest structure (.wave-plugin/plugin.json)
@@ -31,6 +32,7 @@ export interface Plugin extends PluginManifest {
31
32
  path: string;
32
33
  commands: CustomSlashCommand[];
33
34
  skills: Skill[];
35
+ agents: SubagentConfiguration[];
34
36
  lspConfig?: LspConfig;
35
37
  mcpConfig?: McpConfig;
36
38
  hooksConfig?: PartialHookConfiguration;
@@ -145,6 +145,20 @@ export function convertMessagesForAPI(
145
145
  .join("\n");
146
146
  }
147
147
 
148
+ // Extract reasoning content from reasoning blocks
149
+ const reasoningBlocks = message.blocks.filter(
150
+ (block) =>
151
+ block.type === "reasoning" &&
152
+ block.content &&
153
+ block.content.trim().length > 0,
154
+ );
155
+ let reasoning_content: string | undefined;
156
+ if (reasoningBlocks.length > 0) {
157
+ reasoning_content = reasoningBlocks
158
+ .map((block) => (block.type === "reasoning" ? block.content : ""))
159
+ .join("\n");
160
+ }
161
+
148
162
  // Construct tool calls from tool blocks
149
163
  if (toolBlocks.length > 0) {
150
164
  tool_calls = toolBlocks
@@ -176,8 +190,9 @@ export function convertMessagesForAPI(
176
190
  role: "assistant",
177
191
  content: hasContent ? content : undefined,
178
192
  tool_calls,
193
+ ...(reasoning_content ? { reasoning_content } : {}),
179
194
  ...(message.additionalFields ? { ...message.additionalFields } : {}),
180
- };
195
+ } as ChatCompletionMessageParam;
181
196
 
182
197
  recentMessages.unshift(assistantMessage);
183
198
  }
@@ -10,8 +10,10 @@ export interface SubagentConfiguration {
10
10
  model?: string;
11
11
  systemPrompt: string;
12
12
  filePath: string;
13
- scope: "project" | "user" | "builtin";
13
+ scope: "project" | "user" | "builtin" | "plugin";
14
14
  priority: number;
15
+ /** Plugin root directory path, set when scope is "plugin" */
16
+ pluginRoot?: string;
15
17
  }
16
18
 
17
19
  interface SubagentFrontmatter {
@@ -119,11 +121,12 @@ function validateConfiguration(
119
121
  }
120
122
 
121
123
  /**
122
- * Parse a single subagent markdown file
124
+ * Parse a single subagent markdown file with optional pluginRoot support
123
125
  */
124
126
  function parseSubagentFile(
125
127
  filePath: string,
126
- scope: "project" | "user" | "builtin",
128
+ scope: "project" | "user" | "builtin" | "plugin",
129
+ pluginRoot?: string,
127
130
  ): SubagentConfiguration {
128
131
  try {
129
132
  const content = readFileSync(filePath, "utf-8");
@@ -143,16 +146,28 @@ function parseSubagentFile(
143
146
  let priority = 1;
144
147
  if (scope === "user") priority = 2;
145
148
  if (scope === "builtin") priority = 3;
149
+ if (scope === "plugin") priority = 2; // Same priority as user-level
150
+
151
+ let systemPrompt = body;
152
+
153
+ // Substitute ${WAVE_PLUGIN_ROOT} for plugin scope at parse time
154
+ if (scope === "plugin" && pluginRoot) {
155
+ systemPrompt = systemPrompt.replace(
156
+ /\$\{WAVE_PLUGIN_ROOT\}/g,
157
+ pluginRoot,
158
+ );
159
+ }
146
160
 
147
161
  return {
148
162
  name: frontmatter.name!,
149
163
  description: frontmatter.description!,
150
164
  tools: frontmatter.tools,
151
165
  model: frontmatter.model,
152
- systemPrompt: body,
166
+ systemPrompt,
153
167
  filePath,
154
168
  scope,
155
169
  priority,
170
+ pluginRoot: scope === "plugin" ? pluginRoot : undefined,
156
171
  };
157
172
  } catch (error) {
158
173
  throw new Error(
@@ -161,6 +176,18 @@ function parseSubagentFile(
161
176
  }
162
177
  }
163
178
 
179
+ /**
180
+ * Parse a plugin agent markdown file.
181
+ * Exposed as a public API for PluginLoader to use.
182
+ */
183
+ export function parseAgentFile(
184
+ filePath: string,
185
+ scope: "plugin",
186
+ pluginRoot: string,
187
+ ): SubagentConfiguration {
188
+ return parseSubagentFile(filePath, scope, pluginRoot);
189
+ }
190
+
164
191
  /**
165
192
  * Scan directory for subagent files
166
193
  */