wave-code 0.10.4 → 0.11.1
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/acp/agent.d.ts +1 -0
- package/dist/acp/agent.d.ts.map +1 -1
- package/dist/acp/agent.js +122 -18
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +4 -4
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/components/BackgroundTaskManager.js +3 -2
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +2 -1
- package/dist/components/ConfirmationDetails.d.ts.map +1 -1
- package/dist/components/ConfirmationDetails.js +2 -2
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +20 -4
- package/dist/components/InputBox.d.ts +1 -1
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +1 -2
- package/dist/components/MessageList.d.ts +2 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +18 -17
- package/dist/constants/commands.d.ts.map +1 -1
- package/dist/constants/commands.js +0 -6
- package/dist/contexts/useChat.d.ts +3 -2
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +32 -28
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -1
- package/dist/managers/inputHandlers.d.ts.map +1 -1
- package/dist/managers/inputHandlers.js +7 -8
- package/dist/managers/inputReducer.d.ts +2 -3
- package/dist/managers/inputReducer.d.ts.map +1 -1
- package/dist/managers/inputReducer.js +4 -4
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +5 -3
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/acp/agent.ts +147 -21
- package/src/cli.tsx +4 -0
- package/src/components/App.tsx +8 -0
- package/src/components/BackgroundTaskManager.tsx +17 -1
- package/src/components/ChatInterface.tsx +4 -1
- package/src/components/ConfirmationDetails.tsx +5 -1
- package/src/components/ConfirmationSelector.tsx +18 -4
- package/src/components/InputBox.tsx +1 -2
- package/src/components/MessageList.tsx +21 -18
- package/src/constants/commands.ts +0 -6
- package/src/contexts/useChat.tsx +49 -35
- package/src/hooks/useInputManager.ts +4 -1
- package/src/index.ts +43 -1
- package/src/managers/inputHandlers.ts +8 -10
- package/src/managers/inputReducer.ts +6 -6
- package/src/print-cli.ts +6 -2
- package/src/types.ts +2 -0
package/src/acp/agent.ts
CHANGED
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
listAllSessions as listAllWaveSessions,
|
|
10
10
|
deleteSession as deleteWaveSession,
|
|
11
11
|
truncateContent,
|
|
12
|
+
BASH_TOOL_NAME,
|
|
13
|
+
EDIT_TOOL_NAME,
|
|
14
|
+
WRITE_TOOL_NAME,
|
|
15
|
+
EXIT_PLAN_MODE_TOOL_NAME,
|
|
12
16
|
} from "wave-agent-sdk";
|
|
13
17
|
import * as fs from "node:fs/promises";
|
|
14
18
|
import * as path from "node:path";
|
|
@@ -39,6 +43,10 @@ import {
|
|
|
39
43
|
type SetSessionModeRequest,
|
|
40
44
|
type SetSessionConfigOptionRequest,
|
|
41
45
|
type SetSessionConfigOptionResponse,
|
|
46
|
+
type TextContent,
|
|
47
|
+
type ResourceLink,
|
|
48
|
+
type EmbeddedResource,
|
|
49
|
+
type ImageContent,
|
|
42
50
|
AGENT_METHODS,
|
|
43
51
|
} from "@agentclientprotocol/sdk";
|
|
44
52
|
|
|
@@ -74,6 +82,12 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
74
82
|
name: "Bypass Permissions",
|
|
75
83
|
description: "Automatically accept all tool calls",
|
|
76
84
|
},
|
|
85
|
+
{
|
|
86
|
+
id: "dontAsk",
|
|
87
|
+
name: "Don't Ask",
|
|
88
|
+
description:
|
|
89
|
+
"Automatically deny restricted tools unless pre-approved",
|
|
90
|
+
},
|
|
77
91
|
],
|
|
78
92
|
};
|
|
79
93
|
}
|
|
@@ -91,6 +105,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
91
105
|
{ value: "acceptEdits", name: "Accept Edits" },
|
|
92
106
|
{ value: "plan", name: "Plan" },
|
|
93
107
|
{ value: "bypassPermissions", name: "Bypass Permissions" },
|
|
108
|
+
{ value: "dontAsk", name: "Don't Ask" },
|
|
94
109
|
],
|
|
95
110
|
},
|
|
96
111
|
];
|
|
@@ -121,6 +136,10 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
121
136
|
list: {},
|
|
122
137
|
close: {},
|
|
123
138
|
},
|
|
139
|
+
promptCapabilities: {
|
|
140
|
+
image: true,
|
|
141
|
+
embeddedContext: true,
|
|
142
|
+
},
|
|
124
143
|
},
|
|
125
144
|
};
|
|
126
145
|
}
|
|
@@ -139,7 +158,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
139
158
|
const agent = await WaveAgent.create({
|
|
140
159
|
workdir: cwd,
|
|
141
160
|
restoreSessionId: sessionId,
|
|
142
|
-
stream:
|
|
161
|
+
stream: true,
|
|
143
162
|
canUseTool: (context) => {
|
|
144
163
|
if (!agentRef.instance) {
|
|
145
164
|
throw new Error("Agent instance not yet initialized");
|
|
@@ -274,7 +293,12 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
274
293
|
const agent = this.agents.get(sessionId);
|
|
275
294
|
if (!agent) throw new Error(`Session ${sessionId} not found`);
|
|
276
295
|
agent.setPermissionMode(
|
|
277
|
-
modeId as
|
|
296
|
+
modeId as
|
|
297
|
+
| "default"
|
|
298
|
+
| "acceptEdits"
|
|
299
|
+
| "plan"
|
|
300
|
+
| "bypassPermissions"
|
|
301
|
+
| "dontAsk",
|
|
278
302
|
);
|
|
279
303
|
}
|
|
280
304
|
|
|
@@ -287,7 +311,12 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
287
311
|
|
|
288
312
|
if (configId === "permission_mode") {
|
|
289
313
|
agent.setPermissionMode(
|
|
290
|
-
value as
|
|
314
|
+
value as
|
|
315
|
+
| "default"
|
|
316
|
+
| "acceptEdits"
|
|
317
|
+
| "plan"
|
|
318
|
+
| "bypassPermissions"
|
|
319
|
+
| "dontAsk",
|
|
291
320
|
);
|
|
292
321
|
}
|
|
293
322
|
|
|
@@ -299,6 +328,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
299
328
|
async prompt(params: PromptRequest): Promise<PromptResponse> {
|
|
300
329
|
const { sessionId, prompt } = params;
|
|
301
330
|
logger.info(`Received prompt for session ${sessionId}`);
|
|
331
|
+
logger.debug(`Prompt content for session ${sessionId}:`, prompt);
|
|
302
332
|
const agent = this.agents.get(sessionId);
|
|
303
333
|
if (!agent) {
|
|
304
334
|
logger.error(`Session ${sessionId} not found`);
|
|
@@ -306,25 +336,32 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
306
336
|
}
|
|
307
337
|
|
|
308
338
|
// Map ACP prompt to Wave Agent sendMessage
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
339
|
+
const textBlocks: string[] = [];
|
|
340
|
+
const images: { path: string; mimeType: string }[] = [];
|
|
341
|
+
|
|
342
|
+
for (const block of prompt) {
|
|
343
|
+
if (block.type === "text") {
|
|
344
|
+
textBlocks.push((block as TextContent).text);
|
|
345
|
+
} else if (block.type === "resource_link") {
|
|
346
|
+
const link = block as ResourceLink;
|
|
347
|
+
textBlocks.push(`[${link.name}](${link.uri})`);
|
|
348
|
+
} else if (block.type === "resource") {
|
|
349
|
+
const embedded = block as EmbeddedResource;
|
|
350
|
+
textBlocks.push(`[Resource](${embedded.resource.uri})`);
|
|
351
|
+
} else if (block.type === "image") {
|
|
352
|
+
const img = block as ImageContent;
|
|
353
|
+
images.push({
|
|
354
|
+
path: img.data.startsWith("data:")
|
|
355
|
+
? img.data
|
|
356
|
+
: `data:${img.mimeType};base64,${img.data}`,
|
|
320
357
|
mimeType: img.mimeType,
|
|
321
|
-
};
|
|
322
|
-
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const textContent = textBlocks.join("");
|
|
323
363
|
|
|
324
364
|
try {
|
|
325
|
-
logger.info(
|
|
326
|
-
`Sending message to agent: ${textContent.substring(0, 50)}...`,
|
|
327
|
-
);
|
|
328
365
|
await agent.sendMessage(
|
|
329
366
|
textContent,
|
|
330
367
|
images.length > 0 ? images : undefined,
|
|
@@ -354,6 +391,33 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
354
391
|
}
|
|
355
392
|
}
|
|
356
393
|
|
|
394
|
+
private getAllowAlwaysName(context: ToolPermissionContext): string {
|
|
395
|
+
if (context.toolName === BASH_TOOL_NAME) {
|
|
396
|
+
const command = (context.toolInput?.command as string) || "";
|
|
397
|
+
if (command.startsWith("mkdir")) {
|
|
398
|
+
return "Yes, and auto-accept edits";
|
|
399
|
+
}
|
|
400
|
+
if (context.suggestedPrefix) {
|
|
401
|
+
const prefix =
|
|
402
|
+
context.suggestedPrefix.length > 12
|
|
403
|
+
? context.suggestedPrefix.substring(0, 9) + "..."
|
|
404
|
+
: context.suggestedPrefix;
|
|
405
|
+
return `Yes, always allow ${prefix}`;
|
|
406
|
+
}
|
|
407
|
+
return "Yes, always allow this command";
|
|
408
|
+
}
|
|
409
|
+
if (
|
|
410
|
+
context.toolName === EDIT_TOOL_NAME ||
|
|
411
|
+
context.toolName === WRITE_TOOL_NAME
|
|
412
|
+
) {
|
|
413
|
+
return "Yes, and auto-accept edits";
|
|
414
|
+
}
|
|
415
|
+
if (context.toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
416
|
+
return "Yes, auto-accept edits";
|
|
417
|
+
}
|
|
418
|
+
return "Allow Always";
|
|
419
|
+
}
|
|
420
|
+
|
|
357
421
|
private async handlePermissionRequest(
|
|
358
422
|
sessionId: string,
|
|
359
423
|
context: ToolPermissionContext,
|
|
@@ -390,7 +454,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
390
454
|
? `${effectiveName}: ${effectiveCompactParams}`
|
|
391
455
|
: effectiveName || "Tool Call";
|
|
392
456
|
|
|
393
|
-
|
|
457
|
+
let options: PermissionOption[] = [
|
|
394
458
|
{
|
|
395
459
|
optionId: "allow_once",
|
|
396
460
|
name: "Allow Once",
|
|
@@ -408,6 +472,38 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
408
472
|
},
|
|
409
473
|
];
|
|
410
474
|
|
|
475
|
+
if (
|
|
476
|
+
context.toolName === BASH_TOOL_NAME ||
|
|
477
|
+
context.toolName === EDIT_TOOL_NAME ||
|
|
478
|
+
context.toolName === WRITE_TOOL_NAME
|
|
479
|
+
) {
|
|
480
|
+
options = [
|
|
481
|
+
{
|
|
482
|
+
optionId: "allow_once",
|
|
483
|
+
name: "Yes, proceed",
|
|
484
|
+
kind: "allow_once",
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
optionId: "allow_always",
|
|
488
|
+
name: this.getAllowAlwaysName(context),
|
|
489
|
+
kind: "allow_always",
|
|
490
|
+
},
|
|
491
|
+
];
|
|
492
|
+
} else if (context.toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
493
|
+
options = [
|
|
494
|
+
{
|
|
495
|
+
optionId: "allow_once",
|
|
496
|
+
name: "Yes, manually approve edits",
|
|
497
|
+
kind: "allow_once",
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
optionId: "allow_always",
|
|
501
|
+
name: "Yes, auto-accept edits",
|
|
502
|
+
kind: "allow_always",
|
|
503
|
+
},
|
|
504
|
+
];
|
|
505
|
+
}
|
|
506
|
+
|
|
411
507
|
const content = context.toolName
|
|
412
508
|
? await this.getToolContentAsync(
|
|
413
509
|
context.toolName,
|
|
@@ -446,11 +542,33 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
446
542
|
|
|
447
543
|
switch (selectedOptionId) {
|
|
448
544
|
case "allow_always":
|
|
545
|
+
if (context.toolName === BASH_TOOL_NAME) {
|
|
546
|
+
const command = (context.toolInput?.command as string) || "";
|
|
547
|
+
const rule = context.suggestedPrefix
|
|
548
|
+
? `${context.suggestedPrefix}*`
|
|
549
|
+
: command;
|
|
550
|
+
return {
|
|
551
|
+
behavior: "allow",
|
|
552
|
+
newPermissionRule: `${BASH_TOOL_NAME}(${rule})`,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
if (
|
|
556
|
+
context.toolName === EDIT_TOOL_NAME ||
|
|
557
|
+
context.toolName === WRITE_TOOL_NAME
|
|
558
|
+
) {
|
|
559
|
+
return {
|
|
560
|
+
behavior: "allow",
|
|
561
|
+
newPermissionMode: "acceptEdits",
|
|
562
|
+
};
|
|
563
|
+
}
|
|
449
564
|
return {
|
|
450
565
|
behavior: "allow",
|
|
451
|
-
newPermissionRule:
|
|
566
|
+
newPermissionRule: context.toolName,
|
|
452
567
|
};
|
|
453
568
|
case "allow_once":
|
|
569
|
+
if (context.toolName === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
570
|
+
return { behavior: "allow", newPermissionMode: "default" };
|
|
571
|
+
}
|
|
454
572
|
return { behavior: "allow" };
|
|
455
573
|
case "reject_once":
|
|
456
574
|
return { behavior: "deny", message: "Rejected by user" };
|
|
@@ -564,6 +682,14 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
564
682
|
oldText: parameters.old_string as string,
|
|
565
683
|
newText: parameters.new_string as string,
|
|
566
684
|
});
|
|
685
|
+
} else if (name === EXIT_PLAN_MODE_TOOL_NAME) {
|
|
686
|
+
contents.push({
|
|
687
|
+
type: "content",
|
|
688
|
+
content: {
|
|
689
|
+
type: "text",
|
|
690
|
+
text: parameters.plan_content as string,
|
|
691
|
+
},
|
|
692
|
+
});
|
|
567
693
|
}
|
|
568
694
|
}
|
|
569
695
|
|
package/src/cli.tsx
CHANGED
|
@@ -18,6 +18,8 @@ export async function startCli(options: CliOptions): Promise<void> {
|
|
|
18
18
|
permissionMode,
|
|
19
19
|
pluginDirs,
|
|
20
20
|
tools,
|
|
21
|
+
allowedTools,
|
|
22
|
+
disallowedTools,
|
|
21
23
|
worktreeSession,
|
|
22
24
|
workdir,
|
|
23
25
|
version,
|
|
@@ -41,6 +43,8 @@ export async function startCli(options: CliOptions): Promise<void> {
|
|
|
41
43
|
permissionMode={permissionMode}
|
|
42
44
|
pluginDirs={pluginDirs}
|
|
43
45
|
tools={tools}
|
|
46
|
+
allowedTools={allowedTools}
|
|
47
|
+
disallowedTools={disallowedTools}
|
|
44
48
|
worktreeSession={worktreeSession}
|
|
45
49
|
workdir={workdir}
|
|
46
50
|
version={version}
|
package/src/components/App.tsx
CHANGED
|
@@ -26,6 +26,8 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
|
26
26
|
permissionMode,
|
|
27
27
|
pluginDirs,
|
|
28
28
|
tools,
|
|
29
|
+
allowedTools,
|
|
30
|
+
disallowedTools,
|
|
29
31
|
worktreeSession,
|
|
30
32
|
workdir,
|
|
31
33
|
version,
|
|
@@ -98,6 +100,8 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
|
98
100
|
permissionMode={permissionMode}
|
|
99
101
|
pluginDirs={pluginDirs}
|
|
100
102
|
tools={tools}
|
|
103
|
+
allowedTools={allowedTools}
|
|
104
|
+
disallowedTools={disallowedTools}
|
|
101
105
|
workdir={workdir}
|
|
102
106
|
worktreeSession={worktreeSession}
|
|
103
107
|
version={version}
|
|
@@ -162,6 +166,8 @@ export const App: React.FC<AppProps> = ({
|
|
|
162
166
|
permissionMode,
|
|
163
167
|
pluginDirs,
|
|
164
168
|
tools,
|
|
169
|
+
allowedTools,
|
|
170
|
+
disallowedTools,
|
|
165
171
|
worktreeSession,
|
|
166
172
|
workdir,
|
|
167
173
|
version,
|
|
@@ -178,6 +184,8 @@ export const App: React.FC<AppProps> = ({
|
|
|
178
184
|
permissionMode={permissionMode}
|
|
179
185
|
pluginDirs={pluginDirs}
|
|
180
186
|
tools={tools}
|
|
187
|
+
allowedTools={allowedTools}
|
|
188
|
+
disallowedTools={disallowedTools}
|
|
181
189
|
worktreeSession={worktreeSession}
|
|
182
190
|
workdir={workdir}
|
|
183
191
|
version={version}
|
|
@@ -10,6 +10,7 @@ interface Task {
|
|
|
10
10
|
startTime: number;
|
|
11
11
|
exitCode?: number;
|
|
12
12
|
runtime?: number;
|
|
13
|
+
outputPath?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface BackgroundTaskManagerProps {
|
|
@@ -30,6 +31,7 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
|
|
|
30
31
|
stdout: string;
|
|
31
32
|
stderr: string;
|
|
32
33
|
status: string;
|
|
34
|
+
outputPath?: string;
|
|
33
35
|
} | null>(null);
|
|
34
36
|
|
|
35
37
|
// Convert backgroundTasks to local Task format
|
|
@@ -43,6 +45,7 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
|
|
|
43
45
|
startTime: task.startTime,
|
|
44
46
|
exitCode: task.exitCode,
|
|
45
47
|
runtime: task.runtime,
|
|
48
|
+
outputPath: task.outputPath,
|
|
46
49
|
})),
|
|
47
50
|
);
|
|
48
51
|
}, [backgroundTasks]);
|
|
@@ -189,6 +192,13 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
|
|
|
189
192
|
)}
|
|
190
193
|
</Text>
|
|
191
194
|
</Box>
|
|
195
|
+
{task.outputPath && (
|
|
196
|
+
<Box>
|
|
197
|
+
<Text>
|
|
198
|
+
<Text color="blue">Log File:</Text> {task.outputPath}
|
|
199
|
+
</Text>
|
|
200
|
+
</Box>
|
|
201
|
+
)}
|
|
192
202
|
</Box>
|
|
193
203
|
|
|
194
204
|
{detailOutput.stdout && (
|
|
@@ -313,7 +323,13 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
|
|
|
313
323
|
{isSelected && (
|
|
314
324
|
<Box marginLeft={4} flexDirection="column">
|
|
315
325
|
<Text color="gray" dimColor>
|
|
316
|
-
|
|
326
|
+
{task.outputPath ? (
|
|
327
|
+
<Text>
|
|
328
|
+
<Text color="blue">Log File:</Text> {task.outputPath}
|
|
329
|
+
</Text>
|
|
330
|
+
) : (
|
|
331
|
+
`Started: ${formatTime(task.startTime)}`
|
|
332
|
+
)}
|
|
317
333
|
{task.runtime !== undefined &&
|
|
318
334
|
` | Runtime: ${formatDuration(task.runtime)}`}
|
|
319
335
|
{task.exitCode !== undefined && ` | Exit: ${task.exitCode}`}
|
|
@@ -45,6 +45,8 @@ export const ChatInterface: React.FC = () => {
|
|
|
45
45
|
|
|
46
46
|
const model = getModelConfig().model;
|
|
47
47
|
|
|
48
|
+
const displayMessages = messages;
|
|
49
|
+
|
|
48
50
|
const handleDetailsHeightMeasured = useCallback((height: number) => {
|
|
49
51
|
setDetailsHeight(height);
|
|
50
52
|
}, []);
|
|
@@ -116,9 +118,10 @@ export const ChatInterface: React.FC = () => {
|
|
|
116
118
|
return (
|
|
117
119
|
<Box flexDirection="column">
|
|
118
120
|
<MessageList
|
|
119
|
-
messages={
|
|
121
|
+
messages={displayMessages}
|
|
120
122
|
isExpanded={isExpanded}
|
|
121
123
|
forceStatic={isConfirmationVisible && isConfirmationTooTall}
|
|
124
|
+
isFinished={!isLoading && !isCommandRunning && !isCompressing}
|
|
122
125
|
version={version}
|
|
123
126
|
workdir={workdir}
|
|
124
127
|
model={model}
|
|
@@ -97,7 +97,11 @@ export const ConfirmationDetails: React.FC<ConfirmationDetailsProps> = ({
|
|
|
97
97
|
);
|
|
98
98
|
|
|
99
99
|
if (isStatic) {
|
|
100
|
-
return
|
|
100
|
+
return (
|
|
101
|
+
<Static items={[1]}>
|
|
102
|
+
{(item) => <React.Fragment key={item}>{content}</React.Fragment>}
|
|
103
|
+
</Static>
|
|
104
|
+
);
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
return content;
|
|
@@ -100,11 +100,18 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
100
100
|
return "Yes, auto-accept edits";
|
|
101
101
|
}
|
|
102
102
|
if (toolName === BASH_TOOL_NAME) {
|
|
103
|
+
const command = (toolInput?.command as string) || "";
|
|
104
|
+
if (command.trim().startsWith("mkdir")) {
|
|
105
|
+
return "Yes, and auto-accept edits";
|
|
106
|
+
}
|
|
103
107
|
if (suggestedPrefix) {
|
|
104
108
|
return `Yes, and don't ask again for: ${suggestedPrefix}`;
|
|
105
109
|
}
|
|
106
110
|
return "Yes, and don't ask again for this command in this workdir";
|
|
107
111
|
}
|
|
112
|
+
if (toolName.startsWith("mcp__")) {
|
|
113
|
+
return `Yes, and don't ask again for: ${toolName}`;
|
|
114
|
+
}
|
|
108
115
|
return "Yes, and auto-accept edits";
|
|
109
116
|
};
|
|
110
117
|
|
|
@@ -363,10 +370,17 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
|
|
|
363
370
|
}
|
|
364
371
|
} else if (state.selectedOption === "auto") {
|
|
365
372
|
if (toolName === BASH_TOOL_NAME) {
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
:
|
|
369
|
-
|
|
373
|
+
const command = (toolInput?.command as string) || "";
|
|
374
|
+
if (command.trim().startsWith("mkdir")) {
|
|
375
|
+
onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
|
|
376
|
+
} else {
|
|
377
|
+
const rule = suggestedPrefix
|
|
378
|
+
? `Bash(${suggestedPrefix}*)`
|
|
379
|
+
: `Bash(${toolInput?.command})`;
|
|
380
|
+
onDecision({ behavior: "allow", newPermissionRule: rule });
|
|
381
|
+
}
|
|
382
|
+
} else if (toolName.startsWith("mcp__")) {
|
|
383
|
+
onDecision({ behavior: "allow", newPermissionRule: toolName });
|
|
370
384
|
} else {
|
|
371
385
|
onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
|
|
372
386
|
}
|
|
@@ -30,6 +30,7 @@ export interface InputBoxProps {
|
|
|
30
30
|
sendMessage?: (
|
|
31
31
|
message: string,
|
|
32
32
|
images?: Array<{ path: string; mimeType: string }>,
|
|
33
|
+
longTextMap?: Record<string, string>,
|
|
33
34
|
) => void;
|
|
34
35
|
abortMessage?: () => void;
|
|
35
36
|
// MCP related properties
|
|
@@ -57,7 +58,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
57
58
|
backgroundCurrentTask,
|
|
58
59
|
messages,
|
|
59
60
|
getFullMessageThread,
|
|
60
|
-
clearMessages,
|
|
61
61
|
sessionId,
|
|
62
62
|
workingDirectory,
|
|
63
63
|
} = useChat();
|
|
@@ -113,7 +113,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
113
113
|
onAbortMessage: abortMessage,
|
|
114
114
|
onBackgroundCurrentTask: backgroundCurrentTask,
|
|
115
115
|
onPermissionModeChange: setChatPermissionMode,
|
|
116
|
-
onClearMessages: clearMessages,
|
|
117
116
|
sessionId,
|
|
118
117
|
workdir: workingDirectory,
|
|
119
118
|
getFullMessageThread,
|
|
@@ -8,6 +8,7 @@ export interface MessageListProps {
|
|
|
8
8
|
messages: Message[];
|
|
9
9
|
isExpanded?: boolean;
|
|
10
10
|
forceStatic?: boolean;
|
|
11
|
+
isFinished?: boolean;
|
|
11
12
|
version?: string;
|
|
12
13
|
workdir?: string;
|
|
13
14
|
model?: string;
|
|
@@ -19,6 +20,7 @@ export const MessageList = React.memo(
|
|
|
19
20
|
messages,
|
|
20
21
|
isExpanded = false,
|
|
21
22
|
forceStatic = false,
|
|
23
|
+
isFinished = false,
|
|
22
24
|
version,
|
|
23
25
|
workdir,
|
|
24
26
|
model,
|
|
@@ -38,22 +40,15 @@ export const MessageList = React.memo(
|
|
|
38
40
|
</Box>
|
|
39
41
|
);
|
|
40
42
|
|
|
41
|
-
// Limit messages
|
|
42
|
-
const
|
|
43
|
-
const shouldLimitMessages =
|
|
44
|
-
isExpanded && messages.length > maxExpandedMessages;
|
|
45
|
-
const displayMessages = shouldLimitMessages
|
|
46
|
-
? messages.slice(-maxExpandedMessages)
|
|
47
|
-
: messages;
|
|
43
|
+
// Limit messages to prevent long rendering times
|
|
44
|
+
const maxMessages = 10;
|
|
48
45
|
|
|
49
46
|
// Flatten messages into blocks with metadata
|
|
50
|
-
const allBlocks =
|
|
51
|
-
const messageIndex = shouldLimitMessages
|
|
52
|
-
? messages.length - maxExpandedMessages + index
|
|
53
|
-
: index;
|
|
47
|
+
const allBlocks = messages.flatMap((message, messageIndex) => {
|
|
54
48
|
return message.blocks.map((block, blockIndex) => ({
|
|
55
49
|
block,
|
|
56
50
|
message,
|
|
51
|
+
messageIndex,
|
|
57
52
|
isLastMessage: messageIndex === messages.length - 1,
|
|
58
53
|
// Unique key for each block to help Static component
|
|
59
54
|
key: `${message.id}-${blockIndex}`,
|
|
@@ -62,12 +57,8 @@ export const MessageList = React.memo(
|
|
|
62
57
|
|
|
63
58
|
// Determine which blocks are static vs dynamic
|
|
64
59
|
const blocksWithStatus = allBlocks.map((item) => {
|
|
65
|
-
const {
|
|
66
|
-
const isDynamic =
|
|
67
|
-
!forceStatic &&
|
|
68
|
-
isLastMessage &&
|
|
69
|
-
((block.type === "tool" && block.stage !== "end") ||
|
|
70
|
-
(block.type === "bang" && block.isRunning));
|
|
60
|
+
const { isLastMessage } = item;
|
|
61
|
+
const isDynamic = !forceStatic && !isFinished && isLastMessage;
|
|
71
62
|
return { ...item, isDynamic };
|
|
72
63
|
});
|
|
73
64
|
|
|
@@ -86,7 +77,13 @@ export const MessageList = React.memo(
|
|
|
86
77
|
}, [dynamicBlocks, isExpanded, onDynamicBlocksHeightMeasured]);
|
|
87
78
|
|
|
88
79
|
const staticItems = [
|
|
89
|
-
{
|
|
80
|
+
{
|
|
81
|
+
isWelcome: true,
|
|
82
|
+
key: "welcome",
|
|
83
|
+
block: undefined,
|
|
84
|
+
message: undefined,
|
|
85
|
+
messageIndex: -1,
|
|
86
|
+
},
|
|
90
87
|
...staticBlocks.map((b) => ({ ...b, isWelcome: false })),
|
|
91
88
|
];
|
|
92
89
|
|
|
@@ -103,6 +100,12 @@ export const MessageList = React.memo(
|
|
|
103
100
|
</React.Fragment>
|
|
104
101
|
);
|
|
105
102
|
}
|
|
103
|
+
if (
|
|
104
|
+
messages.length > maxMessages &&
|
|
105
|
+
item.messageIndex < messages.length - maxMessages
|
|
106
|
+
) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
106
109
|
return (
|
|
107
110
|
<MessageBlockItem
|
|
108
111
|
key={item.key}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import type { SlashCommand } from "wave-agent-sdk";
|
|
2
2
|
|
|
3
3
|
export const AVAILABLE_COMMANDS: SlashCommand[] = [
|
|
4
|
-
{
|
|
5
|
-
id: "clear",
|
|
6
|
-
name: "clear",
|
|
7
|
-
description: "Clear the chat session and terminal",
|
|
8
|
-
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
9
|
-
},
|
|
10
4
|
{
|
|
11
5
|
id: "tasks",
|
|
12
6
|
name: "tasks",
|