wave-code 0.10.1 → 0.10.3

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/print-cli.js CHANGED
@@ -14,7 +14,7 @@ function displayTimingInfo(startTime, showStats) {
14
14
  }
15
15
  export async function startPrintCli(options) {
16
16
  const startTime = new Date();
17
- const { restoreSessionId, continueLastSession, message, showStats = false, bypassPermissions, pluginDirs, tools, worktreeSession, workdir, model, } = options;
17
+ const { restoreSessionId, continueLastSession, message, showStats = false, bypassPermissions, permissionMode, pluginDirs, tools, worktreeSession, workdir, model, } = options;
18
18
  if ((!message || message.trim() === "") &&
19
19
  !continueLastSession &&
20
20
  !restoreSessionId) {
@@ -96,7 +96,7 @@ export async function startPrintCli(options) {
96
96
  callbacks,
97
97
  restoreSessionId,
98
98
  continueLastSession,
99
- permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
99
+ permissionMode: permissionMode || (bypassPermissions ? "bypassPermissions" : undefined),
100
100
  plugins: pluginDirs?.map((path) => ({ type: "local", path })),
101
101
  tools,
102
102
  workdir,
package/dist/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { WorktreeSession } from "./utils/worktree.js";
2
+ import { PermissionMode } from "wave-agent-sdk";
2
3
  export interface BaseAppProps {
3
4
  bypassPermissions?: boolean;
5
+ permissionMode?: PermissionMode;
4
6
  pluginDirs?: string[];
5
7
  tools?: string[];
6
8
  worktreeSession?: WorktreeSession;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -1 +1 @@
1
- {"version":3,"file":"highlightUtils.d.ts","sourceRoot":"","sources":["../../src/utils/highlightUtils.ts"],"names":[],"mappings":"AA6DA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAsCvE"}
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 WriteToolParameters, type EditToolParameters } from "wave-agent-sdk";
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,EACL,KAAK,MAAM,EACX,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAmBxB;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,mBAAmB,GAC9B,MAAM,EAAE,CAOV;AAED;;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,CA8BV"}
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;
@@ -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 { getGitRepoRoot, getDefaultRemoteBranch } from "wave-agent-sdk";
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 = getGitRepoRoot(cwd);
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.1",
3
+ "version": "0.10.3",
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.1"
44
+ "wave-agent-sdk": "0.10.3"
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: agent.sessionId as AcpSessionId,
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.warn("listSessions called without cwd, returning empty list");
241
- return { sessions: [] };
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(`Listing sessions for ${cwd}`);
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: `Permission for ${context.toolName}`,
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 as string;
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 as string;
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
- if (!parameters) return undefined;
548
- if (name === "Write") {
549
- return [
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
- return undefined;
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 (name === "Write" || name === "Edit" || name === "Read") {
577
- return [
578
- {
579
- path: parameters.file_path as string,
580
- line: parameters.offset as number,
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 { id, name, stage, success, error, result, parameters } = params;
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
- name && parsedParameters
647
- ? this.getToolContent(name, parsedParameters)
721
+ effectiveName && (parsedParameters || effectiveShortResult)
722
+ ? this.getToolContent(
723
+ effectiveName,
724
+ parsedParameters,
725
+ effectiveShortResult,
726
+ )
648
727
  : undefined;
649
728
  const locations =
650
- name && parsedParameters
651
- ? this.getToolLocations(name, parsedParameters)
729
+ effectiveName && parsedParameters
730
+ ? this.getToolLocations(
731
+ effectiveName,
732
+ parsedParameters,
733
+ effectiveStartLineNumber,
734
+ )
652
735
  : undefined;
653
- const kind = name ? this.getToolKind(name) : undefined;
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: name || "Tool Call",
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: name || "Tool Call",
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({
package/src/cli.tsx CHANGED
@@ -15,6 +15,7 @@ export async function startCli(options: CliOptions): Promise<void> {
15
15
  restoreSessionId,
16
16
  continueLastSession,
17
17
  bypassPermissions,
18
+ permissionMode,
18
19
  pluginDirs,
19
20
  tools,
20
21
  worktreeSession,
@@ -37,6 +38,7 @@ export async function startCli(options: CliOptions): Promise<void> {
37
38
  restoreSessionId={restoreSessionId}
38
39
  continueLastSession={continueLastSession}
39
40
  bypassPermissions={bypassPermissions}
41
+ permissionMode={permissionMode}
40
42
  pluginDirs={pluginDirs}
41
43
  tools={tools}
42
44
  worktreeSession={worktreeSession}
@@ -23,6 +23,7 @@ interface AppWithProvidersProps extends BaseAppProps {
23
23
 
24
24
  const AppWithProviders: React.FC<AppWithProvidersProps> = ({
25
25
  bypassPermissions,
26
+ permissionMode,
26
27
  pluginDirs,
27
28
  tools,
28
29
  worktreeSession,
@@ -94,6 +95,7 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
94
95
  return (
95
96
  <ChatProvider
96
97
  bypassPermissions={bypassPermissions}
98
+ permissionMode={permissionMode}
97
99
  pluginDirs={pluginDirs}
98
100
  tools={tools}
99
101
  workdir={workdir}
@@ -157,6 +159,7 @@ export const App: React.FC<AppProps> = ({
157
159
  restoreSessionId,
158
160
  continueLastSession,
159
161
  bypassPermissions,
162
+ permissionMode,
160
163
  pluginDirs,
161
164
  tools,
162
165
  worktreeSession,
@@ -172,6 +175,7 @@ export const App: React.FC<AppProps> = ({
172
175
  >
173
176
  <AppWithProviders
174
177
  bypassPermissions={bypassPermissions}
178
+ permissionMode={permissionMode}
175
179
  pluginDirs={pluginDirs}
176
180
  tools={tools}
177
181
  worktreeSession={worktreeSession}
@@ -1,8 +1,14 @@
1
1
  import React, { useMemo } from "react";
2
2
  import { Box, Text } from "ink";
3
- import { WRITE_TOOL_NAME, EDIT_TOOL_NAME } from "wave-agent-sdk";
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
- {renderExpandedDiff()}
378
+ {toolName === WRITE_TOOL_NAME
379
+ ? renderWriteContent()
380
+ : renderExpandedDiff()}
356
381
  </Box>
357
382
  </Box>
358
383
  );