wave-agent-sdk 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/builtin/skills/artifact/SKILL.md +14 -0
  2. package/builtin/skills/settings/ENV.md +2 -1
  3. package/builtin/skills/settings/MODELS.md +3 -0
  4. package/builtin/skills/settings/SKILL.md +7 -0
  5. package/builtin/skills/settings/SUBAGENTS.md +1 -1
  6. package/builtin/subagents/vision.md +18 -0
  7. package/dist/constants/tools.d.ts +1 -0
  8. package/dist/constants/tools.js +1 -0
  9. package/dist/managers/aiManager.js +46 -4
  10. package/dist/managers/backgroundTaskManager.js +52 -4
  11. package/dist/managers/messageManager.js +8 -2
  12. package/dist/managers/permissionManager.d.ts +5 -0
  13. package/dist/managers/permissionManager.js +18 -2
  14. package/dist/managers/pluginManager.d.ts +9 -0
  15. package/dist/managers/pluginManager.js +20 -0
  16. package/dist/managers/skillManager.d.ts +13 -0
  17. package/dist/managers/skillManager.js +30 -0
  18. package/dist/managers/subagentManager.d.ts +12 -0
  19. package/dist/managers/subagentManager.js +32 -2
  20. package/dist/managers/toolManager.d.ts +8 -0
  21. package/dist/managers/toolManager.js +18 -0
  22. package/dist/services/aiService.d.ts +1 -1
  23. package/dist/services/aiService.js +3 -2
  24. package/dist/services/artifactAvailability.d.ts +9 -0
  25. package/dist/services/artifactAvailability.js +34 -0
  26. package/dist/services/artifactSession.d.ts +27 -0
  27. package/dist/services/artifactSession.js +52 -0
  28. package/dist/services/configurationService.d.ts +2 -1
  29. package/dist/services/configurationService.js +16 -1
  30. package/dist/services/initializationService.js +5 -0
  31. package/dist/services/remoteSettingsService.js +2 -0
  32. package/dist/tools/agentTool.js +2 -1
  33. package/dist/tools/artifactTool.d.ts +2 -0
  34. package/dist/tools/artifactTool.js +357 -0
  35. package/dist/tools/bashTool.js +24 -13
  36. package/dist/tools/editTool.js +7 -2
  37. package/dist/tools/types.d.ts +3 -1
  38. package/dist/tools/webFetchTool.js +141 -0
  39. package/dist/types/agent.d.ts +2 -0
  40. package/dist/types/config.d.ts +2 -0
  41. package/dist/types/configuration.d.ts +2 -0
  42. package/dist/types/messaging.d.ts +3 -1
  43. package/dist/types/permissions.d.ts +3 -1
  44. package/dist/types/permissions.js +2 -1
  45. package/dist/utils/bashParser.d.ts +14 -0
  46. package/dist/utils/bashParser.js +45 -1
  47. package/dist/utils/containerSetup.js +8 -0
  48. package/dist/utils/convertMessagesForAPI.js +51 -4
  49. package/dist/utils/encoding.d.ts +28 -0
  50. package/dist/utils/encoding.js +99 -0
  51. package/dist/utils/messageOperations.d.ts +4 -2
  52. package/dist/utils/messageOperations.js +23 -7
  53. package/dist/utils/subagentParser.d.ts +5 -2
  54. package/dist/utils/subagentParser.js +14 -4
  55. package/package.json +2 -1
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Streaming byte-to-text decoder for bash tool output on Windows.
3
+ *
4
+ * Git Bash (MSYS) tools emit UTF-8, but native Windows programs (taskkill,
5
+ * powershell, ping, ...) write the system OEM code page — GBK (cp936) on
6
+ * zh-CN systems. Node's default `data.toString()` decodes every stream as
7
+ * UTF-8, so GBK bytes turn into U+FFFD mojibake (issue #1753).
8
+ *
9
+ * Strategy (per-chunk buffering, decide-once):
10
+ * - Accumulate raw bytes; try strict UTF-8 (`fatal: true`) over everything
11
+ * buffered so far. If it decodes cleanly, emit the text.
12
+ * - If strict UTF-8 fails, hold back up to 3 trailing bytes (a UTF-8
13
+ * character split across chunk boundaries) and retry on the next chunk.
14
+ * - Once even the head is not valid UTF-8, the stream is GBK: re-decode all
15
+ * buffered bytes with GBK and switch to GBK for every subsequent chunk.
16
+ * - `flush()` decodes any leftover buffered bytes (leniently) at stream end.
17
+ */
18
+ export class WindowsStreamDecoder {
19
+ constructor() {
20
+ this.pending = Buffer.alloc(0);
21
+ this.gbkDecoder = null;
22
+ }
23
+ push(data) {
24
+ // Already determined GBK: decode each chunk as it arrives.
25
+ if (this.gbkDecoder) {
26
+ return this.gbkDecoder.decode(data);
27
+ }
28
+ this.pending = Buffer.concat([this.pending, data]);
29
+ try {
30
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(this.pending);
31
+ this.pending = Buffer.alloc(0);
32
+ return text;
33
+ }
34
+ catch {
35
+ // Strict UTF-8 failed. The trailing bytes may be a UTF-8 sequence split
36
+ // across chunks: hold only the trailing run of non-ASCII bytes (max 3,
37
+ // the longest incomplete UTF-8 tail). Everything before it is already
38
+ // decodable and is emitted immediately.
39
+ const n = this.pending.length;
40
+ let keep = 0;
41
+ while (keep < WindowsStreamDecoder.MAX_UTF8_TRAIL_BYTES &&
42
+ keep < n &&
43
+ this.pending[n - 1 - keep] >= 0x80) {
44
+ keep++;
45
+ }
46
+ const head = this.pending.subarray(0, n - keep);
47
+ if (head.length > 0) {
48
+ try {
49
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(head);
50
+ this.pending = Buffer.from(this.pending.subarray(n - keep));
51
+ return text;
52
+ }
53
+ catch {
54
+ // Head is not valid UTF-8 → the whole stream is GBK. Re-decode
55
+ // everything accumulated so far and commit to GBK.
56
+ this.gbkDecoder = new TextDecoder("gbk");
57
+ const text = this.gbkDecoder.decode(this.pending);
58
+ this.pending = Buffer.alloc(0);
59
+ return text;
60
+ }
61
+ }
62
+ // Only the trailing bytes are suspect: hold them for the next chunk.
63
+ return "";
64
+ }
65
+ }
66
+ /** Decode any bytes still held at stream end (lenient UTF-8, else GBK). */
67
+ flush() {
68
+ if (this.pending.length === 0)
69
+ return "";
70
+ const rest = this.pending;
71
+ this.pending = Buffer.alloc(0);
72
+ if (this.gbkDecoder) {
73
+ return this.gbkDecoder.decode(rest);
74
+ }
75
+ const utf8 = rest.toString("utf-8");
76
+ if (!utf8.includes("\uFFFD"))
77
+ return utf8;
78
+ try {
79
+ return new TextDecoder("gbk").decode(rest);
80
+ }
81
+ catch {
82
+ return utf8;
83
+ }
84
+ }
85
+ }
86
+ /** Longest possible incomplete UTF-8 tail at a chunk boundary is 3 bytes. */
87
+ WindowsStreamDecoder.MAX_UTF8_TRAIL_BYTES = 3;
88
+ /** Decode a single complete byte sequence (used for non-streaming reads). */
89
+ export function decodeBytes(buf) {
90
+ const utf8 = buf.toString("utf-8");
91
+ if (!utf8.includes("\uFFFD"))
92
+ return utf8;
93
+ try {
94
+ return new TextDecoder("gbk").decode(buf);
95
+ }
96
+ catch {
97
+ return utf8;
98
+ }
99
+ }
@@ -40,7 +40,9 @@ export interface UpdateToolBlockParams {
40
40
  }>;
41
41
  compactParams?: string;
42
42
  parametersChunk?: string;
43
- isManuallyBackgrounded?: boolean;
43
+ backgroundTaskId?: string;
44
+ backgroundedByUser?: boolean;
45
+ assistantAutoBackgrounded?: boolean;
44
46
  timestamp?: number;
45
47
  }
46
48
  export type AgentToolBlockUpdateParams = Omit<UpdateToolBlockParams, "messages">;
@@ -86,7 +88,7 @@ export declare const addToolBlockToMessageInMessages: (messages: Message[], mess
86
88
  messages: Message[];
87
89
  toolBlockId: string;
88
90
  };
89
- export declare const updateToolBlockInMessage: ({ messages, id, messageId, parameters, result, success, error, stage, name, shortResult, startLineNumber, images, compactParams, parametersChunk, isManuallyBackgrounded, }: UpdateToolBlockParams) => {
91
+ export declare const updateToolBlockInMessage: ({ messages, id, messageId, parameters, result, success, error, stage, name, shortResult, startLineNumber, images, compactParams, parametersChunk, backgroundTaskId, backgroundedByUser, assistantAutoBackgrounded, }: UpdateToolBlockParams) => {
90
92
  messages: Message[];
91
93
  messageId?: string;
92
94
  };
@@ -158,7 +158,7 @@ export const addToolBlockToMessageInMessages = (messages, messageId, params) =>
158
158
  return { messages: newMessages, toolBlockId };
159
159
  };
160
160
  // Update Tool Block of the last assistant or user message
161
- export const updateToolBlockInMessage = ({ messages, id, messageId, parameters, result, success, error, stage, name, shortResult, startLineNumber, images, compactParams, parametersChunk, isManuallyBackgrounded, }) => {
161
+ export const updateToolBlockInMessage = ({ messages, id, messageId, parameters, result, success, error, stage, name, shortResult, startLineNumber, images, compactParams, parametersChunk, backgroundTaskId, backgroundedByUser, assistantAutoBackgrounded, }) => {
162
162
  const newMessages = [...messages];
163
163
  // If messageId is provided, target that specific message
164
164
  if (messageId) {
@@ -170,6 +170,9 @@ export const updateToolBlockInMessage = ({ messages, id, messageId, parameters,
170
170
  if (toolBlock.type === "tool") {
171
171
  if (parameters !== undefined)
172
172
  toolBlock.parameters = parameters;
173
+ else if (parametersChunk !== undefined)
174
+ toolBlock.parameters =
175
+ (toolBlock.parameters || "") + parametersChunk;
173
176
  if (result !== undefined)
174
177
  toolBlock.result = result;
175
178
  if (shortResult !== undefined)
@@ -188,8 +191,12 @@ export const updateToolBlockInMessage = ({ messages, id, messageId, parameters,
188
191
  toolBlock.compactParams = compactParams;
189
192
  if (parametersChunk !== undefined)
190
193
  toolBlock.parametersChunk = parametersChunk;
191
- if (isManuallyBackgrounded !== undefined)
192
- toolBlock.isManuallyBackgrounded = isManuallyBackgrounded;
194
+ if (backgroundTaskId !== undefined)
195
+ toolBlock.backgroundTaskId = backgroundTaskId;
196
+ if (backgroundedByUser !== undefined)
197
+ toolBlock.backgroundedByUser = backgroundedByUser;
198
+ if (assistantAutoBackgrounded !== undefined)
199
+ toolBlock.assistantAutoBackgrounded = assistantAutoBackgrounded;
193
200
  }
194
201
  }
195
202
  }
@@ -204,6 +211,9 @@ export const updateToolBlockInMessage = ({ messages, id, messageId, parameters,
204
211
  if (toolBlock.type === "tool") {
205
212
  if (parameters !== undefined)
206
213
  toolBlock.parameters = parameters;
214
+ else if (parametersChunk !== undefined)
215
+ toolBlock.parameters =
216
+ (toolBlock.parameters || "") + parametersChunk;
207
217
  if (result !== undefined)
208
218
  toolBlock.result = result;
209
219
  if (shortResult !== undefined)
@@ -222,8 +232,12 @@ export const updateToolBlockInMessage = ({ messages, id, messageId, parameters,
222
232
  toolBlock.compactParams = compactParams;
223
233
  if (parametersChunk !== undefined)
224
234
  toolBlock.parametersChunk = parametersChunk;
225
- if (isManuallyBackgrounded !== undefined)
226
- toolBlock.isManuallyBackgrounded = isManuallyBackgrounded;
235
+ if (backgroundTaskId !== undefined)
236
+ toolBlock.backgroundTaskId = backgroundTaskId;
237
+ if (backgroundedByUser !== undefined)
238
+ toolBlock.backgroundedByUser = backgroundedByUser;
239
+ if (assistantAutoBackgrounded !== undefined)
240
+ toolBlock.assistantAutoBackgrounded = assistantAutoBackgrounded;
227
241
  }
228
242
  const foundMessageId = newMessages[i].id;
229
243
  return { messages: newMessages, messageId: foundMessageId };
@@ -233,7 +247,7 @@ export const updateToolBlockInMessage = ({ messages, id, messageId, parameters,
233
247
  // This handles cases where we're streaming tool parameters before execution
234
248
  newMessages[i].blocks.push({
235
249
  type: "tool",
236
- parameters: parameters,
250
+ parameters: parameters !== undefined ? parameters : (parametersChunk ?? ""),
237
251
  result: result || "",
238
252
  shortResult: shortResult,
239
253
  startLineNumber: startLineNumber,
@@ -245,7 +259,9 @@ export const updateToolBlockInMessage = ({ messages, id, messageId, parameters,
245
259
  stage: stage ?? "start",
246
260
  compactParams: compactParams,
247
261
  parametersChunk: parametersChunk,
248
- isManuallyBackgrounded: isManuallyBackgrounded,
262
+ backgroundTaskId: backgroundTaskId,
263
+ backgroundedByUser: backgroundedByUser,
264
+ assistantAutoBackgrounded: assistantAutoBackgrounded,
249
265
  });
250
266
  const foundMessageId = newMessages[i].id;
251
267
  return { messages: newMessages, messageId: foundMessageId };
@@ -17,9 +17,12 @@ export interface SubagentConfiguration {
17
17
  export declare function parseAgentFile(filePath: string, scope: "plugin", pluginRoot: string): SubagentConfiguration;
18
18
  /**
19
19
  * Load all subagent configurations from project and user directories, plus built-in subagents
20
+ * @param workdir - Working directory to scan for project-level subagents
21
+ * @param env - Merged environment (settings.json env over OS env). Defaults to process.env.
22
+ * Used for conditional registration of builtin subagents (e.g. WAVE_VISION_MODEL).
20
23
  */
21
- export declare function loadSubagentConfigurations(workdir: string): Promise<SubagentConfiguration[]>;
24
+ export declare function loadSubagentConfigurations(workdir: string, env?: Record<string, string>): Promise<SubagentConfiguration[]>;
22
25
  /**
23
26
  * Find subagent by exact name match
24
27
  */
25
- export declare function findSubagentByName(name: string, workdir: string): Promise<SubagentConfiguration | null>;
28
+ export declare function findSubagentByName(name: string, workdir: string, env?: Record<string, string>): Promise<SubagentConfiguration | null>;
@@ -158,15 +158,25 @@ function scanSubagentDirectory(dirPath, scope) {
158
158
  }
159
159
  /**
160
160
  * Load all subagent configurations from project and user directories, plus built-in subagents
161
+ * @param workdir - Working directory to scan for project-level subagents
162
+ * @param env - Merged environment (settings.json env over OS env). Defaults to process.env.
163
+ * Used for conditional registration of builtin subagents (e.g. WAVE_VISION_MODEL).
161
164
  */
162
- export async function loadSubagentConfigurations(workdir) {
165
+ export async function loadSubagentConfigurations(workdir, env = process.env) {
163
166
  const projectWaveDir = join(workdir, ".wave", "agents");
164
167
  const projectClaudeDir = join(workdir, ".claude", "agents");
165
168
  const userWaveDir = join(process.env.HOME || "~", ".wave", "agents");
166
169
  const userClaudeDir = join(process.env.HOME || "~", ".claude", "agents");
167
170
  const builtinDir = getBuiltinSubagentsDir();
168
171
  // Load configurations from all sources
169
- const builtinConfigs = scanSubagentDirectory(builtinDir, "builtin");
172
+ let builtinConfigs = scanSubagentDirectory(builtinDir, "builtin");
173
+ // Conditional registration: builtin subagents whose frontmatter requires the
174
+ // WAVE_VISION_MODEL env var (`model: visionModel`) are only loaded when it is
175
+ // set — otherwise the main model would delegate image recognition to a
176
+ // subagent that resolves to a non-vision model.
177
+ if (!env.WAVE_VISION_MODEL) {
178
+ builtinConfigs = builtinConfigs.filter((config) => config.model !== "visionModel");
179
+ }
170
180
  const userClaudeConfigs = scanSubagentDirectory(userClaudeDir, "user");
171
181
  const userWaveConfigs = scanSubagentDirectory(userWaveDir, "user");
172
182
  const projectClaudeConfigs = scanSubagentDirectory(projectClaudeDir, "project");
@@ -193,7 +203,7 @@ export async function loadSubagentConfigurations(workdir) {
193
203
  /**
194
204
  * Find subagent by exact name match
195
205
  */
196
- export async function findSubagentByName(name, workdir) {
197
- const configurations = await loadSubagentConfigurations(workdir);
206
+ export async function findSubagentByName(name, workdir, env) {
207
+ const configurations = await loadSubagentConfigurations(workdir, env);
198
208
  return configurations.find((config) => config.name === name) || null;
199
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -55,6 +55,7 @@
55
55
  "fuzzysort": "^3.1.0",
56
56
  "glob": "^13.0.0",
57
57
  "lru-cache": "^11.3.5",
58
+ "marked": "^17.0.2",
58
59
  "minimatch": "^10.0.3",
59
60
  "openai": "^5.12.2",
60
61
  "turndown": "^7.2.2"