wave-agent-sdk 1.1.4 → 1.2.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/dist/agent.d.ts +72 -24
- package/dist/agent.js +123 -31
- package/dist/builtin/plugins.js +17 -2
- package/dist/builtin/skills/settings.js +6 -8
- package/dist/core/plugin.d.ts +4 -0
- package/dist/core/plugin.js +7 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/managers/aiManager.d.ts +0 -24
- package/dist/managers/aiManager.js +23 -181
- package/dist/managers/bashModeManager.d.ts +33 -0
- package/dist/managers/bashModeManager.js +110 -0
- package/dist/managers/hookManager.d.ts +5 -0
- package/dist/managers/hookManager.js +7 -0
- package/dist/managers/liveConfigManager.js +3 -3
- package/dist/managers/mcpManager.d.ts +23 -0
- package/dist/managers/mcpManager.js +162 -14
- package/dist/managers/messageManager.d.ts +12 -13
- package/dist/managers/messageManager.js +63 -60
- package/dist/managers/permissionManager.d.ts +29 -0
- package/dist/managers/permissionManager.js +158 -70
- package/dist/managers/planManager.d.ts +9 -0
- package/dist/managers/planManager.js +19 -1
- package/dist/managers/skillManager.d.ts +31 -0
- package/dist/managers/skillManager.js +122 -12
- package/dist/managers/slashCommandManager.js +9 -28
- package/dist/managers/subagentManager.d.ts +7 -0
- package/dist/managers/subagentManager.js +61 -9
- package/dist/managers/workflowManager.js +6 -0
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -4
- package/dist/services/MarketplaceService.js +36 -14
- package/dist/services/configurationService.d.ts +34 -2
- package/dist/services/configurationService.js +123 -16
- package/dist/services/initializationService.js +2 -2
- package/dist/services/jsonlHandler.d.ts +14 -0
- package/dist/services/jsonlHandler.js +44 -1
- package/dist/services/memory.d.ts +14 -0
- package/dist/services/memory.js +33 -0
- package/dist/services/officialMarketplaceMirror.d.ts +85 -0
- package/dist/services/officialMarketplaceMirror.js +289 -0
- package/dist/services/remoteSettingsService.js +4 -4
- package/dist/services/session.js +30 -13
- package/dist/services/worktreeHooks.js +6 -1
- package/dist/stdio/index.d.ts +10 -0
- package/dist/stdio/index.js +10 -0
- package/dist/stdio/notificationRouter.d.ts +38 -0
- package/dist/stdio/notificationRouter.js +96 -0
- package/dist/stdio/rpcClient.d.ts +18 -0
- package/dist/stdio/rpcClient.js +10 -0
- package/dist/stdio/stdioAgent.d.ts +222 -0
- package/dist/stdio/stdioAgent.js +341 -0
- package/dist/tools/bashTool.js +2 -0
- package/dist/tools/exitPlanMode.js +10 -2
- package/dist/types/agent.d.ts +8 -0
- package/dist/types/commands.d.ts +7 -0
- package/dist/types/configuration.d.ts +6 -1
- package/dist/types/hooks.d.ts +1 -0
- package/dist/types/hooks.js +19 -0
- package/dist/types/mcp.d.ts +3 -0
- package/dist/types/messaging.d.ts +1 -8
- package/dist/types/skills.d.ts +11 -0
- package/dist/utils/bashParser.d.ts +17 -0
- package/dist/utils/bashParser.js +72 -0
- package/dist/utils/bashStructure/bashLexer.d.ts +96 -0
- package/dist/utils/bashStructure/bashLexer.js +676 -0
- package/dist/utils/bashStructure/bashParser.d.ts +144 -0
- package/dist/utils/bashStructure/bashParser.js +606 -0
- package/dist/utils/bashStructure/bashSemantics.d.ts +70 -0
- package/dist/utils/bashStructure/bashSemantics.js +477 -0
- package/dist/utils/bashStructure/index.d.ts +26 -0
- package/dist/utils/bashStructure/index.js +27 -0
- package/dist/utils/bashStructure/types.d.ts +62 -0
- package/dist/utils/bashStructure/types.js +47 -0
- package/dist/utils/container.d.ts +6 -0
- package/dist/utils/container.js +9 -0
- package/dist/utils/containerSetup.d.ts +11 -1
- package/dist/utils/containerSetup.js +26 -11
- package/dist/utils/fileUtils.d.ts +11 -0
- package/dist/utils/fileUtils.js +37 -0
- package/dist/utils/messageOperations.d.ts +0 -18
- package/dist/utils/messageOperations.js +0 -62
- package/dist/utils/subagentParser.js +9 -2
- package/dist/utils/tokenCalculation.js +0 -8
- package/dist/utils/worktreeUtils.d.ts +2 -1
- package/dist/utils/worktreeUtils.js +64 -34
- package/package.json +6 -1
- package/dist/managers/bangManager.d.ts +0 -26
- package/dist/managers/bangManager.js +0 -78
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StdioAgent — host-side typed wrapper around a JSON-RPC client that mirrors
|
|
3
|
+
* the Agent API (shared by the VS Code extension and the desktop app).
|
|
4
|
+
*
|
|
5
|
+
* In the single-shared-process architecture, all sessions share one transport
|
|
6
|
+
* client. The NotificationRouter dispatches incoming notifications to the
|
|
7
|
+
* appropriate StdioAgent via `handleNotification()`. The agent no longer
|
|
8
|
+
* subscribes directly to the client.
|
|
9
|
+
*
|
|
10
|
+
* All session-scoped requests carry `this.sessionId` on the JSON-RPC envelope
|
|
11
|
+
* so the server can route them to the right Agent.
|
|
12
|
+
*
|
|
13
|
+
* `destroy()` only unregisters from the router and sends the destroy request;
|
|
14
|
+
* it does NOT dispose the shared client.
|
|
15
|
+
*
|
|
16
|
+
* The client dependency is the structural `RpcClient` surface (request/notify/
|
|
17
|
+
* onNotification) so both hosts keep their own transports: vscode's
|
|
18
|
+
* `StdioClient` (spawn + hostLog) and desktop's `JsonRpcClient` subclasses
|
|
19
|
+
* (StdioClient / SocketClient).
|
|
20
|
+
*/
|
|
21
|
+
// ── StdioAgent ───────────────────────────────────────────────────
|
|
22
|
+
export class StdioAgent {
|
|
23
|
+
constructor(client, router, callbacks) {
|
|
24
|
+
this.latestTotalTokens = 0;
|
|
25
|
+
this.messages = [];
|
|
26
|
+
this.queuedMessages = [];
|
|
27
|
+
this.tasks = [];
|
|
28
|
+
this.backgroundTasks = [];
|
|
29
|
+
this.isStreaming = false;
|
|
30
|
+
this.isCommandRunning = false;
|
|
31
|
+
this.isCompacting = false;
|
|
32
|
+
this.client = client;
|
|
33
|
+
this.router = router;
|
|
34
|
+
this.callbacks = callbacks;
|
|
35
|
+
}
|
|
36
|
+
// ── Lifecycle ─────────────────────────────────────────────────
|
|
37
|
+
async initialize(params) {
|
|
38
|
+
const result = (await this.client.request("initialize", params));
|
|
39
|
+
this.sessionId = result.sessionId;
|
|
40
|
+
this.workingDirectory = result.workingDirectory;
|
|
41
|
+
this.sessionCwd = result.workingDirectory;
|
|
42
|
+
this.permissionMode = result.permissionMode;
|
|
43
|
+
this.latestTotalTokens = result.latestTotalTokens;
|
|
44
|
+
// A fresh session has no usage yet; don't let a previous session's
|
|
45
|
+
// percentage survive into this agent.
|
|
46
|
+
this.contextUsagePercent = undefined;
|
|
47
|
+
// Register with the router so subsequent notifications are routed here
|
|
48
|
+
this.router.register(this.sessionId, this);
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
async destroy() {
|
|
52
|
+
if (this.sessionId) {
|
|
53
|
+
this.router.unregister(this.sessionId);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await this.client.request("destroy", undefined, this.sessionId);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Process may have already exited; best-effort
|
|
60
|
+
}
|
|
61
|
+
// NOTE: do NOT dispose the shared client — other sessions may still use it
|
|
62
|
+
}
|
|
63
|
+
async restoreSession(sessionId) {
|
|
64
|
+
await this.client.request("restoreSession", { sessionId }, this.sessionId);
|
|
65
|
+
}
|
|
66
|
+
async updateConfig(params) {
|
|
67
|
+
const oldSessionId = this.sessionId;
|
|
68
|
+
const result = (await this.client.request("updateConfig", params, this.sessionId));
|
|
69
|
+
// If sessionId changed, re-register with the router
|
|
70
|
+
if (oldSessionId && result.sessionId !== oldSessionId) {
|
|
71
|
+
this.router.unregister(oldSessionId);
|
|
72
|
+
this.sessionId = result.sessionId;
|
|
73
|
+
this.router.register(this.sessionId, this);
|
|
74
|
+
// Sync the host session state: the bridge may have downgraded to a
|
|
75
|
+
// fresh session (session file missing on recreate). Without this the
|
|
76
|
+
// host keeps the destroyed sessionId and every later request fails
|
|
77
|
+
// with "Session not found".
|
|
78
|
+
this.callbacks.onSessionIdChange?.(this.sessionId);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
// ── Messages ──────────────────────────────────────────────────
|
|
83
|
+
async sendMessage(text, images, force) {
|
|
84
|
+
await this.client.request("sendMessage", { text, images, force }, this.sessionId);
|
|
85
|
+
}
|
|
86
|
+
async bang(command) {
|
|
87
|
+
await this.client.request("bang", { command }, this.sessionId);
|
|
88
|
+
}
|
|
89
|
+
async abortMessage() {
|
|
90
|
+
await this.client.request("abortMessage", undefined, this.sessionId);
|
|
91
|
+
}
|
|
92
|
+
async clearMessages() {
|
|
93
|
+
await this.client.request("clearMessages", undefined, this.sessionId);
|
|
94
|
+
}
|
|
95
|
+
async compact(customInstructions) {
|
|
96
|
+
await this.client.request("compact", { customInstructions }, this.sessionId);
|
|
97
|
+
}
|
|
98
|
+
async rewindToMessage(messageId) {
|
|
99
|
+
return (await this.client.request("rewindToMessage", { messageId }, this.sessionId));
|
|
100
|
+
}
|
|
101
|
+
async askBtw(question) {
|
|
102
|
+
return (await this.client.request("askBtw", { question }, this.sessionId));
|
|
103
|
+
}
|
|
104
|
+
async removeQueuedMessage(index) {
|
|
105
|
+
await this.client.request("deleteQueuedMessage", { index }, this.sessionId);
|
|
106
|
+
}
|
|
107
|
+
async updateQueuedMessageById(id, patch) {
|
|
108
|
+
const result = (await this.client.request("updateQueuedMessage", { id, text: patch.content, images: patch.images }, this.sessionId));
|
|
109
|
+
return result?.ok ?? false;
|
|
110
|
+
}
|
|
111
|
+
async removeQueuedMessageById(id) {
|
|
112
|
+
await this.client.request("deleteQueuedMessageById", { id }, this.sessionId);
|
|
113
|
+
}
|
|
114
|
+
async getFullMessageThread() {
|
|
115
|
+
return (await this.client.request("getFullMessageThread", undefined, this.sessionId));
|
|
116
|
+
}
|
|
117
|
+
async getMessages() {
|
|
118
|
+
const result = (await this.client.request("getMessages", undefined, this.sessionId));
|
|
119
|
+
this.messages = result.messages;
|
|
120
|
+
this.contextUsagePercent = result.contextUsagePercent;
|
|
121
|
+
return result.messages;
|
|
122
|
+
}
|
|
123
|
+
async listRewindCheckpoints() {
|
|
124
|
+
return (await this.client.request("listRewindCheckpoints", undefined, this.sessionId));
|
|
125
|
+
}
|
|
126
|
+
async getConfiguredModels() {
|
|
127
|
+
return (await this.client.request("getConfiguredModels", undefined, this.sessionId));
|
|
128
|
+
}
|
|
129
|
+
async setModel(model) {
|
|
130
|
+
await this.client.request("setModel", { model }, this.sessionId);
|
|
131
|
+
}
|
|
132
|
+
// ── Permissions ───────────────────────────────────────────────
|
|
133
|
+
async setPermissionMode(mode) {
|
|
134
|
+
await this.client.request("setPermissionMode", { mode }, this.sessionId);
|
|
135
|
+
}
|
|
136
|
+
/** Returns cached permission mode (updated via notification). */
|
|
137
|
+
getPermissionMode() {
|
|
138
|
+
return this.permissionMode;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Reads the session's current plan file (path + contents). Used by the
|
|
142
|
+
* /plan command to display the plan when already in plan mode; the CLI side
|
|
143
|
+
* awaits the path if it is still being generated after setPermissionMode.
|
|
144
|
+
*/
|
|
145
|
+
async getPlanFile() {
|
|
146
|
+
return (await this.client.request("getPlanFile", undefined, this.sessionId));
|
|
147
|
+
}
|
|
148
|
+
sendPermissionResponse(requestId, decision) {
|
|
149
|
+
this.client.notify("permissionResponse", { requestId, decision }, this.sessionId);
|
|
150
|
+
}
|
|
151
|
+
async getBackgroundTaskOutput(taskId) {
|
|
152
|
+
const result = (await this.client.request("getBackgroundTaskOutput", { taskId }, this.sessionId));
|
|
153
|
+
return result.output;
|
|
154
|
+
}
|
|
155
|
+
async stopBackgroundTask(taskId) {
|
|
156
|
+
const result = (await this.client.request("stopBackgroundTask", { taskId }, this.sessionId));
|
|
157
|
+
return result.success;
|
|
158
|
+
}
|
|
159
|
+
async backgroundCurrentTask() {
|
|
160
|
+
await this.client.request("backgroundCurrentTask", undefined, this.sessionId);
|
|
161
|
+
}
|
|
162
|
+
async getWorkflowRuns() {
|
|
163
|
+
const result = (await this.client.request("getWorkflowRuns", undefined, this.sessionId));
|
|
164
|
+
return result?.runs ?? [];
|
|
165
|
+
}
|
|
166
|
+
async stopWorkflowRun(runId) {
|
|
167
|
+
const result = (await this.client.request("stopWorkflowRun", { runId }, this.sessionId));
|
|
168
|
+
return result?.success ?? false;
|
|
169
|
+
}
|
|
170
|
+
// ── MCP ───────────────────────────────────────────────────────
|
|
171
|
+
async getMcpServers() {
|
|
172
|
+
const result = (await this.client.request("getMcpServers", undefined, this.sessionId));
|
|
173
|
+
return result.servers;
|
|
174
|
+
}
|
|
175
|
+
async connectMcpServer(serverName) {
|
|
176
|
+
const result = (await this.client.request("connectMcpServer", { serverName }, this.sessionId));
|
|
177
|
+
return result.success;
|
|
178
|
+
}
|
|
179
|
+
async disconnectMcpServer(serverName) {
|
|
180
|
+
const result = (await this.client.request("disconnectMcpServer", { serverName }, this.sessionId));
|
|
181
|
+
return result.success;
|
|
182
|
+
}
|
|
183
|
+
async getMcpConfigPaths() {
|
|
184
|
+
const result = (await this.client.request("getMcpConfigPaths", undefined, this.sessionId));
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
async removeMcpServer(scope, serverName) {
|
|
188
|
+
const result = (await this.client.request("removeMcpServer", { scope, serverName }, this.sessionId));
|
|
189
|
+
return result.success;
|
|
190
|
+
}
|
|
191
|
+
async deleteSkill(name) {
|
|
192
|
+
const result = (await this.client.request("deleteSkill", { name }, this.sessionId));
|
|
193
|
+
return result.success;
|
|
194
|
+
}
|
|
195
|
+
async deleteSubagent(name) {
|
|
196
|
+
const result = (await this.client.request("deleteSubagent", { name }, this.sessionId));
|
|
197
|
+
return result.success;
|
|
198
|
+
}
|
|
199
|
+
async getHooksByScope(scope) {
|
|
200
|
+
const result = (await this.client.request("getHooksByScope", { scope }, this.sessionId));
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
async deleteHook(scope, hookName) {
|
|
204
|
+
await this.client.request("deleteHook", { scope, hookName }, this.sessionId);
|
|
205
|
+
}
|
|
206
|
+
// ── Commands ──────────────────────────────────────────────────
|
|
207
|
+
async getSlashCommands() {
|
|
208
|
+
const result = (await this.client.request("getSlashCommands", undefined, this.sessionId));
|
|
209
|
+
return result.commands;
|
|
210
|
+
}
|
|
211
|
+
async getSubagentConfigurations() {
|
|
212
|
+
const result = (await this.client.request("getSubagentConfigurations", undefined, this.sessionId));
|
|
213
|
+
return result.configurations;
|
|
214
|
+
}
|
|
215
|
+
async getSkillMetadata() {
|
|
216
|
+
const result = (await this.client.request("getSkillMetadata", undefined, this.sessionId));
|
|
217
|
+
return result.skills;
|
|
218
|
+
}
|
|
219
|
+
// ── Notification dispatch (called by NotificationRouter) ──────
|
|
220
|
+
handleNotification(method, params) {
|
|
221
|
+
switch (method) {
|
|
222
|
+
case "userMessageAdded": {
|
|
223
|
+
const p = params;
|
|
224
|
+
if (p.message)
|
|
225
|
+
this.callbacks.onUserMessageAdded?.(p.message);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case "assistantMessageAdded": {
|
|
229
|
+
const p = params;
|
|
230
|
+
if (p.message)
|
|
231
|
+
this.callbacks.onAssistantMessageAdded?.(p.message);
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "assistantContentUpdated":
|
|
235
|
+
this.callbacks.onAssistantContentUpdated?.(params);
|
|
236
|
+
break;
|
|
237
|
+
case "assistantReasoningUpdated":
|
|
238
|
+
this.callbacks.onAssistantReasoningUpdated?.(params);
|
|
239
|
+
break;
|
|
240
|
+
case "toolBlockUpdated":
|
|
241
|
+
this.callbacks.onToolBlockUpdated?.(params);
|
|
242
|
+
break;
|
|
243
|
+
case "errorBlockAdded": {
|
|
244
|
+
const p = params;
|
|
245
|
+
this.callbacks.onErrorBlockAdded?.(p.error);
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
case "compactBlockAdded": {
|
|
249
|
+
const p = params;
|
|
250
|
+
this.callbacks.onCompactBlockAdded?.(p.content);
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
case "compactionStateChange": {
|
|
254
|
+
const p = params;
|
|
255
|
+
this.isCompacting = p.isCompacting;
|
|
256
|
+
this.callbacks.onCompactionStateChange?.(p.isCompacting);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case "compactionContentUpdate": {
|
|
260
|
+
const p = params;
|
|
261
|
+
this.callbacks.onCompactionContentUpdate?.(p.content);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case "loadingChange": {
|
|
265
|
+
const p = params;
|
|
266
|
+
if (p.latestTotalTokens !== undefined) {
|
|
267
|
+
this.latestTotalTokens = p.latestTotalTokens;
|
|
268
|
+
}
|
|
269
|
+
this.isStreaming = p.loading;
|
|
270
|
+
this.callbacks.onLoadingChange?.(p.loading);
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case "contextUsage": {
|
|
274
|
+
const p = params;
|
|
275
|
+
this.callbacks.onContextUsage?.(p.percent);
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case "commandRunningChange": {
|
|
279
|
+
const p = params;
|
|
280
|
+
this.isCommandRunning = p.running;
|
|
281
|
+
this.callbacks.onCommandRunningChange?.(p.running);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case "queuedMessagesChange": {
|
|
285
|
+
const p = params;
|
|
286
|
+
this.queuedMessages = p.messages;
|
|
287
|
+
this.callbacks.onQueuedMessagesChange?.(p.messages);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case "tasksChange": {
|
|
291
|
+
const p = params;
|
|
292
|
+
this.tasks = p.tasks;
|
|
293
|
+
this.callbacks.onTasksChange?.(p.tasks);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case "backgroundTasksChange": {
|
|
297
|
+
const p = params;
|
|
298
|
+
this.backgroundTasks = p.tasks;
|
|
299
|
+
this.callbacks.onBackgroundTasksChange?.(p.tasks);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case "sessionIdChange": {
|
|
303
|
+
const p = params;
|
|
304
|
+
this.sessionId = p.sessionId;
|
|
305
|
+
this.callbacks.onSessionIdChange?.(p.sessionId);
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
case "permissionModeChange": {
|
|
309
|
+
const p = params;
|
|
310
|
+
this.permissionMode = p.mode;
|
|
311
|
+
this.callbacks.onPermissionModeChange?.(p.mode);
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
case "mcpServersChange": {
|
|
315
|
+
const p = params;
|
|
316
|
+
this.callbacks.onMcpServersChange?.(p.servers);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case "workdirChange": {
|
|
320
|
+
const p = params;
|
|
321
|
+
this.workingDirectory = p.workdir;
|
|
322
|
+
this.callbacks.onWorkdirChange?.(p.workdir);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case "notificationMessageAdded":
|
|
326
|
+
this.callbacks.onNotificationMessageAdded?.(params);
|
|
327
|
+
break;
|
|
328
|
+
case "permissionRequest": {
|
|
329
|
+
const p = params;
|
|
330
|
+
this.callbacks.onPermissionRequest?.(p.requestId, p.context);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
case "btwContent": {
|
|
334
|
+
const p = params;
|
|
335
|
+
if (p)
|
|
336
|
+
this.callbacks.onBtwContent?.(p);
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
package/dist/tools/bashTool.js
CHANGED
|
@@ -413,6 +413,7 @@ The working directory persists between commands. Try to maintain your current wo
|
|
|
413
413
|
? `${processedOutput}\n\n${reason}`
|
|
414
414
|
: reason,
|
|
415
415
|
error: reason,
|
|
416
|
+
metadata: { exitCode: 130 },
|
|
416
417
|
});
|
|
417
418
|
}
|
|
418
419
|
};
|
|
@@ -554,6 +555,7 @@ The working directory persists between commands. Try to maintain your current wo
|
|
|
554
555
|
error: exitCode !== 0
|
|
555
556
|
? `Command failed with exit code: ${exitCode}`
|
|
556
557
|
: undefined,
|
|
558
|
+
metadata: { exitCode },
|
|
557
559
|
});
|
|
558
560
|
};
|
|
559
561
|
child.on("exit", (code) => {
|
|
@@ -2,6 +2,11 @@ import { readFile } from "fs/promises";
|
|
|
2
2
|
import { logger } from "../utils/globalLogger.js";
|
|
3
3
|
import { EXIT_PLAN_MODE_TOOL_NAME } from "../constants/tools.js";
|
|
4
4
|
import { OPERATION_CANCELLED_BY_USER } from "../types/permissions.js";
|
|
5
|
+
// Rejection feedback format aligned with Claude Code (messages.ts):
|
|
6
|
+
// the model reads the fixed prefix + the user's verbatim feedback, without
|
|
7
|
+
// any additional prompt injection.
|
|
8
|
+
const REJECT_MESSAGE = "The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.";
|
|
9
|
+
const REJECT_MESSAGE_WITH_REASON_PREFIX = "The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). To tell you how to proceed, the user said:\n";
|
|
5
10
|
/**
|
|
6
11
|
* Exit Plan Mode Tool Plugin
|
|
7
12
|
*/
|
|
@@ -90,10 +95,13 @@ Ensure your plan is complete and unambiguous:
|
|
|
90
95
|
content: OPERATION_CANCELLED_BY_USER,
|
|
91
96
|
};
|
|
92
97
|
}
|
|
98
|
+
const feedback = permissionResult.message?.trim();
|
|
93
99
|
return {
|
|
94
100
|
success: false,
|
|
95
|
-
content:
|
|
96
|
-
|
|
101
|
+
content: feedback
|
|
102
|
+
? `${REJECT_MESSAGE_WITH_REASON_PREFIX}${feedback}`
|
|
103
|
+
: REJECT_MESSAGE,
|
|
104
|
+
error: feedback ? undefined : "Plan rejected by user",
|
|
97
105
|
};
|
|
98
106
|
}
|
|
99
107
|
context.permissionManager.setHasExitedPlanMode(true);
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -31,6 +31,14 @@ export interface AgentOptions {
|
|
|
31
31
|
maxTokens?: number;
|
|
32
32
|
/** Preferred language for agent communication */
|
|
33
33
|
language?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Per-session override for auto-memory extraction (settings-page toggle
|
|
36
|
+
* value, carried by hosts over stdio). Takes precedence over settings.json
|
|
37
|
+
* merged config and WAVE_DISABLE_AUTO_MEMORY env; undefined defers to them.
|
|
38
|
+
*/
|
|
39
|
+
autoMemoryEnabled?: boolean;
|
|
40
|
+
/** Per-session override for auto-memory extraction frequency (turns). */
|
|
41
|
+
autoMemoryFrequency?: number;
|
|
34
42
|
callbacks?: AgentCallbacks;
|
|
35
43
|
restoreSessionId?: string;
|
|
36
44
|
continueLastSession?: boolean;
|
package/dist/types/commands.d.ts
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
* Slash command and custom command types
|
|
3
3
|
* Dependencies: None
|
|
4
4
|
*/
|
|
5
|
+
/** UI-facing source of a skill-derived slash command. "user" covers personal
|
|
6
|
+
* (~/.wave、~/.claude、~/.agents) skills; plugin skills carry pluginName. */
|
|
7
|
+
export type SkillSource = "builtin" | "user" | "project" | "plugin";
|
|
5
8
|
export interface SlashCommand {
|
|
6
9
|
id: string;
|
|
7
10
|
name: string;
|
|
8
11
|
description: string;
|
|
12
|
+
/** Set only on commands registered from skills — GUI slash-command popup
|
|
13
|
+
* renders a source tag (内置/用户/项目/插件) from it. Plain/custom/plugin
|
|
14
|
+
* commands leave it undefined. */
|
|
15
|
+
skillSource?: SkillSource;
|
|
9
16
|
handler: (args?: string, signal?: AbortSignal) => Promise<void> | void;
|
|
10
17
|
/** Whether this command should bypass the message queue when AI is busy.
|
|
11
18
|
* - `true`: always immediate
|
|
@@ -25,7 +25,12 @@ export interface WaveConfiguration {
|
|
|
25
25
|
permissions?: {
|
|
26
26
|
allow?: string[];
|
|
27
27
|
deny?: string[];
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Default permission mode for restricted tools. Settings key aligns with
|
|
30
|
+
* Claude Code's `permissions.defaultMode`; the runtime permission context
|
|
31
|
+
* and CLI `--permission-mode` keep the "permission mode" naming.
|
|
32
|
+
*/
|
|
33
|
+
defaultMode?: PermissionMode;
|
|
29
34
|
/**
|
|
30
35
|
* List of directories that are considered part of the Safe Zone.
|
|
31
36
|
* File operations within these directories can be auto-accepted.
|
package/dist/types/hooks.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export type { WaveConfiguration, HookConfiguration, PartialHookConfiguration, HookConfigurationRecord, } from "./configuration.js";
|
|
8
8
|
export type HookEvent = "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "Stop" | "SubagentStop" | "PermissionRequest" | "WorktreeCreate" | "WorktreeRemove" | "CwdChanged" | "SessionStart" | "SessionEnd" | "PreCompact" | "PostCompact";
|
|
9
|
+
export declare const HOOK_EVENT_SUMMARIES: Record<HookEvent, string>;
|
|
9
10
|
export interface HookCommand {
|
|
10
11
|
type: "command";
|
|
11
12
|
command: string;
|
package/dist/types/hooks.js
CHANGED
|
@@ -4,6 +4,25 @@
|
|
|
4
4
|
* Provides comprehensive TypeScript types for the Wave Code hooks system,
|
|
5
5
|
* enabling automated actions at specific workflow points.
|
|
6
6
|
*/
|
|
7
|
+
// One-line summary per hook event, aligned with Claude Code's
|
|
8
|
+
// HookEventMetadata.summary (hooksConfigManager.ts). Shared by the CLI
|
|
9
|
+
// /hooks detail view and the webview settings hooks page; unknown/plugin
|
|
10
|
+
// events fall back to displaying the event name itself.
|
|
11
|
+
export const HOOK_EVENT_SUMMARIES = {
|
|
12
|
+
PreToolUse: "Before tool execution",
|
|
13
|
+
PostToolUse: "After tool execution",
|
|
14
|
+
UserPromptSubmit: "When the user submits a prompt",
|
|
15
|
+
Stop: "Right before Claude concludes its response",
|
|
16
|
+
SubagentStop: "Right before a subagent (Agent tool call) concludes its response",
|
|
17
|
+
PermissionRequest: "When a permission dialog is displayed",
|
|
18
|
+
WorktreeCreate: "Create an isolated worktree for VCS-agnostic isolation",
|
|
19
|
+
WorktreeRemove: "Remove a previously created worktree",
|
|
20
|
+
CwdChanged: "After the working directory changes",
|
|
21
|
+
SessionStart: "When a new session is started",
|
|
22
|
+
SessionEnd: "When a session is ending",
|
|
23
|
+
PreCompact: "Before conversation compaction",
|
|
24
|
+
PostCompact: "After conversation compaction",
|
|
25
|
+
};
|
|
7
26
|
// Hook execution errors (non-blocking)
|
|
8
27
|
export class HookExecutionError extends Error {
|
|
9
28
|
constructor(hookCommand, originalError, context) {
|
package/dist/types/mcp.d.ts
CHANGED
|
@@ -25,6 +25,9 @@ export interface McpServerStatus {
|
|
|
25
25
|
config: McpServerConfig;
|
|
26
26
|
/** Pre-resolution URL with template variables preserved for safe display */
|
|
27
27
|
originalUrl?: string;
|
|
28
|
+
/** Config source: user (~/.wave/mcp.json), project (<workdir>/.mcp.json),
|
|
29
|
+
* or plugin (registered from a plugin manifest) */
|
|
30
|
+
scope?: "user" | "project" | "plugin";
|
|
28
31
|
status: "disconnected" | "connected" | "connecting" | "reconnecting" | "error";
|
|
29
32
|
tools?: McpTool[];
|
|
30
33
|
toolCount?: number;
|
|
@@ -16,7 +16,7 @@ export interface Message {
|
|
|
16
16
|
additionalFields?: Record<string, unknown>;
|
|
17
17
|
isMeta?: boolean;
|
|
18
18
|
}
|
|
19
|
-
export type MessageBlock = TextBlock | ErrorBlock | ToolBlock | ImageBlock |
|
|
19
|
+
export type MessageBlock = TextBlock | ErrorBlock | ToolBlock | ImageBlock | CompactBlock | ReasoningBlock | FileHistoryBlock | TaskNotificationBlock;
|
|
20
20
|
export interface TextBlock {
|
|
21
21
|
type: "text";
|
|
22
22
|
content: string;
|
|
@@ -62,13 +62,6 @@ export interface ImageBlock {
|
|
|
62
62
|
type: "image";
|
|
63
63
|
imageUrls?: string[];
|
|
64
64
|
}
|
|
65
|
-
export interface BangBlock {
|
|
66
|
-
type: "bang";
|
|
67
|
-
command: string;
|
|
68
|
-
output: string;
|
|
69
|
-
stage: "running" | "end";
|
|
70
|
-
exitCode: number | null;
|
|
71
|
-
}
|
|
72
65
|
export interface CompactBlock {
|
|
73
66
|
type: "compact";
|
|
74
67
|
content: string;
|
package/dist/types/skills.d.ts
CHANGED
|
@@ -6,6 +6,17 @@ export interface SkillMetadata {
|
|
|
6
6
|
name: string;
|
|
7
7
|
description: string;
|
|
8
8
|
type: "personal" | "project" | "builtin";
|
|
9
|
+
/**
|
|
10
|
+
* Path to the skill **directory** (the folder containing SKILL.md), never
|
|
11
|
+
* to SKILL.md itself. Directory-level contract — all consumers rely on it:
|
|
12
|
+
* - `SkillManager.prepareSkillContent` injects this value for
|
|
13
|
+
* `${WAVE_SKILL_DIR}` / `${CLAUDE_SKILL_DIR}` substitutions and reports it
|
|
14
|
+
* as the "Base directory for this skill";
|
|
15
|
+
* - `SkillManager.deleteSkill` removes this path with `rm(recursive)`.
|
|
16
|
+
* Callers that need the file itself must `join(skillPath, "SKILL.md")`
|
|
17
|
+
* (misreading this as the file path broke the desktop "Edit" action,
|
|
18
|
+
* bug 3466438649392128).
|
|
19
|
+
*/
|
|
9
20
|
skillPath: string;
|
|
10
21
|
allowedTools?: string[];
|
|
11
22
|
context?: "fork";
|
|
@@ -37,6 +37,23 @@ export declare const DANGEROUS_COMMANDS: string[];
|
|
|
37
37
|
* FR-019.2 through FR-019.7 in tool-permission-system.md.
|
|
38
38
|
*/
|
|
39
39
|
export declare const READ_ONLY_COMMANDS: string[];
|
|
40
|
+
/**
|
|
41
|
+
* Subset of READ_ONLY_COMMANDS whose arguments are file paths (as opposed to
|
|
42
|
+
* strings, names, or flags, e.g. echo, pwd, which, basename). These commands
|
|
43
|
+
* must stay within the Safe Zone: a read-only command accessing a path outside
|
|
44
|
+
* the working directory / additional directories requires confirmation.
|
|
45
|
+
* Aligned with Claude Code's PathCommand list.
|
|
46
|
+
*/
|
|
47
|
+
export declare const PATH_COMMANDS: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Extract file path arguments from a command's argument string.
|
|
50
|
+
* Tokens starting with "-" are treated as flags and skipped; everything after
|
|
51
|
+
* a "--" separator counts as a path. Quoted path arguments are unquoted.
|
|
52
|
+
* Non-path tokens (e.g. a grep pattern) are harmless: they resolve against the
|
|
53
|
+
* working directory and typically do not exist, so isPathInside falls back to
|
|
54
|
+
* the nearest existing parent (the workdir itself) and stays inside.
|
|
55
|
+
*/
|
|
56
|
+
export declare function extractPathArgs(args: string): string[];
|
|
40
57
|
/**
|
|
41
58
|
* Registry of commands and their expected subcommand depth for smart prefix extraction.
|
|
42
59
|
* For example, 'git: 2' means 'git commit' is a valid prefix, but 'git' alone is not.
|
package/dist/utils/bashParser.js
CHANGED
|
@@ -500,6 +500,78 @@ export const READ_ONLY_COMMANDS = [
|
|
|
500
500
|
"bc",
|
|
501
501
|
"sleep",
|
|
502
502
|
];
|
|
503
|
+
/**
|
|
504
|
+
* Subset of READ_ONLY_COMMANDS whose arguments are file paths (as opposed to
|
|
505
|
+
* strings, names, or flags, e.g. echo, pwd, which, basename). These commands
|
|
506
|
+
* must stay within the Safe Zone: a read-only command accessing a path outside
|
|
507
|
+
* the working directory / additional directories requires confirmation.
|
|
508
|
+
* Aligned with Claude Code's PathCommand list.
|
|
509
|
+
*/
|
|
510
|
+
export const PATH_COMMANDS = [
|
|
511
|
+
"ls",
|
|
512
|
+
"find",
|
|
513
|
+
"cat",
|
|
514
|
+
"head",
|
|
515
|
+
"tail",
|
|
516
|
+
"wc",
|
|
517
|
+
"sort",
|
|
518
|
+
"uniq",
|
|
519
|
+
"grep",
|
|
520
|
+
"egrep",
|
|
521
|
+
"fgrep",
|
|
522
|
+
"rg",
|
|
523
|
+
"file",
|
|
524
|
+
"stat",
|
|
525
|
+
"du",
|
|
526
|
+
"df",
|
|
527
|
+
"tree",
|
|
528
|
+
"diff",
|
|
529
|
+
"cmp",
|
|
530
|
+
"md5sum",
|
|
531
|
+
"sha256sum",
|
|
532
|
+
"sha1sum",
|
|
533
|
+
"xxd",
|
|
534
|
+
"od",
|
|
535
|
+
"hexdump",
|
|
536
|
+
"strings",
|
|
537
|
+
"readlink",
|
|
538
|
+
"realpath",
|
|
539
|
+
"jq",
|
|
540
|
+
"yq",
|
|
541
|
+
"cut",
|
|
542
|
+
"paste",
|
|
543
|
+
"column",
|
|
544
|
+
"tr",
|
|
545
|
+
"awk",
|
|
546
|
+
"sed",
|
|
547
|
+
];
|
|
548
|
+
/**
|
|
549
|
+
* Extract file path arguments from a command's argument string.
|
|
550
|
+
* Tokens starting with "-" are treated as flags and skipped; everything after
|
|
551
|
+
* a "--" separator counts as a path. Quoted path arguments are unquoted.
|
|
552
|
+
* Non-path tokens (e.g. a grep pattern) are harmless: they resolve against the
|
|
553
|
+
* working directory and typically do not exist, so isPathInside falls back to
|
|
554
|
+
* the nearest existing parent (the workdir itself) and stays inside.
|
|
555
|
+
*/
|
|
556
|
+
export function extractPathArgs(args) {
|
|
557
|
+
const tokens = args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [];
|
|
558
|
+
const pathArgs = [];
|
|
559
|
+
let afterDoubleDash = false;
|
|
560
|
+
for (const token of tokens) {
|
|
561
|
+
if (afterDoubleDash) {
|
|
562
|
+
pathArgs.push(token);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (token === "--") {
|
|
566
|
+
afterDoubleDash = true;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (!token.startsWith("-")) {
|
|
570
|
+
pathArgs.push(token);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return pathArgs.map((p) => p.replace(/^['"](.*)['"]$/, "$1"));
|
|
574
|
+
}
|
|
503
575
|
/**
|
|
504
576
|
* Global scope flags for git that only change the target repository or
|
|
505
577
|
* configuration, not the subcommand being run. Shared by TOOL_RULES (smart
|