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.
@@ -2,6 +2,7 @@ import { type Agent as AcpAgent, type AgentSideConnection, type InitializeRespon
2
2
  export declare class WaveAcpAgent implements AcpAgent {
3
3
  private agents;
4
4
  private connection;
5
+ private taskCache;
5
6
  constructor(connection: AgentSideConnection);
6
7
  private getSessionModeState;
7
8
  private getSessionConfigOptions;
@@ -19,6 +20,8 @@ export declare class WaveAcpAgent implements AcpAgent {
19
20
  prompt(params: PromptRequest): Promise<PromptResponse>;
20
21
  cancel(params: CancelNotification): Promise<void>;
21
22
  private getAllowAlwaysName;
23
+ private handleAskQuestion;
24
+ private handleCreatePlan;
22
25
  private handlePermissionRequest;
23
26
  private getToolContent;
24
27
  private getToolLocations;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAyBA,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,EAOpC,MAAM,0BAA0B,CAAC;AAGlC,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,mBAAmB;IAI3C,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,uBAAuB;YAmCjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAyBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IA2EnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAcrE,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;IAc5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAuBpC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAyDtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD,OAAO,CAAC,kBAAkB;YA2BZ,uBAAuB;IA8LrC,OAAO,CAAC,cAAc;IA6FtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;YAmBL,yBAAyB;IAiIvC,OAAO,CAAC,eAAe;CAyOxB"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAyBA,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,EAOpC,MAAM,0BAA0B,CAAC;AAwBlC,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,SAAS,CAA6B;gBAElC,UAAU,EAAE,mBAAmB;IAI3C,OAAO,CAAC,mBAAmB;IAkC3B,OAAO,CAAC,uBAAuB;YAmCjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAyBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IA8EnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAcrE,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;IAc5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAuBpC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAyDtD,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD,OAAO,CAAC,kBAAkB;YA2BZ,iBAAiB;YAsEjB,gBAAgB;YAyDhB,uBAAuB;IAmNrC,OAAO,CAAC,cAAc;IA6FtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;YAmBL,yBAAyB;IAoIvC,OAAO,CAAC,eAAe;CAkRxB"}
package/dist/acp/agent.js CHANGED
@@ -4,6 +4,7 @@ import { AGENT_METHODS, } from "@agentclientprotocol/sdk";
4
4
  export class WaveAcpAgent {
5
5
  constructor(connection) {
6
6
  this.agents = new Map();
7
+ this.taskCache = new Map();
7
8
  this.connection = connection;
8
9
  }
9
10
  getSessionModeState(agent) {
@@ -133,13 +134,15 @@ export class WaveAcpAgent {
133
134
  onModelChange: (model) => callbacks.onModelChange?.(model),
134
135
  onUserMessageAdded: (params) => callbacks.onUserMessageAdded?.(params),
135
136
  onMcpServersChange: (servers) => callbacks.onMcpServersChange?.(servers),
137
+ onSessionIdChange: (newSessionId) => callbacks.onSessionIdChange?.(newSessionId),
136
138
  },
137
139
  });
138
140
  agentRef.instance = agent;
139
141
  const actualSessionId = agent.sessionId;
140
142
  this.agents.set(actualSessionId, agent);
141
143
  // Update the callbacks object with the correct sessionId
142
- Object.assign(callbacks, this.createCallbacks(actualSessionId));
144
+ const { callbacks: cb } = this.createCallbacks(actualSessionId);
145
+ Object.assign(callbacks, cb);
143
146
  // Send initial available commands after agent creation
144
147
  // Use setImmediate to ensure the client receives the session response before the update
145
148
  setImmediate(() => {
@@ -327,6 +330,87 @@ export class WaveAcpAgent {
327
330
  }
328
331
  return "Allow Always";
329
332
  }
333
+ async handleAskQuestion(sessionId, toolCallId, context) {
334
+ try {
335
+ const questions = context.toolInput?.questions || [];
336
+ const request = {
337
+ toolCallId,
338
+ title: questions.length === 1 ? questions[0].header : undefined,
339
+ questions: questions.map((q, qi) => ({
340
+ id: `q${qi}`,
341
+ prompt: q.question,
342
+ options: q.options.map((opt, oi) => ({
343
+ id: String(oi),
344
+ label: opt.label,
345
+ description: opt.description,
346
+ })),
347
+ allowMultiple: q.multiSelect,
348
+ })),
349
+ };
350
+ const response = await this.connection.extMethod("wave/ask_question", request);
351
+ const outcome = response.outcome;
352
+ if (outcome === "cancelled") {
353
+ return { behavior: "deny", message: "Cancelled by user" };
354
+ }
355
+ // outcome === "answered"
356
+ const answers = response.answers || [];
357
+ const answerMap = {};
358
+ for (const answer of answers) {
359
+ const qIndex = parseInt(answer.questionId.replace("q", ""), 10);
360
+ const question = questions[qIndex];
361
+ if (!question)
362
+ continue;
363
+ const selectedLabels = answer.selectedOptionIds
364
+ .map((id) => question.options[parseInt(id, 10)]?.label)
365
+ .filter(Boolean);
366
+ answerMap[question.question] = selectedLabels.join(", ");
367
+ }
368
+ return { behavior: "allow", message: JSON.stringify(answerMap) };
369
+ }
370
+ catch (error) {
371
+ logger.warn("wave/ask_question extMethod failed, falling back to requestPermission", { error });
372
+ return null;
373
+ }
374
+ }
375
+ async handleCreatePlan(sessionId, toolCallId, context) {
376
+ try {
377
+ const cachedTasks = this.taskCache.get(sessionId) || [];
378
+ const request = {
379
+ toolCallId,
380
+ plan: context.planContent || "",
381
+ todos: cachedTasks
382
+ .filter((t) => t.status !== "deleted")
383
+ .map((t) => ({
384
+ id: t.id,
385
+ content: t.subject,
386
+ status: t.status === "completed"
387
+ ? "completed"
388
+ : t.status === "in_progress"
389
+ ? "in_progress"
390
+ : "pending",
391
+ })),
392
+ };
393
+ const response = await this.connection.extMethod("wave/create_plan", request);
394
+ const outcome = response.outcome;
395
+ if (outcome === "accepted") {
396
+ const mode = response.mode;
397
+ return {
398
+ behavior: "allow",
399
+ newPermissionMode: (mode || "default"),
400
+ };
401
+ }
402
+ if (outcome === "rejected") {
403
+ const reason = response.reason;
404
+ return { behavior: "deny", message: reason || "Plan rejected" };
405
+ }
406
+ // cancelled or unknown outcome
407
+ return { behavior: "deny", message: "Cancelled by user" };
408
+ }
409
+ catch (error) {
410
+ logger.warn("wave/create_plan extMethod failed, falling back to requestPermission", { error });
411
+ return null;
412
+ }
413
+ }
330
414
  async handlePermissionRequest(sessionId, context) {
331
415
  logger.info(`Handling permission request for ${context.toolName} in session ${sessionId}`);
332
416
  const agent = this.agents.get(sessionId);
@@ -420,6 +504,19 @@ export class WaveAcpAgent {
420
504
  const kind = context.toolName
421
505
  ? this.getToolKind(context.toolName)
422
506
  : undefined;
507
+ // Try extension methods first, fall back to requestPermission
508
+ if (context.toolName === ASK_USER_QUESTION_TOOL_NAME) {
509
+ const extResult = await this.handleAskQuestion(sessionId, toolCallId, context);
510
+ if (extResult !== null)
511
+ return extResult;
512
+ // Fall through to existing requestPermission logic
513
+ }
514
+ if (context.toolName === EXIT_PLAN_MODE_TOOL_NAME) {
515
+ const extResult = await this.handleCreatePlan(sessionId, toolCallId, context);
516
+ if (extResult !== null)
517
+ return extResult;
518
+ // Fall through to existing requestPermission logic
519
+ }
423
520
  try {
424
521
  const response = await this.connection.requestPermission({
425
522
  sessionId: sessionId,
@@ -612,15 +709,7 @@ export class WaveAcpAgent {
612
709
  }
613
710
  async replayConversationHistory(agent) {
614
711
  const sessionId = agent.sessionId;
615
- let history;
616
- try {
617
- const thread = await agent.getFullMessageThread();
618
- history = thread.messages;
619
- }
620
- catch {
621
- // Fallback to in-memory messages if full thread fails
622
- history = agent.messages;
623
- }
712
+ const history = agent.messages;
624
713
  for (const message of history) {
625
714
  if (message.isMeta)
626
715
  continue;
@@ -719,202 +808,248 @@ export class WaveAcpAgent {
719
808
  },
720
809
  });
721
810
  }
722
- // Skip: image, bang, compact, error, file_history, task_notification blocks
811
+ else if (block.type === "compact") {
812
+ const compactBlock = block;
813
+ this.connection.sessionUpdate({
814
+ sessionId,
815
+ update: {
816
+ sessionUpdate: "agent_message_chunk",
817
+ content: { type: "text", text: compactBlock.content },
818
+ messageId,
819
+ },
820
+ });
821
+ }
822
+ // Skip: image, bang, error, file_history, task_notification blocks
723
823
  }
724
824
  }
725
825
  }
726
826
  createCallbacks(sessionId) {
727
- const getAgent = () => this.agents.get(sessionId);
827
+ const sessionRef = { id: sessionId };
828
+ const getAgent = () => this.agents.get(sessionRef.id);
728
829
  const toolStates = new Map();
729
830
  return {
730
- onAssistantContentUpdated: (chunk) => {
731
- this.connection.sessionUpdate({
732
- sessionId: sessionId,
733
- update: {
734
- sessionUpdate: "agent_message_chunk",
735
- content: {
736
- type: "text",
737
- text: chunk,
831
+ callbacks: {
832
+ onAssistantContentUpdated: (chunk) => {
833
+ this.connection.sessionUpdate({
834
+ sessionId: sessionRef.id,
835
+ update: {
836
+ sessionUpdate: "agent_message_chunk",
837
+ content: {
838
+ type: "text",
839
+ text: chunk,
840
+ },
738
841
  },
739
- },
740
- });
741
- },
742
- onAssistantReasoningUpdated: (chunk) => {
743
- this.connection.sessionUpdate({
744
- sessionId: sessionId,
745
- update: {
746
- sessionUpdate: "agent_thought_chunk",
747
- content: {
748
- type: "text",
749
- text: chunk,
842
+ });
843
+ },
844
+ onAssistantReasoningUpdated: (chunk) => {
845
+ this.connection.sessionUpdate({
846
+ sessionId: sessionRef.id,
847
+ update: {
848
+ sessionUpdate: "agent_thought_chunk",
849
+ content: {
850
+ type: "text",
851
+ text: chunk,
852
+ },
750
853
  },
751
- },
752
- });
753
- },
754
- onToolBlockUpdated: (params) => {
755
- const { id, name, stage, success, error, result, parameters, compactParams, shortResult, startLineNumber, } = params;
756
- let state = toolStates.get(id);
757
- if (!state) {
758
- state = {};
759
- toolStates.set(id, state);
760
- }
761
- if (name)
762
- state.name = name;
763
- if (compactParams)
764
- state.compactParams = compactParams;
765
- if (shortResult)
766
- state.shortResult = shortResult;
767
- if (startLineNumber !== undefined)
768
- state.startLineNumber = startLineNumber;
769
- const effectiveName = state.name || name;
770
- const effectiveCompactParams = state.compactParams || compactParams;
771
- const effectiveShortResult = state.shortResult || shortResult;
772
- const effectiveStartLineNumber = state.startLineNumber !== undefined
773
- ? state.startLineNumber
774
- : startLineNumber;
775
- const displayTitle = effectiveName && effectiveCompactParams
776
- ? `${effectiveName}: ${effectiveCompactParams}`
777
- : effectiveName || "Tool Call";
778
- let parsedParameters = undefined;
779
- if (parameters) {
780
- try {
781
- const parsed = JSON.parse(parameters);
782
- parsedParameters = Array.isArray(parsed)
783
- ? { args: parsed }
784
- : parsed;
854
+ });
855
+ },
856
+ onToolBlockUpdated: (params) => {
857
+ const { id, name, stage, success, error, result, parameters, compactParams, shortResult, startLineNumber, } = params;
858
+ let state = toolStates.get(id);
859
+ if (!state) {
860
+ state = {};
861
+ toolStates.set(id, state);
785
862
  }
786
- catch {
787
- // Ignore parse errors during streaming
863
+ if (name)
864
+ state.name = name;
865
+ if (compactParams)
866
+ state.compactParams = compactParams;
867
+ if (shortResult)
868
+ state.shortResult = shortResult;
869
+ if (startLineNumber !== undefined)
870
+ state.startLineNumber = startLineNumber;
871
+ const effectiveName = state.name || name;
872
+ const effectiveCompactParams = state.compactParams || compactParams;
873
+ const effectiveShortResult = state.shortResult || shortResult;
874
+ const effectiveStartLineNumber = state.startLineNumber !== undefined
875
+ ? state.startLineNumber
876
+ : startLineNumber;
877
+ const displayTitle = effectiveName && effectiveCompactParams
878
+ ? `${effectiveName}: ${effectiveCompactParams}`
879
+ : effectiveName || "Tool Call";
880
+ let parsedParameters = undefined;
881
+ if (parameters) {
882
+ try {
883
+ const parsed = JSON.parse(parameters);
884
+ parsedParameters = Array.isArray(parsed)
885
+ ? { args: parsed }
886
+ : parsed;
887
+ }
888
+ catch {
889
+ // Ignore parse errors during streaming
890
+ }
788
891
  }
789
- }
790
- const content = effectiveName && (parsedParameters || effectiveShortResult)
791
- ? this.getToolContent(effectiveName, parsedParameters, effectiveShortResult)
792
- : undefined;
793
- const locations = effectiveName && parsedParameters
794
- ? this.getToolLocations(effectiveName, parsedParameters, effectiveStartLineNumber)
795
- : undefined;
796
- const kind = effectiveName
797
- ? this.getToolKind(effectiveName)
798
- : undefined;
799
- if (stage === "start") {
892
+ const content = effectiveName && (parsedParameters || effectiveShortResult)
893
+ ? this.getToolContent(effectiveName, parsedParameters, effectiveShortResult)
894
+ : undefined;
895
+ const locations = effectiveName && parsedParameters
896
+ ? this.getToolLocations(effectiveName, parsedParameters, effectiveStartLineNumber)
897
+ : undefined;
898
+ const kind = effectiveName
899
+ ? this.getToolKind(effectiveName)
900
+ : undefined;
901
+ if (stage === "start") {
902
+ this.connection.sessionUpdate({
903
+ sessionId: sessionRef.id,
904
+ update: {
905
+ sessionUpdate: "tool_call",
906
+ toolCallId: id,
907
+ title: displayTitle,
908
+ status: "pending",
909
+ content,
910
+ locations,
911
+ kind,
912
+ rawInput: parsedParameters,
913
+ },
914
+ });
915
+ return;
916
+ }
917
+ if (stage === "streaming") {
918
+ // We don't support streaming tool arguments in ACP yet
919
+ return;
920
+ }
921
+ const status = stage === "end"
922
+ ? success
923
+ ? "completed"
924
+ : "failed"
925
+ : stage === "running"
926
+ ? "in_progress"
927
+ : "pending";
800
928
  this.connection.sessionUpdate({
801
- sessionId: sessionId,
929
+ sessionId: sessionRef.id,
802
930
  update: {
803
- sessionUpdate: "tool_call",
931
+ sessionUpdate: "tool_call_update",
804
932
  toolCallId: id,
933
+ status,
805
934
  title: displayTitle,
806
- status: "pending",
935
+ rawOutput: result || error,
807
936
  content,
808
937
  locations,
809
938
  kind,
810
939
  rawInput: parsedParameters,
811
940
  },
812
941
  });
813
- return;
814
- }
815
- if (stage === "streaming") {
816
- // We don't support streaming tool arguments in ACP yet
817
- return;
818
- }
819
- const status = stage === "end"
820
- ? success
821
- ? "completed"
822
- : "failed"
823
- : stage === "running"
824
- ? "in_progress"
825
- : "pending";
826
- this.connection.sessionUpdate({
827
- sessionId: sessionId,
828
- update: {
829
- sessionUpdate: "tool_call_update",
830
- toolCallId: id,
831
- status,
832
- title: displayTitle,
833
- rawOutput: result || error,
834
- content,
835
- locations,
836
- kind,
837
- rawInput: parsedParameters,
838
- },
839
- });
840
- if (stage === "end") {
841
- toolStates.delete(id);
842
- }
843
- },
844
- onTasksChange: (tasks) => {
845
- this.connection.sessionUpdate({
846
- sessionId: sessionId,
847
- update: {
848
- sessionUpdate: "plan",
849
- entries: tasks.map((task) => ({
850
- content: task.subject,
851
- status: task.status === "completed"
852
- ? "completed"
853
- : task.status === "in_progress"
854
- ? "in_progress"
855
- : "pending",
856
- priority: "medium",
857
- })),
858
- },
859
- });
860
- },
861
- onPermissionModeChange: (mode) => {
862
- this.connection.sessionUpdate({
863
- sessionId: sessionId,
864
- update: {
865
- sessionUpdate: "current_mode_update",
866
- currentModeId: mode,
867
- },
868
- });
869
- const agent = getAgent();
870
- if (agent) {
942
+ if (stage === "end") {
943
+ toolStates.delete(id);
944
+ }
945
+ },
946
+ onTasksChange: (tasks) => {
947
+ this.taskCache.set(sessionRef.id, tasks);
871
948
  this.connection.sessionUpdate({
872
- sessionId: sessionId,
949
+ sessionId: sessionRef.id,
873
950
  update: {
874
- sessionUpdate: "config_option_update",
875
- configOptions: this.getSessionConfigOptions(agent),
951
+ sessionUpdate: "plan",
952
+ entries: tasks
953
+ .filter((t) => t.status !== "deleted")
954
+ .map((task) => ({
955
+ content: task.subject,
956
+ status: task.status === "completed"
957
+ ? "completed"
958
+ : task.status === "in_progress"
959
+ ? "in_progress"
960
+ : "pending",
961
+ priority: "medium",
962
+ })),
876
963
  },
877
964
  });
878
- }
879
- },
880
- onModelChange: () => {
881
- const agent = getAgent();
882
- if (agent) {
965
+ },
966
+ onPermissionModeChange: (mode) => {
883
967
  this.connection.sessionUpdate({
884
- sessionId: sessionId,
968
+ sessionId: sessionRef.id,
885
969
  update: {
886
- sessionUpdate: "config_option_update",
887
- configOptions: this.getSessionConfigOptions(agent),
970
+ sessionUpdate: "current_mode_update",
971
+ currentModeId: mode,
888
972
  },
889
973
  });
890
- }
891
- },
892
- onUserMessageAdded: (params) => {
893
- this.connection.sessionUpdate({
894
- sessionId: sessionId,
895
- update: {
896
- sessionUpdate: "user_message_chunk",
897
- content: { type: "text", text: params.content },
898
- },
899
- });
900
- },
901
- onMcpServersChange: (servers) => {
902
- for (const server of servers) {
974
+ const agent = getAgent();
975
+ if (agent) {
976
+ this.connection.sessionUpdate({
977
+ sessionId: sessionRef.id,
978
+ update: {
979
+ sessionUpdate: "config_option_update",
980
+ configOptions: this.getSessionConfigOptions(agent),
981
+ },
982
+ });
983
+ }
984
+ },
985
+ onModelChange: () => {
986
+ const agent = getAgent();
987
+ if (agent) {
988
+ this.connection.sessionUpdate({
989
+ sessionId: sessionRef.id,
990
+ update: {
991
+ sessionUpdate: "config_option_update",
992
+ configOptions: this.getSessionConfigOptions(agent),
993
+ },
994
+ });
995
+ }
996
+ },
997
+ onUserMessageAdded: (params) => {
903
998
  this.connection.sessionUpdate({
904
- sessionId: sessionId,
999
+ sessionId: sessionRef.id,
905
1000
  update: {
906
- sessionUpdate: "ext_notification",
907
- method: "mcp_server_status",
908
- params: {
909
- name: server.name,
910
- status: server.status,
911
- toolCount: server.toolCount,
912
- error: server.error,
1001
+ sessionUpdate: "user_message_chunk",
1002
+ content: { type: "text", text: params.content },
1003
+ },
1004
+ });
1005
+ },
1006
+ onMcpServersChange: (servers) => {
1007
+ for (const server of servers) {
1008
+ this.connection.sessionUpdate({
1009
+ sessionId: sessionRef.id,
1010
+ update: {
1011
+ sessionUpdate: "ext_notification",
1012
+ method: "mcp_server_status",
1013
+ params: {
1014
+ name: server.name,
1015
+ status: server.status,
1016
+ toolCount: server.toolCount,
1017
+ error: server.error,
1018
+ },
913
1019
  },
1020
+ });
1021
+ }
1022
+ },
1023
+ onSessionIdChange: (newSessionId) => {
1024
+ const oldSessionId = sessionRef.id;
1025
+ if (oldSessionId === newSessionId)
1026
+ return;
1027
+ // Update agents Map key
1028
+ const agent = this.agents.get(oldSessionId);
1029
+ if (agent) {
1030
+ this.agents.delete(oldSessionId);
1031
+ this.agents.set(newSessionId, agent);
1032
+ }
1033
+ // Update taskCache key
1034
+ const tasks = this.taskCache.get(oldSessionId);
1035
+ if (tasks) {
1036
+ this.taskCache.delete(oldSessionId);
1037
+ this.taskCache.set(newSessionId, tasks);
1038
+ }
1039
+ // Update ref so all subsequent callbacks use new ID
1040
+ sessionRef.id = newSessionId;
1041
+ // Notify ACP client
1042
+ this.connection.sessionUpdate({
1043
+ sessionId: oldSessionId,
1044
+ update: {
1045
+ sessionUpdate: "ext_notification",
1046
+ method: "session_id_change",
1047
+ params: { oldSessionId, newSessionId },
914
1048
  },
915
1049
  });
916
- }
1050
+ },
917
1051
  },
1052
+ sessionRef,
918
1053
  };
919
1054
  }
920
1055
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BtwDisplay.d.ts","sourceRoot":"","sources":["../../src/components/BtwDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,UAAU,eAAe;IACvB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAuBhD,CAAC"}
1
+ {"version":3,"file":"BtwDisplay.d.ts","sourceRoot":"","sources":["../../src/components/BtwDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,UAAU,eAAe;IACvB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA0BhD,CAAC"}
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { Markdown } from "./Markdown.js";
4
4
  export const BtwDisplay = ({ btwState }) => {
5
- if (!btwState.isActive) {
5
+ if (!btwState.question) {
6
6
  return null;
7
7
  }
8
- return (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginBottom: 0, children: [btwState.question && (_jsxs(Box, { children: [_jsx(Text, { color: btwState.isLoading ? "yellow" : "green", children: "/ " }), _jsxs(Text, { italic: true, color: "gray", children: ["btw ", btwState.question] })] })), btwState.answer && (_jsx(Box, { flexDirection: "column", children: _jsx(Markdown, { children: btwState.answer }) }))] }));
8
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginBottom: 0, children: [btwState.question && (_jsxs(Box, { children: [_jsx(Text, { color: btwState.isLoading ? "yellow" : "green", children: "/ " }), _jsxs(Text, { italic: true, color: "gray", children: ["btw ", btwState.question] })] })), btwState.answer && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Markdown, { children: btwState.answer }), _jsx(Text, { color: "gray", dimColor: true, children: "ESC to dismiss" })] }))] }));
9
9
  };
@@ -21,7 +21,7 @@ import { useChat } from "../contexts/useChat.js";
21
21
  export const INPUT_PLACEHOLDER_TEXT = "Type your message (use /help for more info)...";
22
22
  export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
23
23
  export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, latestTotalTokens = 0, maxInputTokens = 128000, isGoalActive, goalElapsed, }) => {
24
- const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, currentModel, configuredModels, setModel, recreateAgent, recallQueuedMessage, queuedMessages, } = useChat();
24
+ const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, clearMessages, compact, goalCommand, currentModel, configuredModels, setModel, recreateAgent, recallQueuedMessage, queuedMessages, } = useChat();
25
25
  // Ref to hold setInputText so queue callbacks can access it before useInputManager returns
26
26
  const setInputTextRef = useRef(() => { });
27
27
  const hasQueuedMessages = (queuedMessages?.length ?? 0) > 0;
@@ -56,6 +56,9 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
56
56
  setInputText, } = useInputManager({
57
57
  onSendMessage: sendMessage,
58
58
  onAskBtw: askBtw,
59
+ onClearMessages: clearMessages,
60
+ onCompact: compact,
61
+ onGoalCommand: goalCommand,
59
62
  onHasSlashCommand: hasSlashCommand,
60
63
  onAbortMessage: abortMessage,
61
64
  onBackgroundCurrentTask: backgroundCurrentTask,
@@ -136,12 +139,14 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
136
139
  if (showModelSelector) {
137
140
  return (_jsx(ModelSelector, { onCancel: () => setShowModelSelector(false), currentModel: currentModel, configuredModels: configuredModels, onSelectModel: setModel }));
138
141
  }
139
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showWorkflowManager && (_jsx(WorkflowManager, { onCancel: () => setShowWorkflowManager(false) })), showBackgroundTaskManager ||
140
- showMcpManager ||
141
- showRewindManager ||
142
- showHelp ||
143
- showStatusCommand ||
144
- showLoginCommand ||
145
- showPluginManager ||
146
- showWorkflowManager || (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "single", borderColor: btwState.isActive ? "cyan" : "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: btwState.isActive && isPlaceholder ? (_jsxs(_Fragment, { children: [_jsx(Text, { backgroundColor: "white", color: "black", children: " " }), _jsx(Text, { color: "cyan", children: "Type your side question..." })] })) : shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsx(StatusLine, { permissionMode: permissionMode, isShellCommand: isShellCommand, isBtwActive: btwState.isActive, isGoalActive: isGoalActive, goalElapsed: goalElapsed, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens })] }))] }));
142
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showWorkflowManager && (_jsx(WorkflowManager, { onCancel: () => setShowWorkflowManager(false) })), btwState.question
143
+ ? null
144
+ : showBackgroundTaskManager ||
145
+ showMcpManager ||
146
+ showRewindManager ||
147
+ showHelp ||
148
+ showStatusCommand ||
149
+ showLoginCommand ||
150
+ showPluginManager ||
151
+ showWorkflowManager || (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "single", borderColor: "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsx(StatusLine, { permissionMode: permissionMode, isShellCommand: isShellCommand, isGoalActive: isGoalActive, goalElapsed: goalElapsed, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens })] }))] }));
147
152
  };