wave-agent-sdk 0.0.1 → 0.0.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/dist/agent.d.ts +37 -3
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +82 -5
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/managers/aiManager.d.ts +7 -1
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +11 -5
- package/dist/managers/messageManager.d.ts +8 -0
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +26 -2
- package/dist/managers/skillManager.d.ts +4 -5
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +6 -82
- package/dist/managers/subagentManager.d.ts +96 -0
- package/dist/managers/subagentManager.d.ts.map +1 -0
- package/dist/managers/subagentManager.js +261 -0
- package/dist/managers/toolManager.d.ts +33 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +43 -5
- package/dist/services/aiService.d.ts +5 -0
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +58 -28
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +4 -0
- package/dist/tools/grepTool.d.ts.map +1 -1
- package/dist/tools/grepTool.js +8 -6
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +36 -6
- package/dist/tools/skillTool.d.ts +8 -0
- package/dist/tools/skillTool.d.ts.map +1 -0
- package/dist/tools/skillTool.js +72 -0
- package/dist/tools/taskTool.d.ts +8 -0
- package/dist/tools/taskTool.d.ts.map +1 -0
- package/dist/tools/taskTool.js +109 -0
- package/dist/tools/todoWriteTool.d.ts +6 -0
- package/dist/tools/todoWriteTool.d.ts.map +1 -0
- package/dist/tools/todoWriteTool.js +203 -0
- package/dist/types.d.ts +65 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +16 -0
- package/dist/utils/configResolver.d.ts +38 -0
- package/dist/utils/configResolver.d.ts.map +1 -0
- package/dist/utils/configResolver.js +106 -0
- package/dist/utils/configValidator.d.ts +36 -0
- package/dist/utils/configValidator.d.ts.map +1 -0
- package/dist/utils/configValidator.js +78 -0
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +10 -0
- package/dist/utils/fileFormat.d.ts +17 -0
- package/dist/utils/fileFormat.d.ts.map +1 -0
- package/dist/utils/fileFormat.js +35 -0
- package/dist/utils/messageOperations.d.ts +18 -0
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +43 -0
- package/dist/utils/subagentParser.d.ts +19 -0
- package/dist/utils/subagentParser.d.ts.map +1 -0
- package/dist/utils/subagentParser.js +159 -0
- package/package.json +11 -15
- package/src/agent.ts +130 -9
- package/src/index.ts +0 -1
- package/src/managers/aiManager.ts +22 -10
- package/src/managers/messageManager.ts +55 -1
- package/src/managers/skillManager.ts +7 -96
- package/src/managers/subagentManager.ts +368 -0
- package/src/managers/toolManager.ts +50 -5
- package/src/services/aiService.ts +92 -36
- package/src/services/session.ts +5 -0
- package/src/tools/grepTool.ts +9 -6
- package/src/tools/readTool.ts +40 -6
- package/src/tools/skillTool.ts +82 -0
- package/src/tools/taskTool.ts +128 -0
- package/src/tools/todoWriteTool.ts +232 -0
- package/src/types.ts +85 -1
- package/src/utils/configResolver.ts +142 -0
- package/src/utils/configValidator.ts +133 -0
- package/src/utils/constants.ts +10 -0
- package/src/utils/fileFormat.ts +40 -0
- package/src/utils/messageOperations.ts +80 -0
- package/src/utils/subagentParser.ts +223 -0
|
@@ -504,3 +504,83 @@ export const completeCommandInMessage = ({
|
|
|
504
504
|
}
|
|
505
505
|
return newMessages;
|
|
506
506
|
};
|
|
507
|
+
|
|
508
|
+
// Subagent block message operations
|
|
509
|
+
export interface AddSubagentBlockParams {
|
|
510
|
+
messages: Message[];
|
|
511
|
+
subagentId: string;
|
|
512
|
+
subagentName: string;
|
|
513
|
+
status: "active" | "completed" | "error" | "aborted";
|
|
514
|
+
subagentMessages?: Message[];
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export interface UpdateSubagentBlockParams {
|
|
518
|
+
messages: Message[];
|
|
519
|
+
subagentId: string;
|
|
520
|
+
status: "active" | "completed" | "error" | "aborted";
|
|
521
|
+
subagentMessages: Message[];
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export const addSubagentBlockToMessage = ({
|
|
525
|
+
messages,
|
|
526
|
+
subagentId,
|
|
527
|
+
subagentName,
|
|
528
|
+
status,
|
|
529
|
+
subagentMessages = [],
|
|
530
|
+
}: AddSubagentBlockParams): Message[] => {
|
|
531
|
+
const newMessages = [...messages];
|
|
532
|
+
|
|
533
|
+
// Find the last assistant message or create one
|
|
534
|
+
let lastAssistantMessage = newMessages[newMessages.length - 1];
|
|
535
|
+
|
|
536
|
+
if (!lastAssistantMessage || lastAssistantMessage.role !== "assistant") {
|
|
537
|
+
// Create new assistant message if the last message is not from assistant
|
|
538
|
+
lastAssistantMessage = {
|
|
539
|
+
role: "assistant",
|
|
540
|
+
blocks: [],
|
|
541
|
+
};
|
|
542
|
+
newMessages.push(lastAssistantMessage);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Add subagent block
|
|
546
|
+
lastAssistantMessage.blocks.push({
|
|
547
|
+
type: "subagent",
|
|
548
|
+
subagentId,
|
|
549
|
+
subagentName,
|
|
550
|
+
status,
|
|
551
|
+
messages: subagentMessages,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
return newMessages;
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
export const updateSubagentBlockInMessage = (
|
|
558
|
+
messages: Message[],
|
|
559
|
+
subagentId: string,
|
|
560
|
+
updates: Partial<{
|
|
561
|
+
status: "active" | "completed" | "error" | "aborted";
|
|
562
|
+
messages: Message[];
|
|
563
|
+
}>,
|
|
564
|
+
): Message[] => {
|
|
565
|
+
const newMessages = [...messages];
|
|
566
|
+
|
|
567
|
+
// Find and update the subagent block
|
|
568
|
+
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
569
|
+
const message = newMessages[i];
|
|
570
|
+
if (message.role === "assistant") {
|
|
571
|
+
for (const block of message.blocks) {
|
|
572
|
+
if (block.type === "subagent" && block.subagentId === subagentId) {
|
|
573
|
+
if (updates.status !== undefined) {
|
|
574
|
+
block.status = updates.status;
|
|
575
|
+
}
|
|
576
|
+
if (updates.messages !== undefined) {
|
|
577
|
+
block.messages = updates.messages;
|
|
578
|
+
}
|
|
579
|
+
return newMessages;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return newMessages;
|
|
586
|
+
};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { join, extname } from "path";
|
|
3
|
+
|
|
4
|
+
export interface SubagentConfiguration {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
tools?: string[];
|
|
8
|
+
model?: string;
|
|
9
|
+
systemPrompt: string;
|
|
10
|
+
filePath: string;
|
|
11
|
+
scope: "project" | "user";
|
|
12
|
+
priority: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface SubagentFrontmatter {
|
|
16
|
+
name?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
tools?: string[];
|
|
19
|
+
model?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse YAML frontmatter from markdown file content
|
|
24
|
+
*/
|
|
25
|
+
function parseFrontmatter(content: string): {
|
|
26
|
+
frontmatter: SubagentFrontmatter;
|
|
27
|
+
body: string;
|
|
28
|
+
} {
|
|
29
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
30
|
+
const match = content.match(frontmatterRegex);
|
|
31
|
+
|
|
32
|
+
if (!match) {
|
|
33
|
+
return { frontmatter: {}, body: content.trim() };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [, yamlContent, body] = match;
|
|
37
|
+
const frontmatter = parseYamlFrontmatter(yamlContent);
|
|
38
|
+
|
|
39
|
+
return { frontmatter, body: body.trim() };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Simple YAML frontmatter parser for subagent files
|
|
44
|
+
*/
|
|
45
|
+
function parseYamlFrontmatter(yamlContent: string): SubagentFrontmatter {
|
|
46
|
+
const frontmatter: SubagentFrontmatter = {};
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const lines = yamlContent.split("\n");
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
const trimmed = line.trim();
|
|
52
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
53
|
+
|
|
54
|
+
const colonIndex = trimmed.indexOf(":");
|
|
55
|
+
if (colonIndex === -1) continue;
|
|
56
|
+
|
|
57
|
+
const key = trimmed.substring(0, colonIndex).trim();
|
|
58
|
+
const value = trimmed
|
|
59
|
+
.substring(colonIndex + 1)
|
|
60
|
+
.trim()
|
|
61
|
+
.replace(/^["']|["']$/g, "");
|
|
62
|
+
|
|
63
|
+
if (key && value) {
|
|
64
|
+
// Handle array values for tools
|
|
65
|
+
if (key === "tools" && value) {
|
|
66
|
+
let arrayValue = value;
|
|
67
|
+
if (arrayValue.startsWith("[") && arrayValue.endsWith("]")) {
|
|
68
|
+
arrayValue = arrayValue.slice(1, -1);
|
|
69
|
+
}
|
|
70
|
+
frontmatter[key] = arrayValue
|
|
71
|
+
.split(",")
|
|
72
|
+
.map((s) => s.trim())
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
} else {
|
|
75
|
+
if (key === "name" || key === "description" || key === "model") {
|
|
76
|
+
frontmatter[key] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
// Return empty frontmatter on parse error - validation will catch missing fields
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return frontmatter;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validate subagent configuration
|
|
90
|
+
*/
|
|
91
|
+
function validateConfiguration(
|
|
92
|
+
config: SubagentFrontmatter,
|
|
93
|
+
filePath: string,
|
|
94
|
+
): void {
|
|
95
|
+
if (!config.name) {
|
|
96
|
+
throw new Error(`Missing required field 'name' in ${filePath}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!config.description) {
|
|
100
|
+
throw new Error(`Missing required field 'description' in ${filePath}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Validate name pattern
|
|
104
|
+
const namePattern = /^[a-z][a-z0-9-]*$/;
|
|
105
|
+
if (!namePattern.test(config.name)) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Invalid subagent name '${config.name}' in ${filePath}. Must start with a letter and contain only lowercase letters, numbers, and hyphens.`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Validate model if specified - allow any non-empty string
|
|
112
|
+
if (config.model && typeof config.model !== "string") {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Invalid model '${config.model}' in ${filePath}. Must be a string.`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Parse a single subagent markdown file
|
|
121
|
+
*/
|
|
122
|
+
function parseSubagentFile(
|
|
123
|
+
filePath: string,
|
|
124
|
+
scope: "project" | "user",
|
|
125
|
+
): SubagentConfiguration {
|
|
126
|
+
try {
|
|
127
|
+
const content = readFileSync(filePath, "utf-8");
|
|
128
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
129
|
+
|
|
130
|
+
validateConfiguration(frontmatter, filePath);
|
|
131
|
+
|
|
132
|
+
if (!body.trim()) {
|
|
133
|
+
throw new Error(`Empty system prompt in ${filePath}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
name: frontmatter.name!,
|
|
138
|
+
description: frontmatter.description!,
|
|
139
|
+
tools: frontmatter.tools,
|
|
140
|
+
model: frontmatter.model,
|
|
141
|
+
systemPrompt: body,
|
|
142
|
+
filePath,
|
|
143
|
+
scope,
|
|
144
|
+
priority: scope === "project" ? 1 : 2,
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Failed to parse subagent file ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Scan directory for subagent files
|
|
155
|
+
*/
|
|
156
|
+
function scanSubagentDirectory(
|
|
157
|
+
dirPath: string,
|
|
158
|
+
scope: "project" | "user",
|
|
159
|
+
): SubagentConfiguration[] {
|
|
160
|
+
const configurations: SubagentConfiguration[] = [];
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const entries = readdirSync(dirPath);
|
|
164
|
+
|
|
165
|
+
for (const entry of entries) {
|
|
166
|
+
const fullPath = join(dirPath, entry);
|
|
167
|
+
const stat = statSync(fullPath);
|
|
168
|
+
|
|
169
|
+
if (stat.isFile() && extname(entry) === ".md") {
|
|
170
|
+
try {
|
|
171
|
+
const config = parseSubagentFile(fullPath, scope);
|
|
172
|
+
configurations.push(config);
|
|
173
|
+
} catch (parseError) {
|
|
174
|
+
// Log error but continue with other files
|
|
175
|
+
console.warn(
|
|
176
|
+
`Warning: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
// Directory doesn't exist or can't be read - this is OK
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return configurations;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Load all subagent configurations from project and user directories
|
|
190
|
+
*/
|
|
191
|
+
export async function loadSubagentConfigurations(
|
|
192
|
+
workdir: string,
|
|
193
|
+
): Promise<SubagentConfiguration[]> {
|
|
194
|
+
const projectDir = join(workdir, ".wave", "agents");
|
|
195
|
+
const userDir = join(process.env.HOME || "~", ".wave", "agents");
|
|
196
|
+
|
|
197
|
+
const projectConfigs = scanSubagentDirectory(projectDir, "project");
|
|
198
|
+
const userConfigs = scanSubagentDirectory(userDir, "user");
|
|
199
|
+
|
|
200
|
+
// Merge configurations, with project configs taking precedence
|
|
201
|
+
const configMap = new Map<string, SubagentConfiguration>();
|
|
202
|
+
|
|
203
|
+
// Process in reverse priority order (user first, then project)
|
|
204
|
+
for (const config of [...userConfigs, ...projectConfigs]) {
|
|
205
|
+
configMap.set(config.name, config);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return Array.from(configMap.values()).sort((a, b) => {
|
|
209
|
+
if (a.priority !== b.priority) return a.priority - b.priority;
|
|
210
|
+
return a.name.localeCompare(b.name);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Find subagent by exact name match
|
|
216
|
+
*/
|
|
217
|
+
export async function findSubagentByName(
|
|
218
|
+
name: string,
|
|
219
|
+
workdir: string,
|
|
220
|
+
): Promise<SubagentConfiguration | null> {
|
|
221
|
+
const configurations = await loadSubagentConfigurations(workdir);
|
|
222
|
+
return configurations.find((config) => config.name === name) || null;
|
|
223
|
+
}
|