@robota-sdk/agent-transport 3.0.0-beta.74 → 3.0.0-beta.76

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 (197) hide show
  1. package/README.md +10 -10
  2. package/dist/node/headless/index.cjs +1 -1
  3. package/dist/node/headless/index.d.ts +1 -1
  4. package/dist/node/headless/index.js +1 -1
  5. package/dist/node/headless-OnpVk4-k.cjs +15 -0
  6. package/dist/node/{headless-D02zUEGh.js → headless-mRYilLfC.js} +2 -2
  7. package/dist/node/{headless-D02zUEGh.js.map → headless-mRYilLfC.js.map} +1 -1
  8. package/dist/node/{index-DE3-dHqw.d.ts → index-CYl7ksS6.d.ts} +12 -2
  9. package/dist/node/{index-DE3-dHqw.d.ts.map → index-CYl7ksS6.d.ts.map} +1 -1
  10. package/dist/node/{index-WKTgvhlg.d.ts → index-E8Gx4-lc.d.ts} +12 -2
  11. package/dist/node/{index-WKTgvhlg.d.ts.map → index-E8Gx4-lc.d.ts.map} +1 -1
  12. package/dist/node/index.cjs +1 -1
  13. package/dist/node/index.d.ts +2 -7
  14. package/dist/node/index.d.ts.map +1 -1
  15. package/dist/node/index.js +1 -1
  16. package/dist/node/index.js.map +1 -1
  17. package/package.json +7 -75
  18. package/src/headless/HeadlessInteractionChannel.ts +21 -1
  19. package/src/index.ts +1 -5
  20. package/src/transport-registry.ts +0 -9
  21. package/dist/node/headless-BeHAOlIM.cjs +0 -15
  22. package/dist/node/http/index.cjs +0 -1
  23. package/dist/node/http/index.d.ts +0 -2
  24. package/dist/node/http/index.js +0 -1
  25. package/dist/node/http-2Jiuflc1.js +0 -2
  26. package/dist/node/http-2Jiuflc1.js.map +0 -1
  27. package/dist/node/http-CBAvefLw.cjs +0 -1
  28. package/dist/node/index-BQLN_Lc9.d.ts +0 -78
  29. package/dist/node/index-BQLN_Lc9.d.ts.map +0 -1
  30. package/dist/node/index-BnAGE-u9.d.ts +0 -33
  31. package/dist/node/index-BnAGE-u9.d.ts.map +0 -1
  32. package/dist/node/index-BrQ4gGw0.d.ts +0 -213
  33. package/dist/node/index-BrQ4gGw0.d.ts.map +0 -1
  34. package/dist/node/index-CoeBF21y.d.ts +0 -213
  35. package/dist/node/index-CoeBF21y.d.ts.map +0 -1
  36. package/dist/node/index-DHt-2VQ-.d.ts +0 -46
  37. package/dist/node/index-DHt-2VQ-.d.ts.map +0 -1
  38. package/dist/node/index-DMwKN5Le.d.ts +0 -33
  39. package/dist/node/index-DMwKN5Le.d.ts.map +0 -1
  40. package/dist/node/index-IvYaYY6v.d.ts +0 -78
  41. package/dist/node/index-IvYaYY6v.d.ts.map +0 -1
  42. package/dist/node/index-c0M42fsA.d.ts +0 -46
  43. package/dist/node/index-c0M42fsA.d.ts.map +0 -1
  44. package/dist/node/mcp/index.cjs +0 -1
  45. package/dist/node/mcp/index.d.ts +0 -2
  46. package/dist/node/mcp/index.js +0 -1
  47. package/dist/node/mcp-BOglBJNy.cjs +0 -1
  48. package/dist/node/mcp-D3BBVK7C.js +0 -2
  49. package/dist/node/mcp-D3BBVK7C.js.map +0 -1
  50. package/dist/node/rolldown-runtime-CMqjfN_6.cjs +0 -1
  51. package/dist/node/tui/index.cjs +0 -1
  52. package/dist/node/tui/index.d.ts +0 -2
  53. package/dist/node/tui/index.js +0 -1
  54. package/dist/node/tui-Btb1q88j.js +0 -25
  55. package/dist/node/tui-Btb1q88j.js.map +0 -1
  56. package/dist/node/tui-SbUT7Zlt.cjs +0 -24
  57. package/dist/node/ws/index.cjs +0 -1
  58. package/dist/node/ws/index.d.ts +0 -2
  59. package/dist/node/ws/index.js +0 -1
  60. package/dist/node/ws-Dc2RUwVs.js +0 -2
  61. package/dist/node/ws-Dc2RUwVs.js.map +0 -1
  62. package/dist/node/ws-QNMQn5kg.cjs +0 -1
  63. package/src/http/__tests__/http-transport.test.ts +0 -55
  64. package/src/http/__tests__/routes.test.ts +0 -168
  65. package/src/http/http-transport.ts +0 -41
  66. package/src/http/index.ts +0 -4
  67. package/src/http/routes.ts +0 -152
  68. package/src/mcp/__tests__/mcp-server.test.ts +0 -66
  69. package/src/mcp/__tests__/mcp-transport.test.ts +0 -46
  70. package/src/mcp/index.ts +0 -4
  71. package/src/mcp/mcp-server.ts +0 -163
  72. package/src/mcp/mcp-transport.ts +0 -48
  73. package/src/tui/App.tsx +0 -488
  74. package/src/tui/BackgroundTaskPanel.tsx +0 -36
  75. package/src/tui/CjkTextInput.tsx +0 -199
  76. package/src/tui/ConfirmPrompt.tsx +0 -70
  77. package/src/tui/ContextWarningBanner.tsx +0 -34
  78. package/src/tui/ExecutionWorkspaceDetailPane.tsx +0 -64
  79. package/src/tui/ExecutionWorkspaceSwitcher.tsx +0 -187
  80. package/src/tui/InputArea.tsx +0 -310
  81. package/src/tui/InteractivePrompt.tsx +0 -59
  82. package/src/tui/ListPicker.tsx +0 -95
  83. package/src/tui/MenuSelect.tsx +0 -104
  84. package/src/tui/MessageList.tsx +0 -284
  85. package/src/tui/PermissionPrompt.tsx +0 -86
  86. package/src/tui/PluginTUI.tsx +0 -258
  87. package/src/tui/SessionPicker.tsx +0 -68
  88. package/src/tui/SessionStatusBar.tsx +0 -70
  89. package/src/tui/SlashAutocomplete.tsx +0 -110
  90. package/src/tui/StatusBar.tsx +0 -209
  91. package/src/tui/StreamingIndicator.tsx +0 -93
  92. package/src/tui/TextPrompt.tsx +0 -81
  93. package/src/tui/ToolCommandOutput.tsx +0 -39
  94. package/src/tui/ToolDiffBlock.tsx +0 -32
  95. package/src/tui/TransportTUI.tsx +0 -117
  96. package/src/tui/TuiInteractionChannel.ts +0 -483
  97. package/src/tui/UpdateNotice.tsx +0 -14
  98. package/src/tui/UsageSummaryEntry.tsx +0 -39
  99. package/src/tui/WaveText.tsx +0 -44
  100. package/src/tui/__tests__/InteractivePrompt.test.tsx +0 -82
  101. package/src/tui/__tests__/ListPicker.test.tsx +0 -159
  102. package/src/tui/__tests__/MenuSelect.test.tsx +0 -103
  103. package/src/tui/__tests__/PluginTUI.test.tsx +0 -167
  104. package/src/tui/__tests__/SlashAutocomplete.test.tsx +0 -140
  105. package/src/tui/__tests__/TextPrompt.test.tsx +0 -98
  106. package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +0 -239
  107. package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +0 -297
  108. package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +0 -124
  109. package/src/tui/__tests__/UpdateNotice.test.tsx +0 -15
  110. package/src/tui/__tests__/abort-after-permission.test.tsx +0 -169
  111. package/src/tui/__tests__/abort-streaming-e2e.test.tsx +0 -183
  112. package/src/tui/__tests__/background-task-panel.test.tsx +0 -53
  113. package/src/tui/__tests__/background-task-row-format.test.ts +0 -59
  114. package/src/tui/__tests__/channel-factory-integration.test.ts +0 -138
  115. package/src/tui/__tests__/cjk-text-input-flow.test.ts +0 -109
  116. package/src/tui/__tests__/cjk-text-input.test.ts +0 -191
  117. package/src/tui/__tests__/command-effect-handler.test.ts +0 -127
  118. package/src/tui/__tests__/command-output-summary.test.ts +0 -95
  119. package/src/tui/__tests__/compact-event-bridge.test.ts +0 -20
  120. package/src/tui/__tests__/confirm-permission-flow.test.ts +0 -130
  121. package/src/tui/__tests__/confirm-prompt.test.tsx +0 -87
  122. package/src/tui/__tests__/execution-workspace-switcher.test.tsx +0 -110
  123. package/src/tui/__tests__/execution-workspace-view-model.test.ts +0 -93
  124. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +0 -125
  125. package/src/tui/__tests__/input-area-flow.test.ts +0 -164
  126. package/src/tui/__tests__/message-list-rendering.test.tsx +0 -353
  127. package/src/tui/__tests__/prompt-queue.test.tsx +0 -255
  128. package/src/tui/__tests__/provider-setup-pty-e2e.test.ts +0 -233
  129. package/src/tui/__tests__/pty/pty-driver.ts +0 -135
  130. package/src/tui/__tests__/pty/tui-pty.ptytest.ts +0 -61
  131. package/src/tui/__tests__/render-channel-options.test.ts +0 -32
  132. package/src/tui/__tests__/render-markdown.test.ts +0 -72
  133. package/src/tui/__tests__/selection-flow.test.ts +0 -61
  134. package/src/tui/__tests__/session-init-poller.test.ts +0 -102
  135. package/src/tui/__tests__/session-naming.test.ts +0 -64
  136. package/src/tui/__tests__/session-switch-channel.test.tsx +0 -307
  137. package/src/tui/__tests__/slash-routing-effects.test.ts +0 -228
  138. package/src/tui/__tests__/status-activity.test.ts +0 -71
  139. package/src/tui/__tests__/status-bar.test.tsx +0 -158
  140. package/src/tui/__tests__/streaming-indicator.test.tsx +0 -137
  141. package/src/tui/__tests__/text-prompt-flow.test.ts +0 -77
  142. package/src/tui/__tests__/tui-channel-init-failure.test.ts +0 -57
  143. package/src/tui/__tests__/tui-state-manager.test.ts +0 -401
  144. package/src/tui/background-task-row-format.ts +0 -53
  145. package/src/tui/command-interaction.ts +0 -9
  146. package/src/tui/command-output-summary.ts +0 -122
  147. package/src/tui/create-default-tui-cli-adapter.ts +0 -41
  148. package/src/tui/execution-workspace-view-model.ts +0 -123
  149. package/src/tui/flows/cjk-text-input-flow.ts +0 -285
  150. package/src/tui/flows/confirm-prompt-flow.ts +0 -45
  151. package/src/tui/flows/input-area-flow.ts +0 -189
  152. package/src/tui/flows/permission-prompt-flow.ts +0 -85
  153. package/src/tui/flows/selection-flow.ts +0 -126
  154. package/src/tui/flows/session-init-poller.ts +0 -77
  155. package/src/tui/flows/text-prompt-flow.ts +0 -98
  156. package/src/tui/hooks/command-effect-handler.ts +0 -97
  157. package/src/tui/hooks/command-effect-queue.ts +0 -39
  158. package/src/tui/hooks/side-effects-types.ts +0 -35
  159. package/src/tui/hooks/useAutocomplete.ts +0 -87
  160. package/src/tui/hooks/usePluginCallbacks.ts +0 -31
  161. package/src/tui/hooks/usePluginScreenData.ts +0 -85
  162. package/src/tui/hooks/useSideEffects.ts +0 -175
  163. package/src/tui/hooks/useSlashRouting.ts +0 -118
  164. package/src/tui/hooks/useStatusLineSettings.ts +0 -37
  165. package/src/tui/hooks/useTuiChannel.ts +0 -95
  166. package/src/tui/index.ts +0 -14
  167. package/src/tui/interactions/CommandConfirm.tsx +0 -36
  168. package/src/tui/interactions/CommandPicker.tsx +0 -77
  169. package/src/tui/interactions/__tests__/CommandConfirm.test.tsx +0 -124
  170. package/src/tui/interactions/__tests__/CommandPicker.test.tsx +0 -138
  171. package/src/tui/plugin-tui-handlers.ts +0 -163
  172. package/src/tui/render-markdown.ts +0 -130
  173. package/src/tui/render.tsx +0 -117
  174. package/src/tui/session-naming.ts +0 -33
  175. package/src/tui/status-activity.ts +0 -63
  176. package/src/tui/tui-cli-adapter-context.tsx +0 -13
  177. package/src/tui/tui-cli-adapter.ts +0 -25
  178. package/src/tui/tui-state-manager.ts +0 -226
  179. package/src/tui/tui-transport.ts +0 -35
  180. package/src/tui/types.ts +0 -15
  181. package/src/tui/utils/__tests__/edit-diff.test.ts +0 -426
  182. package/src/tui/utils/__tests__/paste-detection.test.ts +0 -116
  183. package/src/tui/utils/__tests__/paste-labels.test.ts +0 -46
  184. package/src/tui/utils/__tests__/tool-call-extractor.test.ts +0 -227
  185. package/src/tui/utils/__tests__/tool-diff-summary.test.ts +0 -104
  186. package/src/tui/utils/edit-diff.ts +0 -153
  187. package/src/tui/utils/paste-labels.ts +0 -9
  188. package/src/tui/utils/tool-call-extractor.ts +0 -92
  189. package/src/tui/utils/tool-diff-summary.ts +0 -75
  190. package/src/ws/__tests__/ws-handler.test.ts +0 -409
  191. package/src/ws/__tests__/ws-transport.test.ts +0 -53
  192. package/src/ws/index.ts +0 -13
  193. package/src/ws/ws-background-messages.ts +0 -170
  194. package/src/ws/ws-handler.ts +0 -280
  195. package/src/ws/ws-protocol.ts +0 -78
  196. package/src/ws/ws-transport-configurable.ts +0 -128
  197. package/src/ws/ws-transport.ts +0 -42
@@ -1,284 +0,0 @@
1
- import { isToolMessage, isAssistantMessage } from '@robota-sdk/agent-core';
2
- import { Box, Text } from 'ink';
3
- import React from 'react';
4
-
5
- import { formatCommandOutputSummary } from './command-output-summary.js';
6
- import { renderMarkdown } from './render-markdown.js';
7
- import ToolCommandOutput from './ToolCommandOutput.js';
8
- import ToolDiffBlock from './ToolDiffBlock.js';
9
- import UsageSummaryEntry from './UsageSummaryEntry.js';
10
-
11
- import type { IToolCallSummary } from './utils/tool-call-extractor.js';
12
- import type { IHistoryEntry, TUniversalMessage, TUniversalValue } from '@robota-sdk/agent-core';
13
-
14
- interface IProps {
15
- history: IHistoryEntry[];
16
- }
17
-
18
- type TToolSummaryItem = {
19
- toolName: string;
20
- firstArg?: string;
21
- isRunning?: boolean;
22
- result?: string;
23
- diffLines?: IToolCallSummary['diffLines'];
24
- diffFile?: string;
25
- toolResultData?: string;
26
- };
27
-
28
- function getToolSummaryStatus(tool: TToolSummaryItem): string {
29
- if (formatCommandOutputSummary(tool)?.status === 'error') return '✗';
30
- if (tool.isRunning) return '⟳';
31
- if (tool.result === 'error') return '✗';
32
- if (tool.result === 'denied') return '⊘';
33
- return '✓';
34
- }
35
-
36
- function getToolSummaryColor(tool: TToolSummaryItem): string {
37
- if (formatCommandOutputSummary(tool)?.status === 'error' || tool.result === 'error') return 'red';
38
- if (tool.isRunning || tool.result === 'denied') return 'yellow';
39
- return 'green';
40
- }
41
-
42
- function getToolSummaryLabel(tool: TToolSummaryItem): string {
43
- return `${getToolSummaryStatus(tool)} ${tool.toolName}${tool.firstArg ? `(${tool.firstArg})` : ''}`;
44
- }
45
-
46
- function RoleLabel({ role }: { role: TUniversalMessage['role'] }): React.ReactElement {
47
- switch (role) {
48
- case 'user':
49
- return (
50
- <Text color="green" bold>
51
- You:{' '}
52
- </Text>
53
- );
54
- case 'assistant':
55
- return (
56
- <Text color="cyan" bold>
57
- Robota:{' '}
58
- </Text>
59
- );
60
- case 'system':
61
- return (
62
- <Text color="yellow" bold>
63
- System:{' '}
64
- </Text>
65
- );
66
- case 'tool':
67
- return (
68
- <Text color="white" bold>
69
- Tool:{' '}
70
- </Text>
71
- );
72
- }
73
- }
74
-
75
- function ToolMessage({ message }: { message: TUniversalMessage }): React.ReactElement {
76
- if (!isToolMessage(message)) {
77
- return <></>;
78
- }
79
- const toolName = message.name;
80
- const content = message.content;
81
-
82
- // Try to parse structured tool summaries (with diff info)
83
- let summaries: IToolCallSummary[] | null = null;
84
- try {
85
- const parsed = JSON.parse(content) as IToolCallSummary[];
86
- if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === 'string') {
87
- summaries = parsed as IToolCallSummary[];
88
- }
89
- } catch {
90
- // Not JSON — fall back to plain text lines
91
- }
92
-
93
- if (summaries) {
94
- return (
95
- <Box flexDirection="column" marginBottom={1}>
96
- <Box>
97
- <Text color="white" bold>
98
- Tool:{' '}
99
- </Text>
100
- {toolName && (
101
- <Text color="white" dimColor>
102
- [{toolName}]
103
- </Text>
104
- )}
105
- </Box>
106
- <Text> </Text>
107
- {summaries.map((s, i) => (
108
- <Box key={i} flexDirection="column">
109
- <Text color="green">
110
- {' '}
111
- {'✓'} {s.line}
112
- </Text>
113
- {s.diffLines && s.diffLines.length > 0 && (
114
- <ToolDiffBlock file={s.diffFile} lines={s.diffLines} />
115
- )}
116
- </Box>
117
- ))}
118
- </Box>
119
- );
120
- }
121
-
122
- // Fallback: plain text lines
123
- const lines = content.split('\n').filter((l) => l.trim());
124
- return (
125
- <Box flexDirection="column" marginBottom={1}>
126
- <Box>
127
- <Text color="white" bold>
128
- Tool:{' '}
129
- </Text>
130
- {toolName && (
131
- <Text color="white" dimColor>
132
- [{toolName}]
133
- </Text>
134
- )}
135
- </Box>
136
- <Text> </Text>
137
- {lines.map((line, i) => (
138
- <Text key={i} color="green">
139
- {' '}
140
- {'✓'} {line}
141
- </Text>
142
- ))}
143
- </Box>
144
- );
145
- }
146
-
147
- const MessageItem = React.memo(function MessageItem({
148
- message,
149
- }: {
150
- message: TUniversalMessage;
151
- }): React.ReactElement {
152
- if (isToolMessage(message)) {
153
- return <ToolMessage message={message} />;
154
- }
155
-
156
- const content = message.content ?? '';
157
- const isInterrupted = message.state === 'interrupted';
158
-
159
- return (
160
- <Box flexDirection="column" marginBottom={1}>
161
- <Box>
162
- <RoleLabel role={message.role} />
163
- </Box>
164
- <Text> </Text>
165
- <Box marginLeft={2}>
166
- <Text wrap="wrap">
167
- {isAssistantMessage(message)
168
- ? renderMarkdown(content + (isInterrupted ? '\n\n_(interrupted)_' : ''))
169
- : content}
170
- </Text>
171
- </Box>
172
- </Box>
173
- );
174
- });
175
-
176
- function ToolSummaryEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {
177
- const data = entry.data as
178
- | {
179
- summary?: string;
180
- tools?: TToolSummaryItem[];
181
- }
182
- | undefined;
183
- const tools = data?.tools;
184
- const lines = data?.summary?.split('\n') ?? [];
185
-
186
- if (tools && tools.length > 0) {
187
- return (
188
- <Box flexDirection="column" marginBottom={1}>
189
- <Box>
190
- <Text color="white" bold>
191
- Tool:{' '}
192
- </Text>
193
- </Box>
194
- <Text> </Text>
195
- {tools.map((tool, i) => (
196
- <Box key={i} flexDirection="column">
197
- <Text color={getToolSummaryColor(tool)}>
198
- {' '}
199
- {getToolSummaryLabel(tool)}
200
- </Text>
201
- <ToolCommandOutput tool={tool} />
202
- {tool.diffLines && tool.diffLines.length > 0 && (
203
- <ToolDiffBlock file={tool.diffFile} lines={tool.diffLines} />
204
- )}
205
- </Box>
206
- ))}
207
- </Box>
208
- );
209
- }
210
-
211
- return (
212
- <Box flexDirection="column" marginBottom={1}>
213
- <Box>
214
- <Text color="white" bold>
215
- Tool:{' '}
216
- </Text>
217
- </Box>
218
- <Text> </Text>
219
- {lines.map((line, i) => (
220
- <Text key={i} color="green">
221
- {' '}
222
- {line}
223
- </Text>
224
- ))}
225
- </Box>
226
- );
227
- }
228
-
229
- function EventEntry({ entry }: { entry: IHistoryEntry }): React.ReactElement {
230
- const eventData = entry.data as Record<string, TUniversalValue> | undefined;
231
- const eventMessage =
232
- typeof eventData?.message === 'string'
233
- ? eventData.message
234
- : typeof eventData?.content === 'string'
235
- ? eventData.content
236
- : entry.type;
237
-
238
- return (
239
- <Box flexDirection="column" marginBottom={1}>
240
- <Box>
241
- <Text color="yellow" bold>
242
- System:{' '}
243
- </Text>
244
- </Box>
245
- <Text> </Text>
246
- <Box marginLeft={2}>
247
- <Text wrap="wrap">{eventMessage}</Text>
248
- </Box>
249
- </Box>
250
- );
251
- }
252
-
253
- function EntryItem({ entry }: { entry: IHistoryEntry }): React.ReactElement {
254
- if (entry.category === 'chat') {
255
- const message = entry.data as TUniversalMessage;
256
- return <MessageItem message={message} />;
257
- }
258
-
259
- if (entry.type === 'tool-summary') {
260
- return <ToolSummaryEntry entry={entry} />;
261
- }
262
-
263
- if (entry.type === 'usage-summary') {
264
- return <UsageSummaryEntry entry={entry} />;
265
- }
266
-
267
- // tool-start/tool-end are recorded in history for persistence but not rendered
268
- // (StreamingIndicator shows them during streaming, tool-summary shows them after)
269
- if (entry.type === 'tool-start' || entry.type === 'tool-end') {
270
- return <></>;
271
- }
272
-
273
- return <EventEntry entry={entry} />;
274
- }
275
-
276
- export default function MessageList({ history }: IProps): React.ReactElement {
277
- return (
278
- <Box flexDirection="column">
279
- {history.map((entry) => (
280
- <EntryItem key={entry.id} entry={entry} />
281
- ))}
282
- </Box>
283
- );
284
- }
@@ -1,86 +0,0 @@
1
- import { Box, Text, useInput } from 'ink';
2
- import React from 'react';
3
-
4
- import {
5
- applyPermissionPromptInput,
6
- getPermissionPromptInputAction,
7
- PERMISSION_PROMPT_OPTIONS,
8
- type TPermissionPromptInputAction,
9
- } from './flows/permission-prompt-flow.js';
10
- import { createSelectionFlowState, type ISelectionFlowState } from './flows/selection-flow.js';
11
-
12
- import type { IPermissionRequest } from './types.js';
13
- import type { TToolArgs } from '@robota-sdk/agent-core';
14
-
15
- interface IProps {
16
- request: IPermissionRequest;
17
- }
18
-
19
- function formatArgs(args: TToolArgs): string {
20
- const entries = Object.entries(args);
21
- if (entries.length === 0) return '(no arguments)';
22
- return entries
23
- .map(([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`)
24
- .join(', ');
25
- }
26
-
27
- export default function PermissionPrompt({ request }: IProps): React.ReactElement {
28
- const [state, setState] = React.useState<ISelectionFlowState>(() => createSelectionFlowState());
29
- const stateRef = React.useRef(state);
30
- const prevRequestRef = React.useRef(request);
31
-
32
- if (prevRequestRef.current !== request) {
33
- prevRequestRef.current = request;
34
- const nextState = createSelectionFlowState();
35
- stateRef.current = nextState;
36
- setState(nextState);
37
- }
38
-
39
- const applyAction = React.useCallback(
40
- (action: TPermissionPromptInputAction): void => {
41
- const result = applyPermissionPromptInput(stateRef.current, action);
42
- stateRef.current = result.state;
43
- setState(result.state);
44
- if (result.effect.type === 'resolve') {
45
- request.resolve(result.effect.decision);
46
- }
47
- },
48
- [request],
49
- );
50
-
51
- useInput((input, key) => {
52
- const action = getPermissionPromptInputAction(input, key);
53
- if (action !== undefined) {
54
- applyAction(action);
55
- }
56
- });
57
-
58
- return (
59
- <Box flexDirection="column" borderStyle="round" borderColor="yellow" paddingX={1}>
60
- <Text color="yellow" bold>
61
- [Permission Required]
62
- </Text>
63
- <Text>
64
- Tool:{' '}
65
- <Text color="cyan" bold>
66
- {request.toolName}
67
- </Text>
68
- </Text>
69
- <Text dimColor> {formatArgs(request.toolArgs)}</Text>
70
- <Box marginTop={1}>
71
- {PERMISSION_PROMPT_OPTIONS.map((opt, i) => (
72
- <Box key={opt} marginRight={2}>
73
- <Text
74
- color={i === state.selectedIndex ? 'cyan' : undefined}
75
- bold={i === state.selectedIndex}
76
- >
77
- {i === state.selectedIndex ? '> ' : ' '}
78
- {opt}
79
- </Text>
80
- </Box>
81
- ))}
82
- </Box>
83
- <Text dimColor> left/right to select, Enter to confirm</Text>
84
- </Box>
85
- );
86
- }
@@ -1,258 +0,0 @@
1
- /**
2
- * PluginTUI — Main orchestrator component for plugin management.
3
- * Manages a stack of screens: main, marketplace, installed plugins, etc.
4
- */
5
-
6
- import React, { useState, useCallback } from 'react';
7
-
8
- import ConfirmPrompt from './ConfirmPrompt.js';
9
- import { usePluginScreenData } from './hooks/usePluginScreenData.js';
10
- import MenuSelect from './MenuSelect.js';
11
- import {
12
- handleMainSelect,
13
- handleMarketplaceListSelect,
14
- handleMarketplaceActionSelect,
15
- handleMarketplaceBrowseSelect,
16
- handleInstallScopeSelect,
17
- handleInstalledListSelect,
18
- handleInstalledActionSelect,
19
- } from './plugin-tui-handlers.js';
20
- import TextPrompt from './TextPrompt.js';
21
-
22
- import type { IMenuSelectItem } from './MenuSelect.js';
23
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
24
-
25
- type TScreenId =
26
- | 'main'
27
- | 'marketplace-list'
28
- | 'marketplace-action'
29
- | 'marketplace-browse'
30
- | 'marketplace-install-scope'
31
- | 'marketplace-add'
32
- | 'installed-list'
33
- | 'installed-action';
34
-
35
- interface IMenuContext {
36
- marketplace?: string;
37
- pluginId?: string;
38
- }
39
-
40
- interface IMenuState {
41
- screen: TScreenId;
42
- context?: IMenuContext;
43
- }
44
-
45
- interface IConfirmState {
46
- message: string;
47
- onConfirm: () => void;
48
- onCancel: () => void;
49
- }
50
-
51
- interface IProps {
52
- callbacks: ICommandPluginAdapter;
53
- onClose: () => void;
54
- addMessage?: (msg: { role: string; content: string }) => void;
55
- }
56
-
57
- export default function PluginTUI({ callbacks, onClose, addMessage }: IProps): React.ReactElement {
58
- const [stack, setStack] = useState<IMenuState[]>([{ screen: 'main' }]);
59
- const [confirm, setConfirm] = useState<IConfirmState | undefined>();
60
- const [refreshCounter, setRefreshCounter] = useState(0);
61
-
62
- const current = stack[stack.length - 1] ?? { screen: 'main' };
63
-
64
- const push = useCallback((state: IMenuState) => {
65
- setStack((prev) => [...prev, state]);
66
- }, []);
67
-
68
- const pop = useCallback(() => {
69
- setStack((prev) => {
70
- if (prev.length <= 1) {
71
- onClose();
72
- return prev;
73
- }
74
- return prev.slice(0, -1);
75
- });
76
- }, [onClose]);
77
-
78
- const popN = useCallback(
79
- (n: number) => {
80
- setStack((prev) => {
81
- const next = prev.slice(0, Math.max(1, prev.length - n));
82
- if (next.length === 0) {
83
- onClose();
84
- return prev;
85
- }
86
- return next;
87
- });
88
- },
89
- [onClose],
90
- );
91
-
92
- const notify = useCallback(
93
- (content: string) => {
94
- addMessage?.({ role: 'system', content });
95
- },
96
- [addMessage],
97
- );
98
-
99
- const refresh = useCallback(() => {
100
- setRefreshCounter((c) => c + 1);
101
- }, []);
102
-
103
- const setConfirmNav = useCallback(
104
- (state: IConfirmState | undefined) => setConfirm(state),
105
- [setConfirm],
106
- );
107
- // nav.push accepts a loose { screen: string } shape to satisfy plugin-tui-handlers types;
108
- // we cast screen to TScreenId which is safe because handlers only push valid screen names.
109
- const pushNav = useCallback(
110
- (state: { screen: string; context?: IMenuContext }) =>
111
- push({ screen: state.screen as TScreenId, context: state.context }),
112
- [push],
113
- );
114
- const nav = { push: pushNav, pop, popN, notify, setConfirm: setConfirmNav, refresh };
115
-
116
- const { items, loading, error } = usePluginScreenData(
117
- current.screen,
118
- current.context?.marketplace,
119
- callbacks,
120
- refreshCounter,
121
- stack.length,
122
- );
123
-
124
- const handleSelect = useCallback(
125
- (value: string) => {
126
- const screen = current.screen;
127
- const ctx = current.context;
128
-
129
- if (screen === 'main') handleMainSelect(value, nav);
130
- else if (screen === 'marketplace-list') handleMarketplaceListSelect(value, nav);
131
- else if (screen === 'marketplace-action')
132
- handleMarketplaceActionSelect(value, ctx?.marketplace ?? '', callbacks, nav);
133
- else if (screen === 'marketplace-browse')
134
- handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? '', items, nav);
135
- else if (screen === 'marketplace-install-scope')
136
- handleInstallScopeSelect(value, ctx?.pluginId ?? '', callbacks, nav);
137
- else if (screen === 'installed-list') handleInstalledListSelect(value, callbacks, nav);
138
- else if (screen === 'installed-action')
139
- handleInstalledActionSelect(value, ctx?.pluginId ?? '', callbacks, nav);
140
- },
141
- [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh],
142
- );
143
-
144
- const handleTextSubmit = useCallback(
145
- (value: string) => {
146
- if (current.screen === 'marketplace-add') {
147
- callbacks
148
- .marketplaceAdd(value)
149
- .then((name) => {
150
- notify(`Added marketplace "${name}" from ${value}.`);
151
- pop();
152
- })
153
- .catch((err) => {
154
- notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
155
- pop();
156
- });
157
- }
158
- },
159
- [current.screen, callbacks, notify, pop],
160
- );
161
-
162
- // Confirm overlay intercepts everything
163
- if (confirm) {
164
- return (
165
- <ConfirmPrompt
166
- message={confirm.message}
167
- onSelect={(index) => {
168
- if (index === 0) confirm.onConfirm();
169
- else confirm.onCancel();
170
- }}
171
- />
172
- );
173
- }
174
-
175
- const screen = current.screen;
176
-
177
- if (screen === 'marketplace-add') {
178
- return (
179
- <TextPrompt
180
- title="Add Marketplace Source"
181
- placeholder="owner/repo or git URL"
182
- onSubmit={handleTextSubmit}
183
- onCancel={pop}
184
- validate={(v) => (!v.includes('/') ? 'Must be owner/repo or a git URL' : undefined)}
185
- />
186
- );
187
- }
188
-
189
- if (screen === 'marketplace-action') {
190
- return (
191
- <MenuSelect
192
- key={stack.length}
193
- title={`Marketplace: ${current.context?.marketplace ?? ''}`}
194
- items={[
195
- { label: 'Browse plugins', value: 'browse' },
196
- { label: 'Update', value: 'update' },
197
- { label: 'Remove', value: 'remove' },
198
- ]}
199
- onSelect={handleSelect}
200
- onBack={pop}
201
- />
202
- );
203
- }
204
-
205
- if (screen === 'marketplace-install-scope') {
206
- return (
207
- <MenuSelect
208
- key={stack.length}
209
- title={`Install scope for "${current.context?.pluginId ?? ''}"`}
210
- items={[
211
- { label: 'User scope', value: 'user' },
212
- { label: 'Project scope', value: 'project' },
213
- ]}
214
- onSelect={handleSelect}
215
- onBack={pop}
216
- />
217
- );
218
- }
219
-
220
- if (screen === 'installed-action') {
221
- return (
222
- <MenuSelect
223
- key={stack.length}
224
- title={`Plugin: ${current.context?.pluginId ?? ''}`}
225
- items={[{ label: 'Uninstall', value: 'uninstall' }]}
226
- onSelect={handleSelect}
227
- onBack={pop}
228
- />
229
- );
230
- }
231
-
232
- // Screens with async items: main, marketplace-list, marketplace-browse, installed-list
233
- const titleMap: Record<string, string> = {
234
- main: 'Plugin Management',
235
- 'marketplace-list': 'Marketplace',
236
- 'marketplace-browse': `Browse: ${current.context?.marketplace ?? ''}`,
237
- 'installed-list': 'Installed Plugins',
238
- };
239
-
240
- const staticItemsMap: Record<string, IMenuSelectItem[]> = {
241
- main: [
242
- { label: 'Marketplace', value: 'marketplace' },
243
- { label: 'Installed Plugins', value: 'installed' },
244
- ],
245
- };
246
-
247
- return (
248
- <MenuSelect
249
- key={`${screen}-${stack.length}-${refreshCounter}`}
250
- title={titleMap[screen] ?? 'Plugin Management'}
251
- items={staticItemsMap[screen] ?? items}
252
- onSelect={handleSelect}
253
- onBack={pop}
254
- loading={loading}
255
- error={error}
256
- />
257
- );
258
- }
@@ -1,68 +0,0 @@
1
- /**
2
- * Session picker component for /resume command.
3
- * Shows a list of sessions for the current cwd.
4
- */
5
-
6
- import { Box, Text } from 'ink';
7
- import React from 'react';
8
-
9
- import ListPicker from './ListPicker.js';
10
-
11
- import type { IResumableSessionSummary } from '@robota-sdk/agent-interface-transport';
12
-
13
- const SESSION_ID_DISPLAY_LENGTH = 8;
14
- const SESSION_PREVIEW_DISPLAY_LENGTH = 60;
15
-
16
- interface IProps {
17
- sessions: readonly IResumableSessionSummary[];
18
- onSelect: (sessionId: string) => void;
19
- onCancel: () => void;
20
- }
21
-
22
- export default function SessionPicker({
23
- sessions,
24
- onSelect,
25
- onCancel,
26
- }: IProps): React.ReactElement {
27
- return (
28
- <Box flexDirection="column" paddingX={1} marginBottom={1}>
29
- <Text bold color="cyan">
30
- Select a session to resume (ESC to cancel):
31
- </Text>
32
- <ListPicker<IResumableSessionSummary>
33
- items={[...sessions]}
34
- renderItem={(session: IResumableSessionSummary, isSelected: boolean) => {
35
- const preview = session.preview
36
- ? session.preview.slice(0, SESSION_PREVIEW_DISPLAY_LENGTH) +
37
- (session.preview.length > SESSION_PREVIEW_DISPLAY_LENGTH ? '...' : '')
38
- : '';
39
- return (
40
- <Text>
41
- {isSelected ? '> ' : ' '}
42
- <Text bold>{session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH)}</Text>
43
- {' '}
44
- <Text dimColor>
45
- {new Date(session.updatedAt).toLocaleString(undefined, {
46
- month: 'short',
47
- day: 'numeric',
48
- hour: '2-digit',
49
- minute: '2-digit',
50
- })}
51
- </Text>
52
- {' '}
53
- <Text dimColor>msgs: {session.messageCount}</Text>
54
- {preview ? (
55
- <>
56
- {'\n '}
57
- <Text color="gray">{preview}</Text>
58
- </>
59
- ) : null}
60
- </Text>
61
- );
62
- }}
63
- onSelect={(session: IResumableSessionSummary) => onSelect(session.id)}
64
- onCancel={onCancel}
65
- />
66
- </Box>
67
- );
68
- }