wave-agent-sdk 0.11.7 → 0.12.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.
- package/builtin/skills/settings/MODELS.md +67 -0
- package/builtin/skills/settings/SKILL.md +13 -7
- package/dist/agent.d.ts +10 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +16 -1
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +1 -0
- package/dist/managers/liveConfigManager.d.ts +2 -0
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/liveConfigManager.js +3 -0
- package/dist/managers/messageManager.d.ts +5 -1
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +28 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +38 -41
- package/dist/managers/toolManager.d.ts +1 -0
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +30 -5
- package/dist/services/configurationService.d.ts +8 -0
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +67 -1
- package/dist/tools/skillTool.d.ts.map +1 -1
- package/dist/tools/skillTool.js +4 -1
- package/dist/tools/types.d.ts +1 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/agent.d.ts +3 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +3 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +11 -2
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/utils/configValidator.d.ts +4 -5
- package/dist/utils/configValidator.d.ts.map +1 -1
- package/dist/utils/configValidator.js +4 -4
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +7 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +16 -1
- package/dist/utils/messageOperations.d.ts +28 -3
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +79 -7
- package/package.json +1 -1
- package/src/agent.ts +18 -4
- package/src/managers/aiManager.ts +1 -0
- package/src/managers/liveConfigManager.ts +5 -1
- package/src/managers/messageManager.ts +40 -0
- package/src/managers/slashCommandManager.ts +39 -46
- package/src/managers/toolManager.ts +1 -0
- package/src/services/aiService.ts +58 -5
- package/src/services/configurationService.ts +81 -1
- package/src/tools/skillTool.ts +12 -2
- package/src/tools/types.ts +1 -0
- package/src/types/agent.ts +3 -0
- package/src/types/config.ts +1 -0
- package/src/types/configuration.ts +3 -0
- package/src/types/messaging.ts +12 -1
- package/src/utils/configValidator.ts +6 -4
- package/src/utils/containerSetup.ts +7 -1
- package/src/utils/convertMessagesForAPI.ts +17 -1
- package/src/utils/messageOperations.ts +111 -8
|
@@ -29,6 +29,7 @@ import { logger } from "../utils/globalLogger.js";
|
|
|
29
29
|
|
|
30
30
|
export interface LiveConfigManagerOptions {
|
|
31
31
|
workdir: string;
|
|
32
|
+
onReload?: (config: WaveConfiguration) => void;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export class LiveConfigManager {
|
|
@@ -48,7 +49,7 @@ export class LiveConfigManager {
|
|
|
48
49
|
|
|
49
50
|
constructor(
|
|
50
51
|
private container: Container,
|
|
51
|
-
options: LiveConfigManagerOptions,
|
|
52
|
+
private options: LiveConfigManagerOptions,
|
|
52
53
|
) {
|
|
53
54
|
this.workdir = options.workdir;
|
|
54
55
|
this.fileWatcher = new FileWatcherService(logger);
|
|
@@ -272,6 +273,9 @@ export class LiveConfigManager {
|
|
|
272
273
|
);
|
|
273
274
|
}
|
|
274
275
|
|
|
276
|
+
// Trigger reload callback
|
|
277
|
+
this.options.onReload?.(this.currentConfiguration);
|
|
278
|
+
|
|
275
279
|
return this.currentConfiguration;
|
|
276
280
|
} catch (error) {
|
|
277
281
|
const errorMessage = `Configuration reload failed with exception: ${(error as Error).message}`;
|
|
@@ -7,9 +7,13 @@ import {
|
|
|
7
7
|
addBangMessage,
|
|
8
8
|
updateBangInMessage,
|
|
9
9
|
completeBangInMessage,
|
|
10
|
+
addSlashMessageToMessages,
|
|
11
|
+
updateSlashBlockInMessage,
|
|
10
12
|
removeLastUserMessage,
|
|
11
13
|
addToolBlockToMessageInMessages,
|
|
12
14
|
UserMessageParams,
|
|
15
|
+
AddSlashParams,
|
|
16
|
+
UpdateSlashParams,
|
|
13
17
|
type AgentToolBlockUpdateParams,
|
|
14
18
|
generateMessageId,
|
|
15
19
|
} from "../utils/messageOperations.js";
|
|
@@ -50,6 +54,13 @@ export interface MessageManagerCallbacks {
|
|
|
50
54
|
onAddBangMessage?: (command: string) => void;
|
|
51
55
|
onUpdateBangMessage?: (command: string, output: string) => void;
|
|
52
56
|
onCompleteBangMessage?: (command: string, exitCode: number) => void;
|
|
57
|
+
// Slash callback
|
|
58
|
+
onAddSlashMessage?: (
|
|
59
|
+
params: import("../utils/messageOperations.js").AddSlashParams,
|
|
60
|
+
) => void;
|
|
61
|
+
onUpdateSlashBlock?: (
|
|
62
|
+
params: import("../utils/messageOperations.js").UpdateSlashParams,
|
|
63
|
+
) => void;
|
|
53
64
|
onInfoBlockAdded?: (content: string) => void;
|
|
54
65
|
// Rewind callbacks
|
|
55
66
|
onShowRewind?: () => void;
|
|
@@ -583,6 +594,35 @@ export class MessageManager {
|
|
|
583
594
|
this.callbacks.onCompleteBangMessage?.(command, exitCode);
|
|
584
595
|
}
|
|
585
596
|
|
|
597
|
+
// Slash related message operations
|
|
598
|
+
public addSlashMessage(params: Omit<AddSlashParams, "messages">): string {
|
|
599
|
+
const id = params.id || generateMessageId();
|
|
600
|
+
const updatedMessages = addSlashMessageToMessages({
|
|
601
|
+
messages: this.messages,
|
|
602
|
+
...params,
|
|
603
|
+
id,
|
|
604
|
+
});
|
|
605
|
+
this.setMessages(updatedMessages);
|
|
606
|
+
this.callbacks.onAddSlashMessage?.({
|
|
607
|
+
messages: updatedMessages,
|
|
608
|
+
...params,
|
|
609
|
+
id,
|
|
610
|
+
});
|
|
611
|
+
return id;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
public updateSlashBlock(params: Omit<UpdateSlashParams, "messages">): void {
|
|
615
|
+
const updatedMessages = updateSlashBlockInMessage({
|
|
616
|
+
messages: this.messages,
|
|
617
|
+
...params,
|
|
618
|
+
});
|
|
619
|
+
this.setMessages(updatedMessages);
|
|
620
|
+
this.callbacks.onUpdateSlashBlock?.({
|
|
621
|
+
messages: updatedMessages,
|
|
622
|
+
...params,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
|
|
586
626
|
/**
|
|
587
627
|
* Rebuild usage array from messages containing usage metadata
|
|
588
628
|
* Called during session restoration to reconstruct usage tracking
|
|
@@ -171,18 +171,21 @@ export class SlashCommandManager {
|
|
|
171
171
|
args,
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const messageId = this.messageManager.addUserMessage({
|
|
180
|
-
content: originalInput,
|
|
181
|
-
customCommandContent: prepared.content,
|
|
174
|
+
// 2. Add slash message immediately
|
|
175
|
+
const messageId = this.messageManager.addSlashMessage({
|
|
176
|
+
command: skill.name,
|
|
177
|
+
args,
|
|
178
|
+
content: prepared.content,
|
|
182
179
|
});
|
|
183
180
|
|
|
184
181
|
if (!prepared.skill) {
|
|
185
182
|
// If skill not found or invalid, we're done (error message already in prepared.content)
|
|
183
|
+
this.messageManager.updateSlashBlock({
|
|
184
|
+
command: skill.name,
|
|
185
|
+
messageId,
|
|
186
|
+
stage: "error",
|
|
187
|
+
error: prepared.content,
|
|
188
|
+
});
|
|
186
189
|
return;
|
|
187
190
|
}
|
|
188
191
|
|
|
@@ -200,20 +203,6 @@ export class SlashCommandManager {
|
|
|
200
203
|
);
|
|
201
204
|
}
|
|
202
205
|
|
|
203
|
-
// Add a ToolBlock to the initial command message for progress tracking
|
|
204
|
-
const toolBlockId = this.messageManager.addToolBlockToMessage(
|
|
205
|
-
messageId,
|
|
206
|
-
{
|
|
207
|
-
name: subagentType,
|
|
208
|
-
parameters: JSON.stringify({
|
|
209
|
-
description: skill.description,
|
|
210
|
-
prompt: prepared.content,
|
|
211
|
-
subagent_type: subagentType,
|
|
212
|
-
}),
|
|
213
|
-
stage: "running",
|
|
214
|
-
},
|
|
215
|
-
);
|
|
216
|
-
|
|
217
206
|
try {
|
|
218
207
|
const instance = await this.subagentManager.createInstance(
|
|
219
208
|
config,
|
|
@@ -225,7 +214,7 @@ export class SlashCommandManager {
|
|
|
225
214
|
},
|
|
226
215
|
false,
|
|
227
216
|
() => {
|
|
228
|
-
// Update the
|
|
217
|
+
// Update the slash block with progress
|
|
229
218
|
const subagent = this.subagentManager.getInstance(
|
|
230
219
|
instance.subagentId,
|
|
231
220
|
);
|
|
@@ -248,8 +237,8 @@ export class SlashCommandManager {
|
|
|
248
237
|
|
|
249
238
|
shortResult += summary;
|
|
250
239
|
|
|
251
|
-
this.messageManager.
|
|
252
|
-
|
|
240
|
+
this.messageManager.updateSlashBlock({
|
|
241
|
+
command: skill.name,
|
|
253
242
|
messageId,
|
|
254
243
|
shortResult,
|
|
255
244
|
});
|
|
@@ -264,25 +253,25 @@ export class SlashCommandManager {
|
|
|
264
253
|
signal,
|
|
265
254
|
);
|
|
266
255
|
|
|
267
|
-
// Update the
|
|
268
|
-
this.messageManager.
|
|
269
|
-
|
|
256
|
+
// Update the SlashBlock with final result
|
|
257
|
+
this.messageManager.updateSlashBlock({
|
|
258
|
+
command: skill.name,
|
|
270
259
|
messageId,
|
|
271
260
|
result,
|
|
272
|
-
|
|
273
|
-
stage: "end",
|
|
261
|
+
stage: "success",
|
|
274
262
|
});
|
|
275
263
|
} finally {
|
|
276
264
|
this.subagentManager.cleanupInstance(instance.subagentId);
|
|
277
265
|
}
|
|
278
266
|
} catch (error) {
|
|
279
|
-
// Update the
|
|
280
|
-
|
|
281
|
-
|
|
267
|
+
// Update the SlashBlock with error
|
|
268
|
+
const isAborted =
|
|
269
|
+
error instanceof Error && error.name === "AbortError";
|
|
270
|
+
this.messageManager.updateSlashBlock({
|
|
271
|
+
command: skill.name,
|
|
282
272
|
messageId,
|
|
283
|
-
|
|
273
|
+
stage: isAborted ? "aborted" : "error",
|
|
284
274
|
error: error instanceof Error ? error.message : String(error),
|
|
285
|
-
stage: "end",
|
|
286
275
|
});
|
|
287
276
|
throw error; // Re-throw to be caught by outer catch for logging/error block
|
|
288
277
|
}
|
|
@@ -296,8 +285,11 @@ export class SlashCommandManager {
|
|
|
296
285
|
});
|
|
297
286
|
|
|
298
287
|
// 4. Update the message with final content
|
|
299
|
-
this.messageManager.
|
|
300
|
-
|
|
288
|
+
this.messageManager.updateSlashBlock({
|
|
289
|
+
command: skill.name,
|
|
290
|
+
messageId,
|
|
291
|
+
content: result.content,
|
|
292
|
+
stage: "success",
|
|
301
293
|
});
|
|
302
294
|
|
|
303
295
|
// 5. Trigger AI response
|
|
@@ -498,13 +490,11 @@ export class SlashCommandManager {
|
|
|
498
490
|
args?: string,
|
|
499
491
|
): Promise<void> {
|
|
500
492
|
try {
|
|
501
|
-
// Add
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
content: originalInput,
|
|
507
|
-
customCommandContent: content, // Initial content with bash placeholders
|
|
493
|
+
// Add slash command message immediately to show the command being executed
|
|
494
|
+
const messageId = this.messageManager.addSlashMessage({
|
|
495
|
+
command: commandName,
|
|
496
|
+
args,
|
|
497
|
+
content, // Initial content with bash placeholders
|
|
508
498
|
});
|
|
509
499
|
|
|
510
500
|
// Parse bash commands from the content
|
|
@@ -521,8 +511,11 @@ export class SlashCommandManager {
|
|
|
521
511
|
}
|
|
522
512
|
|
|
523
513
|
// Update the message with final content
|
|
524
|
-
this.messageManager.
|
|
525
|
-
|
|
514
|
+
this.messageManager.updateSlashBlock({
|
|
515
|
+
command: commandName,
|
|
516
|
+
messageId,
|
|
517
|
+
content: finalContent,
|
|
518
|
+
stage: "success",
|
|
526
519
|
});
|
|
527
520
|
|
|
528
521
|
// Execute the AI conversation with custom configuration
|
|
@@ -284,6 +284,7 @@ class ToolManager {
|
|
|
284
284
|
availableSubagents?: SubagentConfiguration[];
|
|
285
285
|
availableSkills?: SkillMetadata[];
|
|
286
286
|
workdir?: string;
|
|
287
|
+
isSubagent?: boolean;
|
|
287
288
|
}): ChatCompletionFunctionTool[] {
|
|
288
289
|
const permissionManager =
|
|
289
290
|
this.container.get<PermissionManager>("PermissionManager");
|
|
@@ -135,10 +135,12 @@ function getModelConfig(
|
|
|
135
135
|
...baseConfig,
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
// Handle parameter exclusion: if a parameter is explicitly set to null, remove it.
|
|
139
|
+
// This allows users to "unset" default parameters like temperature for models that don't support them.
|
|
140
|
+
for (const key in config) {
|
|
141
|
+
if (config[key as keyof OpenAIModelConfig] === null) {
|
|
142
|
+
delete config[key as keyof OpenAIModelConfig];
|
|
143
|
+
}
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
return config;
|
|
@@ -261,9 +263,21 @@ export async function callAgent(
|
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
// Get model configuration - use injected modelConfig with optional override
|
|
266
|
+
const {
|
|
267
|
+
model: _model,
|
|
268
|
+
fastModel: _fastModel,
|
|
269
|
+
maxTokens: _maxTokens,
|
|
270
|
+
permissionMode: _permissionMode,
|
|
271
|
+
...extraParams
|
|
272
|
+
} = modelConfig;
|
|
273
|
+
void _model;
|
|
274
|
+
void _fastModel;
|
|
275
|
+
void _maxTokens;
|
|
276
|
+
void _permissionMode;
|
|
277
|
+
|
|
264
278
|
const openaiModelConfig = getModelConfig(model || modelConfig.model, {
|
|
265
|
-
temperature: 0,
|
|
266
279
|
max_tokens: resolvedMaxTokens,
|
|
280
|
+
...extraParams,
|
|
267
281
|
});
|
|
268
282
|
|
|
269
283
|
// Determine if streaming is needed
|
|
@@ -780,9 +794,22 @@ export async function compressMessages(
|
|
|
780
794
|
});
|
|
781
795
|
|
|
782
796
|
// Get model configuration - use injected agent model
|
|
797
|
+
const {
|
|
798
|
+
model: _model,
|
|
799
|
+
fastModel: _fastModel,
|
|
800
|
+
maxTokens: _maxTokens,
|
|
801
|
+
permissionMode: _permissionMode,
|
|
802
|
+
...extraParams
|
|
803
|
+
} = modelConfig;
|
|
804
|
+
void _model;
|
|
805
|
+
void _fastModel;
|
|
806
|
+
void _maxTokens;
|
|
807
|
+
void _permissionMode;
|
|
808
|
+
|
|
783
809
|
const openaiModelConfig = getModelConfig(options.model || modelConfig.model, {
|
|
784
810
|
temperature: 0.1,
|
|
785
811
|
max_tokens: 8192,
|
|
812
|
+
...extraParams,
|
|
786
813
|
});
|
|
787
814
|
|
|
788
815
|
try {
|
|
@@ -878,9 +905,22 @@ export async function processWebContent(
|
|
|
878
905
|
});
|
|
879
906
|
|
|
880
907
|
// Get model configuration - use injected agent model
|
|
908
|
+
const {
|
|
909
|
+
model: _model,
|
|
910
|
+
fastModel: _fastModel,
|
|
911
|
+
maxTokens: _maxTokens,
|
|
912
|
+
permissionMode: _permissionMode,
|
|
913
|
+
...extraParams
|
|
914
|
+
} = modelConfig;
|
|
915
|
+
void _model;
|
|
916
|
+
void _fastModel;
|
|
917
|
+
void _maxTokens;
|
|
918
|
+
void _permissionMode;
|
|
919
|
+
|
|
881
920
|
const openaiModelConfig = getModelConfig(options.model || modelConfig.model, {
|
|
882
921
|
temperature: 0.1,
|
|
883
922
|
max_tokens: 4096,
|
|
923
|
+
...extraParams,
|
|
884
924
|
});
|
|
885
925
|
|
|
886
926
|
try {
|
|
@@ -972,9 +1012,22 @@ export async function btw(options: BtwOptions): Promise<BtwResult> {
|
|
|
972
1012
|
});
|
|
973
1013
|
|
|
974
1014
|
// Get model configuration - use injected agent model
|
|
1015
|
+
const {
|
|
1016
|
+
model: _model,
|
|
1017
|
+
fastModel: _fastModel,
|
|
1018
|
+
maxTokens: _maxTokens,
|
|
1019
|
+
permissionMode: _permissionMode,
|
|
1020
|
+
...extraParams
|
|
1021
|
+
} = modelConfig;
|
|
1022
|
+
void _model;
|
|
1023
|
+
void _fastModel;
|
|
1024
|
+
void _maxTokens;
|
|
1025
|
+
void _permissionMode;
|
|
1026
|
+
|
|
975
1027
|
const openaiModelConfig = getModelConfig(options.model || modelConfig.model, {
|
|
976
1028
|
temperature: 0.1,
|
|
977
1029
|
max_tokens: 4096,
|
|
1030
|
+
...extraParams,
|
|
978
1031
|
});
|
|
979
1032
|
|
|
980
1033
|
try {
|
|
@@ -275,6 +275,23 @@ export class ConfigurationService {
|
|
|
275
275
|
result.errors.push("autoMemoryEnabled configuration must be a boolean");
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
// Validate models if present
|
|
279
|
+
if (config.models !== undefined) {
|
|
280
|
+
if (typeof config.models !== "object" || config.models === null) {
|
|
281
|
+
result.isValid = false;
|
|
282
|
+
result.errors.push("models configuration must be an object");
|
|
283
|
+
} else {
|
|
284
|
+
for (const [modelName, modelConfig] of Object.entries(config.models)) {
|
|
285
|
+
if (typeof modelConfig !== "object" || modelConfig === null) {
|
|
286
|
+
result.isValid = false;
|
|
287
|
+
result.errors.push(
|
|
288
|
+
`Configuration for model '${modelName}' must be an object`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
278
295
|
return result;
|
|
279
296
|
}
|
|
280
297
|
|
|
@@ -461,12 +478,25 @@ export class ConfigurationService {
|
|
|
461
478
|
// Resolve max output tokens
|
|
462
479
|
const resolvedMaxTokens = this.resolveMaxOutputTokens(maxTokens);
|
|
463
480
|
|
|
464
|
-
|
|
481
|
+
const baseConfig: ModelConfig = {
|
|
465
482
|
model: resolvedAgentModel,
|
|
466
483
|
fastModel: resolvedFastModel,
|
|
467
484
|
maxTokens: resolvedMaxTokens,
|
|
468
485
|
permissionMode: permissionMode ?? this.options.permissionMode,
|
|
469
486
|
};
|
|
487
|
+
|
|
488
|
+
// Merge model-specific settings from configuration
|
|
489
|
+
const modelSpecificConfig =
|
|
490
|
+
this.currentConfiguration?.models?.[resolvedAgentModel];
|
|
491
|
+
|
|
492
|
+
if (modelSpecificConfig) {
|
|
493
|
+
return {
|
|
494
|
+
...baseConfig,
|
|
495
|
+
...modelSpecificConfig,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return baseConfig;
|
|
470
500
|
}
|
|
471
501
|
|
|
472
502
|
/**
|
|
@@ -578,6 +608,40 @@ export class ConfigurationService {
|
|
|
578
608
|
return DEFAULT_WAVE_MAX_OUTPUT_TOKENS;
|
|
579
609
|
}
|
|
580
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Set the active model in the session
|
|
613
|
+
*/
|
|
614
|
+
setModel(model: string): void {
|
|
615
|
+
this.options.model = model;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Get all configured models from settings.json and defaults
|
|
620
|
+
*/
|
|
621
|
+
getConfiguredModels(): string[] {
|
|
622
|
+
const DEFAULT_AGENT_MODEL = "gemini-3-flash";
|
|
623
|
+
const models = new Set<string>();
|
|
624
|
+
|
|
625
|
+
// Add default model
|
|
626
|
+
models.add(DEFAULT_AGENT_MODEL);
|
|
627
|
+
|
|
628
|
+
// Add current model from options or environment
|
|
629
|
+
const currentModel =
|
|
630
|
+
this.options.model || this.env.WAVE_MODEL || process.env.WAVE_MODEL;
|
|
631
|
+
if (currentModel) {
|
|
632
|
+
models.add(currentModel);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Add models from merged configuration
|
|
636
|
+
if (this.currentConfiguration?.models) {
|
|
637
|
+
Object.keys(this.currentConfiguration.models).forEach((model) => {
|
|
638
|
+
models.add(model);
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return Array.from(models);
|
|
643
|
+
}
|
|
644
|
+
|
|
581
645
|
/**
|
|
582
646
|
* Resolve all configuration file paths
|
|
583
647
|
*/
|
|
@@ -874,6 +938,7 @@ export function loadWaveConfigFromFile(
|
|
|
874
938
|
config.autoMemoryEnabled !== undefined
|
|
875
939
|
? config.autoMemoryEnabled
|
|
876
940
|
: undefined,
|
|
941
|
+
models: config.models || undefined,
|
|
877
942
|
};
|
|
878
943
|
} catch (error) {
|
|
879
944
|
if (error instanceof SyntaxError) {
|
|
@@ -1001,6 +1066,17 @@ export function loadMergedWaveConfig(
|
|
|
1001
1066
|
if (config.autoMemoryEnabled !== undefined) {
|
|
1002
1067
|
mergedConfig.autoMemoryEnabled = config.autoMemoryEnabled;
|
|
1003
1068
|
}
|
|
1069
|
+
|
|
1070
|
+
// Merge models
|
|
1071
|
+
if (config.models) {
|
|
1072
|
+
if (!mergedConfig.models) mergedConfig.models = {};
|
|
1073
|
+
for (const [modelName, modelConfig] of Object.entries(config.models)) {
|
|
1074
|
+
if (!mergedConfig.models[modelName]) {
|
|
1075
|
+
mergedConfig.models[modelName] = {};
|
|
1076
|
+
}
|
|
1077
|
+
Object.assign(mergedConfig.models[modelName], modelConfig);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1004
1080
|
}
|
|
1005
1081
|
|
|
1006
1082
|
return {
|
|
@@ -1024,5 +1100,9 @@ export function loadMergedWaveConfig(
|
|
|
1024
1100
|
: undefined,
|
|
1025
1101
|
language: mergedConfig.language,
|
|
1026
1102
|
autoMemoryEnabled: mergedConfig.autoMemoryEnabled,
|
|
1103
|
+
models:
|
|
1104
|
+
mergedConfig.models && Object.keys(mergedConfig.models).length > 0
|
|
1105
|
+
? mergedConfig.models
|
|
1106
|
+
: undefined,
|
|
1027
1107
|
};
|
|
1028
1108
|
}
|
package/src/tools/skillTool.ts
CHANGED
|
@@ -38,10 +38,20 @@ export const skillTool: ToolPlugin = {
|
|
|
38
38
|
},
|
|
39
39
|
},
|
|
40
40
|
|
|
41
|
-
prompt: (args?: {
|
|
42
|
-
|
|
41
|
+
prompt: (args?: {
|
|
42
|
+
availableSkills?: SkillMetadata[];
|
|
43
|
+
isSubagent?: boolean;
|
|
44
|
+
}) => {
|
|
45
|
+
let availableSkills = args?.availableSkills?.filter(
|
|
43
46
|
(skill) => !skill.disableModelInvocation,
|
|
44
47
|
);
|
|
48
|
+
|
|
49
|
+
if (args?.isSubagent) {
|
|
50
|
+
availableSkills = availableSkills?.filter(
|
|
51
|
+
(skill) => skill.context !== "fork",
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
if (!availableSkills || availableSkills.length === 0) {
|
|
46
56
|
return `${SKILL_TOOL_DESCRIPTION} No skills are currently available.`;
|
|
47
57
|
}
|
package/src/tools/types.ts
CHANGED
package/src/types/agent.ts
CHANGED
|
@@ -77,6 +77,7 @@ export interface AgentOptions {
|
|
|
77
77
|
* These rules follow the standard permission rule syntax: `ToolName` or `ToolName(pattern)`.
|
|
78
78
|
*/
|
|
79
79
|
disallowedTools?: string[];
|
|
80
|
+
[key: string]: unknown;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
export interface AgentCallbacks
|
|
@@ -92,4 +93,6 @@ export interface AgentCallbacks
|
|
|
92
93
|
tokens: number,
|
|
93
94
|
) => void;
|
|
94
95
|
onBackgroundCurrentTask?: () => void;
|
|
96
|
+
onModelChange?: (model: string) => void;
|
|
97
|
+
onConfiguredModelsChange?: (models: string[]) => void;
|
|
95
98
|
}
|
package/src/types/config.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { HookEvent, HookEventConfig } from "./hooks.js";
|
|
10
10
|
import type { PermissionMode } from "./permissions.js";
|
|
11
|
+
import type { ModelConfig } from "./config.js";
|
|
11
12
|
|
|
12
13
|
export type Scope = "user" | "project" | "local";
|
|
13
14
|
|
|
@@ -34,6 +35,8 @@ export interface WaveConfiguration {
|
|
|
34
35
|
language?: string;
|
|
35
36
|
/** Whether auto-memory is enabled */
|
|
36
37
|
autoMemoryEnabled?: boolean;
|
|
38
|
+
/** Model-specific configuration overrides */
|
|
39
|
+
models?: Record<string, Partial<ModelConfig>>;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
/**
|
package/src/types/messaging.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type MessageBlock =
|
|
|
25
25
|
| ToolBlock
|
|
26
26
|
| ImageBlock
|
|
27
27
|
| BangBlock
|
|
28
|
+
| SlashBlock
|
|
28
29
|
| CompressBlock
|
|
29
30
|
| ReasoningBlock
|
|
30
31
|
| FileHistoryBlock;
|
|
@@ -32,10 +33,20 @@ export type MessageBlock =
|
|
|
32
33
|
export interface TextBlock {
|
|
33
34
|
type: "text";
|
|
34
35
|
content: string;
|
|
35
|
-
customCommandContent?: string;
|
|
36
36
|
source?: MessageSource;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export interface SlashBlock {
|
|
40
|
+
type: "slash";
|
|
41
|
+
command: string;
|
|
42
|
+
args?: string;
|
|
43
|
+
content?: string; // The expanded prompt (template + args + bash output)
|
|
44
|
+
result?: string; // The final output (e.g., from a forked skill)
|
|
45
|
+
stage: "running" | "success" | "error" | "aborted";
|
|
46
|
+
error?: string;
|
|
47
|
+
shortResult?: string; // Progress summary (e.g., "3 tools | 1,234 tokens")
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
export interface ErrorBlock {
|
|
40
51
|
type: "error";
|
|
41
52
|
content: string;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
GatewayConfig,
|
|
8
|
+
ModelConfig,
|
|
8
9
|
ConfigurationError,
|
|
9
10
|
CONFIG_ERRORS,
|
|
10
11
|
} from "../types/index.js";
|
|
@@ -89,11 +90,11 @@ export class ConfigValidator {
|
|
|
89
90
|
|
|
90
91
|
/**
|
|
91
92
|
* Validates model configuration (basic validation)
|
|
92
|
-
* @param
|
|
93
|
-
* @param fastModel - Fast model string
|
|
93
|
+
* @param config - Model configuration object
|
|
94
94
|
* @throws ConfigurationError if invalid
|
|
95
95
|
*/
|
|
96
|
-
static validateModelConfig(
|
|
96
|
+
static validateModelConfig(config: ModelConfig): void {
|
|
97
|
+
const { model, fastModel } = config;
|
|
97
98
|
if (!model || typeof model !== "string" || model.trim() === "") {
|
|
98
99
|
throw new ConfigurationError(
|
|
99
100
|
"Agent model must be a non-empty string.",
|
|
@@ -123,5 +124,6 @@ export class ConfigValidator {
|
|
|
123
124
|
export const configValidator = {
|
|
124
125
|
validateGatewayConfig: ConfigValidator.validateGatewayConfig,
|
|
125
126
|
validateMaxInputTokens: ConfigValidator.validateMaxInputTokens,
|
|
126
|
-
validateModelConfig:
|
|
127
|
+
validateModelConfig: (config: ModelConfig) =>
|
|
128
|
+
ConfigValidator.validateModelConfig(config),
|
|
127
129
|
};
|
|
@@ -246,7 +246,13 @@ export function setupAgentContainer(
|
|
|
246
246
|
});
|
|
247
247
|
container.register("CanUseToolCallback", canUseToolWithPermissionRequest);
|
|
248
248
|
|
|
249
|
-
const liveConfigManager = new LiveConfigManager(container, {
|
|
249
|
+
const liveConfigManager = new LiveConfigManager(container, {
|
|
250
|
+
workdir,
|
|
251
|
+
onReload: () => {
|
|
252
|
+
const models = configurationService.getConfiguredModels();
|
|
253
|
+
callbacks.onConfiguredModelsChange?.(models);
|
|
254
|
+
},
|
|
255
|
+
});
|
|
250
256
|
container.register("LiveConfigManager", liveConfigManager);
|
|
251
257
|
|
|
252
258
|
const subagentManager = new SubagentManager(container, {
|
|
@@ -193,10 +193,26 @@ export function convertMessagesForAPI(
|
|
|
193
193
|
) {
|
|
194
194
|
contentParts.push({
|
|
195
195
|
type: "text",
|
|
196
|
-
text: block.
|
|
196
|
+
text: block.content,
|
|
197
197
|
});
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
// Handle SlashBlock
|
|
201
|
+
if (block.type === "slash") {
|
|
202
|
+
if (block.content && block.content.trim().length > 0) {
|
|
203
|
+
contentParts.push({
|
|
204
|
+
type: "text",
|
|
205
|
+
text: block.content,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
if (block.result && block.result.trim().length > 0) {
|
|
209
|
+
contentParts.push({
|
|
210
|
+
type: "text",
|
|
211
|
+
text: `<local-command-stdout>\n${stripAnsiColors(block.result)}\n</local-command-stdout>`,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
200
216
|
// If there is an image, add image content
|
|
201
217
|
if (
|
|
202
218
|
block.type === "image" &&
|