wave-code 0.10.1 → 0.10.2
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.map +1 -1
- package/dist/acp/agent.js +125 -82
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +23 -2
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +15 -1
- package/dist/utils/highlightUtils.d.ts.map +1 -1
- package/dist/utils/highlightUtils.js +8 -0
- package/dist/utils/toolParameterTransforms.d.ts +1 -5
- package/dist/utils/toolParameterTransforms.d.ts.map +1 -1
- package/dist/utils/toolParameterTransforms.js +0 -14
- package/dist/utils/worktree.js +2 -2
- package/package.json +2 -2
- package/src/acp/agent.ts +170 -81
- package/src/components/DiffDisplay.tsx +27 -2
- package/src/contexts/useChat.tsx +20 -1
- package/src/utils/highlightUtils.ts +8 -0
- package/src/utils/toolParameterTransforms.ts +1 -23
- package/src/utils/worktree.ts +2 -2
package/dist/acp/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,KAAK,KAAK,IAAI,QAAQ,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAUzB,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EAEpC,MAAM,0BAA0B,CAAC;AAElC,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,mBAAmB;IAI3C,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,uBAAuB;YAkBjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAoBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IAiEnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,qBAAqB,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,SAAS,CACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAO7B,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAgBpC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAiDtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;YASzC,uBAAuB;YAgHvB,mBAAmB;IA6EjC,OAAO,CAAC,cAAc;IAqCtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,eAAe;CAgMxB"}
|
package/dist/acp/agent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent as WaveAgent, listSessions as listWaveSessions, deleteSession as deleteWaveSession, } from "wave-agent-sdk";
|
|
1
|
+
import { Agent as WaveAgent, listSessions as listWaveSessions, listAllSessions as listAllWaveSessions, deleteSession as deleteWaveSession, truncateContent, } from "wave-agent-sdk";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { logger } from "../utils/logger.js";
|
|
@@ -86,6 +86,7 @@ export class WaveAcpAgent {
|
|
|
86
86
|
const agent = await WaveAgent.create({
|
|
87
87
|
workdir: cwd,
|
|
88
88
|
restoreSessionId: sessionId,
|
|
89
|
+
stream: false,
|
|
89
90
|
canUseTool: (context) => {
|
|
90
91
|
if (!agentRef.instance) {
|
|
91
92
|
throw new Error("Agent instance not yet initialized");
|
|
@@ -108,17 +109,11 @@ export class WaveAcpAgent {
|
|
|
108
109
|
this.agents.set(actualSessionId, agent);
|
|
109
110
|
// Update the callbacks object with the correct sessionId
|
|
110
111
|
Object.assign(callbacks, this.createCallbacks(actualSessionId));
|
|
111
|
-
return agent;
|
|
112
|
-
}
|
|
113
|
-
async newSession(params) {
|
|
114
|
-
const { cwd } = params;
|
|
115
|
-
logger.info(`Creating new session in ${cwd}`);
|
|
116
|
-
const agent = await this.createAgent(undefined, cwd);
|
|
117
|
-
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
118
112
|
// Send initial available commands after agent creation
|
|
113
|
+
// Use setImmediate to ensure the client receives the session response before the update
|
|
119
114
|
setImmediate(() => {
|
|
120
115
|
this.connection.sessionUpdate({
|
|
121
|
-
sessionId:
|
|
116
|
+
sessionId: actualSessionId,
|
|
122
117
|
update: {
|
|
123
118
|
sessionUpdate: "available_commands_update",
|
|
124
119
|
availableCommands: agent.getSlashCommands().map((cmd) => ({
|
|
@@ -131,6 +126,13 @@ export class WaveAcpAgent {
|
|
|
131
126
|
},
|
|
132
127
|
});
|
|
133
128
|
});
|
|
129
|
+
return agent;
|
|
130
|
+
}
|
|
131
|
+
async newSession(params) {
|
|
132
|
+
const { cwd } = params;
|
|
133
|
+
logger.info(`Creating new session in ${cwd}`);
|
|
134
|
+
const agent = await this.createAgent(undefined, cwd);
|
|
135
|
+
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
134
136
|
return {
|
|
135
137
|
sessionId: agent.sessionId,
|
|
136
138
|
modes: this.getSessionModeState(agent),
|
|
@@ -141,22 +143,6 @@ export class WaveAcpAgent {
|
|
|
141
143
|
const { sessionId, cwd } = params;
|
|
142
144
|
logger.info(`Loading session: ${sessionId} in ${cwd}`);
|
|
143
145
|
const agent = await this.createAgent(sessionId, cwd);
|
|
144
|
-
// Send initial available commands after agent creation
|
|
145
|
-
setImmediate(() => {
|
|
146
|
-
this.connection.sessionUpdate({
|
|
147
|
-
sessionId: agent.sessionId,
|
|
148
|
-
update: {
|
|
149
|
-
sessionUpdate: "available_commands_update",
|
|
150
|
-
availableCommands: agent.getSlashCommands().map((cmd) => ({
|
|
151
|
-
name: cmd.name,
|
|
152
|
-
description: cmd.description,
|
|
153
|
-
input: {
|
|
154
|
-
hint: "Enter arguments...",
|
|
155
|
-
},
|
|
156
|
-
})),
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
146
|
return {
|
|
161
147
|
modes: this.getSessionModeState(agent),
|
|
162
148
|
configOptions: this.getSessionConfigOptions(agent),
|
|
@@ -165,16 +151,20 @@ export class WaveAcpAgent {
|
|
|
165
151
|
async listSessions(params) {
|
|
166
152
|
const { cwd } = params;
|
|
167
153
|
logger.info(`listSessions called with params: ${JSON.stringify(params)}`);
|
|
154
|
+
let waveSessions;
|
|
168
155
|
if (!cwd) {
|
|
169
|
-
logger.
|
|
170
|
-
|
|
156
|
+
logger.info("listSessions called without cwd, listing all sessions");
|
|
157
|
+
waveSessions = await listAllWaveSessions();
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
logger.info(`Listing sessions for ${cwd}`);
|
|
161
|
+
waveSessions = await listWaveSessions(cwd);
|
|
171
162
|
}
|
|
172
|
-
logger.info(`
|
|
173
|
-
const waveSessions = await listWaveSessions(cwd);
|
|
174
|
-
logger.info(`Found ${waveSessions.length} sessions for ${cwd}`);
|
|
163
|
+
logger.info(`Found ${waveSessions.length} sessions`);
|
|
175
164
|
const sessions = waveSessions.map((meta) => ({
|
|
176
165
|
sessionId: meta.id,
|
|
177
166
|
cwd: meta.workdir,
|
|
167
|
+
title: meta.firstMessage ? truncateContent(meta.firstMessage) : undefined,
|
|
178
168
|
updatedAt: meta.lastActiveAt.toISOString(),
|
|
179
169
|
}));
|
|
180
170
|
return { sessions };
|
|
@@ -272,6 +262,21 @@ export class WaveAcpAgent {
|
|
|
272
262
|
const workdir = agent?.workingDirectory || process.cwd();
|
|
273
263
|
const toolCallId = context.toolCallId ||
|
|
274
264
|
"perm-" + Math.random().toString(36).substring(2, 9);
|
|
265
|
+
let effectiveName = context.toolName;
|
|
266
|
+
let effectiveCompactParams = undefined;
|
|
267
|
+
if (agent?.messages && context.toolCallId) {
|
|
268
|
+
const toolBlock = agent.messages
|
|
269
|
+
.flatMap((m) => m.blocks)
|
|
270
|
+
.find((b) => b.type === "tool" && b.id === context.toolCallId);
|
|
271
|
+
if (toolBlock) {
|
|
272
|
+
effectiveName = toolBlock.name || effectiveName;
|
|
273
|
+
effectiveCompactParams =
|
|
274
|
+
toolBlock.compactParams || effectiveCompactParams;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const displayTitle = effectiveName && effectiveCompactParams
|
|
278
|
+
? `${effectiveName}: ${effectiveCompactParams}`
|
|
279
|
+
: effectiveName || "Tool Call";
|
|
275
280
|
const options = [
|
|
276
281
|
{
|
|
277
282
|
optionId: "allow_once",
|
|
@@ -288,11 +293,6 @@ export class WaveAcpAgent {
|
|
|
288
293
|
name: "Reject Once",
|
|
289
294
|
kind: "reject_once",
|
|
290
295
|
},
|
|
291
|
-
{
|
|
292
|
-
optionId: "reject_always",
|
|
293
|
-
name: "Reject Always",
|
|
294
|
-
kind: "reject_always",
|
|
295
|
-
},
|
|
296
296
|
];
|
|
297
297
|
const content = context.toolName
|
|
298
298
|
? await this.getToolContentAsync(context.toolName, context.toolInput, workdir)
|
|
@@ -308,7 +308,7 @@ export class WaveAcpAgent {
|
|
|
308
308
|
sessionId: sessionId,
|
|
309
309
|
toolCall: {
|
|
310
310
|
toolCallId,
|
|
311
|
-
title:
|
|
311
|
+
title: displayTitle,
|
|
312
312
|
status: "pending",
|
|
313
313
|
rawInput: context.toolInput,
|
|
314
314
|
content,
|
|
@@ -323,21 +323,15 @@ export class WaveAcpAgent {
|
|
|
323
323
|
const selectedOptionId = response.outcome.optionId;
|
|
324
324
|
logger.info(`User selected permission option: ${selectedOptionId}`);
|
|
325
325
|
switch (selectedOptionId) {
|
|
326
|
-
case "allow_once":
|
|
327
|
-
return { behavior: "allow" };
|
|
328
326
|
case "allow_always":
|
|
329
327
|
return {
|
|
330
328
|
behavior: "allow",
|
|
331
329
|
newPermissionRule: `${context.toolName}(*)`,
|
|
332
330
|
};
|
|
331
|
+
case "allow_once":
|
|
332
|
+
return { behavior: "allow" };
|
|
333
333
|
case "reject_once":
|
|
334
334
|
return { behavior: "deny", message: "Rejected by user" };
|
|
335
|
-
case "reject_always":
|
|
336
|
-
return {
|
|
337
|
-
behavior: "deny",
|
|
338
|
-
message: "Rejected by user",
|
|
339
|
-
newPermissionRule: `!${context.toolName}(*)`,
|
|
340
|
-
};
|
|
341
335
|
default:
|
|
342
336
|
return { behavior: "deny", message: "Unknown option selected" };
|
|
343
337
|
}
|
|
@@ -356,7 +350,8 @@ export class WaveAcpAgent {
|
|
|
356
350
|
if (name === "Write") {
|
|
357
351
|
let oldText = null;
|
|
358
352
|
try {
|
|
359
|
-
const filePath = parameters.file_path
|
|
353
|
+
const filePath = (parameters.file_path ||
|
|
354
|
+
parameters.filePath);
|
|
360
355
|
const fullPath = path.isAbsolute(filePath)
|
|
361
356
|
? filePath
|
|
362
357
|
: path.join(workdir, filePath);
|
|
@@ -368,7 +363,7 @@ export class WaveAcpAgent {
|
|
|
368
363
|
return [
|
|
369
364
|
{
|
|
370
365
|
type: "diff",
|
|
371
|
-
path: parameters.file_path,
|
|
366
|
+
path: (parameters.file_path || parameters.filePath),
|
|
372
367
|
oldText,
|
|
373
368
|
newText: parameters.content,
|
|
374
369
|
},
|
|
@@ -378,7 +373,8 @@ export class WaveAcpAgent {
|
|
|
378
373
|
let oldText = null;
|
|
379
374
|
let newText = null;
|
|
380
375
|
try {
|
|
381
|
-
const filePath = parameters.file_path
|
|
376
|
+
const filePath = (parameters.file_path ||
|
|
377
|
+
parameters.filePath);
|
|
382
378
|
const fullPath = path.isAbsolute(filePath)
|
|
383
379
|
? filePath
|
|
384
380
|
: path.join(workdir, filePath);
|
|
@@ -401,7 +397,7 @@ export class WaveAcpAgent {
|
|
|
401
397
|
return [
|
|
402
398
|
{
|
|
403
399
|
type: "diff",
|
|
404
|
-
path: parameters.file_path,
|
|
400
|
+
path: (parameters.file_path || parameters.filePath),
|
|
405
401
|
oldText,
|
|
406
402
|
newText,
|
|
407
403
|
},
|
|
@@ -411,49 +407,68 @@ export class WaveAcpAgent {
|
|
|
411
407
|
return [
|
|
412
408
|
{
|
|
413
409
|
type: "diff",
|
|
414
|
-
path: parameters.file_path,
|
|
410
|
+
path: (parameters.file_path || parameters.filePath),
|
|
415
411
|
oldText: parameters.old_string,
|
|
416
412
|
newText: parameters.new_string,
|
|
417
413
|
},
|
|
418
414
|
];
|
|
419
415
|
}
|
|
420
|
-
return this.getToolContent(name, parameters);
|
|
416
|
+
return this.getToolContent(name, parameters, undefined);
|
|
421
417
|
}
|
|
422
|
-
getToolContent(name, parameters) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
{
|
|
418
|
+
getToolContent(name, parameters, shortResult) {
|
|
419
|
+
const contents = [];
|
|
420
|
+
if (parameters) {
|
|
421
|
+
if (name === "Write") {
|
|
422
|
+
contents.push({
|
|
428
423
|
type: "diff",
|
|
429
|
-
path: parameters.file_path,
|
|
424
|
+
path: (parameters.file_path || parameters.filePath),
|
|
430
425
|
oldText: null,
|
|
431
426
|
newText: parameters.content,
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return [
|
|
437
|
-
{
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
else if (name === "Edit") {
|
|
430
|
+
contents.push({
|
|
438
431
|
type: "diff",
|
|
439
|
-
path: parameters.file_path,
|
|
432
|
+
path: (parameters.file_path || parameters.filePath),
|
|
440
433
|
oldText: parameters.old_string,
|
|
441
434
|
newText: parameters.new_string,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (shortResult) {
|
|
439
|
+
contents.push({
|
|
440
|
+
type: "content",
|
|
441
|
+
content: {
|
|
442
|
+
type: "text",
|
|
443
|
+
text: shortResult,
|
|
442
444
|
},
|
|
443
|
-
|
|
445
|
+
});
|
|
444
446
|
}
|
|
445
|
-
return undefined;
|
|
447
|
+
return contents.length > 0 ? contents : undefined;
|
|
446
448
|
}
|
|
447
|
-
getToolLocations(name, parameters) {
|
|
449
|
+
getToolLocations(name, parameters, extraStartLineNumber) {
|
|
448
450
|
if (!parameters)
|
|
449
451
|
return undefined;
|
|
450
|
-
if (name === "Write" ||
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
452
|
+
if (name === "Write" ||
|
|
453
|
+
name === "Edit" ||
|
|
454
|
+
name === "Read" ||
|
|
455
|
+
name === "LSP") {
|
|
456
|
+
const filePath = (parameters.file_path || parameters.filePath);
|
|
457
|
+
let line = extraStartLineNumber ??
|
|
458
|
+
parameters.startLineNumber ??
|
|
459
|
+
parameters.line ??
|
|
460
|
+
parameters.offset;
|
|
461
|
+
if (name === "Write" && line === undefined) {
|
|
462
|
+
line = 1;
|
|
463
|
+
}
|
|
464
|
+
if (filePath) {
|
|
465
|
+
return [
|
|
466
|
+
{
|
|
467
|
+
path: filePath,
|
|
468
|
+
line: line,
|
|
469
|
+
},
|
|
470
|
+
];
|
|
471
|
+
}
|
|
457
472
|
}
|
|
458
473
|
return undefined;
|
|
459
474
|
}
|
|
@@ -477,6 +492,7 @@ export class WaveAcpAgent {
|
|
|
477
492
|
}
|
|
478
493
|
createCallbacks(sessionId) {
|
|
479
494
|
const getAgent = () => this.agents.get(sessionId);
|
|
495
|
+
const toolStates = new Map();
|
|
480
496
|
return {
|
|
481
497
|
onAssistantContentUpdated: (chunk) => {
|
|
482
498
|
this.connection.sessionUpdate({
|
|
@@ -503,7 +519,29 @@ export class WaveAcpAgent {
|
|
|
503
519
|
});
|
|
504
520
|
},
|
|
505
521
|
onToolBlockUpdated: (params) => {
|
|
506
|
-
const { id, name, stage, success, error, result, parameters } = params;
|
|
522
|
+
const { id, name, stage, success, error, result, parameters, compactParams, shortResult, startLineNumber, } = params;
|
|
523
|
+
let state = toolStates.get(id);
|
|
524
|
+
if (!state) {
|
|
525
|
+
state = {};
|
|
526
|
+
toolStates.set(id, state);
|
|
527
|
+
}
|
|
528
|
+
if (name)
|
|
529
|
+
state.name = name;
|
|
530
|
+
if (compactParams)
|
|
531
|
+
state.compactParams = compactParams;
|
|
532
|
+
if (shortResult)
|
|
533
|
+
state.shortResult = shortResult;
|
|
534
|
+
if (startLineNumber !== undefined)
|
|
535
|
+
state.startLineNumber = startLineNumber;
|
|
536
|
+
const effectiveName = state.name || name;
|
|
537
|
+
const effectiveCompactParams = state.compactParams || compactParams;
|
|
538
|
+
const effectiveShortResult = state.shortResult || shortResult;
|
|
539
|
+
const effectiveStartLineNumber = state.startLineNumber !== undefined
|
|
540
|
+
? state.startLineNumber
|
|
541
|
+
: startLineNumber;
|
|
542
|
+
const displayTitle = effectiveName && effectiveCompactParams
|
|
543
|
+
? `${effectiveName}: ${effectiveCompactParams}`
|
|
544
|
+
: effectiveName || "Tool Call";
|
|
507
545
|
let parsedParameters = undefined;
|
|
508
546
|
if (parameters) {
|
|
509
547
|
try {
|
|
@@ -513,20 +551,22 @@ export class WaveAcpAgent {
|
|
|
513
551
|
// Ignore parse errors during streaming
|
|
514
552
|
}
|
|
515
553
|
}
|
|
516
|
-
const content =
|
|
517
|
-
? this.getToolContent(
|
|
554
|
+
const content = effectiveName && (parsedParameters || effectiveShortResult)
|
|
555
|
+
? this.getToolContent(effectiveName, parsedParameters, effectiveShortResult)
|
|
518
556
|
: undefined;
|
|
519
|
-
const locations =
|
|
520
|
-
? this.getToolLocations(
|
|
557
|
+
const locations = effectiveName && parsedParameters
|
|
558
|
+
? this.getToolLocations(effectiveName, parsedParameters, effectiveStartLineNumber)
|
|
559
|
+
: undefined;
|
|
560
|
+
const kind = effectiveName
|
|
561
|
+
? this.getToolKind(effectiveName)
|
|
521
562
|
: undefined;
|
|
522
|
-
const kind = name ? this.getToolKind(name) : undefined;
|
|
523
563
|
if (stage === "start") {
|
|
524
564
|
this.connection.sessionUpdate({
|
|
525
565
|
sessionId: sessionId,
|
|
526
566
|
update: {
|
|
527
567
|
sessionUpdate: "tool_call",
|
|
528
568
|
toolCallId: id,
|
|
529
|
-
title:
|
|
569
|
+
title: displayTitle,
|
|
530
570
|
status: "pending",
|
|
531
571
|
content,
|
|
532
572
|
locations,
|
|
@@ -553,7 +593,7 @@ export class WaveAcpAgent {
|
|
|
553
593
|
sessionUpdate: "tool_call_update",
|
|
554
594
|
toolCallId: id,
|
|
555
595
|
status,
|
|
556
|
-
title:
|
|
596
|
+
title: displayTitle,
|
|
557
597
|
rawOutput: result || error,
|
|
558
598
|
content,
|
|
559
599
|
locations,
|
|
@@ -561,6 +601,9 @@ export class WaveAcpAgent {
|
|
|
561
601
|
rawInput: parsedParameters,
|
|
562
602
|
},
|
|
563
603
|
});
|
|
604
|
+
if (stage === "end") {
|
|
605
|
+
toolStates.delete(id);
|
|
606
|
+
}
|
|
564
607
|
},
|
|
565
608
|
onTasksChange: (tasks) => {
|
|
566
609
|
this.connection.sessionUpdate({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DiffDisplay.d.ts","sourceRoot":"","sources":["../../src/components/DiffDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DiffDisplay.d.ts","sourceRoot":"","sources":["../../src/components/DiffDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAYvC,UAAU,gBAAgB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA6WlD,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
|
-
import { WRITE_TOOL_NAME, EDIT_TOOL_NAME } from "wave-agent-sdk";
|
|
4
|
+
import { WRITE_TOOL_NAME, EDIT_TOOL_NAME, } from "wave-agent-sdk";
|
|
5
5
|
import { transformToolBlockToChanges } from "../utils/toolParameterTransforms.js";
|
|
6
6
|
import { diffLines, diffWords } from "diff";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { Markdown } from "./Markdown.js";
|
|
7
9
|
export const DiffDisplay = ({ toolName, parameters, startLineNumber, }) => {
|
|
8
10
|
const showDiff = toolName && [WRITE_TOOL_NAME, EDIT_TOOL_NAME].includes(toolName);
|
|
9
11
|
// Diff detection and transformation using typed parameters
|
|
@@ -53,6 +55,23 @@ export const DiffDisplay = ({ toolName, parameters, startLineNumber, }) => {
|
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
};
|
|
58
|
+
// Render highlighted code for Write tool
|
|
59
|
+
const renderWriteContent = () => {
|
|
60
|
+
if (!parameters)
|
|
61
|
+
return null;
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(parameters);
|
|
64
|
+
const content = parsed.content || "";
|
|
65
|
+
const filePath = parsed.file_path || "";
|
|
66
|
+
const ext = path.extname(filePath).slice(1);
|
|
67
|
+
const markdown = `\`\`\`${ext}\n${content}\n\`\`\``;
|
|
68
|
+
return _jsx(Markdown, { children: markdown });
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.warn("Error rendering write content:", error);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
56
75
|
// Render expanded diff display
|
|
57
76
|
const renderExpandedDiff = () => {
|
|
58
77
|
try {
|
|
@@ -197,5 +216,7 @@ export const DiffDisplay = ({ toolName, parameters, startLineNumber, }) => {
|
|
|
197
216
|
if (!showDiff) {
|
|
198
217
|
return null;
|
|
199
218
|
}
|
|
200
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "cyan", flexDirection: "column", children:
|
|
219
|
+
return (_jsx(Box, { flexDirection: "column", children: _jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "cyan", flexDirection: "column", children: toolName === WRITE_TOOL_NAME
|
|
220
|
+
? renderWriteContent()
|
|
221
|
+
: renderExpandedDiff() }) }));
|
|
201
222
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
|
package/dist/contexts/useChat.js
CHANGED
|
@@ -55,6 +55,16 @@ export const ChatProvider = ({ children, bypassPermissions, pluginDirs, tools, w
|
|
|
55
55
|
// Confirmation too tall state
|
|
56
56
|
const [wasLastDetailsTooTall, setWasLastDetailsTooTall] = useState(0);
|
|
57
57
|
const agentRef = useRef(null);
|
|
58
|
+
const taskUpdateTimerRef = useRef(null);
|
|
59
|
+
const debouncedSetTasks = useCallback((newTasks) => {
|
|
60
|
+
if (taskUpdateTimerRef.current) {
|
|
61
|
+
clearTimeout(taskUpdateTimerRef.current);
|
|
62
|
+
}
|
|
63
|
+
taskUpdateTimerRef.current = setTimeout(() => {
|
|
64
|
+
setTasks([...newTasks]);
|
|
65
|
+
taskUpdateTimerRef.current = null;
|
|
66
|
+
}, 100);
|
|
67
|
+
}, [setTasks]);
|
|
58
68
|
// Permission confirmation methods with queue support
|
|
59
69
|
const showConfirmation = useCallback(async (toolName, toolInput, suggestedPrefix, hidePersistentOption) => {
|
|
60
70
|
return new Promise((resolve, reject) => {
|
|
@@ -95,7 +105,7 @@ export const ChatProvider = ({ children, bypassPermissions, pluginDirs, tools, w
|
|
|
95
105
|
setBackgroundTasks([...tasks]);
|
|
96
106
|
},
|
|
97
107
|
onTasksChange: (tasks) => {
|
|
98
|
-
|
|
108
|
+
debouncedSetTasks(tasks);
|
|
99
109
|
},
|
|
100
110
|
onSubagentMessagesChange: (subagentId, messages) => {
|
|
101
111
|
logger.debug("onSubagentMessagesChange", subagentId, messages.length);
|
|
@@ -177,10 +187,14 @@ export const ChatProvider = ({ children, bypassPermissions, pluginDirs, tools, w
|
|
|
177
187
|
workdir,
|
|
178
188
|
worktreeSession,
|
|
179
189
|
model,
|
|
190
|
+
debouncedSetTasks,
|
|
180
191
|
]);
|
|
181
192
|
// Cleanup on unmount
|
|
182
193
|
useEffect(() => {
|
|
183
194
|
return () => {
|
|
195
|
+
if (taskUpdateTimerRef.current) {
|
|
196
|
+
clearTimeout(taskUpdateTimerRef.current);
|
|
197
|
+
}
|
|
184
198
|
if (agentRef.current) {
|
|
185
199
|
try {
|
|
186
200
|
// Display usage summary before cleanup
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"highlightUtils.d.ts","sourceRoot":"","sources":["../../src/utils/highlightUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"highlightUtils.d.ts","sourceRoot":"","sources":["../../src/utils/highlightUtils.ts"],"names":[],"mappings":"AAqEA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAsCvE"}
|
|
@@ -45,6 +45,14 @@ function nodeToAnsi(node) {
|
|
|
45
45
|
const classes = node.getAttribute("class")?.split(/\s+/) || [];
|
|
46
46
|
for (const className of classes) {
|
|
47
47
|
if (theme[className]) {
|
|
48
|
+
// If content has newlines, split it and apply style to each line
|
|
49
|
+
// to ensure ANSI codes are correctly applied when splitting the final string by lines.
|
|
50
|
+
if (content.includes("\n")) {
|
|
51
|
+
return content
|
|
52
|
+
.split("\n")
|
|
53
|
+
.map((line) => theme[className](line))
|
|
54
|
+
.join("\n");
|
|
55
|
+
}
|
|
48
56
|
return theme[className](content);
|
|
49
57
|
}
|
|
50
58
|
}
|
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
* Tool parameter transformation utilities for UI rendering
|
|
3
3
|
* Forces type judgment based on tool name using type assertions
|
|
4
4
|
*/
|
|
5
|
-
import { type Change, type
|
|
6
|
-
/**
|
|
7
|
-
* Transform Write tool parameters to changes
|
|
8
|
-
*/
|
|
9
|
-
export declare function transformWriteParameters(parameters: WriteToolParameters): Change[];
|
|
5
|
+
import { type Change, type EditToolParameters } from "wave-agent-sdk";
|
|
10
6
|
/**
|
|
11
7
|
* Transform Edit tool parameters to changes
|
|
12
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolParameterTransforms.d.ts","sourceRoot":"","sources":["../../src/utils/toolParameterTransforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"toolParameterTransforms.d.ts","sourceRoot":"","sources":["../../src/utils/toolParameterTransforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAmBtE;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,kBAAkB,GAC7B,MAAM,EAAE,CAOV;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,EAAE,CA0BV"}
|
|
@@ -18,17 +18,6 @@ function parseToolParameters(parameters) {
|
|
|
18
18
|
return {};
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Transform Write tool parameters to changes
|
|
23
|
-
*/
|
|
24
|
-
export function transformWriteParameters(parameters) {
|
|
25
|
-
return [
|
|
26
|
-
{
|
|
27
|
-
oldContent: "", // No previous content for write operations
|
|
28
|
-
newContent: parameters.content,
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
21
|
/**
|
|
33
22
|
* Transform Edit tool parameters to changes
|
|
34
23
|
*/
|
|
@@ -52,9 +41,6 @@ export function transformToolBlockToChanges(toolName, parameters, startLineNumbe
|
|
|
52
41
|
const parsedParams = parseToolParameters(parameters);
|
|
53
42
|
let changes = [];
|
|
54
43
|
switch (toolName) {
|
|
55
|
-
case "Write":
|
|
56
|
-
changes = transformWriteParameters(parsedParams);
|
|
57
|
-
break;
|
|
58
44
|
case "Edit":
|
|
59
45
|
changes = transformEditParameters(parsedParams);
|
|
60
46
|
break;
|
package/dist/utils/worktree.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
|
-
import {
|
|
4
|
+
import { getDefaultRemoteBranch, getGitMainRepoRoot } from "wave-agent-sdk";
|
|
5
5
|
export const WORKTREE_DIR = ".wave/worktrees";
|
|
6
6
|
/**
|
|
7
7
|
* Create a new git worktree
|
|
@@ -10,7 +10,7 @@ export const WORKTREE_DIR = ".wave/worktrees";
|
|
|
10
10
|
* @returns Worktree session details
|
|
11
11
|
*/
|
|
12
12
|
export function createWorktree(name, cwd) {
|
|
13
|
-
const repoRoot =
|
|
13
|
+
const repoRoot = getGitMainRepoRoot(cwd);
|
|
14
14
|
const worktreePath = path.join(repoRoot, WORKTREE_DIR, name);
|
|
15
15
|
const branchName = `worktree-${name}`;
|
|
16
16
|
const baseBranch = getDefaultRemoteBranch(cwd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"yargs": "^17.7.2",
|
|
42
42
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
43
43
|
"zod": "^3.23.8",
|
|
44
|
-
"wave-agent-sdk": "0.10.
|
|
44
|
+
"wave-agent-sdk": "0.10.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/react": "^19.1.8",
|
package/src/acp/agent.ts
CHANGED
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
AgentToolBlockUpdateParams,
|
|
7
7
|
Task,
|
|
8
8
|
listSessions as listWaveSessions,
|
|
9
|
+
listAllSessions as listAllWaveSessions,
|
|
9
10
|
deleteSession as deleteWaveSession,
|
|
11
|
+
truncateContent,
|
|
10
12
|
} from "wave-agent-sdk";
|
|
11
13
|
import * as fs from "node:fs/promises";
|
|
12
14
|
import * as path from "node:path";
|
|
@@ -137,6 +139,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
137
139
|
const agent = await WaveAgent.create({
|
|
138
140
|
workdir: cwd,
|
|
139
141
|
restoreSessionId: sessionId,
|
|
142
|
+
stream: false,
|
|
140
143
|
canUseTool: (context) => {
|
|
141
144
|
if (!agentRef.instance) {
|
|
142
145
|
throw new Error("Agent instance not yet initialized");
|
|
@@ -170,19 +173,11 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
170
173
|
// Update the callbacks object with the correct sessionId
|
|
171
174
|
Object.assign(callbacks, this.createCallbacks(actualSessionId));
|
|
172
175
|
|
|
173
|
-
return agent;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
|
177
|
-
const { cwd } = params;
|
|
178
|
-
logger.info(`Creating new session in ${cwd}`);
|
|
179
|
-
const agent = await this.createAgent(undefined, cwd);
|
|
180
|
-
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
181
|
-
|
|
182
176
|
// Send initial available commands after agent creation
|
|
177
|
+
// Use setImmediate to ensure the client receives the session response before the update
|
|
183
178
|
setImmediate(() => {
|
|
184
179
|
this.connection.sessionUpdate({
|
|
185
|
-
sessionId:
|
|
180
|
+
sessionId: actualSessionId as AcpSessionId,
|
|
186
181
|
update: {
|
|
187
182
|
sessionUpdate: "available_commands_update",
|
|
188
183
|
availableCommands: agent.getSlashCommands().map((cmd) => ({
|
|
@@ -196,6 +191,15 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
196
191
|
});
|
|
197
192
|
});
|
|
198
193
|
|
|
194
|
+
return agent;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
|
198
|
+
const { cwd } = params;
|
|
199
|
+
logger.info(`Creating new session in ${cwd}`);
|
|
200
|
+
const agent = await this.createAgent(undefined, cwd);
|
|
201
|
+
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
202
|
+
|
|
199
203
|
return {
|
|
200
204
|
sessionId: agent.sessionId as AcpSessionId,
|
|
201
205
|
modes: this.getSessionModeState(agent),
|
|
@@ -208,23 +212,6 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
208
212
|
logger.info(`Loading session: ${sessionId} in ${cwd}`);
|
|
209
213
|
const agent = await this.createAgent(sessionId, cwd);
|
|
210
214
|
|
|
211
|
-
// Send initial available commands after agent creation
|
|
212
|
-
setImmediate(() => {
|
|
213
|
-
this.connection.sessionUpdate({
|
|
214
|
-
sessionId: agent.sessionId as AcpSessionId,
|
|
215
|
-
update: {
|
|
216
|
-
sessionUpdate: "available_commands_update",
|
|
217
|
-
availableCommands: agent.getSlashCommands().map((cmd) => ({
|
|
218
|
-
name: cmd.name,
|
|
219
|
-
description: cmd.description,
|
|
220
|
-
input: {
|
|
221
|
-
hint: "Enter arguments...",
|
|
222
|
-
},
|
|
223
|
-
})),
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
215
|
return {
|
|
229
216
|
modes: this.getSessionModeState(agent),
|
|
230
217
|
configOptions: this.getSessionConfigOptions(agent),
|
|
@@ -236,17 +223,21 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
236
223
|
): Promise<ListSessionsResponse> {
|
|
237
224
|
const { cwd } = params;
|
|
238
225
|
logger.info(`listSessions called with params: ${JSON.stringify(params)}`);
|
|
226
|
+
|
|
227
|
+
let waveSessions;
|
|
239
228
|
if (!cwd) {
|
|
240
|
-
logger.
|
|
241
|
-
|
|
229
|
+
logger.info("listSessions called without cwd, listing all sessions");
|
|
230
|
+
waveSessions = await listAllWaveSessions();
|
|
231
|
+
} else {
|
|
232
|
+
logger.info(`Listing sessions for ${cwd}`);
|
|
233
|
+
waveSessions = await listWaveSessions(cwd);
|
|
242
234
|
}
|
|
243
235
|
|
|
244
|
-
logger.info(`
|
|
245
|
-
const waveSessions = await listWaveSessions(cwd);
|
|
246
|
-
logger.info(`Found ${waveSessions.length} sessions for ${cwd}`);
|
|
236
|
+
logger.info(`Found ${waveSessions.length} sessions`);
|
|
247
237
|
const sessions: SessionInfo[] = waveSessions.map((meta) => ({
|
|
248
238
|
sessionId: meta.id as AcpSessionId,
|
|
249
239
|
cwd: meta.workdir,
|
|
240
|
+
title: meta.firstMessage ? truncateContent(meta.firstMessage) : undefined,
|
|
250
241
|
updatedAt: meta.lastActiveAt.toISOString(),
|
|
251
242
|
}));
|
|
252
243
|
return { sessions };
|
|
@@ -378,6 +369,27 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
378
369
|
context.toolCallId ||
|
|
379
370
|
"perm-" + Math.random().toString(36).substring(2, 9);
|
|
380
371
|
|
|
372
|
+
let effectiveName = context.toolName;
|
|
373
|
+
let effectiveCompactParams: string | undefined = undefined;
|
|
374
|
+
|
|
375
|
+
if (agent?.messages && context.toolCallId) {
|
|
376
|
+
const toolBlock = agent.messages
|
|
377
|
+
.flatMap((m) => m.blocks)
|
|
378
|
+
.find((b) => b.type === "tool" && b.id === context.toolCallId) as
|
|
379
|
+
| import("wave-agent-sdk").ToolBlock
|
|
380
|
+
| undefined;
|
|
381
|
+
if (toolBlock) {
|
|
382
|
+
effectiveName = toolBlock.name || effectiveName;
|
|
383
|
+
effectiveCompactParams =
|
|
384
|
+
toolBlock.compactParams || effectiveCompactParams;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const displayTitle =
|
|
389
|
+
effectiveName && effectiveCompactParams
|
|
390
|
+
? `${effectiveName}: ${effectiveCompactParams}`
|
|
391
|
+
: effectiveName || "Tool Call";
|
|
392
|
+
|
|
381
393
|
const options: PermissionOption[] = [
|
|
382
394
|
{
|
|
383
395
|
optionId: "allow_once",
|
|
@@ -394,11 +406,6 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
394
406
|
name: "Reject Once",
|
|
395
407
|
kind: "reject_once",
|
|
396
408
|
},
|
|
397
|
-
{
|
|
398
|
-
optionId: "reject_always",
|
|
399
|
-
name: "Reject Always",
|
|
400
|
-
kind: "reject_always",
|
|
401
|
-
},
|
|
402
409
|
];
|
|
403
410
|
|
|
404
411
|
const content = context.toolName
|
|
@@ -420,7 +427,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
420
427
|
sessionId: sessionId as AcpSessionId,
|
|
421
428
|
toolCall: {
|
|
422
429
|
toolCallId,
|
|
423
|
-
title:
|
|
430
|
+
title: displayTitle,
|
|
424
431
|
status: "pending",
|
|
425
432
|
rawInput: context.toolInput,
|
|
426
433
|
content,
|
|
@@ -438,21 +445,15 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
438
445
|
logger.info(`User selected permission option: ${selectedOptionId}`);
|
|
439
446
|
|
|
440
447
|
switch (selectedOptionId) {
|
|
441
|
-
case "allow_once":
|
|
442
|
-
return { behavior: "allow" };
|
|
443
448
|
case "allow_always":
|
|
444
449
|
return {
|
|
445
450
|
behavior: "allow",
|
|
446
451
|
newPermissionRule: `${context.toolName}(*)`,
|
|
447
452
|
};
|
|
453
|
+
case "allow_once":
|
|
454
|
+
return { behavior: "allow" };
|
|
448
455
|
case "reject_once":
|
|
449
456
|
return { behavior: "deny", message: "Rejected by user" };
|
|
450
|
-
case "reject_always":
|
|
451
|
-
return {
|
|
452
|
-
behavior: "deny",
|
|
453
|
-
message: "Rejected by user",
|
|
454
|
-
newPermissionRule: `!${context.toolName}(*)`,
|
|
455
|
-
};
|
|
456
457
|
default:
|
|
457
458
|
return { behavior: "deny", message: "Unknown option selected" };
|
|
458
459
|
}
|
|
@@ -474,7 +475,8 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
474
475
|
if (name === "Write") {
|
|
475
476
|
let oldText: string | null = null;
|
|
476
477
|
try {
|
|
477
|
-
const filePath = parameters.file_path
|
|
478
|
+
const filePath = (parameters.file_path ||
|
|
479
|
+
parameters.filePath) as string;
|
|
478
480
|
const fullPath = path.isAbsolute(filePath)
|
|
479
481
|
? filePath
|
|
480
482
|
: path.join(workdir, filePath);
|
|
@@ -485,7 +487,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
485
487
|
return [
|
|
486
488
|
{
|
|
487
489
|
type: "diff",
|
|
488
|
-
path: parameters.file_path as string,
|
|
490
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
489
491
|
oldText,
|
|
490
492
|
newText: parameters.content as string,
|
|
491
493
|
},
|
|
@@ -495,7 +497,8 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
495
497
|
let oldText: string | null = null;
|
|
496
498
|
let newText: string | null = null;
|
|
497
499
|
try {
|
|
498
|
-
const filePath = parameters.file_path
|
|
500
|
+
const filePath = (parameters.file_path ||
|
|
501
|
+
parameters.filePath) as string;
|
|
499
502
|
const fullPath = path.isAbsolute(filePath)
|
|
500
503
|
? filePath
|
|
501
504
|
: path.join(workdir, filePath);
|
|
@@ -520,7 +523,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
520
523
|
return [
|
|
521
524
|
{
|
|
522
525
|
type: "diff",
|
|
523
|
-
path: parameters.file_path as string,
|
|
526
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
524
527
|
oldText,
|
|
525
528
|
newText,
|
|
526
529
|
},
|
|
@@ -531,55 +534,83 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
531
534
|
return [
|
|
532
535
|
{
|
|
533
536
|
type: "diff",
|
|
534
|
-
path: parameters.file_path as string,
|
|
537
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
535
538
|
oldText: parameters.old_string as string,
|
|
536
539
|
newText: parameters.new_string as string,
|
|
537
540
|
},
|
|
538
541
|
];
|
|
539
542
|
}
|
|
540
|
-
return this.getToolContent(name, parameters);
|
|
543
|
+
return this.getToolContent(name, parameters, undefined);
|
|
541
544
|
}
|
|
542
545
|
|
|
543
546
|
private getToolContent(
|
|
544
547
|
name: string,
|
|
545
548
|
parameters: Record<string, unknown> | undefined,
|
|
549
|
+
shortResult: string | undefined,
|
|
546
550
|
): ToolCallContent[] | undefined {
|
|
547
|
-
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
{
|
|
551
|
+
const contents: ToolCallContent[] = [];
|
|
552
|
+
if (parameters) {
|
|
553
|
+
if (name === "Write") {
|
|
554
|
+
contents.push({
|
|
551
555
|
type: "diff",
|
|
552
|
-
path: parameters.file_path as string,
|
|
556
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
553
557
|
oldText: null,
|
|
554
558
|
newText: parameters.content as string,
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (name === "Edit") {
|
|
559
|
-
return [
|
|
560
|
-
{
|
|
559
|
+
});
|
|
560
|
+
} else if (name === "Edit") {
|
|
561
|
+
contents.push({
|
|
561
562
|
type: "diff",
|
|
562
|
-
path: parameters.file_path as string,
|
|
563
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
563
564
|
oldText: parameters.old_string as string,
|
|
564
565
|
newText: parameters.new_string as string,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (shortResult) {
|
|
571
|
+
contents.push({
|
|
572
|
+
type: "content",
|
|
573
|
+
content: {
|
|
574
|
+
type: "text",
|
|
575
|
+
text: shortResult,
|
|
565
576
|
},
|
|
566
|
-
|
|
577
|
+
});
|
|
567
578
|
}
|
|
568
|
-
|
|
579
|
+
|
|
580
|
+
return contents.length > 0 ? contents : undefined;
|
|
569
581
|
}
|
|
570
582
|
|
|
571
583
|
private getToolLocations(
|
|
572
584
|
name: string,
|
|
573
585
|
parameters: Record<string, unknown> | undefined,
|
|
586
|
+
extraStartLineNumber?: number,
|
|
574
587
|
): ToolCallLocation[] | undefined {
|
|
575
588
|
if (!parameters) return undefined;
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
589
|
+
if (
|
|
590
|
+
name === "Write" ||
|
|
591
|
+
name === "Edit" ||
|
|
592
|
+
name === "Read" ||
|
|
593
|
+
name === "LSP"
|
|
594
|
+
) {
|
|
595
|
+
const filePath = (parameters.file_path || parameters.filePath) as string;
|
|
596
|
+
let line =
|
|
597
|
+
extraStartLineNumber ??
|
|
598
|
+
(parameters.startLineNumber as number) ??
|
|
599
|
+
(parameters.line as number) ??
|
|
600
|
+
(parameters.offset as number);
|
|
601
|
+
|
|
602
|
+
if (name === "Write" && line === undefined) {
|
|
603
|
+
line = 1;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (filePath) {
|
|
607
|
+
return [
|
|
608
|
+
{
|
|
609
|
+
path: filePath,
|
|
610
|
+
line: line,
|
|
611
|
+
},
|
|
612
|
+
];
|
|
613
|
+
}
|
|
583
614
|
}
|
|
584
615
|
return undefined;
|
|
585
616
|
}
|
|
@@ -605,6 +636,15 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
605
636
|
|
|
606
637
|
private createCallbacks(sessionId: string): AgentOptions["callbacks"] {
|
|
607
638
|
const getAgent = () => this.agents.get(sessionId);
|
|
639
|
+
const toolStates = new Map<
|
|
640
|
+
string,
|
|
641
|
+
{
|
|
642
|
+
name?: string;
|
|
643
|
+
compactParams?: string;
|
|
644
|
+
shortResult?: string;
|
|
645
|
+
startLineNumber?: number;
|
|
646
|
+
}
|
|
647
|
+
>();
|
|
608
648
|
return {
|
|
609
649
|
onAssistantContentUpdated: (chunk: string) => {
|
|
610
650
|
this.connection.sessionUpdate({
|
|
@@ -631,7 +671,42 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
631
671
|
});
|
|
632
672
|
},
|
|
633
673
|
onToolBlockUpdated: (params: AgentToolBlockUpdateParams) => {
|
|
634
|
-
const {
|
|
674
|
+
const {
|
|
675
|
+
id,
|
|
676
|
+
name,
|
|
677
|
+
stage,
|
|
678
|
+
success,
|
|
679
|
+
error,
|
|
680
|
+
result,
|
|
681
|
+
parameters,
|
|
682
|
+
compactParams,
|
|
683
|
+
shortResult,
|
|
684
|
+
startLineNumber,
|
|
685
|
+
} = params;
|
|
686
|
+
|
|
687
|
+
let state = toolStates.get(id);
|
|
688
|
+
if (!state) {
|
|
689
|
+
state = {};
|
|
690
|
+
toolStates.set(id, state);
|
|
691
|
+
}
|
|
692
|
+
if (name) state.name = name;
|
|
693
|
+
if (compactParams) state.compactParams = compactParams;
|
|
694
|
+
if (shortResult) state.shortResult = shortResult;
|
|
695
|
+
if (startLineNumber !== undefined)
|
|
696
|
+
state.startLineNumber = startLineNumber;
|
|
697
|
+
|
|
698
|
+
const effectiveName = state.name || name;
|
|
699
|
+
const effectiveCompactParams = state.compactParams || compactParams;
|
|
700
|
+
const effectiveShortResult = state.shortResult || shortResult;
|
|
701
|
+
const effectiveStartLineNumber =
|
|
702
|
+
state.startLineNumber !== undefined
|
|
703
|
+
? state.startLineNumber
|
|
704
|
+
: startLineNumber;
|
|
705
|
+
|
|
706
|
+
const displayTitle =
|
|
707
|
+
effectiveName && effectiveCompactParams
|
|
708
|
+
? `${effectiveName}: ${effectiveCompactParams}`
|
|
709
|
+
: effectiveName || "Tool Call";
|
|
635
710
|
|
|
636
711
|
let parsedParameters: Record<string, unknown> | undefined = undefined;
|
|
637
712
|
if (parameters) {
|
|
@@ -643,14 +718,24 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
643
718
|
}
|
|
644
719
|
|
|
645
720
|
const content =
|
|
646
|
-
|
|
647
|
-
? this.getToolContent(
|
|
721
|
+
effectiveName && (parsedParameters || effectiveShortResult)
|
|
722
|
+
? this.getToolContent(
|
|
723
|
+
effectiveName,
|
|
724
|
+
parsedParameters,
|
|
725
|
+
effectiveShortResult,
|
|
726
|
+
)
|
|
648
727
|
: undefined;
|
|
649
728
|
const locations =
|
|
650
|
-
|
|
651
|
-
? this.getToolLocations(
|
|
729
|
+
effectiveName && parsedParameters
|
|
730
|
+
? this.getToolLocations(
|
|
731
|
+
effectiveName,
|
|
732
|
+
parsedParameters,
|
|
733
|
+
effectiveStartLineNumber,
|
|
734
|
+
)
|
|
652
735
|
: undefined;
|
|
653
|
-
const kind =
|
|
736
|
+
const kind = effectiveName
|
|
737
|
+
? this.getToolKind(effectiveName)
|
|
738
|
+
: undefined;
|
|
654
739
|
|
|
655
740
|
if (stage === "start") {
|
|
656
741
|
this.connection.sessionUpdate({
|
|
@@ -658,7 +743,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
658
743
|
update: {
|
|
659
744
|
sessionUpdate: "tool_call",
|
|
660
745
|
toolCallId: id,
|
|
661
|
-
title:
|
|
746
|
+
title: displayTitle,
|
|
662
747
|
status: "pending",
|
|
663
748
|
content,
|
|
664
749
|
locations,
|
|
@@ -689,7 +774,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
689
774
|
sessionUpdate: "tool_call_update",
|
|
690
775
|
toolCallId: id,
|
|
691
776
|
status,
|
|
692
|
-
title:
|
|
777
|
+
title: displayTitle,
|
|
693
778
|
rawOutput: result || error,
|
|
694
779
|
content,
|
|
695
780
|
locations,
|
|
@@ -697,6 +782,10 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
697
782
|
rawInput: parsedParameters,
|
|
698
783
|
},
|
|
699
784
|
});
|
|
785
|
+
|
|
786
|
+
if (stage === "end") {
|
|
787
|
+
toolStates.delete(id);
|
|
788
|
+
}
|
|
700
789
|
},
|
|
701
790
|
onTasksChange: (tasks) => {
|
|
702
791
|
this.connection.sessionUpdate({
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
WRITE_TOOL_NAME,
|
|
5
|
+
EDIT_TOOL_NAME,
|
|
6
|
+
type WriteToolParameters,
|
|
7
|
+
} from "wave-agent-sdk";
|
|
4
8
|
import { transformToolBlockToChanges } from "../utils/toolParameterTransforms.js";
|
|
5
9
|
import { diffLines, diffWords } from "diff";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { Markdown } from "./Markdown.js";
|
|
6
12
|
|
|
7
13
|
interface DiffDisplayProps {
|
|
8
14
|
toolName?: string;
|
|
@@ -97,6 +103,23 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
97
103
|
}
|
|
98
104
|
};
|
|
99
105
|
|
|
106
|
+
// Render highlighted code for Write tool
|
|
107
|
+
const renderWriteContent = () => {
|
|
108
|
+
if (!parameters) return null;
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(parameters) as WriteToolParameters;
|
|
111
|
+
const content = parsed.content || "";
|
|
112
|
+
const filePath = parsed.file_path || "";
|
|
113
|
+
const ext = path.extname(filePath).slice(1);
|
|
114
|
+
|
|
115
|
+
const markdown = `\`\`\`${ext}\n${content}\n\`\`\``;
|
|
116
|
+
return <Markdown>{markdown}</Markdown>;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.warn("Error rendering write content:", error);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
100
123
|
// Render expanded diff display
|
|
101
124
|
const renderExpandedDiff = () => {
|
|
102
125
|
try {
|
|
@@ -352,7 +375,9 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
352
375
|
return (
|
|
353
376
|
<Box flexDirection="column">
|
|
354
377
|
<Box paddingLeft={2} borderLeft borderColor="cyan" flexDirection="column">
|
|
355
|
-
{
|
|
378
|
+
{toolName === WRITE_TOOL_NAME
|
|
379
|
+
? renderWriteContent()
|
|
380
|
+
: renderExpandedDiff()}
|
|
356
381
|
</Box>
|
|
357
382
|
</Box>
|
|
358
383
|
);
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -223,6 +223,20 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
223
223
|
const [wasLastDetailsTooTall, setWasLastDetailsTooTall] = useState(0);
|
|
224
224
|
|
|
225
225
|
const agentRef = useRef<Agent | null>(null);
|
|
226
|
+
const taskUpdateTimerRef = useRef<NodeJS.Timeout | null>(null);
|
|
227
|
+
|
|
228
|
+
const debouncedSetTasks = useCallback(
|
|
229
|
+
(newTasks: Task[]) => {
|
|
230
|
+
if (taskUpdateTimerRef.current) {
|
|
231
|
+
clearTimeout(taskUpdateTimerRef.current);
|
|
232
|
+
}
|
|
233
|
+
taskUpdateTimerRef.current = setTimeout(() => {
|
|
234
|
+
setTasks([...newTasks]);
|
|
235
|
+
taskUpdateTimerRef.current = null;
|
|
236
|
+
}, 100);
|
|
237
|
+
},
|
|
238
|
+
[setTasks],
|
|
239
|
+
);
|
|
226
240
|
|
|
227
241
|
// Permission confirmation methods with queue support
|
|
228
242
|
const showConfirmation = useCallback(
|
|
@@ -274,7 +288,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
274
288
|
setBackgroundTasks([...tasks]);
|
|
275
289
|
},
|
|
276
290
|
onTasksChange: (tasks) => {
|
|
277
|
-
|
|
291
|
+
debouncedSetTasks(tasks);
|
|
278
292
|
},
|
|
279
293
|
onSubagentMessagesChange: (subagentId: string, messages: Message[]) => {
|
|
280
294
|
logger.debug("onSubagentMessagesChange", subagentId, messages.length);
|
|
@@ -371,11 +385,16 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
371
385
|
workdir,
|
|
372
386
|
worktreeSession,
|
|
373
387
|
model,
|
|
388
|
+
debouncedSetTasks,
|
|
374
389
|
]);
|
|
375
390
|
|
|
376
391
|
// Cleanup on unmount
|
|
377
392
|
useEffect(() => {
|
|
378
393
|
return () => {
|
|
394
|
+
if (taskUpdateTimerRef.current) {
|
|
395
|
+
clearTimeout(taskUpdateTimerRef.current);
|
|
396
|
+
}
|
|
397
|
+
|
|
379
398
|
if (agentRef.current) {
|
|
380
399
|
try {
|
|
381
400
|
// Display usage summary before cleanup
|
|
@@ -49,6 +49,14 @@ function nodeToAnsi(node: Node): string {
|
|
|
49
49
|
|
|
50
50
|
for (const className of classes) {
|
|
51
51
|
if (theme[className]) {
|
|
52
|
+
// If content has newlines, split it and apply style to each line
|
|
53
|
+
// to ensure ANSI codes are correctly applied when splitting the final string by lines.
|
|
54
|
+
if (content.includes("\n")) {
|
|
55
|
+
return content
|
|
56
|
+
.split("\n")
|
|
57
|
+
.map((line) => theme[className](line))
|
|
58
|
+
.join("\n");
|
|
59
|
+
}
|
|
52
60
|
return theme[className](content);
|
|
53
61
|
}
|
|
54
62
|
}
|
|
@@ -3,11 +3,7 @@
|
|
|
3
3
|
* Forces type judgment based on tool name using type assertions
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
type Change,
|
|
8
|
-
type WriteToolParameters,
|
|
9
|
-
type EditToolParameters,
|
|
10
|
-
} from "wave-agent-sdk";
|
|
6
|
+
import { type Change, type EditToolParameters } from "wave-agent-sdk";
|
|
11
7
|
import { logger } from "./logger.js";
|
|
12
8
|
|
|
13
9
|
/**
|
|
@@ -26,20 +22,6 @@ function parseToolParameters(parameters: string): unknown {
|
|
|
26
22
|
}
|
|
27
23
|
}
|
|
28
24
|
|
|
29
|
-
/**
|
|
30
|
-
* Transform Write tool parameters to changes
|
|
31
|
-
*/
|
|
32
|
-
export function transformWriteParameters(
|
|
33
|
-
parameters: WriteToolParameters,
|
|
34
|
-
): Change[] {
|
|
35
|
-
return [
|
|
36
|
-
{
|
|
37
|
-
oldContent: "", // No previous content for write operations
|
|
38
|
-
newContent: parameters.content,
|
|
39
|
-
},
|
|
40
|
-
];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
25
|
/**
|
|
44
26
|
* Transform Edit tool parameters to changes
|
|
45
27
|
*/
|
|
@@ -72,10 +54,6 @@ export function transformToolBlockToChanges(
|
|
|
72
54
|
|
|
73
55
|
let changes: Change[] = [];
|
|
74
56
|
switch (toolName) {
|
|
75
|
-
case "Write":
|
|
76
|
-
changes = transformWriteParameters(parsedParams as WriteToolParameters);
|
|
77
|
-
break;
|
|
78
|
-
|
|
79
57
|
case "Edit":
|
|
80
58
|
changes = transformEditParameters(parsedParams as EditToolParameters);
|
|
81
59
|
break;
|
package/src/utils/worktree.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
|
-
import {
|
|
4
|
+
import { getDefaultRemoteBranch, getGitMainRepoRoot } from "wave-agent-sdk";
|
|
5
5
|
|
|
6
6
|
export interface WorktreeSession {
|
|
7
7
|
name: string;
|
|
@@ -22,7 +22,7 @@ export const WORKTREE_DIR = ".wave/worktrees";
|
|
|
22
22
|
* @returns Worktree session details
|
|
23
23
|
*/
|
|
24
24
|
export function createWorktree(name: string, cwd: string): WorktreeSession {
|
|
25
|
-
const repoRoot =
|
|
25
|
+
const repoRoot = getGitMainRepoRoot(cwd);
|
|
26
26
|
const worktreePath = path.join(repoRoot, WORKTREE_DIR, name);
|
|
27
27
|
const branchName = `worktree-${name}`;
|
|
28
28
|
const baseBranch = getDefaultRemoteBranch(cwd);
|