wave-code 0.17.5 → 0.17.7

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/src/acp/agent.ts CHANGED
@@ -17,10 +17,10 @@ import {
17
17
  ASK_USER_QUESTION_TOOL_NAME,
18
18
  AskUserQuestion,
19
19
  AskUserQuestionOption,
20
- type Message,
21
20
  type TextBlock,
22
21
  type ReasoningBlock,
23
22
  type ToolBlock,
23
+ type CompactBlock,
24
24
  } from "wave-agent-sdk";
25
25
  import { logger } from "../utils/logger.js";
26
26
  import {
@@ -58,9 +58,31 @@ import {
58
58
  } from "@agentclientprotocol/sdk";
59
59
  import type { McpServerConfig, McpServerStatus } from "wave-agent-sdk";
60
60
 
61
+ interface WaveAskQuestionRequest {
62
+ toolCallId: string;
63
+ title?: string;
64
+ questions: Array<{
65
+ id: string;
66
+ prompt: string;
67
+ options: Array<{ id: string; label: string; description?: string }>;
68
+ allowMultiple?: boolean;
69
+ }>;
70
+ }
71
+
72
+ interface WaveCreatePlanRequest {
73
+ toolCallId: string;
74
+ plan: string;
75
+ todos?: Array<{
76
+ id: string;
77
+ content: string;
78
+ status: "pending" | "in_progress" | "completed";
79
+ }>;
80
+ }
81
+
61
82
  export class WaveAcpAgent implements AcpAgent {
62
83
  private agents: Map<string, WaveAgent> = new Map();
63
84
  private connection: AgentSideConnection;
85
+ private taskCache = new Map<string, Task[]>();
64
86
 
65
87
  constructor(connection: AgentSideConnection) {
66
88
  this.connection = connection;
@@ -217,6 +239,8 @@ export class WaveAcpAgent implements AcpAgent {
217
239
  onUserMessageAdded: (params) => callbacks.onUserMessageAdded?.(params),
218
240
  onMcpServersChange: (servers) =>
219
241
  callbacks.onMcpServersChange?.(servers),
242
+ onSessionIdChange: (newSessionId: string) =>
243
+ callbacks.onSessionIdChange?.(newSessionId),
220
244
  },
221
245
  });
222
246
 
@@ -225,7 +249,8 @@ export class WaveAcpAgent implements AcpAgent {
225
249
  this.agents.set(actualSessionId, agent);
226
250
 
227
251
  // Update the callbacks object with the correct sessionId
228
- Object.assign(callbacks, this.createCallbacks(actualSessionId));
252
+ const { callbacks: cb } = this.createCallbacks(actualSessionId);
253
+ Object.assign(callbacks, cb);
229
254
 
230
255
  // Send initial available commands after agent creation
231
256
  // Use setImmediate to ensure the client receives the session response before the update
@@ -458,6 +483,133 @@ export class WaveAcpAgent implements AcpAgent {
458
483
  return "Allow Always";
459
484
  }
460
485
 
486
+ private async handleAskQuestion(
487
+ sessionId: string,
488
+ toolCallId: string,
489
+ context: ToolPermissionContext,
490
+ ): Promise<PermissionDecision | null> {
491
+ try {
492
+ const questions =
493
+ (context.toolInput?.questions as Array<{
494
+ question: string;
495
+ header?: string;
496
+ options: Array<{ label: string; description?: string }>;
497
+ multiSelect?: boolean;
498
+ }>) || [];
499
+
500
+ const request: WaveAskQuestionRequest = {
501
+ toolCallId,
502
+ title: questions.length === 1 ? questions[0].header : undefined,
503
+ questions: questions.map((q, qi) => ({
504
+ id: `q${qi}`,
505
+ prompt: q.question,
506
+ options: q.options.map((opt, oi) => ({
507
+ id: String(oi),
508
+ label: opt.label,
509
+ description: opt.description,
510
+ })),
511
+ allowMultiple: q.multiSelect,
512
+ })),
513
+ };
514
+
515
+ const response = await this.connection.extMethod(
516
+ "wave/ask_question",
517
+ request as unknown as Record<string, unknown>,
518
+ );
519
+
520
+ const outcome = (response as { outcome?: string }).outcome;
521
+ if (outcome === "cancelled") {
522
+ return { behavior: "deny", message: "Cancelled by user" };
523
+ }
524
+
525
+ // outcome === "answered"
526
+ const answers =
527
+ (
528
+ response as {
529
+ answers?: Array<{
530
+ questionId: string;
531
+ selectedOptionIds: string[];
532
+ }>;
533
+ }
534
+ ).answers || [];
535
+ const answerMap: Record<string, string> = {};
536
+ for (const answer of answers) {
537
+ const qIndex = parseInt(answer.questionId.replace("q", ""), 10);
538
+ const question = questions[qIndex];
539
+ if (!question) continue;
540
+ const selectedLabels = answer.selectedOptionIds
541
+ .map((id) => question.options[parseInt(id, 10)]?.label)
542
+ .filter(Boolean);
543
+ answerMap[question.question] = selectedLabels.join(", ");
544
+ }
545
+
546
+ return { behavior: "allow", message: JSON.stringify(answerMap) };
547
+ } catch (error) {
548
+ logger.warn(
549
+ "wave/ask_question extMethod failed, falling back to requestPermission",
550
+ { error },
551
+ );
552
+ return null;
553
+ }
554
+ }
555
+
556
+ private async handleCreatePlan(
557
+ sessionId: string,
558
+ toolCallId: string,
559
+ context: ToolPermissionContext,
560
+ ): Promise<PermissionDecision | null> {
561
+ try {
562
+ const cachedTasks = this.taskCache.get(sessionId) || [];
563
+ const request: WaveCreatePlanRequest = {
564
+ toolCallId,
565
+ plan: context.planContent || "",
566
+ todos: cachedTasks
567
+ .filter((t) => t.status !== "deleted")
568
+ .map((t) => ({
569
+ id: t.id,
570
+ content: t.subject,
571
+ status:
572
+ t.status === "completed"
573
+ ? ("completed" as const)
574
+ : t.status === "in_progress"
575
+ ? ("in_progress" as const)
576
+ : ("pending" as const),
577
+ })),
578
+ };
579
+
580
+ const response = await this.connection.extMethod(
581
+ "wave/create_plan",
582
+ request as unknown as Record<string, unknown>,
583
+ );
584
+
585
+ const outcome = (response as { outcome?: string }).outcome;
586
+ if (outcome === "accepted") {
587
+ const mode = (response as { mode?: string }).mode;
588
+ return {
589
+ behavior: "allow",
590
+ newPermissionMode: (mode || "default") as
591
+ | "default"
592
+ | "acceptEdits"
593
+ | "plan"
594
+ | "bypassPermissions"
595
+ | "dontAsk",
596
+ };
597
+ }
598
+ if (outcome === "rejected") {
599
+ const reason = (response as { reason?: string }).reason;
600
+ return { behavior: "deny", message: reason || "Plan rejected" };
601
+ }
602
+ // cancelled or unknown outcome
603
+ return { behavior: "deny", message: "Cancelled by user" };
604
+ } catch (error) {
605
+ logger.warn(
606
+ "wave/create_plan extMethod failed, falling back to requestPermission",
607
+ { error },
608
+ );
609
+ return null;
610
+ }
611
+ }
612
+
461
613
  private async handlePermissionRequest(
462
614
  sessionId: string,
463
615
  context: ToolPermissionContext,
@@ -573,6 +725,27 @@ export class WaveAcpAgent implements AcpAgent {
573
725
  ? this.getToolKind(context.toolName)
574
726
  : undefined;
575
727
 
728
+ // Try extension methods first, fall back to requestPermission
729
+ if (context.toolName === ASK_USER_QUESTION_TOOL_NAME) {
730
+ const extResult = await this.handleAskQuestion(
731
+ sessionId,
732
+ toolCallId,
733
+ context,
734
+ );
735
+ if (extResult !== null) return extResult;
736
+ // Fall through to existing requestPermission logic
737
+ }
738
+
739
+ if (context.toolName === EXIT_PLAN_MODE_TOOL_NAME) {
740
+ const extResult = await this.handleCreatePlan(
741
+ sessionId,
742
+ toolCallId,
743
+ context,
744
+ );
745
+ if (extResult !== null) return extResult;
746
+ // Fall through to existing requestPermission logic
747
+ }
748
+
576
749
  try {
577
750
  const response = await this.connection.requestPermission({
578
751
  sessionId: sessionId as AcpSessionId,
@@ -798,14 +971,7 @@ export class WaveAcpAgent implements AcpAgent {
798
971
  private async replayConversationHistory(agent: WaveAgent): Promise<void> {
799
972
  const sessionId = agent.sessionId as AcpSessionId;
800
973
 
801
- let history: Message[];
802
- try {
803
- const thread = await agent.getFullMessageThread();
804
- history = thread.messages;
805
- } catch {
806
- // Fallback to in-memory messages if full thread fails
807
- history = agent.messages;
808
- }
974
+ const history = agent.messages;
809
975
 
810
976
  for (const message of history) {
811
977
  if (message.isMeta) continue;
@@ -918,14 +1084,28 @@ export class WaveAcpAgent implements AcpAgent {
918
1084
  rawInput: parsedParameters,
919
1085
  },
920
1086
  });
1087
+ } else if (block.type === "compact") {
1088
+ const compactBlock = block as CompactBlock;
1089
+ this.connection.sessionUpdate({
1090
+ sessionId,
1091
+ update: {
1092
+ sessionUpdate: "agent_message_chunk",
1093
+ content: { type: "text", text: compactBlock.content },
1094
+ messageId,
1095
+ },
1096
+ });
921
1097
  }
922
- // Skip: image, bang, compact, error, file_history, task_notification blocks
1098
+ // Skip: image, bang, error, file_history, task_notification blocks
923
1099
  }
924
1100
  }
925
1101
  }
926
1102
 
927
- private createCallbacks(sessionId: string): AgentOptions["callbacks"] {
928
- const getAgent = () => this.agents.get(sessionId);
1103
+ private createCallbacks(sessionId: string): {
1104
+ callbacks: AgentOptions["callbacks"];
1105
+ sessionRef: { id: string };
1106
+ } {
1107
+ const sessionRef = { id: sessionId };
1108
+ const getAgent = () => this.agents.get(sessionRef.id);
929
1109
  const toolStates = new Map<
930
1110
  string,
931
1111
  {
@@ -936,225 +1116,262 @@ export class WaveAcpAgent implements AcpAgent {
936
1116
  }
937
1117
  >();
938
1118
  return {
939
- onAssistantContentUpdated: (chunk: string) => {
940
- this.connection.sessionUpdate({
941
- sessionId: sessionId as AcpSessionId,
942
- update: {
943
- sessionUpdate: "agent_message_chunk",
944
- content: {
945
- type: "text",
946
- text: chunk,
1119
+ callbacks: {
1120
+ onAssistantContentUpdated: (chunk: string) => {
1121
+ this.connection.sessionUpdate({
1122
+ sessionId: sessionRef.id as AcpSessionId,
1123
+ update: {
1124
+ sessionUpdate: "agent_message_chunk",
1125
+ content: {
1126
+ type: "text",
1127
+ text: chunk,
1128
+ },
947
1129
  },
948
- },
949
- });
950
- },
951
- onAssistantReasoningUpdated: (chunk: string) => {
952
- this.connection.sessionUpdate({
953
- sessionId: sessionId as AcpSessionId,
954
- update: {
955
- sessionUpdate: "agent_thought_chunk",
956
- content: {
957
- type: "text",
958
- text: chunk,
1130
+ });
1131
+ },
1132
+ onAssistantReasoningUpdated: (chunk: string) => {
1133
+ this.connection.sessionUpdate({
1134
+ sessionId: sessionRef.id as AcpSessionId,
1135
+ update: {
1136
+ sessionUpdate: "agent_thought_chunk",
1137
+ content: {
1138
+ type: "text",
1139
+ text: chunk,
1140
+ },
959
1141
  },
960
- },
961
- });
962
- },
963
- onToolBlockUpdated: (params: AgentToolBlockUpdateParams) => {
964
- const {
965
- id,
966
- name,
967
- stage,
968
- success,
969
- error,
970
- result,
971
- parameters,
972
- compactParams,
973
- shortResult,
974
- startLineNumber,
975
- } = params;
976
-
977
- let state = toolStates.get(id);
978
- if (!state) {
979
- state = {};
980
- toolStates.set(id, state);
981
- }
982
- if (name) state.name = name;
983
- if (compactParams) state.compactParams = compactParams;
984
- if (shortResult) state.shortResult = shortResult;
985
- if (startLineNumber !== undefined)
986
- state.startLineNumber = startLineNumber;
987
-
988
- const effectiveName = state.name || name;
989
- const effectiveCompactParams = state.compactParams || compactParams;
990
- const effectiveShortResult = state.shortResult || shortResult;
991
- const effectiveStartLineNumber =
992
- state.startLineNumber !== undefined
993
- ? state.startLineNumber
994
- : startLineNumber;
995
-
996
- const displayTitle =
997
- effectiveName && effectiveCompactParams
998
- ? `${effectiveName}: ${effectiveCompactParams}`
999
- : effectiveName || "Tool Call";
1000
-
1001
- let parsedParameters: Record<string, unknown> | undefined = undefined;
1002
- if (parameters) {
1003
- try {
1004
- const parsed = JSON.parse(parameters);
1005
- parsedParameters = Array.isArray(parsed)
1006
- ? { args: parsed }
1007
- : parsed;
1008
- } catch {
1009
- // Ignore parse errors during streaming
1142
+ });
1143
+ },
1144
+ onToolBlockUpdated: (params: AgentToolBlockUpdateParams) => {
1145
+ const {
1146
+ id,
1147
+ name,
1148
+ stage,
1149
+ success,
1150
+ error,
1151
+ result,
1152
+ parameters,
1153
+ compactParams,
1154
+ shortResult,
1155
+ startLineNumber,
1156
+ } = params;
1157
+
1158
+ let state = toolStates.get(id);
1159
+ if (!state) {
1160
+ state = {};
1161
+ toolStates.set(id, state);
1010
1162
  }
1011
- }
1163
+ if (name) state.name = name;
1164
+ if (compactParams) state.compactParams = compactParams;
1165
+ if (shortResult) state.shortResult = shortResult;
1166
+ if (startLineNumber !== undefined)
1167
+ state.startLineNumber = startLineNumber;
1168
+
1169
+ const effectiveName = state.name || name;
1170
+ const effectiveCompactParams = state.compactParams || compactParams;
1171
+ const effectiveShortResult = state.shortResult || shortResult;
1172
+ const effectiveStartLineNumber =
1173
+ state.startLineNumber !== undefined
1174
+ ? state.startLineNumber
1175
+ : startLineNumber;
1012
1176
 
1013
- const content =
1014
- effectiveName && (parsedParameters || effectiveShortResult)
1015
- ? this.getToolContent(
1016
- effectiveName,
1017
- parsedParameters,
1018
- effectiveShortResult,
1019
- )
1020
- : undefined;
1021
- const locations =
1022
- effectiveName && parsedParameters
1023
- ? this.getToolLocations(
1024
- effectiveName,
1025
- parsedParameters,
1026
- effectiveStartLineNumber,
1027
- )
1177
+ const displayTitle =
1178
+ effectiveName && effectiveCompactParams
1179
+ ? `${effectiveName}: ${effectiveCompactParams}`
1180
+ : effectiveName || "Tool Call";
1181
+
1182
+ let parsedParameters: Record<string, unknown> | undefined = undefined;
1183
+ if (parameters) {
1184
+ try {
1185
+ const parsed = JSON.parse(parameters);
1186
+ parsedParameters = Array.isArray(parsed)
1187
+ ? { args: parsed }
1188
+ : parsed;
1189
+ } catch {
1190
+ // Ignore parse errors during streaming
1191
+ }
1192
+ }
1193
+
1194
+ const content =
1195
+ effectiveName && (parsedParameters || effectiveShortResult)
1196
+ ? this.getToolContent(
1197
+ effectiveName,
1198
+ parsedParameters,
1199
+ effectiveShortResult,
1200
+ )
1201
+ : undefined;
1202
+ const locations =
1203
+ effectiveName && parsedParameters
1204
+ ? this.getToolLocations(
1205
+ effectiveName,
1206
+ parsedParameters,
1207
+ effectiveStartLineNumber,
1208
+ )
1209
+ : undefined;
1210
+ const kind = effectiveName
1211
+ ? this.getToolKind(effectiveName)
1028
1212
  : undefined;
1029
- const kind = effectiveName
1030
- ? this.getToolKind(effectiveName)
1031
- : undefined;
1032
1213
 
1033
- if (stage === "start") {
1214
+ if (stage === "start") {
1215
+ this.connection.sessionUpdate({
1216
+ sessionId: sessionRef.id as AcpSessionId,
1217
+ update: {
1218
+ sessionUpdate: "tool_call",
1219
+ toolCallId: id,
1220
+ title: displayTitle,
1221
+ status: "pending",
1222
+ content,
1223
+ locations,
1224
+ kind,
1225
+ rawInput: parsedParameters,
1226
+ },
1227
+ });
1228
+ return;
1229
+ }
1230
+
1231
+ if (stage === "streaming") {
1232
+ // We don't support streaming tool arguments in ACP yet
1233
+ return;
1234
+ }
1235
+
1236
+ const status: ToolCallStatus =
1237
+ stage === "end"
1238
+ ? success
1239
+ ? "completed"
1240
+ : "failed"
1241
+ : stage === "running"
1242
+ ? "in_progress"
1243
+ : "pending";
1244
+
1034
1245
  this.connection.sessionUpdate({
1035
- sessionId: sessionId as AcpSessionId,
1246
+ sessionId: sessionRef.id as AcpSessionId,
1036
1247
  update: {
1037
- sessionUpdate: "tool_call",
1248
+ sessionUpdate: "tool_call_update",
1038
1249
  toolCallId: id,
1250
+ status,
1039
1251
  title: displayTitle,
1040
- status: "pending",
1252
+ rawOutput: result || error,
1041
1253
  content,
1042
1254
  locations,
1043
1255
  kind,
1044
1256
  rawInput: parsedParameters,
1045
1257
  },
1046
1258
  });
1047
- return;
1048
- }
1049
-
1050
- if (stage === "streaming") {
1051
- // We don't support streaming tool arguments in ACP yet
1052
- return;
1053
- }
1054
-
1055
- const status: ToolCallStatus =
1056
- stage === "end"
1057
- ? success
1058
- ? "completed"
1059
- : "failed"
1060
- : stage === "running"
1061
- ? "in_progress"
1062
- : "pending";
1063
-
1064
- this.connection.sessionUpdate({
1065
- sessionId: sessionId as AcpSessionId,
1066
- update: {
1067
- sessionUpdate: "tool_call_update",
1068
- toolCallId: id,
1069
- status,
1070
- title: displayTitle,
1071
- rawOutput: result || error,
1072
- content,
1073
- locations,
1074
- kind,
1075
- rawInput: parsedParameters,
1076
- },
1077
- });
1078
1259
 
1079
- if (stage === "end") {
1080
- toolStates.delete(id);
1081
- }
1082
- },
1083
- onTasksChange: (tasks) => {
1084
- this.connection.sessionUpdate({
1085
- sessionId: sessionId as AcpSessionId,
1086
- update: {
1087
- sessionUpdate: "plan",
1088
- entries: tasks.map((task) => ({
1089
- content: task.subject,
1090
- status:
1091
- task.status === "completed"
1092
- ? "completed"
1093
- : task.status === "in_progress"
1094
- ? "in_progress"
1095
- : "pending",
1096
- priority: "medium",
1097
- })),
1098
- },
1099
- });
1100
- },
1101
- onPermissionModeChange: (mode) => {
1102
- this.connection.sessionUpdate({
1103
- sessionId: sessionId as AcpSessionId,
1104
- update: {
1105
- sessionUpdate: "current_mode_update",
1106
- currentModeId: mode,
1107
- },
1108
- });
1109
- const agent = getAgent();
1110
- if (agent) {
1260
+ if (stage === "end") {
1261
+ toolStates.delete(id);
1262
+ }
1263
+ },
1264
+ onTasksChange: (tasks) => {
1265
+ this.taskCache.set(sessionRef.id, tasks);
1111
1266
  this.connection.sessionUpdate({
1112
- sessionId: sessionId as AcpSessionId,
1267
+ sessionId: sessionRef.id as AcpSessionId,
1113
1268
  update: {
1114
- sessionUpdate: "config_option_update",
1115
- configOptions: this.getSessionConfigOptions(agent),
1269
+ sessionUpdate: "plan",
1270
+ entries: tasks
1271
+ .filter((t) => t.status !== "deleted")
1272
+ .map((task) => ({
1273
+ content: task.subject,
1274
+ status:
1275
+ task.status === "completed"
1276
+ ? "completed"
1277
+ : task.status === "in_progress"
1278
+ ? "in_progress"
1279
+ : "pending",
1280
+ priority: "medium" as const,
1281
+ })),
1116
1282
  },
1117
1283
  });
1118
- }
1119
- },
1120
- onModelChange: () => {
1121
- const agent = getAgent();
1122
- if (agent) {
1284
+ },
1285
+ onPermissionModeChange: (mode) => {
1123
1286
  this.connection.sessionUpdate({
1124
- sessionId: sessionId as AcpSessionId,
1287
+ sessionId: sessionRef.id as AcpSessionId,
1125
1288
  update: {
1126
- sessionUpdate: "config_option_update",
1127
- configOptions: this.getSessionConfigOptions(agent),
1289
+ sessionUpdate: "current_mode_update",
1290
+ currentModeId: mode,
1128
1291
  },
1129
1292
  });
1130
- }
1131
- },
1132
- onUserMessageAdded: (params) => {
1133
- this.connection.sessionUpdate({
1134
- sessionId: sessionId as AcpSessionId,
1135
- update: {
1136
- sessionUpdate: "user_message_chunk",
1137
- content: { type: "text", text: params.content },
1138
- },
1139
- });
1140
- },
1141
- onMcpServersChange: (servers: McpServerStatus[]) => {
1142
- for (const server of servers) {
1293
+ const agent = getAgent();
1294
+ if (agent) {
1295
+ this.connection.sessionUpdate({
1296
+ sessionId: sessionRef.id as AcpSessionId,
1297
+ update: {
1298
+ sessionUpdate: "config_option_update",
1299
+ configOptions: this.getSessionConfigOptions(agent),
1300
+ },
1301
+ });
1302
+ }
1303
+ },
1304
+ onModelChange: () => {
1305
+ const agent = getAgent();
1306
+ if (agent) {
1307
+ this.connection.sessionUpdate({
1308
+ sessionId: sessionRef.id as AcpSessionId,
1309
+ update: {
1310
+ sessionUpdate: "config_option_update",
1311
+ configOptions: this.getSessionConfigOptions(agent),
1312
+ },
1313
+ });
1314
+ }
1315
+ },
1316
+ onUserMessageAdded: (params) => {
1143
1317
  this.connection.sessionUpdate({
1144
- sessionId: sessionId as AcpSessionId,
1318
+ sessionId: sessionRef.id as AcpSessionId,
1145
1319
  update: {
1146
- sessionUpdate: "ext_notification" as const,
1147
- method: "mcp_server_status",
1148
- params: {
1149
- name: server.name,
1150
- status: server.status,
1151
- toolCount: server.toolCount,
1152
- error: server.error,
1320
+ sessionUpdate: "user_message_chunk",
1321
+ content: { type: "text", text: params.content },
1322
+ },
1323
+ });
1324
+ },
1325
+ onMcpServersChange: (servers: McpServerStatus[]) => {
1326
+ for (const server of servers) {
1327
+ this.connection.sessionUpdate({
1328
+ sessionId: sessionRef.id as AcpSessionId,
1329
+ update: {
1330
+ sessionUpdate: "ext_notification" as const,
1331
+ method: "mcp_server_status",
1332
+ params: {
1333
+ name: server.name,
1334
+ status: server.status,
1335
+ toolCount: server.toolCount,
1336
+ error: server.error,
1337
+ },
1153
1338
  },
1339
+ });
1340
+ }
1341
+ },
1342
+ onSessionIdChange: (newSessionId: string) => {
1343
+ const oldSessionId = sessionRef.id;
1344
+ if (oldSessionId === newSessionId) return;
1345
+
1346
+ // Update agents Map key
1347
+ const agent = this.agents.get(oldSessionId);
1348
+ if (agent) {
1349
+ this.agents.delete(oldSessionId);
1350
+ this.agents.set(newSessionId, agent);
1351
+ }
1352
+
1353
+ // Update taskCache key
1354
+ const tasks = this.taskCache.get(oldSessionId);
1355
+ if (tasks) {
1356
+ this.taskCache.delete(oldSessionId);
1357
+ this.taskCache.set(newSessionId, tasks);
1358
+ }
1359
+
1360
+ // Update ref so all subsequent callbacks use new ID
1361
+ sessionRef.id = newSessionId;
1362
+
1363
+ // Notify ACP client
1364
+ this.connection.sessionUpdate({
1365
+ sessionId: oldSessionId as AcpSessionId,
1366
+ update: {
1367
+ sessionUpdate: "ext_notification" as const,
1368
+ method: "session_id_change",
1369
+ params: { oldSessionId, newSessionId },
1154
1370
  },
1155
1371
  });
1156
- }
1372
+ },
1157
1373
  },
1374
+ sessionRef,
1158
1375
  };
1159
1376
  }
1160
1377
  }