wave-code 0.9.7 → 0.10.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.
@@ -0,0 +1,28 @@
1
+ import { type Agent as AcpAgent, type AgentSideConnection, type InitializeResponse, type NewSessionRequest, type NewSessionResponse, type LoadSessionRequest, type LoadSessionResponse, type ListSessionsRequest, type ListSessionsResponse, type PromptRequest, type PromptResponse, type CancelNotification, type AuthenticateResponse, type SetSessionModeRequest, type SetSessionConfigOptionRequest, type SetSessionConfigOptionResponse } from "@agentclientprotocol/sdk";
2
+ export declare class WaveAcpAgent implements AcpAgent {
3
+ private agents;
4
+ private connection;
5
+ constructor(connection: AgentSideConnection);
6
+ private getSessionModeState;
7
+ private getSessionConfigOptions;
8
+ private cleanupAllAgents;
9
+ initialize(): Promise<InitializeResponse>;
10
+ authenticate(): Promise<AuthenticateResponse | void>;
11
+ private createAgent;
12
+ newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
13
+ loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>;
14
+ listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse>;
15
+ unstable_closeSession(params: Record<string, unknown>): Promise<Record<string, unknown>>;
16
+ extMethod(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
17
+ setSessionMode(params: SetSessionModeRequest): Promise<void>;
18
+ setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse>;
19
+ prompt(params: PromptRequest): Promise<PromptResponse>;
20
+ cancel(params: CancelNotification): Promise<void>;
21
+ private handlePermissionRequest;
22
+ private getToolContentAsync;
23
+ private getToolContent;
24
+ private getToolLocations;
25
+ private getToolKind;
26
+ private createCallbacks;
27
+ }
28
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAaA,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;IA8CnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8BlE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA4BrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAmB1B,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;YAsGvB,mBAAmB;IA2EjC,OAAO,CAAC,cAAc;IA4BtB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,eAAe;CAsIxB"}
@@ -0,0 +1,603 @@
1
+ import { Agent as WaveAgent, listSessions as listWaveSessions, deleteSession as deleteWaveSession, } from "wave-agent-sdk";
2
+ import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
4
+ import { logger } from "../utils/logger.js";
5
+ import { AGENT_METHODS, } from "@agentclientprotocol/sdk";
6
+ export class WaveAcpAgent {
7
+ constructor(connection) {
8
+ this.agents = new Map();
9
+ this.connection = connection;
10
+ }
11
+ getSessionModeState(agent) {
12
+ return {
13
+ currentModeId: agent.getPermissionMode(),
14
+ availableModes: [
15
+ {
16
+ id: "default",
17
+ name: "Default",
18
+ description: "Ask for permission for restricted tools",
19
+ },
20
+ {
21
+ id: "acceptEdits",
22
+ name: "Accept Edits",
23
+ description: "Automatically accept file edits",
24
+ },
25
+ {
26
+ id: "plan",
27
+ name: "Plan",
28
+ description: "Plan mode for complex tasks",
29
+ },
30
+ {
31
+ id: "bypassPermissions",
32
+ name: "Bypass Permissions",
33
+ description: "Automatically accept all tool calls",
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ getSessionConfigOptions(agent) {
39
+ return [
40
+ {
41
+ id: "permission_mode",
42
+ name: "Permission Mode",
43
+ type: "select",
44
+ category: "mode",
45
+ currentValue: agent.getPermissionMode(),
46
+ options: [
47
+ { value: "default", name: "Default" },
48
+ { value: "acceptEdits", name: "Accept Edits" },
49
+ { value: "plan", name: "Plan" },
50
+ { value: "bypassPermissions", name: "Bypass Permissions" },
51
+ ],
52
+ },
53
+ ];
54
+ }
55
+ async cleanupAllAgents() {
56
+ logger.info("Cleaning up all active agents due to connection closure");
57
+ const destroyPromises = Array.from(this.agents.values()).map((agent) => agent.destroy());
58
+ await Promise.all(destroyPromises);
59
+ this.agents.clear();
60
+ }
61
+ async initialize() {
62
+ logger.info("Initializing WaveAcpAgent");
63
+ // Setup cleanup on connection closure
64
+ this.connection.closed.then(() => this.cleanupAllAgents());
65
+ return {
66
+ protocolVersion: 1,
67
+ agentInfo: {
68
+ name: "wave-agent",
69
+ version: "0.1.0",
70
+ },
71
+ agentCapabilities: {
72
+ loadSession: true,
73
+ sessionCapabilities: {
74
+ list: {},
75
+ close: {},
76
+ },
77
+ },
78
+ };
79
+ }
80
+ async authenticate() {
81
+ // No authentication required for now
82
+ }
83
+ async createAgent(sessionId, cwd) {
84
+ const callbacks = {};
85
+ const agentRef = {};
86
+ const agent = await WaveAgent.create({
87
+ workdir: cwd,
88
+ restoreSessionId: sessionId,
89
+ canUseTool: (context) => {
90
+ if (!agentRef.instance) {
91
+ throw new Error("Agent instance not yet initialized");
92
+ }
93
+ return this.handlePermissionRequest(agentRef.instance.sessionId, context);
94
+ },
95
+ callbacks: {
96
+ onAssistantContentUpdated: (chunk) => callbacks.onAssistantContentUpdated?.(chunk, ""),
97
+ onAssistantReasoningUpdated: (chunk) => callbacks.onAssistantReasoningUpdated?.(chunk, ""),
98
+ onToolBlockUpdated: (params) => {
99
+ const cb = callbacks.onToolBlockUpdated;
100
+ cb?.(params);
101
+ },
102
+ onTasksChange: (tasks) => callbacks.onTasksChange?.(tasks),
103
+ onPermissionModeChange: (mode) => callbacks.onPermissionModeChange?.(mode),
104
+ },
105
+ });
106
+ agentRef.instance = agent;
107
+ const actualSessionId = agent.sessionId;
108
+ this.agents.set(actualSessionId, agent);
109
+ // Update the callbacks object with the correct sessionId
110
+ 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
+ // Send initial available commands after agent creation
119
+ setImmediate(() => {
120
+ this.connection.sessionUpdate({
121
+ sessionId: agent.sessionId,
122
+ update: {
123
+ sessionUpdate: "available_commands_update",
124
+ availableCommands: agent.getSlashCommands().map((cmd) => ({
125
+ name: cmd.name,
126
+ description: cmd.description,
127
+ input: {
128
+ hint: "Enter arguments...",
129
+ },
130
+ })),
131
+ },
132
+ });
133
+ });
134
+ return {
135
+ sessionId: agent.sessionId,
136
+ modes: this.getSessionModeState(agent),
137
+ configOptions: this.getSessionConfigOptions(agent),
138
+ };
139
+ }
140
+ async loadSession(params) {
141
+ const { sessionId, cwd } = params;
142
+ logger.info(`Loading session: ${sessionId} in ${cwd}`);
143
+ 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
+ return {
161
+ modes: this.getSessionModeState(agent),
162
+ configOptions: this.getSessionConfigOptions(agent),
163
+ };
164
+ }
165
+ async listSessions(params) {
166
+ const { cwd } = params;
167
+ logger.info(`listSessions called with params: ${JSON.stringify(params)}`);
168
+ if (!cwd) {
169
+ logger.warn("listSessions called without cwd, returning empty list");
170
+ return { sessions: [] };
171
+ }
172
+ logger.info(`Listing sessions for ${cwd}`);
173
+ const waveSessions = await listWaveSessions(cwd);
174
+ logger.info(`Found ${waveSessions.length} sessions for ${cwd}`);
175
+ const sessions = waveSessions.map((meta) => ({
176
+ sessionId: meta.id,
177
+ cwd: meta.workdir,
178
+ updatedAt: meta.lastActiveAt.toISOString(),
179
+ }));
180
+ return { sessions };
181
+ }
182
+ async unstable_closeSession(params) {
183
+ const sessionId = params.sessionId;
184
+ logger.info(`Stopping session ${sessionId}`);
185
+ const agent = this.agents.get(sessionId);
186
+ if (agent) {
187
+ const workdir = agent.workingDirectory;
188
+ await agent.destroy();
189
+ this.agents.delete(sessionId);
190
+ // Delete the session file so it doesn't show up in listSessions
191
+ await deleteWaveSession(sessionId, workdir);
192
+ }
193
+ return {};
194
+ }
195
+ async extMethod(method, params) {
196
+ if (method === AGENT_METHODS.session_close) {
197
+ return this.unstable_closeSession(params);
198
+ }
199
+ throw new Error(`Method ${method} not implemented`);
200
+ }
201
+ async setSessionMode(params) {
202
+ const { sessionId, modeId } = params;
203
+ const agent = this.agents.get(sessionId);
204
+ if (!agent)
205
+ throw new Error(`Session ${sessionId} not found`);
206
+ agent.setPermissionMode(modeId);
207
+ }
208
+ async setSessionConfigOption(params) {
209
+ const { sessionId, configId, value } = params;
210
+ const agent = this.agents.get(sessionId);
211
+ if (!agent)
212
+ throw new Error(`Session ${sessionId} not found`);
213
+ if (configId === "permission_mode") {
214
+ agent.setPermissionMode(value);
215
+ }
216
+ return {
217
+ configOptions: this.getSessionConfigOptions(agent),
218
+ };
219
+ }
220
+ async prompt(params) {
221
+ const { sessionId, prompt } = params;
222
+ logger.info(`Received prompt for session ${sessionId}`);
223
+ const agent = this.agents.get(sessionId);
224
+ if (!agent) {
225
+ logger.error(`Session ${sessionId} not found`);
226
+ throw new Error(`Session ${sessionId} not found`);
227
+ }
228
+ // Map ACP prompt to Wave Agent sendMessage
229
+ const textContent = prompt
230
+ .filter((block) => block.type === "text")
231
+ .map((block) => block.text)
232
+ .join("\n");
233
+ const images = prompt
234
+ .filter((block) => block.type === "image")
235
+ .map((block) => {
236
+ const img = block;
237
+ return {
238
+ path: `data:${img.mimeType};base64,${img.data}`,
239
+ mimeType: img.mimeType,
240
+ };
241
+ });
242
+ try {
243
+ logger.info(`Sending message to agent: ${textContent.substring(0, 50)}...`);
244
+ await agent.sendMessage(textContent, images.length > 0 ? images : undefined);
245
+ logger.info(`Message sent successfully for session ${sessionId}`);
246
+ return {
247
+ stopReason: "end_turn",
248
+ };
249
+ }
250
+ catch (error) {
251
+ if (error instanceof Error && error.message.includes("abort")) {
252
+ logger.info(`Message aborted for session ${sessionId}`);
253
+ return {
254
+ stopReason: "cancelled",
255
+ };
256
+ }
257
+ logger.error(`Error sending message for session ${sessionId}:`, error);
258
+ throw error;
259
+ }
260
+ }
261
+ async cancel(params) {
262
+ const { sessionId } = params;
263
+ logger.info(`Cancelling message for session ${sessionId}`);
264
+ const agent = this.agents.get(sessionId);
265
+ if (agent) {
266
+ agent.abortMessage();
267
+ }
268
+ }
269
+ async handlePermissionRequest(sessionId, context) {
270
+ logger.info(`Handling permission request for ${context.toolName} in session ${sessionId}`);
271
+ const agent = this.agents.get(sessionId);
272
+ const workdir = agent?.workingDirectory || process.cwd();
273
+ const toolCallId = context.toolCallId ||
274
+ "perm-" + Math.random().toString(36).substring(2, 9);
275
+ const options = [
276
+ {
277
+ optionId: "allow_once",
278
+ name: "Allow Once",
279
+ kind: "allow_once",
280
+ },
281
+ {
282
+ optionId: "allow_always",
283
+ name: "Allow Always",
284
+ kind: "allow_always",
285
+ },
286
+ {
287
+ optionId: "reject_once",
288
+ name: "Reject Once",
289
+ kind: "reject_once",
290
+ },
291
+ {
292
+ optionId: "reject_always",
293
+ name: "Reject Always",
294
+ kind: "reject_always",
295
+ },
296
+ ];
297
+ const content = context.toolName
298
+ ? await this.getToolContentAsync(context.toolName, context.toolInput, workdir)
299
+ : undefined;
300
+ const locations = context.toolName
301
+ ? this.getToolLocations(context.toolName, context.toolInput)
302
+ : undefined;
303
+ const kind = context.toolName
304
+ ? this.getToolKind(context.toolName)
305
+ : undefined;
306
+ try {
307
+ const response = await this.connection.requestPermission({
308
+ sessionId: sessionId,
309
+ toolCall: {
310
+ toolCallId,
311
+ title: `Permission for ${context.toolName}`,
312
+ status: "pending",
313
+ rawInput: context.toolInput,
314
+ content,
315
+ locations,
316
+ kind,
317
+ },
318
+ options,
319
+ });
320
+ if (response.outcome.outcome === "cancelled") {
321
+ return { behavior: "deny", message: "Cancelled by user" };
322
+ }
323
+ const selectedOptionId = response.outcome.optionId;
324
+ logger.info(`User selected permission option: ${selectedOptionId}`);
325
+ switch (selectedOptionId) {
326
+ case "allow_once":
327
+ return { behavior: "allow" };
328
+ case "allow_always":
329
+ return {
330
+ behavior: "allow",
331
+ newPermissionRule: `${context.toolName}(*)`,
332
+ };
333
+ case "reject_once":
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
+ default:
342
+ return { behavior: "deny", message: "Unknown option selected" };
343
+ }
344
+ }
345
+ catch (error) {
346
+ logger.error("Error requesting permission via ACP:", error);
347
+ return {
348
+ behavior: "deny",
349
+ message: `Error requesting permission: ${error instanceof Error ? error.message : String(error)}`,
350
+ };
351
+ }
352
+ }
353
+ async getToolContentAsync(name, parameters, workdir) {
354
+ if (!parameters)
355
+ return undefined;
356
+ if (name === "Write") {
357
+ let oldText = null;
358
+ try {
359
+ const filePath = parameters.file_path;
360
+ const fullPath = path.isAbsolute(filePath)
361
+ ? filePath
362
+ : path.join(workdir, filePath);
363
+ oldText = await fs.readFile(fullPath, "utf-8");
364
+ }
365
+ catch {
366
+ // File might not exist, which is fine for Write
367
+ }
368
+ return [
369
+ {
370
+ type: "diff",
371
+ path: parameters.file_path,
372
+ oldText,
373
+ newText: parameters.content,
374
+ },
375
+ ];
376
+ }
377
+ if (name === "Edit") {
378
+ let oldText = null;
379
+ let newText = null;
380
+ try {
381
+ const filePath = parameters.file_path;
382
+ const fullPath = path.isAbsolute(filePath)
383
+ ? filePath
384
+ : path.join(workdir, filePath);
385
+ oldText = await fs.readFile(fullPath, "utf-8");
386
+ if (oldText) {
387
+ if (parameters.replace_all) {
388
+ newText = oldText
389
+ .split(parameters.old_string)
390
+ .join(parameters.new_string);
391
+ }
392
+ else {
393
+ newText = oldText.replace(parameters.old_string, parameters.new_string);
394
+ }
395
+ }
396
+ }
397
+ catch {
398
+ logger.error("Failed to read file for Edit diff");
399
+ }
400
+ if (oldText && newText) {
401
+ return [
402
+ {
403
+ type: "diff",
404
+ path: parameters.file_path,
405
+ oldText,
406
+ newText,
407
+ },
408
+ ];
409
+ }
410
+ // Fallback to snippets if file reading fails
411
+ return [
412
+ {
413
+ type: "diff",
414
+ path: parameters.file_path,
415
+ oldText: parameters.old_string,
416
+ newText: parameters.new_string,
417
+ },
418
+ ];
419
+ }
420
+ return this.getToolContent(name, parameters);
421
+ }
422
+ getToolContent(name, parameters) {
423
+ if (!parameters)
424
+ return undefined;
425
+ if (name === "Write") {
426
+ return [
427
+ {
428
+ type: "diff",
429
+ path: parameters.file_path,
430
+ oldText: null,
431
+ newText: parameters.content,
432
+ },
433
+ ];
434
+ }
435
+ if (name === "Edit") {
436
+ return [
437
+ {
438
+ type: "diff",
439
+ path: parameters.file_path,
440
+ oldText: parameters.old_string,
441
+ newText: parameters.new_string,
442
+ },
443
+ ];
444
+ }
445
+ return undefined;
446
+ }
447
+ getToolLocations(name, parameters) {
448
+ if (!parameters)
449
+ return undefined;
450
+ if (name === "Write" || name === "Edit" || name === "Read") {
451
+ return [
452
+ {
453
+ path: parameters.file_path,
454
+ line: parameters.offset,
455
+ },
456
+ ];
457
+ }
458
+ return undefined;
459
+ }
460
+ getToolKind(name) {
461
+ switch (name) {
462
+ case "Read":
463
+ case "Glob":
464
+ case "Grep":
465
+ case "LSP":
466
+ return "read";
467
+ case "Write":
468
+ case "Edit":
469
+ return "edit";
470
+ case "Bash":
471
+ return "execute";
472
+ case "Agent":
473
+ return "other";
474
+ default:
475
+ return "other";
476
+ }
477
+ }
478
+ createCallbacks(sessionId) {
479
+ const getAgent = () => this.agents.get(sessionId);
480
+ return {
481
+ onAssistantContentUpdated: (chunk) => {
482
+ this.connection.sessionUpdate({
483
+ sessionId: sessionId,
484
+ update: {
485
+ sessionUpdate: "agent_message_chunk",
486
+ content: {
487
+ type: "text",
488
+ text: chunk,
489
+ },
490
+ },
491
+ });
492
+ },
493
+ onAssistantReasoningUpdated: (chunk) => {
494
+ this.connection.sessionUpdate({
495
+ sessionId: sessionId,
496
+ update: {
497
+ sessionUpdate: "agent_thought_chunk",
498
+ content: {
499
+ type: "text",
500
+ text: chunk,
501
+ },
502
+ },
503
+ });
504
+ },
505
+ onToolBlockUpdated: (params) => {
506
+ const { id, name, stage, success, error, result, parameters } = params;
507
+ let parsedParameters = undefined;
508
+ if (parameters) {
509
+ try {
510
+ parsedParameters = JSON.parse(parameters);
511
+ }
512
+ catch {
513
+ // Ignore parse errors during streaming
514
+ }
515
+ }
516
+ const content = name && parsedParameters
517
+ ? this.getToolContent(name, parsedParameters)
518
+ : undefined;
519
+ const locations = name && parsedParameters
520
+ ? this.getToolLocations(name, parsedParameters)
521
+ : undefined;
522
+ const kind = name ? this.getToolKind(name) : undefined;
523
+ if (stage === "start") {
524
+ this.connection.sessionUpdate({
525
+ sessionId: sessionId,
526
+ update: {
527
+ sessionUpdate: "tool_call",
528
+ toolCallId: id,
529
+ title: name || "Tool Call",
530
+ status: "pending",
531
+ content,
532
+ locations,
533
+ kind,
534
+ rawInput: parsedParameters,
535
+ },
536
+ });
537
+ return;
538
+ }
539
+ if (stage === "streaming") {
540
+ // We don't support streaming tool arguments in ACP yet
541
+ return;
542
+ }
543
+ const status = stage === "end"
544
+ ? success
545
+ ? "completed"
546
+ : "failed"
547
+ : stage === "running"
548
+ ? "in_progress"
549
+ : "pending";
550
+ this.connection.sessionUpdate({
551
+ sessionId: sessionId,
552
+ update: {
553
+ sessionUpdate: "tool_call_update",
554
+ toolCallId: id,
555
+ status,
556
+ title: name || "Tool Call",
557
+ rawOutput: result || error,
558
+ content,
559
+ locations,
560
+ kind,
561
+ rawInput: parsedParameters,
562
+ },
563
+ });
564
+ },
565
+ onTasksChange: (tasks) => {
566
+ this.connection.sessionUpdate({
567
+ sessionId: sessionId,
568
+ update: {
569
+ sessionUpdate: "plan",
570
+ entries: tasks.map((task) => ({
571
+ content: task.subject,
572
+ status: task.status === "completed"
573
+ ? "completed"
574
+ : task.status === "in_progress"
575
+ ? "in_progress"
576
+ : "pending",
577
+ priority: "medium",
578
+ })),
579
+ },
580
+ });
581
+ },
582
+ onPermissionModeChange: (mode) => {
583
+ this.connection.sessionUpdate({
584
+ sessionId: sessionId,
585
+ update: {
586
+ sessionUpdate: "current_mode_update",
587
+ currentModeId: mode,
588
+ },
589
+ });
590
+ const agent = getAgent();
591
+ if (agent) {
592
+ this.connection.sessionUpdate({
593
+ sessionId: sessionId,
594
+ update: {
595
+ sessionUpdate: "config_option_update",
596
+ configOptions: this.getSessionConfigOptions(agent),
597
+ },
598
+ });
599
+ }
600
+ },
601
+ };
602
+ }
603
+ }
@@ -0,0 +1,2 @@
1
+ export declare function startAcpCli(): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/acp/index.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,kBAsBhC"}
@@ -0,0 +1,22 @@
1
+ import { Readable, Writable } from "node:stream";
2
+ import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
3
+ import { WaveAcpAgent } from "./agent.js";
4
+ import { logger } from "../utils/logger.js";
5
+ export async function startAcpCli() {
6
+ // Redirect console.log to logger to avoid interfering with JSON-RPC over stdio
7
+ console.log = (...args) => {
8
+ logger.info(...args);
9
+ };
10
+ logger.info("Starting ACP bridge...");
11
+ // Convert Node.js stdio to Web streams
12
+ const stdin = Readable.toWeb(process.stdin);
13
+ const stdout = Writable.toWeb(process.stdout);
14
+ // Create ACP stream
15
+ const stream = ndJsonStream(stdout, stdin);
16
+ // Initialize AgentSideConnection
17
+ const connection = new AgentSideConnection((conn) => {
18
+ return new WaveAcpAgent(conn);
19
+ }, stream);
20
+ // Wait for connection to close
21
+ await connection.closed;
22
+ }
@@ -0,0 +1,2 @@
1
+ export declare function runAcp(): Promise<void>;
2
+ //# sourceMappingURL=acp-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acp-cli.d.ts","sourceRoot":"","sources":["../src/acp-cli.ts"],"names":[],"mappings":"AAEA,wBAAsB,MAAM,kBAE3B"}
@@ -0,0 +1,4 @@
1
+ import { startAcpCli } from "./acp/index.js";
2
+ export async function runAcp() {
3
+ await startAcpCli();
4
+ }
@@ -39,7 +39,7 @@ export const ChatInterface = () => {
39
39
  }
40
40
  const terminalHeight = stdout?.rows || 24;
41
41
  const totalHeight = detailsHeight + selectorHeight + dynamicBlocksHeight;
42
- if (totalHeight > terminalHeight) {
42
+ if (totalHeight > terminalHeight - 3) {
43
43
  setIsConfirmationTooTall(true);
44
44
  }
45
45
  }, [