wave-code 0.12.11 → 0.13.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.
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAoBA,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,EAMpC,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;IAkC3B,OAAO,CAAC,uBAAuB;YAmBjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAwBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IAiEnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,qBAAqB,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,SAAS,CACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAO7B,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D,sBAAsB,CAC1B,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,8BAA8B,CAAC;IAqBpC,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;IA8KrC,OAAO,CAAC,cAAc;IAwGtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,eAAe;CAmMxB"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/acp/agent.ts"],"names":[],"mappings":"AAqBA,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,EAMpC,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;IAkC3B,OAAO,CAAC,uBAAuB;YAmCjB,gBAAgB;IASxB,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAwBzC,YAAY,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAI5C,WAAW;IAkEnB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalE,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWrE,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,qBAAqB,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAc7B,SAAS,CACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAO7B,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;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;IAwGtB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,eAAe;CA+MxB"}
package/dist/acp/agent.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Agent as WaveAgent, listSessions as listWaveSessions, listAllSessions as listAllWaveSessions, deleteSession as deleteWaveSession, truncateContent, BASH_TOOL_NAME, EDIT_TOOL_NAME, WRITE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
1
+ import { Agent as WaveAgent, listSessions as listWaveSessions, listAllSessions as listAllWaveSessions, deleteSession as deleteWaveSession, truncateContent, BASH_TOOL_NAME, EDIT_TOOL_NAME, WRITE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
2
2
  import { logger } from "../utils/logger.js";
3
3
  import { AGENT_METHODS, } from "@agentclientprotocol/sdk";
4
4
  export class WaveAcpAgent {
@@ -39,10 +39,13 @@ export class WaveAcpAgent {
39
39
  };
40
40
  }
41
41
  getSessionConfigOptions(agent) {
42
+ const configuredModels = agent.getConfiguredModels();
43
+ const currentModel = agent.getModelConfig().model;
42
44
  return [
43
45
  {
44
46
  id: "permission_mode",
45
47
  name: "Permission Mode",
48
+ description: "Controls how the agent requests permission",
46
49
  type: "select",
47
50
  category: "mode",
48
51
  currentValue: agent.getPermissionMode(),
@@ -54,6 +57,18 @@ export class WaveAcpAgent {
54
57
  { value: "dontAsk", name: "Don't Ask" },
55
58
  ],
56
59
  },
60
+ {
61
+ id: "model",
62
+ name: "Model",
63
+ description: "The AI model to use for this session",
64
+ type: "select",
65
+ category: "model",
66
+ currentValue: currentModel,
67
+ options: configuredModels.map((m) => ({
68
+ value: m,
69
+ name: m,
70
+ })),
71
+ },
57
72
  ];
58
73
  }
59
74
  async cleanupAllAgents() {
@@ -110,6 +125,7 @@ export class WaveAcpAgent {
110
125
  },
111
126
  onTasksChange: (tasks) => callbacks.onTasksChange?.(tasks),
112
127
  onPermissionModeChange: (mode) => callbacks.onPermissionModeChange?.(mode),
128
+ onModelChange: (model) => callbacks.onModelChange?.(model),
113
129
  },
114
130
  });
115
131
  agentRef.instance = agent;
@@ -211,6 +227,9 @@ export class WaveAcpAgent {
211
227
  if (configId === "permission_mode") {
212
228
  agent.setPermissionMode(value);
213
229
  }
230
+ else if (configId === "model" && typeof value === "string") {
231
+ agent.setModel(value);
232
+ }
214
233
  return {
215
234
  configOptions: this.getSessionConfigOptions(agent),
216
235
  };
@@ -366,6 +385,20 @@ export class WaveAcpAgent {
366
385
  },
367
386
  ];
368
387
  }
388
+ else if (context.toolName === ENTER_PLAN_MODE_TOOL_NAME) {
389
+ options = [
390
+ {
391
+ optionId: "allow_once",
392
+ name: "Yes, enter plan mode",
393
+ kind: "allow_once",
394
+ },
395
+ {
396
+ optionId: "reject_once",
397
+ name: "No, start implementing now",
398
+ kind: "reject_once",
399
+ },
400
+ ];
401
+ }
369
402
  else if (context.toolName === ASK_USER_QUESTION_TOOL_NAME) {
370
403
  options = [];
371
404
  }
@@ -429,6 +462,9 @@ export class WaveAcpAgent {
429
462
  if (context.toolName === EXIT_PLAN_MODE_TOOL_NAME) {
430
463
  return { behavior: "allow", newPermissionMode: "default" };
431
464
  }
465
+ if (context.toolName === ENTER_PLAN_MODE_TOOL_NAME) {
466
+ return { behavior: "allow", newPermissionMode: "plan" };
467
+ }
432
468
  return { behavior: "allow" };
433
469
  case "reject_once":
434
470
  return { behavior: "deny", message: "Rejected by user" };
@@ -729,6 +765,18 @@ export class WaveAcpAgent {
729
765
  });
730
766
  }
731
767
  },
768
+ onModelChange: () => {
769
+ const agent = getAgent();
770
+ if (agent) {
771
+ this.connection.sessionUpdate({
772
+ sessionId: sessionId,
773
+ update: {
774
+ sessionUpdate: "config_option_update",
775
+ configOptions: this.getSessionConfigOptions(agent),
776
+ },
777
+ });
778
+ }
779
+ },
732
780
  };
733
781
  }
734
782
  }
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { getLastLines } from "wave-agent-sdk";
4
4
  export const BangDisplay = ({ block, isExpanded = false, }) => {
5
- const { command, output, isRunning, exitCode } = block;
5
+ const { command, output, stage, exitCode } = block;
6
6
  const MAX_LINES = 3; // Set maximum display lines
7
7
  const getStatusColor = () => {
8
- if (isRunning)
8
+ if (stage === "running")
9
9
  return "yellow";
10
10
  if (exitCode === 0)
11
11
  return "green";
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmationDetails.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationDetails.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAsC1B,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAoDlE,CAAC"}
1
+ {"version":3,"file":"ConfirmationDetails.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationDetails.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAyC1B,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAqDlE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
- import { BASH_TOOL_NAME, EDIT_TOOL_NAME, WRITE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
3
+ import { BASH_TOOL_NAME, EDIT_TOOL_NAME, WRITE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
4
4
  import { DiffDisplay } from "./DiffDisplay.js";
5
5
  import { PlanDisplay } from "./PlanDisplay.js";
6
6
  import { highlightToAnsi } from "../utils/highlightUtils.js";
@@ -18,6 +18,8 @@ const getActionDescription = (toolName, toolInput) => {
18
18
  return `Write to file: ${toolInput.file_path || "unknown file"}`;
19
19
  case EXIT_PLAN_MODE_TOOL_NAME:
20
20
  return "Review and approve the plan";
21
+ case ENTER_PLAN_MODE_TOOL_NAME:
22
+ return "Enter plan mode for complex task planning";
21
23
  case ASK_USER_QUESTION_TOOL_NAME:
22
24
  return "Answer questions to clarify intent";
23
25
  default:
@@ -30,6 +32,7 @@ export const ConfirmationDetails = ({ toolName, toolInput, planContent, isExpand
30
32
  const content = (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", borderBottom: false, borderLeft: false, borderRight: false, children: [_jsxs(Text, { color: "yellow", bold: true, children: ["Tool: ", toolName] }), _jsx(Text, { color: "yellow", children: getActionDescription(toolName, toolInput) }), _jsx(DiffDisplay, { toolName: toolName, parameters: JSON.stringify(toolInput), startLineNumber: startLineNumber }), toolName !== WRITE_TOOL_NAME &&
31
33
  toolName !== EDIT_TOOL_NAME &&
32
34
  toolName !== EXIT_PLAN_MODE_TOOL_NAME &&
35
+ toolName !== ENTER_PLAN_MODE_TOOL_NAME &&
33
36
  toolName !== ASK_USER_QUESTION_TOOL_NAME &&
34
37
  toolName !== BASH_TOOL_NAME &&
35
38
  !!toolInput && (_jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "cyan", children: _jsx(Text, { children: highlightToAnsi(JSON.stringify(toolInput, null, 2), "json") }) })), toolName !== ASK_USER_QUESTION_TOOL_NAME &&
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAgB/E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AASD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAgnBpE,CAAC"}
1
+ {"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAiB/E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AASD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CA6nBpE,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
- import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
4
+ import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
5
5
  const getHeaderColor = (header) => {
6
6
  const colors = ["red", "green", "blue", "magenta", "cyan"];
7
7
  let hash = 0;
@@ -285,6 +285,9 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
285
285
  if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
286
286
  onDecision({ behavior: "allow", newPermissionMode: "default" });
287
287
  }
288
+ else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
289
+ onDecision({ behavior: "allow", newPermissionMode: "plan" });
290
+ }
288
291
  else {
289
292
  onDecision({ behavior: "allow" });
290
293
  }
@@ -302,6 +305,9 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
302
305
  onDecision({ behavior: "allow", newPermissionRule: rule });
303
306
  }
304
307
  }
308
+ else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
309
+ onDecision({ behavior: "allow", newPermissionMode: "plan" });
310
+ }
305
311
  else if (toolName.startsWith("mcp__")) {
306
312
  onDecision({ behavior: "allow", newPermissionRule: toolName });
307
313
  }
@@ -312,6 +318,12 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
312
318
  else if (state.alternativeText.trim()) {
313
319
  onDecision({ behavior: "deny", message: state.alternativeText.trim() });
314
320
  }
321
+ else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
322
+ onDecision({
323
+ behavior: "deny",
324
+ message: "User chose not to enter plan mode",
325
+ });
326
+ }
315
327
  return;
316
328
  }
317
329
  if (state.selectedOption === "alternative") {
@@ -421,6 +433,6 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
421
433
  : "", option.label, option.description ? ` - ${option.description}` : "", isOther && isSelected && (_jsxs(Text, { children: [":", " ", questionState.otherText ? (_jsxs(_Fragment, { children: [questionState.otherText.slice(0, questionState.otherCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: questionState.otherText[questionState.otherCursorPosition] || " " }), questionState.otherText.slice(questionState.otherCursorPosition + 1)] })) : (_jsx(Text, { color: "gray", dimColor: true, children: "[Type your answer...]" }))] }))] }) }, index));
422
434
  }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Question ", questionState.currentQuestionIndex + 1, " of", " ", questions.length, " \u2022", currentQuestion.multiSelect ? " Space to toggle •" : "", " Use \u2191\u2193 or Tab to navigate \u2022 Enter to confirm"] }) })] })), toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Do you want to proceed?" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [toolName === EXIT_PLAN_MODE_TOOL_NAME && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "clear" ? "black" : "white", backgroundColor: state.selectedOption === "clear" ? "yellow" : undefined, bold: state.selectedOption === "clear", children: [state.selectedOption === "clear" ? "> " : " ", "Yes, clear context and auto-accept edits"] }) }, "clear-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "allow" ? "black" : "white", backgroundColor: state.selectedOption === "allow" ? "yellow" : undefined, bold: state.selectedOption === "allow", children: [state.selectedOption === "allow" ? "> " : " ", toolName === EXIT_PLAN_MODE_TOOL_NAME
423
435
  ? "Yes, manually approve edits"
424
- : "Yes, proceed"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ? (_jsxs(_Fragment, { children: [state.alternativeText.slice(0, state.alternativeCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: state.alternativeText[state.alternativeCursorPosition] || " " }), state.alternativeText.slice(state.alternativeCursorPosition + 1)] })) : ("Type here to tell Wave what to change") }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 or Tab to navigate \u2022 ESC to cancel" }) })] }))] }));
436
+ : "Yes, proceed"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", toolName === ENTER_PLAN_MODE_TOOL_NAME && showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: "No, start implementing now" })) : showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ? (_jsxs(_Fragment, { children: [state.alternativeText.slice(0, state.alternativeCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: state.alternativeText[state.alternativeCursorPosition] || " " }), state.alternativeText.slice(state.alternativeCursorPosition + 1)] })) : ("Type here to tell Wave what to change") }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 or Tab to navigate \u2022 ESC to cancel" }) })] }))] }));
425
437
  };
426
438
  ConfirmationSelector.displayName = "ConfirmationSelector";
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,uFAOnB,gBAAgB,6CAwGpB,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,uFAOnB,gBAAgB,6CAuIpB,CAAC"}
@@ -9,11 +9,18 @@ export const MessageList = React.memo(({ messages, isExpanded = false, forceStat
9
9
  const maxMessages = 10;
10
10
  // Filter out meta messages
11
11
  const visibleMessages = messages.filter((m) => !m.isMeta);
12
- const isRunning = (b) => (b.type === "tool" && b.stage === "running") ||
13
- (b.type === "bang" && b.isRunning);
12
+ const isRunning = (b) => (b.type === "tool" &&
13
+ (b.stage === "running" ||
14
+ b.stage === "streaming" ||
15
+ b.stage === "start")) ||
16
+ (b.type === "bang" && b.stage === "running");
14
17
  // Flatten messages into blocks with metadata
18
+ // Filter out streaming text/reasoning blocks to avoid frequent height changes
15
19
  const allBlocks = visibleMessages.flatMap((message, messageIndex) => {
16
- return message.blocks.map((block, blockIndex) => ({
20
+ return message.blocks
21
+ .filter((block) => !((block.type === "text" || block.type === "reasoning") &&
22
+ block.stage === "streaming"))
23
+ .map((block, blockIndex) => ({
17
24
  block,
18
25
  message,
19
26
  messageIndex,
@@ -21,11 +28,27 @@ export const MessageList = React.memo(({ messages, isExpanded = false, forceStat
21
28
  key: `${message.id}-${blockIndex}`,
22
29
  }));
23
30
  });
31
+ // Find message indices that have any running/streaming block
32
+ const runningMessageIndices = new Set();
33
+ for (const item of allBlocks) {
34
+ if (isRunning(item.block)) {
35
+ runningMessageIndices.add(item.messageIndex);
36
+ }
37
+ }
24
38
  // Determine which blocks are static vs dynamic
25
- // Only mark individual running blocks as dynamic, not entire messages
39
+ // Blocks not in the last message are always static.
40
+ // For the last message: if any block is running/streaming, blocks in that message are dynamic,
41
+ // except text/reasoning blocks that have already completed (stage === "end")
42
+ const lastMessageIndex = visibleMessages.length - 1;
26
43
  const blocksWithStatus = allBlocks.map((item) => {
27
- const { block } = item;
28
- const isDynamic = !forceStatic && !isExpanded && isRunning(block);
44
+ const isInLastMessage = item.messageIndex === lastMessageIndex;
45
+ const isBlockCompleted = (item.block.type === "text" || item.block.type === "reasoning") &&
46
+ item.block.stage === "end";
47
+ const isDynamic = !forceStatic &&
48
+ !isExpanded &&
49
+ isInLastMessage &&
50
+ !isBlockCompleted &&
51
+ runningMessageIndices.has(item.messageIndex);
29
52
  return { ...item, isDynamic };
30
53
  });
31
54
  const staticBlocks = blocksWithStatus.filter((b) => !b.isDynamic);
@@ -1 +1 @@
1
- {"version":3,"file":"ToolDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,UAAU,gBAAgB;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA+HlD,CAAC"}
1
+ {"version":3,"file":"ToolDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,UAAU,gBAAgB;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA0IlD,CAAC"}
@@ -37,5 +37,11 @@ export const ToolDisplay = ({ block, isExpanded = false, }) => {
37
37
  return null;
38
38
  };
39
39
  const shortResult = getShortResult();
40
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Box, { flexShrink: 0, children: [_jsx(Text, { color: getStatusColor(), children: "\u25CF " }), _jsx(Text, { color: "white", children: toolName })] }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" ", compactParams] })), hasImages() && _jsxs(Text, { color: "blue", children: [" ", getImageIndicator()] })] }), !isExpanded && shortResult && !error && (_jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: shortResult.split("\n").map((line, index) => (_jsx(Text, { color: "gray", wrap: "truncate-end", children: line }, index))) })), isExpanded && parameters && (_jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Parameters:" }), _jsx(Text, { color: "gray", children: parameters })] })), isExpanded && result && (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "green", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Result:" }), _jsx(Text, { color: "white", children: result })] }) })), error && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", typeof error === "string" ? error : String(error)] }) })), !isExpanded && stage === "end" && success && (_jsx(DiffDisplay, { toolName: name, parameters: parameters, startLineNumber: block.startLineNumber }))] }));
40
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Box, { flexShrink: 0, children: [_jsx(Text, { color: getStatusColor(), children: "\u25CF " }), _jsx(Text, { color: "white", children: toolName })] }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" ", compactParams] })), !isExpanded &&
41
+ stage === "streaming" &&
42
+ parameters &&
43
+ !compactParams && (_jsx(Text, { color: "gray", wrap: "truncate-end", children: ` ${(() => {
44
+ const flat = parameters.replace(/\n/g, "\\n");
45
+ return flat.length > 30 ? `…${flat.slice(-30)}` : flat;
46
+ })()}` })), hasImages() && _jsxs(Text, { color: "blue", children: [" ", getImageIndicator()] })] }), !isExpanded && shortResult && !error && (_jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: shortResult.split("\n").map((line, index) => (_jsx(Text, { color: "gray", wrap: "truncate-end", children: line }, index))) })), isExpanded && parameters && (_jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Parameters:" }), _jsx(Text, { color: "gray", children: parameters })] })), isExpanded && result && (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "green", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Result:" }), _jsx(Text, { color: "white", children: result })] }) })), error && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", typeof error === "string" ? error : String(error)] }) })), !isExpanded && stage === "end" && success && (_jsx(DiffDisplay, { toolName: name, parameters: parameters, startLineNumber: block.startLineNumber }))] }));
41
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
@@ -172,7 +172,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
172
172
  permissionMode: initialPermissionMode ||
173
173
  (bypassPermissions ? "bypassPermissions" : undefined),
174
174
  canUseTool: permissionCallback,
175
- stream: false, // 关闭流式模式
175
+ stream: true,
176
176
  plugins: pluginDirs?.map((path) => ({ type: "local", path })),
177
177
  tools,
178
178
  allowedTools,
@@ -220,6 +220,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
220
220
  model,
221
221
  initialPermissionMode,
222
222
  throttledSetMessages,
223
+ throttledSetTokens,
223
224
  ]);
224
225
  // Cleanup on unmount
225
226
  useEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-code",
3
- "version": "0.12.11",
3
+ "version": "0.13.1",
4
4
  "description": "CLI-based code assistant powered by AI, built with React and Ink",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,7 +42,7 @@
42
42
  "semver": "^7.7.4",
43
43
  "yargs": "^17.7.2",
44
44
  "zod": "^3.23.8",
45
- "wave-agent-sdk": "0.12.11"
45
+ "wave-agent-sdk": "0.13.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/react": "^19.1.8",
package/src/acp/agent.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  EDIT_TOOL_NAME,
14
14
  WRITE_TOOL_NAME,
15
15
  EXIT_PLAN_MODE_TOOL_NAME,
16
+ ENTER_PLAN_MODE_TOOL_NAME,
16
17
  ASK_USER_QUESTION_TOOL_NAME,
17
18
  AskUserQuestion,
18
19
  AskUserQuestionOption,
@@ -94,10 +95,14 @@ export class WaveAcpAgent implements AcpAgent {
94
95
  }
95
96
 
96
97
  private getSessionConfigOptions(agent: WaveAgent): SessionConfigOption[] {
98
+ const configuredModels = agent.getConfiguredModels();
99
+ const currentModel = agent.getModelConfig().model;
100
+
97
101
  return [
98
102
  {
99
103
  id: "permission_mode",
100
104
  name: "Permission Mode",
105
+ description: "Controls how the agent requests permission",
101
106
  type: "select",
102
107
  category: "mode",
103
108
  currentValue: agent.getPermissionMode(),
@@ -109,6 +114,18 @@ export class WaveAcpAgent implements AcpAgent {
109
114
  { value: "dontAsk", name: "Don't Ask" },
110
115
  ],
111
116
  },
117
+ {
118
+ id: "model",
119
+ name: "Model",
120
+ description: "The AI model to use for this session",
121
+ type: "select",
122
+ category: "model",
123
+ currentValue: currentModel,
124
+ options: configuredModels.map((m) => ({
125
+ value: m,
126
+ name: m,
127
+ })),
128
+ },
112
129
  ];
113
130
  }
114
131
 
@@ -183,6 +200,7 @@ export class WaveAcpAgent implements AcpAgent {
183
200
  onTasksChange: (tasks) => callbacks.onTasksChange?.(tasks as Task[]),
184
201
  onPermissionModeChange: (mode) =>
185
202
  callbacks.onPermissionModeChange?.(mode),
203
+ onModelChange: (model) => callbacks.onModelChange?.(model),
186
204
  },
187
205
  });
188
206
 
@@ -319,6 +337,8 @@ export class WaveAcpAgent implements AcpAgent {
319
337
  | "bypassPermissions"
320
338
  | "dontAsk",
321
339
  );
340
+ } else if (configId === "model" && typeof value === "string") {
341
+ agent.setModel(value);
322
342
  }
323
343
 
324
344
  return {
@@ -502,6 +522,19 @@ export class WaveAcpAgent implements AcpAgent {
502
522
  kind: "allow_always",
503
523
  },
504
524
  ];
525
+ } else if (context.toolName === ENTER_PLAN_MODE_TOOL_NAME) {
526
+ options = [
527
+ {
528
+ optionId: "allow_once",
529
+ name: "Yes, enter plan mode",
530
+ kind: "allow_once",
531
+ },
532
+ {
533
+ optionId: "reject_once",
534
+ name: "No, start implementing now",
535
+ kind: "reject_once",
536
+ },
537
+ ];
505
538
  } else if (context.toolName === ASK_USER_QUESTION_TOOL_NAME) {
506
539
  options = [];
507
540
  }
@@ -578,6 +611,9 @@ export class WaveAcpAgent implements AcpAgent {
578
611
  if (context.toolName === EXIT_PLAN_MODE_TOOL_NAME) {
579
612
  return { behavior: "allow", newPermissionMode: "default" };
580
613
  }
614
+ if (context.toolName === ENTER_PLAN_MODE_TOOL_NAME) {
615
+ return { behavior: "allow", newPermissionMode: "plan" };
616
+ }
581
617
  return { behavior: "allow" };
582
618
  case "reject_once":
583
619
  return { behavior: "deny", message: "Rejected by user" };
@@ -944,6 +980,18 @@ export class WaveAcpAgent implements AcpAgent {
944
980
  });
945
981
  }
946
982
  },
983
+ onModelChange: () => {
984
+ const agent = getAgent();
985
+ if (agent) {
986
+ this.connection.sessionUpdate({
987
+ sessionId: sessionId as AcpSessionId,
988
+ update: {
989
+ sessionUpdate: "config_option_update",
990
+ configOptions: this.getSessionConfigOptions(agent),
991
+ },
992
+ });
993
+ }
994
+ },
947
995
  };
948
996
  }
949
997
  }
@@ -12,11 +12,11 @@ export const BangDisplay: React.FC<BangDisplayProps> = ({
12
12
  block,
13
13
  isExpanded = false,
14
14
  }) => {
15
- const { command, output, isRunning, exitCode } = block;
15
+ const { command, output, stage, exitCode } = block;
16
16
  const MAX_LINES = 3; // Set maximum display lines
17
17
 
18
18
  const getStatusColor = () => {
19
- if (isRunning) return "yellow";
19
+ if (stage === "running") return "yellow";
20
20
  if (exitCode === 0) return "green";
21
21
  if (exitCode !== null && exitCode !== 0) return "red";
22
22
  return "gray"; // Unknown state
@@ -5,6 +5,7 @@ import {
5
5
  EDIT_TOOL_NAME,
6
6
  WRITE_TOOL_NAME,
7
7
  EXIT_PLAN_MODE_TOOL_NAME,
8
+ ENTER_PLAN_MODE_TOOL_NAME,
8
9
  ASK_USER_QUESTION_TOOL_NAME,
9
10
  } from "wave-agent-sdk";
10
11
  import { DiffDisplay } from "./DiffDisplay.js";
@@ -29,6 +30,8 @@ const getActionDescription = (
29
30
  return `Write to file: ${toolInput.file_path || "unknown file"}`;
30
31
  case EXIT_PLAN_MODE_TOOL_NAME:
31
32
  return "Review and approve the plan";
33
+ case ENTER_PLAN_MODE_TOOL_NAME:
34
+ return "Enter plan mode for complex task planning";
32
35
  case ASK_USER_QUESTION_TOOL_NAME:
33
36
  return "Answer questions to clarify intent";
34
37
  default:
@@ -76,6 +79,7 @@ export const ConfirmationDetails: React.FC<ConfirmationDetailsProps> = ({
76
79
  {toolName !== WRITE_TOOL_NAME &&
77
80
  toolName !== EDIT_TOOL_NAME &&
78
81
  toolName !== EXIT_PLAN_MODE_TOOL_NAME &&
82
+ toolName !== ENTER_PLAN_MODE_TOOL_NAME &&
79
83
  toolName !== ASK_USER_QUESTION_TOOL_NAME &&
80
84
  toolName !== BASH_TOOL_NAME &&
81
85
  !!toolInput && (
@@ -4,6 +4,7 @@ import type { PermissionDecision, AskUserQuestionInput } from "wave-agent-sdk";
4
4
  import {
5
5
  BASH_TOOL_NAME,
6
6
  EXIT_PLAN_MODE_TOOL_NAME,
7
+ ENTER_PLAN_MODE_TOOL_NAME,
7
8
  ASK_USER_QUESTION_TOOL_NAME,
8
9
  } from "wave-agent-sdk";
9
10
 
@@ -353,6 +354,8 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
353
354
  } else if (state.selectedOption === "allow") {
354
355
  if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
355
356
  onDecision({ behavior: "allow", newPermissionMode: "default" });
357
+ } else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
358
+ onDecision({ behavior: "allow", newPermissionMode: "plan" });
356
359
  } else {
357
360
  onDecision({ behavior: "allow" });
358
361
  }
@@ -367,6 +370,8 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
367
370
  : `Bash(${toolInput?.command})`;
368
371
  onDecision({ behavior: "allow", newPermissionRule: rule });
369
372
  }
373
+ } else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
374
+ onDecision({ behavior: "allow", newPermissionMode: "plan" });
370
375
  } else if (toolName.startsWith("mcp__")) {
371
376
  onDecision({ behavior: "allow", newPermissionRule: toolName });
372
377
  } else {
@@ -374,6 +379,11 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
374
379
  }
375
380
  } else if (state.alternativeText.trim()) {
376
381
  onDecision({ behavior: "deny", message: state.alternativeText.trim() });
382
+ } else if (toolName === ENTER_PLAN_MODE_TOOL_NAME) {
383
+ onDecision({
384
+ behavior: "deny",
385
+ message: "User chose not to enter plan mode",
386
+ });
377
387
  }
378
388
  return;
379
389
  }
@@ -622,7 +632,11 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
622
632
  bold={state.selectedOption === "alternative"}
623
633
  >
624
634
  {state.selectedOption === "alternative" ? "> " : " "}
625
- {showPlaceholder ? (
635
+ {toolName === ENTER_PLAN_MODE_TOOL_NAME && showPlaceholder ? (
636
+ <Text color="gray" dimColor>
637
+ No, start implementing now
638
+ </Text>
639
+ ) : showPlaceholder ? (
626
640
  <Text color="gray" dimColor>
627
641
  {placeholderText}
628
642
  </Text>
@@ -38,25 +38,56 @@ export const MessageList = React.memo(
38
38
  const visibleMessages = messages.filter((m) => !m.isMeta);
39
39
 
40
40
  const isRunning = (b: MessageBlock) =>
41
- (b.type === "tool" && b.stage === "running") ||
42
- (b.type === "bang" && b.isRunning);
41
+ (b.type === "tool" &&
42
+ (b.stage === "running" ||
43
+ b.stage === "streaming" ||
44
+ b.stage === "start")) ||
45
+ (b.type === "bang" && b.stage === "running");
43
46
 
44
47
  // Flatten messages into blocks with metadata
48
+ // Filter out streaming text/reasoning blocks to avoid frequent height changes
45
49
  const allBlocks = visibleMessages.flatMap((message, messageIndex) => {
46
- return message.blocks.map((block, blockIndex) => ({
47
- block,
48
- message,
49
- messageIndex,
50
- // Unique key for each block to help Static component
51
- key: `${message.id}-${blockIndex}`,
52
- }));
50
+ return message.blocks
51
+ .filter(
52
+ (block) =>
53
+ !(
54
+ (block.type === "text" || block.type === "reasoning") &&
55
+ block.stage === "streaming"
56
+ ),
57
+ )
58
+ .map((block, blockIndex) => ({
59
+ block,
60
+ message,
61
+ messageIndex,
62
+ // Unique key for each block to help Static component
63
+ key: `${message.id}-${blockIndex}`,
64
+ }));
53
65
  });
54
66
 
67
+ // Find message indices that have any running/streaming block
68
+ const runningMessageIndices = new Set<number>();
69
+ for (const item of allBlocks) {
70
+ if (isRunning(item.block)) {
71
+ runningMessageIndices.add(item.messageIndex);
72
+ }
73
+ }
74
+
55
75
  // Determine which blocks are static vs dynamic
56
- // Only mark individual running blocks as dynamic, not entire messages
76
+ // Blocks not in the last message are always static.
77
+ // For the last message: if any block is running/streaming, blocks in that message are dynamic,
78
+ // except text/reasoning blocks that have already completed (stage === "end")
79
+ const lastMessageIndex = visibleMessages.length - 1;
57
80
  const blocksWithStatus = allBlocks.map((item) => {
58
- const { block } = item;
59
- const isDynamic = !forceStatic && !isExpanded && isRunning(block);
81
+ const isInLastMessage = item.messageIndex === lastMessageIndex;
82
+ const isBlockCompleted =
83
+ (item.block.type === "text" || item.block.type === "reasoning") &&
84
+ item.block.stage === "end";
85
+ const isDynamic =
86
+ !forceStatic &&
87
+ !isExpanded &&
88
+ isInLastMessage &&
89
+ !isBlockCompleted &&
90
+ runningMessageIndices.has(item.messageIndex);
60
91
  return { ...item, isDynamic };
61
92
  });
62
93
 
@@ -61,10 +61,21 @@ export const ToolDisplay: React.FC<ToolDisplayProps> = ({
61
61
  <Text color={getStatusColor()}>● </Text>
62
62
  <Text color="white">{toolName}</Text>
63
63
  </Box>
64
- {/* Display compactParams in collapsed state */}
64
+ {/* Display compactParams or streaming parameters in collapsed state */}
65
65
  {!isExpanded && compactParams && (
66
66
  <Text color="gray"> {compactParams}</Text>
67
67
  )}
68
+ {!isExpanded &&
69
+ stage === "streaming" &&
70
+ parameters &&
71
+ !compactParams && (
72
+ <Text color="gray" wrap="truncate-end">
73
+ {` ${(() => {
74
+ const flat = parameters.replace(/\n/g, "\\n");
75
+ return flat.length > 30 ? `…${flat.slice(-30)}` : flat;
76
+ })()}`}
77
+ </Text>
78
+ )}
68
79
  {/* Display image indicator */}
69
80
  {hasImages() && <Text color="blue"> {getImageIndicator()}</Text>}
70
81
  </Box>
@@ -410,7 +410,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
410
410
  initialPermissionMode ||
411
411
  (bypassPermissions ? "bypassPermissions" : undefined),
412
412
  canUseTool: permissionCallback,
413
- stream: false, // 关闭流式模式
413
+ stream: true,
414
414
  plugins: pluginDirs?.map((path) => ({ type: "local", path })),
415
415
  tools,
416
416
  allowedTools,
@@ -462,6 +462,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
462
462
  model,
463
463
  initialPermissionMode,
464
464
  throttledSetMessages,
465
+ throttledSetTokens,
465
466
  ]);
466
467
 
467
468
  // Cleanup on unmount