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.
- package/builtin/skills/settings/SKILL.md +1 -1
- package/builtin/skills/settings/SUBAGENTS.md +21 -2
- package/dist/managers/pluginManager.d.ts +1 -0
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +7 -0
- package/dist/managers/subagentManager.d.ts +5 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +35 -0
- package/dist/services/pluginLoader.d.ts +5 -0
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +29 -0
- package/dist/types/plugins.d.ts +2 -0
- package/dist/types/plugins.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +11 -0
- package/dist/utils/subagentParser.d.ts +8 -1
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +18 -3
- package/package.json +1 -1
- package/src/managers/pluginManager.ts +10 -0
- package/src/managers/subagentManager.ts +47 -0
- package/src/services/pluginLoader.ts +37 -0
- package/src/types/plugins.ts +2 -0
- package/src/utils/convertMessagesForAPI.ts +16 -1
- package/src/utils/subagentParser.ts +31 -4
|
@@ -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
|
|
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
|
|
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
|
|
|
@@ -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;
|
|
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;
|
|
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
|
*/
|
package/dist/types/plugins.d.ts
CHANGED
|
@@ -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;
|
|
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,
|
|
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;
|
|
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
|
|
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
|
@@ -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
|
*/
|
package/src/types/plugins.ts
CHANGED
|
@@ -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
|
|
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
|
*/
|