wave-code 0.0.6 → 0.0.10

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.
Files changed (80) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +2 -2
  5. package/dist/components/App.d.ts +1 -0
  6. package/dist/components/App.d.ts.map +1 -1
  7. package/dist/components/App.js +4 -4
  8. package/dist/components/BashHistorySelector.d.ts.map +1 -1
  9. package/dist/components/BashHistorySelector.js +17 -3
  10. package/dist/components/ChatInterface.d.ts.map +1 -1
  11. package/dist/components/ChatInterface.js +4 -2
  12. package/dist/components/Confirmation.d.ts +13 -0
  13. package/dist/components/Confirmation.d.ts.map +1 -0
  14. package/dist/components/Confirmation.js +172 -0
  15. package/dist/components/DiffDisplay.d.ts +8 -0
  16. package/dist/components/DiffDisplay.d.ts.map +1 -0
  17. package/dist/components/DiffDisplay.js +168 -0
  18. package/dist/components/FileSelector.d.ts +2 -4
  19. package/dist/components/FileSelector.d.ts.map +1 -1
  20. package/dist/components/InputBox.d.ts.map +1 -1
  21. package/dist/components/InputBox.js +10 -1
  22. package/dist/components/Markdown.d.ts.map +1 -1
  23. package/dist/components/Markdown.js +115 -16
  24. package/dist/components/MemoryDisplay.js +1 -1
  25. package/dist/components/MessageItem.d.ts +1 -2
  26. package/dist/components/MessageItem.d.ts.map +1 -1
  27. package/dist/components/MessageItem.js +3 -3
  28. package/dist/components/MessageList.d.ts.map +1 -1
  29. package/dist/components/MessageList.js +2 -2
  30. package/dist/components/ReasoningDisplay.d.ts +8 -0
  31. package/dist/components/ReasoningDisplay.d.ts.map +1 -0
  32. package/dist/components/ReasoningDisplay.js +10 -0
  33. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  34. package/dist/components/ToolResultDisplay.js +2 -1
  35. package/dist/contexts/useChat.d.ts +15 -1
  36. package/dist/contexts/useChat.d.ts.map +1 -1
  37. package/dist/contexts/useChat.js +124 -15
  38. package/dist/hooks/useInputManager.d.ts +3 -0
  39. package/dist/hooks/useInputManager.d.ts.map +1 -1
  40. package/dist/hooks/useInputManager.js +17 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +22 -4
  43. package/dist/managers/InputManager.d.ts +8 -0
  44. package/dist/managers/InputManager.d.ts.map +1 -1
  45. package/dist/managers/InputManager.js +33 -2
  46. package/dist/print-cli.d.ts +1 -0
  47. package/dist/print-cli.d.ts.map +1 -1
  48. package/dist/print-cli.js +36 -3
  49. package/dist/utils/toolParameterTransforms.d.ts +23 -0
  50. package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
  51. package/dist/utils/toolParameterTransforms.js +77 -0
  52. package/package.json +10 -8
  53. package/src/cli.tsx +3 -1
  54. package/src/components/App.tsx +7 -3
  55. package/src/components/BashHistorySelector.tsx +26 -3
  56. package/src/components/ChatInterface.tsx +31 -15
  57. package/src/components/Confirmation.tsx +286 -0
  58. package/src/components/DiffDisplay.tsx +300 -0
  59. package/src/components/FileSelector.tsx +2 -4
  60. package/src/components/InputBox.tsx +37 -14
  61. package/src/components/Markdown.tsx +329 -16
  62. package/src/components/MemoryDisplay.tsx +1 -1
  63. package/src/components/MessageItem.tsx +4 -12
  64. package/src/components/MessageList.tsx +0 -2
  65. package/src/components/ReasoningDisplay.tsx +33 -0
  66. package/src/components/ToolResultDisplay.tsx +4 -0
  67. package/src/contexts/useChat.tsx +206 -14
  68. package/src/hooks/useInputManager.ts +19 -0
  69. package/src/index.ts +34 -4
  70. package/src/managers/InputManager.ts +46 -2
  71. package/src/print-cli.ts +42 -2
  72. package/src/utils/toolParameterTransforms.ts +104 -0
  73. package/dist/components/DiffViewer.d.ts +0 -9
  74. package/dist/components/DiffViewer.d.ts.map +0 -1
  75. package/dist/components/DiffViewer.js +0 -221
  76. package/dist/utils/fileSearch.d.ts +0 -20
  77. package/dist/utils/fileSearch.d.ts.map +0 -1
  78. package/dist/utils/fileSearch.js +0 -102
  79. package/src/components/DiffViewer.tsx +0 -323
  80. package/src/utils/fileSearch.ts +0 -133
@@ -1,22 +1,121 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useMemo } from "react";
3
- import { Text } from "ink";
3
+ import { Box, Text, useStdout } from "ink";
4
4
  import { marked } from "marked";
5
- import TerminalRenderer from "marked-terminal";
6
- // Markdown component using marked-terminal with proper unescape option
7
- export const Markdown = React.memo(({ children }) => {
8
- const result = useMemo(() => {
9
- // Configure marked with TerminalRenderer using default options
10
- marked.setOptions({
11
- renderer: new TerminalRenderer({
12
- // Use official unescape option to handle HTML entities
13
- unescape: true,
14
- }),
5
+ const unescapeHtml = (html) => {
6
+ return html
7
+ .replace(/&/g, "&")
8
+ .replace(/&lt;/g, "<")
9
+ .replace(/&gt;/g, ">")
10
+ .replace(/&quot;/g, '"')
11
+ .replace(/&#39;/g, "'");
12
+ };
13
+ const InlineRenderer = ({ tokens }) => {
14
+ return (_jsx(_Fragment, { children: tokens.map((token, index) => {
15
+ switch (token.type) {
16
+ case "text": {
17
+ const t = token;
18
+ if (t.tokens) {
19
+ return _jsx(InlineRenderer, { tokens: t.tokens }, index);
20
+ }
21
+ return _jsx(Text, { children: unescapeHtml(t.text) }, index);
22
+ }
23
+ case "strong":
24
+ return (_jsx(Text, { bold: true, children: token.tokens ? (_jsx(InlineRenderer, { tokens: token.tokens })) : (unescapeHtml(token.text)) }, index));
25
+ case "em":
26
+ return (_jsx(Text, { italic: true, children: token.tokens ? (_jsx(InlineRenderer, { tokens: token.tokens })) : (unescapeHtml(token.text)) }, index));
27
+ case "codespan":
28
+ return (_jsx(Text, { color: "yellow", children: unescapeHtml(token.text) }, index));
29
+ case "link":
30
+ return (_jsx(Text, { color: "blue", underline: true, children: token.tokens ? (_jsx(InlineRenderer, { tokens: token.tokens })) : (unescapeHtml(token.text)) }, index));
31
+ case "br":
32
+ return _jsx(Text, { children: "\n" }, index);
33
+ case "del":
34
+ return (_jsx(Text, { strikethrough: true, children: token.tokens ? (_jsx(InlineRenderer, { tokens: token.tokens })) : (unescapeHtml(token.text)) }, index));
35
+ default:
36
+ return _jsx(Text, { children: token.raw }, index);
37
+ }
38
+ }) }));
39
+ };
40
+ const TableRenderer = ({ token }) => {
41
+ const { stdout } = useStdout();
42
+ const terminalWidth = (stdout?.columns || 80) - 2;
43
+ const columnWidths = useMemo(() => {
44
+ const numCols = token.header.length;
45
+ const minWidth = 5;
46
+ const maxColWidth = 40;
47
+ const widths = token.header.map((h) => Math.min(maxColWidth, Math.max(minWidth, h.text.length)));
48
+ token.rows.forEach((row) => {
49
+ row.forEach((cell, i) => {
50
+ widths[i] = Math.min(maxColWidth, Math.max(widths[i] || minWidth, cell.text.length));
51
+ });
15
52
  });
16
- const output = marked(children);
17
- return typeof output === "string" ? output.trim() : "";
18
- }, [children]);
19
- return _jsx(Text, { children: result });
53
+ const paddedWidths = widths.map((w) => w + 2);
54
+ const totalWidth = paddedWidths.reduce((a, b) => a + b, 0) + numCols + 1;
55
+ if (totalWidth <= terminalWidth) {
56
+ return paddedWidths;
57
+ }
58
+ // If table is too wide, scale down columns proportionally
59
+ const availableWidth = terminalWidth - numCols - 1;
60
+ const scaleFactor = availableWidth / (totalWidth - numCols - 1);
61
+ return paddedWidths.map((w) => Math.max(minWidth, Math.floor(w * scaleFactor)));
62
+ }, [token, terminalWidth]);
63
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: "gray", width: columnWidths.reduce((a, b) => a + b, 0) + token.header.length + 1, children: [_jsx(Box, { flexDirection: "row", borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: "gray", children: token.header.map((cell, i) => (_jsx(Box, { width: columnWidths[i], paddingX: 1, borderStyle: "single", borderLeft: i > 0, borderRight: false, borderTop: false, borderBottom: false, borderColor: "gray", children: _jsx(Text, { bold: true, wrap: "wrap", children: _jsx(InlineRenderer, { tokens: cell.tokens }) }) }, i))) }), token.rows.map((row, rowIndex) => (_jsx(Box, { flexDirection: "row", children: row.map((cell, i) => (_jsx(Box, { width: columnWidths[i], paddingX: 1, borderStyle: "single", borderLeft: i > 0, borderRight: false, borderTop: false, borderBottom: false, borderColor: "gray", children: _jsx(Text, { wrap: "wrap", children: _jsx(InlineRenderer, { tokens: cell.tokens }) }) }, i))) }, rowIndex)))] }));
64
+ };
65
+ const BlockRenderer = ({ tokens }) => {
66
+ return (_jsx(_Fragment, { children: tokens.map((token, index) => {
67
+ switch (token.type) {
68
+ case "heading": {
69
+ const t = token;
70
+ return (_jsx(Box, { marginBottom: 1, flexDirection: "column", children: _jsxs(Text, { bold: true, color: "cyan", children: ["#".repeat(t.depth), " ", _jsx(InlineRenderer, { tokens: t.tokens })] }) }, index));
71
+ }
72
+ case "paragraph": {
73
+ const t = token;
74
+ return (_jsx(Box, { marginBottom: 1, flexDirection: "row", flexWrap: "wrap", children: _jsx(InlineRenderer, { tokens: t.tokens }) }, index));
75
+ }
76
+ case "code": {
77
+ const t = token;
78
+ if (t.lang !== undefined) {
79
+ const lines = token.raw.replace(/\n$/, "").split("\n");
80
+ const opening = lines[0];
81
+ const closing = lines[lines.length - 1];
82
+ const content = lines.slice(1, -1).join("\n");
83
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { color: "gray", children: opening }), content && _jsx(Text, { children: content }), _jsx(Text, { color: "gray", children: closing })] }, index));
84
+ }
85
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: _jsx(Text, { children: t.text }) }, index));
86
+ }
87
+ case "list": {
88
+ const t = token;
89
+ return (_jsx(Box, { flexDirection: "column", marginBottom: 1, paddingLeft: 2, children: t.items.map((item, i) => {
90
+ const start = t.start || 1;
91
+ return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "gray", children: t.ordered ? `${start + i}. ` : "• " }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: item.tokens.map((itemToken, itemIndex) => {
92
+ if (itemToken.type === "text") {
93
+ const it = itemToken;
94
+ return (_jsx(Box, { flexDirection: "row", flexWrap: "wrap", children: _jsx(InlineRenderer, { tokens: it.tokens || [itemToken] }) }, itemIndex));
95
+ }
96
+ return (_jsx(BlockRenderer, { tokens: [itemToken] }, itemIndex));
97
+ }) })] }, i));
98
+ }) }, index));
99
+ }
100
+ case "blockquote": {
101
+ const t = token;
102
+ return (_jsx(Box, { flexDirection: "column", paddingLeft: 2, borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderColor: "gray", marginBottom: 1, children: _jsx(BlockRenderer, { tokens: t.tokens }) }, index));
103
+ }
104
+ case "hr":
105
+ return (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "gray", children: "─".repeat(20) }) }, index));
106
+ case "table":
107
+ return _jsx(TableRenderer, { token: token }, index);
108
+ case "space":
109
+ return null;
110
+ default:
111
+ return (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: token.raw }) }, index));
112
+ }
113
+ }) }));
114
+ };
115
+ // Markdown component using custom Ink-based renderer
116
+ export const Markdown = React.memo(({ children }) => {
117
+ const tokens = useMemo(() => marked.lexer(children), [children]);
118
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(BlockRenderer, { tokens: tokens }) }));
20
119
  });
21
120
  // Add display name for debugging
22
121
  Markdown.displayName = "Markdown";
@@ -15,7 +15,7 @@ export const MemoryDisplay = ({ block }) => {
15
15
  if (!isSuccess)
16
16
  return null;
17
17
  if (memoryType === "user") {
18
- return `Memory saved to ${storagePath || "user-memory.md"}`;
18
+ return `Memory saved to ${storagePath || "AGENTS.md"}`;
19
19
  }
20
20
  else {
21
21
  return `Memory saved to ${storagePath || "AGENTS.md"}`;
@@ -3,7 +3,6 @@ export interface MessageItemProps {
3
3
  message: Message;
4
4
  isExpanded: boolean;
5
5
  shouldShowHeader: boolean;
6
- isStatic?: boolean;
7
6
  }
8
- export declare const MessageItem: ({ message, isExpanded, shouldShowHeader, isStatic, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
7
+ export declare const MessageItem: ({ message, isExpanded, shouldShowHeader, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
9
8
  //# sourceMappingURL=MessageItem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../src/components/MessageItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAU9C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,GAAI,sDAKzB,gBAAgB,mDA+ElB,CAAC"}
1
+ {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../src/components/MessageItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAU9C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,WAAW,GAAI,4CAIzB,gBAAgB,mDAyElB,CAAC"}
@@ -1,15 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { MessageSource } from "wave-agent-sdk";
4
- import { DiffViewer } from "./DiffViewer.js";
5
4
  import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
6
5
  import { ToolResultDisplay } from "./ToolResultDisplay.js";
7
6
  import { MemoryDisplay } from "./MemoryDisplay.js";
8
7
  import { CompressDisplay } from "./CompressDisplay.js";
9
8
  import { SubagentBlock } from "./SubagentBlock.js";
9
+ import { ReasoningDisplay } from "./ReasoningDisplay.js";
10
10
  import { Markdown } from "./Markdown.js";
11
- export const MessageItem = ({ message, isExpanded, shouldShowHeader, isStatic = true, }) => {
11
+ export const MessageItem = ({ message, isExpanded, shouldShowHeader, }) => {
12
12
  if (message.blocks.length === 0)
13
13
  return null;
14
- return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: 1, children: [shouldShowHeader && (_jsx(Box, { children: _jsx(Text, { color: message.role === "user" ? "cyan" : "green", bold: true, children: message.role === "user" ? "👤 You" : "🤖 Assistant" }) })), _jsx(Box, { flexDirection: "column", gap: 1, children: message.blocks.map((block, blockIndex) => (_jsxs(Box, { children: [block.type === "text" && block.content.trim() && (_jsxs(Box, { children: [block.customCommandContent && (_jsxs(Text, { color: "cyan", bold: true, children: ["\u26A1", " "] })), block.source === MessageSource.HOOK && (_jsxs(Text, { color: "magenta", bold: true, children: ["\uD83D\uDD17", " "] })), isStatic ? (_jsx(Markdown, { children: block.content })) : (_jsx(Text, { children: block.content.split("\n").slice(-10).join("\n") }))] })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["\u274C Error: ", block.content] }) })), block.type === "diff" && (_jsx(DiffViewer, { block: block, isStatic: isStatic })), block.type === "command_output" && (_jsx(CommandOutputDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolResultDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "\uD83D\uDCF7 Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "memory" && _jsx(MemoryDisplay, { block: block }), block.type === "compress" && (_jsx(CompressDisplay, { block: block, isExpanded: isExpanded })), block.type === "subagent" && _jsx(SubagentBlock, { block: block })] }, blockIndex))) })] }));
14
+ return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: 1, children: [shouldShowHeader && (_jsx(Box, { children: _jsx(Text, { color: message.role === "user" ? "cyan" : "green", bold: true, children: message.role === "user" ? "👤 You" : "🤖 Assistant" }) })), _jsx(Box, { flexDirection: "column", gap: 1, children: message.blocks.map((block, blockIndex) => (_jsxs(Box, { children: [block.type === "text" && block.content.trim() && (_jsxs(Box, { children: [block.customCommandContent && (_jsxs(Text, { color: "cyan", bold: true, children: ["\u26A1", " "] })), block.source === MessageSource.HOOK && (_jsxs(Text, { color: "magenta", bold: true, children: ["\uD83D\uDD17", " "] })), _jsx(Markdown, { children: block.content })] })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["\u274C Error: ", block.content] }) })), block.type === "command_output" && (_jsx(CommandOutputDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolResultDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "\uD83D\uDCF7 Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "memory" && _jsx(MemoryDisplay, { block: block }), block.type === "compress" && (_jsx(CompressDisplay, { block: block, isExpanded: isExpanded })), block.type === "subagent" && _jsx(SubagentBlock, { block: block }), block.type === "reasoning" && _jsx(ReasoningDisplay, { block: block })] }, blockIndex))) })] }));
15
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,uHAQnB,gBAAgB,6CA0IpB,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,uHAQnB,gBAAgB,6CAwIpB,CAAC"}
@@ -27,11 +27,11 @@ export const MessageList = React.memo(({ messages, isLoading = false, isCommandR
27
27
  return (_jsxs(Box, { flexDirection: "column", paddingX: 1, gap: 1, children: [omittedCount > 0 && (_jsx(Box, { children: _jsxs(Text, { color: "gray", dimColor: true, children: ["... ", omittedCount, " earlier message", omittedCount !== 1 ? "s" : "", " ", "omitted (showing latest ", maxExpandedMessages, ")"] }) })), _jsx(Static, { items: staticMessages, children: (message, key) => {
28
28
  // Get previous message
29
29
  const previousMessage = key > 0 ? staticMessages[key - 1] : undefined;
30
- return (_jsx(MessageItem, { message: message, shouldShowHeader: previousMessage?.role !== message.role, isExpanded: isExpanded, isStatic: true }, key));
30
+ return (_jsx(MessageItem, { message: message, shouldShowHeader: previousMessage?.role !== message.role, isExpanded: isExpanded }, key));
31
31
  } }), dynamicMessages.map((message, index) => {
32
32
  const messageIndex = staticMessages.length + index;
33
33
  const previousMessage = messageIndex > 0 ? displayMessages[messageIndex - 1] : undefined;
34
- return (_jsx(Box, { marginTop: -1, children: _jsx(MessageItem, { message: message, shouldShowHeader: previousMessage?.role !== message.role, isExpanded: isExpanded, isStatic: false }) }, `dynamic-${index}`));
34
+ return (_jsx(Box, { marginTop: -1, children: _jsx(MessageItem, { message: message, shouldShowHeader: previousMessage?.role !== message.role, isExpanded: isExpanded }) }, `dynamic-${index}`));
35
35
  }), (isLoading || isCommandRunning || isCompressing) && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [isLoading && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\uD83D\uDCAD AI is thinking... " }), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && (_jsx(Text, { color: "blue", children: "\uD83D\uDE80 Command is running..." })), isCompressing && (_jsx(Text, { color: "magenta", children: "\uD83D\uDDDC\uFE0F Compressing message history..." }))] })), messages.length > 0 && (_jsx(Box, { children: _jsxs(Box, { justifyContent: "space-between", width: "100%", children: [_jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["Messages ", messages.length, latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens"] })] }))] }) }), _jsxs(Text, { color: "gray", dimColor: true, children: [_jsx(Text, { color: "cyan", children: "Ctrl+O" }), " Toggle", " ", isExpanded ? "Collapse" : "Expand"] })] }) }))] }));
36
36
  });
37
37
  // Add display name for debugging
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import type { ReasoningBlock } from "wave-agent-sdk";
3
+ interface ReasoningDisplayProps {
4
+ block: ReasoningBlock;
5
+ }
6
+ export declare const ReasoningDisplay: React.FC<ReasoningDisplayProps>;
7
+ export {};
8
+ //# sourceMappingURL=ReasoningDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReasoningDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ReasoningDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,UAAU,qBAAqB;IAC7B,KAAK,EAAE,cAAc,CAAC;CACvB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAuB5D,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from "ink";
3
+ import { Markdown } from "./Markdown.js";
4
+ export const ReasoningDisplay = ({ block, }) => {
5
+ const { content } = block;
6
+ if (!content || !content.trim()) {
7
+ return null;
8
+ }
9
+ return (_jsx(Box, { borderRight: false, borderTop: false, borderBottom: false, borderStyle: "classic", borderColor: "blue", paddingLeft: 1, children: _jsx(Box, { flexDirection: "column", children: _jsx(Markdown, { children: content }) }) }));
10
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"ToolResultDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolResultDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,sBAAsB;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAgI9D,CAAC"}
1
+ {"version":3,"file":"ToolResultDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ToolResultDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,UAAU,sBAAsB;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAmI9D,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
+ import { DiffDisplay } from "./DiffDisplay.js";
3
4
  export const ToolResultDisplay = ({ block, isExpanded = false, }) => {
4
5
  const { parameters, result, compactParams, stage, success, error, name } = block;
5
6
  // Directly use compactParams
@@ -48,5 +49,5 @@ export const ToolResultDisplay = ({ block, isExpanded = false, }) => {
48
49
  return null;
49
50
  };
50
51
  const shortResult = getShortResult();
51
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "magenta", children: "\uD83D\uDD27 " }), _jsx(Text, { color: "white", children: toolName }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" ", compactParams] })), _jsxs(Text, { color: getStatusColor(), children: [" ", getStatusText()] }), 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: "white", 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)] }) }))] }));
52
+ return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "magenta", children: "\uD83D\uDD27 " }), _jsx(Text, { color: "white", children: toolName }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" ", compactParams] })), _jsxs(Text, { color: getStatusColor(), children: [" ", getStatusText()] }), 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: "white", 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)] }) })), _jsx(DiffDisplay, { toolBlock: block })] }));
52
53
  };
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import type { Message, McpServerStatus, BackgroundShell, SlashCommand } from "wave-agent-sdk";
2
+ import type { Message, McpServerStatus, BackgroundShell, SlashCommand, PermissionDecision, PermissionMode } from "wave-agent-sdk";
3
3
  export interface ChatContextType {
4
4
  messages: Message[];
5
5
  isLoading: boolean;
@@ -28,10 +28,24 @@ export interface ChatContextType {
28
28
  slashCommands: SlashCommand[];
29
29
  hasSlashCommand: (commandId: string) => boolean;
30
30
  subagentMessages: Record<string, Message[]>;
31
+ permissionMode: PermissionMode;
32
+ setPermissionMode: (mode: PermissionMode) => void;
33
+ isConfirmationVisible: boolean;
34
+ confirmingTool?: {
35
+ name: string;
36
+ input?: Record<string, unknown>;
37
+ suggestedPrefix?: string;
38
+ hidePersistentOption?: boolean;
39
+ };
40
+ showConfirmation: (toolName: string, toolInput?: Record<string, unknown>, suggestedPrefix?: string, hidePersistentOption?: boolean) => Promise<PermissionDecision>;
41
+ hideConfirmation: () => void;
42
+ handleConfirmationDecision: (decision: PermissionDecision) => void;
43
+ handleConfirmationCancel: () => void;
31
44
  }
32
45
  export declare const useChat: () => ChatContextType;
33
46
  export interface ChatProviderProps {
34
47
  children: React.ReactNode;
48
+ bypassPermissions?: boolean;
35
49
  }
36
50
  export declare const ChatProvider: React.FC<ChatProviderProps>;
37
51
  //# sourceMappingURL=useChat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,OAAO,EACP,eAAe,EACf,eAAe,EACf,YAAY,EACb,MAAM,gBAAgB,CAAC;AAMxB,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAE3B,UAAU,EAAE,OAAO,CAAC;IAEpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CACX,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAE1B,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzE,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,mBAAmB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9D,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,wBAAwB,EAAE,CACxB,OAAO,EAAE,MAAM,KACZ;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/D,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAElD,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAEhD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;CAC7C;AAID,eAAO,MAAM,OAAO,uBAMnB,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqQpD,CAAC"}
1
+ {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,OAAO,EACP,eAAe,EACf,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACf,MAAM,gBAAgB,CAAC;AAUxB,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAE3B,UAAU,EAAE,OAAO,CAAC;IAEpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CACX,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAE1B,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzE,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,mBAAmB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9D,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,wBAAwB,EAAE,CACxB,OAAO,EAAE,MAAM,KACZ;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/D,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAElD,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAEhD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAE5C,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAElD,qBAAqB,EAAE,OAAO,CAAC;IAC/B,cAAc,CAAC,EAAE;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;IACF,gBAAgB,EAAE,CAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,eAAe,CAAC,EAAE,MAAM,EACxB,oBAAoB,CAAC,EAAE,OAAO,KAC3B,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,0BAA0B,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnE,wBAAwB,EAAE,MAAM,IAAI,CAAC;CACtC;AAID,eAAO,MAAM,OAAO,uBAMnB,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA0apD,CAAC"}
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useCallback, useRef, useEffect, useState, } from "react";
3
3
  import { useInput } from "ink";
4
4
  import { useAppConfig } from "./useAppConfig.js";
5
- import { Agent } from "wave-agent-sdk";
5
+ import { Agent, } from "wave-agent-sdk";
6
6
  import { logger } from "../utils/logger.js";
7
7
  import { displayUsageSummary } from "../utils/usageSummary.js";
8
8
  const ChatContext = createContext(null);
@@ -13,7 +13,7 @@ export const useChat = () => {
13
13
  }
14
14
  return context;
15
15
  };
16
- export const ChatProvider = ({ children }) => {
16
+ export const ChatProvider = ({ children, bypassPermissions, }) => {
17
17
  const { restoreSessionId, continueLastSession } = useAppConfig();
18
18
  // Message Display State
19
19
  const [isExpanded, setIsExpanded] = useState(false);
@@ -33,19 +33,29 @@ export const ChatProvider = ({ children }) => {
33
33
  const [slashCommands, setSlashCommands] = useState([]);
34
34
  // Subagent messages state
35
35
  const [subagentMessages, setSubagentMessages] = useState({});
36
+ // Permission state
37
+ const [permissionMode, setPermissionModeState] = useState("default");
38
+ // Confirmation state with queue-based architecture
39
+ const [isConfirmationVisible, setIsConfirmationVisible] = useState(false);
40
+ const [confirmingTool, setConfirmingTool] = useState();
41
+ const [confirmationQueue, setConfirmationQueue] = useState([]);
42
+ const [currentConfirmation, setCurrentConfirmation] = useState(null);
36
43
  const agentRef = useRef(null);
37
- // Listen for Ctrl+O hotkey to toggle collapse/expand state
38
- useInput((input, key) => {
39
- if (key.ctrl && input === "o") {
40
- // Clear terminal screen when expanded state changes
41
- process.stdout.write("\x1Bc", () => {
42
- setIsExpanded((prev) => {
43
- const newExpanded = !prev;
44
- return newExpanded;
45
- });
46
- });
47
- }
48
- });
44
+ // Permission confirmation methods with queue support
45
+ const showConfirmation = useCallback(async (toolName, toolInput, suggestedPrefix, hidePersistentOption) => {
46
+ return new Promise((resolve, reject) => {
47
+ const queueItem = {
48
+ toolName,
49
+ toolInput,
50
+ suggestedPrefix,
51
+ hidePersistentOption,
52
+ resolver: resolve,
53
+ reject,
54
+ };
55
+ setConfirmationQueue((prev) => [...prev, queueItem]);
56
+ // processNextConfirmation will be called via useEffect
57
+ });
58
+ }, []);
49
59
  // Initialize AI manager
50
60
  useEffect(() => {
51
61
  const initializeAgent = async () => {
@@ -74,18 +84,40 @@ export const ChatProvider = ({ children }) => {
74
84
  setBackgroundShells([...shells]);
75
85
  },
76
86
  onSubagentMessagesChange: (subagentId, messages) => {
87
+ logger.debug("onSubagentMessagesChange", subagentId, messages.length);
77
88
  setSubagentMessages((prev) => ({
78
89
  ...prev,
79
90
  [subagentId]: [...messages],
80
91
  }));
81
92
  },
93
+ onPermissionModeChange: (mode) => {
94
+ setPermissionModeState(mode);
95
+ },
82
96
  };
83
97
  try {
98
+ // Create the permission callback inside the try block to access showConfirmation
99
+ const permissionCallback = bypassPermissions
100
+ ? undefined
101
+ : async (context) => {
102
+ try {
103
+ return await showConfirmation(context.toolName, context.toolInput, context.suggestedPrefix, context.hidePersistentOption);
104
+ }
105
+ catch {
106
+ // If confirmation was cancelled or failed, deny the operation
107
+ return {
108
+ behavior: "deny",
109
+ message: "Operation cancelled by user",
110
+ };
111
+ }
112
+ };
84
113
  const agent = await Agent.create({
85
114
  callbacks,
86
115
  restoreSessionId,
87
116
  continueLastSession,
88
117
  logger,
118
+ permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
119
+ canUseTool: permissionCallback,
120
+ stream: false, // 关闭流式模式
89
121
  });
90
122
  agentRef.current = agent;
91
123
  // Get initial state
@@ -96,6 +128,7 @@ export const ChatProvider = ({ children }) => {
96
128
  setIsCommandRunning(agent.isCommandRunning);
97
129
  setIsCompressing(agent.isCompressing);
98
130
  setUserInputHistory(agent.userInputHistory);
131
+ setPermissionModeState(agent.getPermissionMode());
99
132
  // Get initial MCP servers state
100
133
  const mcpServers = agent.getMcpServers?.() || [];
101
134
  setMcpServers(mcpServers);
@@ -108,7 +141,12 @@ export const ChatProvider = ({ children }) => {
108
141
  }
109
142
  };
110
143
  initializeAgent();
111
- }, [restoreSessionId, continueLastSession]);
144
+ }, [
145
+ restoreSessionId,
146
+ continueLastSession,
147
+ bypassPermissions,
148
+ showConfirmation,
149
+ ]);
112
150
  // Cleanup on unmount
113
151
  useEffect(() => {
114
152
  return () => {
@@ -186,6 +224,17 @@ export const ChatProvider = ({ children }) => {
186
224
  const saveMemory = useCallback(async (message, type) => {
187
225
  await agentRef.current?.saveMemory(message, type);
188
226
  }, []);
227
+ // Permission management methods
228
+ const setPermissionMode = useCallback((mode) => {
229
+ setPermissionModeState((prev) => {
230
+ if (prev === mode)
231
+ return prev;
232
+ if (agentRef.current && agentRef.current.getPermissionMode() !== mode) {
233
+ agentRef.current.setPermissionMode(mode);
234
+ }
235
+ return mode;
236
+ });
237
+ }, []);
189
238
  // MCP management methods - delegate to Agent
190
239
  const connectMcpServer = useCallback(async (serverName) => {
191
240
  return (await agentRef.current?.connectMcpServer(serverName)) ?? false;
@@ -209,6 +258,58 @@ export const ChatProvider = ({ children }) => {
209
258
  return false;
210
259
  return agentRef.current.hasSlashCommand(commandId);
211
260
  }, []);
261
+ // Queue processing helper
262
+ const processNextConfirmation = useCallback(() => {
263
+ if (confirmationQueue.length > 0 && !isConfirmationVisible) {
264
+ const next = confirmationQueue[0];
265
+ setCurrentConfirmation(next);
266
+ setConfirmingTool({
267
+ name: next.toolName,
268
+ input: next.toolInput,
269
+ suggestedPrefix: next.suggestedPrefix,
270
+ hidePersistentOption: next.hidePersistentOption,
271
+ });
272
+ setIsConfirmationVisible(true);
273
+ setConfirmationQueue((prev) => prev.slice(1));
274
+ }
275
+ }, [confirmationQueue, isConfirmationVisible]);
276
+ // Process queue when queue changes or confirmation is hidden
277
+ useEffect(() => {
278
+ processNextConfirmation();
279
+ }, [processNextConfirmation]);
280
+ const hideConfirmation = useCallback(() => {
281
+ setIsConfirmationVisible(false);
282
+ setConfirmingTool(undefined);
283
+ setCurrentConfirmation(null);
284
+ }, []);
285
+ const handleConfirmationDecision = useCallback((decision) => {
286
+ if (currentConfirmation) {
287
+ currentConfirmation.resolver(decision);
288
+ }
289
+ hideConfirmation();
290
+ }, [currentConfirmation, hideConfirmation]);
291
+ const handleConfirmationCancel = useCallback(() => {
292
+ if (currentConfirmation) {
293
+ currentConfirmation.reject();
294
+ }
295
+ hideConfirmation();
296
+ }, [currentConfirmation, hideConfirmation]);
297
+ // Listen for Ctrl+O hotkey to toggle collapse/expand state and ESC to cancel confirmation
298
+ useInput((input, key) => {
299
+ if (key.ctrl && input === "o") {
300
+ // Clear terminal screen when expanded state changes
301
+ process.stdout.write("\x1Bc", () => {
302
+ setIsExpanded((prev) => {
303
+ const newExpanded = !prev;
304
+ return newExpanded;
305
+ });
306
+ });
307
+ }
308
+ // Handle ESC key to cancel confirmation
309
+ if (key.escape && isConfirmationVisible) {
310
+ handleConfirmationCancel();
311
+ }
312
+ });
212
313
  const contextValue = {
213
314
  messages,
214
315
  isLoading,
@@ -230,6 +331,14 @@ export const ChatProvider = ({ children }) => {
230
331
  slashCommands,
231
332
  hasSlashCommand,
232
333
  subagentMessages,
334
+ permissionMode,
335
+ setPermissionMode,
336
+ isConfirmationVisible,
337
+ confirmingTool,
338
+ showConfirmation,
339
+ hideConfirmation,
340
+ handleConfirmationDecision,
341
+ handleConfirmationCancel,
233
342
  };
234
343
  return (_jsx(ChatContext.Provider, { value: contextValue, children: children }));
235
344
  };
@@ -1,6 +1,7 @@
1
1
  import { Key } from "ink";
2
2
  import { InputManager, InputManagerCallbacks, AttachedImage } from "../managers/InputManager.js";
3
3
  import { FileItem } from "../components/FileSelector.js";
4
+ import { PermissionMode } from "wave-agent-sdk";
4
5
  export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks>) => {
5
6
  inputText: string;
6
7
  cursorPosition: number;
@@ -18,6 +19,7 @@ export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks
18
19
  memoryMessage: string;
19
20
  showBashManager: boolean;
20
21
  showMcpManager: boolean;
22
+ permissionMode: PermissionMode;
21
23
  attachedImages: AttachedImage[];
22
24
  isManagerReady: boolean;
23
25
  insertTextAtCursor: (text: string, callback?: (newText: string, newCursorPosition: number) => void) => void;
@@ -68,6 +70,7 @@ export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks
68
70
  handleSpecialCharInput: (char: string) => void;
69
71
  setShowBashManager: (show: boolean) => void;
70
72
  setShowMcpManager: (show: boolean) => void;
73
+ setPermissionMode: (mode: PermissionMode) => void;
71
74
  addImage: (imagePath: string, mimeType: string) => AttachedImage | undefined;
72
75
  removeImage: (imageId: number) => void;
73
76
  clearImages: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useInputManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useInputManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,aAAa,EACd,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAEzD,eAAO,MAAM,eAAe,GAC1B,YAAW,OAAO,CAAC,qBAAqB,CAAM;;;;;;;;;;;;;;;;;;;+BA4GpC,MAAM,aACD,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,KAAK,IAAI;oCAQrD,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,KAAK,IAAI;;;;;;qCA2Bd,MAAM;iCAK7C,MAAM;;;;;mCAe+B,MAAM;oCAIL,MAAM;wCAKF,MAAM;mCAKjD,MAAM;;;;mCAYN,MAAM;;;;;sCAemC,MAAM;uCAIL,MAAM;4CAKD,MAAM;uCAKrD,MAAM;;;;;0CAeuC,MAAM;wCAIR,MAAM;6CAID,MAAM;0CAKT,MAAM;mCAKhD,SAAS,GAAG,MAAM;;mCAWiB,MAAM,EAAE;iCAK5C,IAAI,GAAG,MAAM,gBAAgB,MAAM;;;;;mCAgBC,MAAM;+BAsFf,OAAO;8BAGR,OAAO;0BAKX,MAAM,YAAY,MAAM;2BAGvB,MAAM;;;8BAWH,MAAM;mCAKxB,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,cAC1D,OAAO,qBACA,OAAO;uCAUkB,MAAM;;+CA/GO,MAAM;yBA4HvD,MAAM,OACR,GAAG,kBACQ,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,cAC1D,OAAO,qBACA,OAAO,gBACX,MAAM,IAAI;yBA1IgB,MAAM;kCAIG,MAAM;;CA6J9D,CAAC"}
1
+ {"version":3,"file":"useInputManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useInputManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,aAAa,EACd,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,eAAO,MAAM,eAAe,GAC1B,YAAW,OAAO,CAAC,qBAAqB,CAAM;;;;;;;;;;;;;;;;;;;;+BAwHpC,MAAM,aACD,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,KAAK,IAAI;oCAQrD,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,KAAK,IAAI;;;;;;qCA2Bd,MAAM;iCAK7C,MAAM;;;;;mCAe+B,MAAM;oCAIL,MAAM;wCAKF,MAAM;mCAKjD,MAAM;;;;mCAYN,MAAM;;;;;sCAemC,MAAM;uCAIL,MAAM;4CAKD,MAAM;uCAKrD,MAAM;;;;;0CAeuC,MAAM;wCAIR,MAAM;6CAID,MAAM;0CAKT,MAAM;mCAKhD,SAAS,GAAG,MAAM;;mCAWiB,MAAM,EAAE;iCAK5C,IAAI,GAAG,MAAM,gBAAgB,MAAM;;;;;mCAgBC,MAAM;+BAuFf,OAAO;8BAGR,OAAO;8BAGP,cAAc;0BAMlB,MAAM,YAAY,MAAM;2BAGvB,MAAM;;;8BAWH,MAAM;mCAKxB,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,cAC1D,OAAO,qBACA,OAAO;uCAUkB,MAAM;;+CApHO,MAAM;yBAiIvD,MAAM,OACR,GAAG,kBACQ,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,cAC1D,OAAO,qBACA,OAAO,gBACX,MAAM,IAAI;yBA/IgB,MAAM;kCAIG,MAAM;;CAkK9D,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useRef, useState, useCallback } from "react";
2
2
  import { InputManager, } from "../managers/InputManager.js";
3
+ import { logger } from "../utils/logger.js";
3
4
  export const useInputManager = (callbacks = {}) => {
4
5
  const managerRef = useRef(null);
5
6
  const [isManagerReady, setIsManagerReady] = useState(false);
@@ -28,12 +29,14 @@ export const useInputManager = (callbacks = {}) => {
28
29
  });
29
30
  const [showBashManager, setShowBashManager] = useState(false);
30
31
  const [showMcpManager, setShowMcpManager] = useState(false);
32
+ const [permissionMode, setPermissionModeState] = useState("default");
31
33
  const [attachedImages, setAttachedImages] = useState([]);
32
34
  // Create InputManager on mount and update callbacks when they change
33
35
  useEffect(() => {
34
36
  if (!managerRef.current) {
35
37
  // Create InputManager on first mount
36
38
  const manager = new InputManager({
39
+ logger,
37
40
  onInputTextChange: setInputText,
38
41
  onCursorPositionChange: setCursorPosition,
39
42
  onFileSelectorStateChange: (show, files, query, position) => {
@@ -54,6 +57,10 @@ export const useInputManager = (callbacks = {}) => {
54
57
  onMcpManagerStateChange: (show) => {
55
58
  setShowMcpManager(show);
56
59
  },
60
+ onPermissionModeChange: (mode) => {
61
+ setPermissionModeState(mode);
62
+ callbacks.onPermissionModeChange?.(mode);
63
+ },
57
64
  onImagesStateChange: setAttachedImages,
58
65
  onShowBashManager: () => setShowBashManager(true),
59
66
  onShowMcpManager: () => setShowMcpManager(true),
@@ -65,6 +72,7 @@ export const useInputManager = (callbacks = {}) => {
65
72
  else {
66
73
  // Update callbacks on existing manager
67
74
  managerRef.current.updateCallbacks({
75
+ logger,
68
76
  onInputTextChange: setInputText,
69
77
  onCursorPositionChange: setCursorPosition,
70
78
  onFileSelectorStateChange: (show, files, query, position) => {
@@ -85,6 +93,10 @@ export const useInputManager = (callbacks = {}) => {
85
93
  onMcpManagerStateChange: (show) => {
86
94
  setShowMcpManager(show);
87
95
  },
96
+ onPermissionModeChange: (mode) => {
97
+ setPermissionModeState(mode);
98
+ callbacks.onPermissionModeChange?.(mode);
99
+ },
88
100
  onImagesStateChange: setAttachedImages,
89
101
  onShowBashManager: () => setShowBashManager(true),
90
102
  onShowMcpManager: () => setShowMcpManager(true),
@@ -244,6 +256,7 @@ export const useInputManager = (callbacks = {}) => {
244
256
  memoryMessage: memoryTypeSelectorState.message,
245
257
  showBashManager,
246
258
  showMcpManager,
259
+ permissionMode,
247
260
  attachedImages,
248
261
  isManagerReady,
249
262
  // Methods
@@ -291,6 +304,10 @@ export const useInputManager = (callbacks = {}) => {
291
304
  setShowMcpManager: useCallback((show) => {
292
305
  managerRef.current?.setShowMcpManager(show);
293
306
  }, []),
307
+ setPermissionMode: useCallback((mode) => {
308
+ setPermissionModeState(mode);
309
+ managerRef.current?.setPermissionMode(mode);
310
+ }, []),
294
311
  // Image management
295
312
  addImage: useCallback((imagePath, mimeType) => {
296
313
  return managerRef.current?.addImage(imagePath, mimeType);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,wBAAsB,IAAI,kBAyFzB;AAGD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,wBAAsB,IAAI,kBAmHzB;AAGD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC"}