@robota-sdk/agent-transport 3.0.0-beta.75 → 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 (187) hide show
  1. package/README.md +10 -10
  2. package/dist/node/headless/index.cjs +1 -1
  3. package/dist/node/{headless-CT2ibQnr.cjs → headless-OnpVk4-k.cjs} +7 -7
  4. package/dist/node/index.cjs +1 -1
  5. package/dist/node/index.d.ts +1 -6
  6. package/dist/node/index.d.ts.map +1 -1
  7. package/dist/node/index.js +1 -1
  8. package/dist/node/index.js.map +1 -1
  9. package/package.json +7 -75
  10. package/src/index.ts +1 -5
  11. package/src/transport-registry.ts +0 -9
  12. package/dist/node/http/index.cjs +0 -1
  13. package/dist/node/http/index.d.ts +0 -2
  14. package/dist/node/http/index.js +0 -1
  15. package/dist/node/http-2Jiuflc1.js +0 -2
  16. package/dist/node/http-2Jiuflc1.js.map +0 -1
  17. package/dist/node/http-CBAvefLw.cjs +0 -1
  18. package/dist/node/index-BNccqSpv.d.ts +0 -86
  19. package/dist/node/index-BNccqSpv.d.ts.map +0 -1
  20. package/dist/node/index-BUhHIf7X.d.ts +0 -86
  21. package/dist/node/index-BUhHIf7X.d.ts.map +0 -1
  22. package/dist/node/index-BnAGE-u9.d.ts +0 -33
  23. package/dist/node/index-BnAGE-u9.d.ts.map +0 -1
  24. package/dist/node/index-BrQ4gGw0.d.ts +0 -213
  25. package/dist/node/index-BrQ4gGw0.d.ts.map +0 -1
  26. package/dist/node/index-CoeBF21y.d.ts +0 -213
  27. package/dist/node/index-CoeBF21y.d.ts.map +0 -1
  28. package/dist/node/index-DHt-2VQ-.d.ts +0 -46
  29. package/dist/node/index-DHt-2VQ-.d.ts.map +0 -1
  30. package/dist/node/index-DMwKN5Le.d.ts +0 -33
  31. package/dist/node/index-DMwKN5Le.d.ts.map +0 -1
  32. package/dist/node/index-c0M42fsA.d.ts +0 -46
  33. package/dist/node/index-c0M42fsA.d.ts.map +0 -1
  34. package/dist/node/mcp/index.cjs +0 -1
  35. package/dist/node/mcp/index.d.ts +0 -2
  36. package/dist/node/mcp/index.js +0 -1
  37. package/dist/node/mcp-BOglBJNy.cjs +0 -1
  38. package/dist/node/mcp-D3BBVK7C.js +0 -2
  39. package/dist/node/mcp-D3BBVK7C.js.map +0 -1
  40. package/dist/node/rolldown-runtime-CMqjfN_6.cjs +0 -1
  41. package/dist/node/tui/index.cjs +0 -1
  42. package/dist/node/tui/index.d.ts +0 -2
  43. package/dist/node/tui/index.js +0 -1
  44. package/dist/node/tui-CcH5EsQh.js +0 -25
  45. package/dist/node/tui-CcH5EsQh.js.map +0 -1
  46. package/dist/node/tui-DznRbcku.cjs +0 -24
  47. package/dist/node/ws/index.cjs +0 -1
  48. package/dist/node/ws/index.d.ts +0 -2
  49. package/dist/node/ws/index.js +0 -1
  50. package/dist/node/ws-Dc2RUwVs.js +0 -2
  51. package/dist/node/ws-Dc2RUwVs.js.map +0 -1
  52. package/dist/node/ws-QNMQn5kg.cjs +0 -1
  53. package/src/http/__tests__/http-transport.test.ts +0 -55
  54. package/src/http/__tests__/routes.test.ts +0 -168
  55. package/src/http/http-transport.ts +0 -41
  56. package/src/http/index.ts +0 -4
  57. package/src/http/routes.ts +0 -152
  58. package/src/mcp/__tests__/mcp-server.test.ts +0 -66
  59. package/src/mcp/__tests__/mcp-transport.test.ts +0 -46
  60. package/src/mcp/index.ts +0 -4
  61. package/src/mcp/mcp-server.ts +0 -163
  62. package/src/mcp/mcp-transport.ts +0 -48
  63. package/src/tui/App.tsx +0 -491
  64. package/src/tui/BackgroundTaskPanel.tsx +0 -36
  65. package/src/tui/CjkTextInput.tsx +0 -199
  66. package/src/tui/ConfirmPrompt.tsx +0 -70
  67. package/src/tui/ContextWarningBanner.tsx +0 -34
  68. package/src/tui/ExecutionWorkspaceDetailPane.tsx +0 -64
  69. package/src/tui/ExecutionWorkspaceSwitcher.tsx +0 -187
  70. package/src/tui/InputArea.tsx +0 -310
  71. package/src/tui/InteractivePrompt.tsx +0 -59
  72. package/src/tui/ListPicker.tsx +0 -95
  73. package/src/tui/MenuSelect.tsx +0 -104
  74. package/src/tui/MessageList.tsx +0 -284
  75. package/src/tui/PermissionPrompt.tsx +0 -86
  76. package/src/tui/PluginTUI.tsx +0 -258
  77. package/src/tui/SessionPicker.tsx +0 -68
  78. package/src/tui/SessionStatusBar.tsx +0 -73
  79. package/src/tui/SlashAutocomplete.tsx +0 -110
  80. package/src/tui/StatusBar.tsx +0 -236
  81. package/src/tui/StreamingIndicator.tsx +0 -93
  82. package/src/tui/TextPrompt.tsx +0 -81
  83. package/src/tui/ToolCommandOutput.tsx +0 -39
  84. package/src/tui/ToolDiffBlock.tsx +0 -32
  85. package/src/tui/TransportTUI.tsx +0 -117
  86. package/src/tui/TuiInteractionChannel.ts +0 -495
  87. package/src/tui/UpdateNotice.tsx +0 -14
  88. package/src/tui/UsageSummaryEntry.tsx +0 -39
  89. package/src/tui/WaveText.tsx +0 -44
  90. package/src/tui/__tests__/InteractivePrompt.test.tsx +0 -82
  91. package/src/tui/__tests__/ListPicker.test.tsx +0 -159
  92. package/src/tui/__tests__/MenuSelect.test.tsx +0 -103
  93. package/src/tui/__tests__/PluginTUI.test.tsx +0 -167
  94. package/src/tui/__tests__/SlashAutocomplete.test.tsx +0 -140
  95. package/src/tui/__tests__/TextPrompt.test.tsx +0 -98
  96. package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +0 -239
  97. package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +0 -297
  98. package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +0 -124
  99. package/src/tui/__tests__/UpdateNotice.test.tsx +0 -15
  100. package/src/tui/__tests__/abort-after-permission.test.tsx +0 -169
  101. package/src/tui/__tests__/abort-streaming-e2e.test.tsx +0 -183
  102. package/src/tui/__tests__/background-task-panel.test.tsx +0 -53
  103. package/src/tui/__tests__/background-task-row-format.test.ts +0 -59
  104. package/src/tui/__tests__/channel-factory-integration.test.ts +0 -138
  105. package/src/tui/__tests__/cjk-text-input-flow.test.ts +0 -109
  106. package/src/tui/__tests__/cjk-text-input.test.ts +0 -191
  107. package/src/tui/__tests__/command-effect-handler.test.ts +0 -127
  108. package/src/tui/__tests__/command-output-summary.test.ts +0 -95
  109. package/src/tui/__tests__/compact-event-bridge.test.ts +0 -20
  110. package/src/tui/__tests__/confirm-permission-flow.test.ts +0 -130
  111. package/src/tui/__tests__/confirm-prompt.test.tsx +0 -87
  112. package/src/tui/__tests__/execution-workspace-switcher.test.tsx +0 -110
  113. package/src/tui/__tests__/execution-workspace-view-model.test.ts +0 -93
  114. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +0 -125
  115. package/src/tui/__tests__/input-area-flow.test.ts +0 -164
  116. package/src/tui/__tests__/message-list-rendering.test.tsx +0 -353
  117. package/src/tui/__tests__/prompt-queue.test.tsx +0 -255
  118. package/src/tui/__tests__/provider-setup-pty-e2e.test.ts +0 -233
  119. package/src/tui/__tests__/pty/pty-driver.ts +0 -135
  120. package/src/tui/__tests__/pty/tui-pty.ptytest.ts +0 -61
  121. package/src/tui/__tests__/render-channel-options.test.ts +0 -32
  122. package/src/tui/__tests__/render-markdown.test.ts +0 -72
  123. package/src/tui/__tests__/selection-flow.test.ts +0 -61
  124. package/src/tui/__tests__/session-init-poller.test.ts +0 -102
  125. package/src/tui/__tests__/session-naming.test.ts +0 -64
  126. package/src/tui/__tests__/session-switch-channel.test.tsx +0 -307
  127. package/src/tui/__tests__/slash-routing-effects.test.ts +0 -228
  128. package/src/tui/__tests__/status-activity.test.ts +0 -71
  129. package/src/tui/__tests__/status-bar.test.tsx +0 -177
  130. package/src/tui/__tests__/streaming-indicator.test.tsx +0 -137
  131. package/src/tui/__tests__/text-prompt-flow.test.ts +0 -77
  132. package/src/tui/__tests__/tui-channel-init-failure.test.ts +0 -57
  133. package/src/tui/__tests__/tui-state-manager.test.ts +0 -401
  134. package/src/tui/background-task-row-format.ts +0 -53
  135. package/src/tui/command-interaction.ts +0 -9
  136. package/src/tui/command-output-summary.ts +0 -122
  137. package/src/tui/create-default-tui-cli-adapter.ts +0 -41
  138. package/src/tui/execution-workspace-view-model.ts +0 -123
  139. package/src/tui/flows/cjk-text-input-flow.ts +0 -285
  140. package/src/tui/flows/confirm-prompt-flow.ts +0 -45
  141. package/src/tui/flows/input-area-flow.ts +0 -189
  142. package/src/tui/flows/permission-prompt-flow.ts +0 -85
  143. package/src/tui/flows/selection-flow.ts +0 -126
  144. package/src/tui/flows/session-init-poller.ts +0 -77
  145. package/src/tui/flows/text-prompt-flow.ts +0 -98
  146. package/src/tui/hooks/command-effect-handler.ts +0 -97
  147. package/src/tui/hooks/command-effect-queue.ts +0 -39
  148. package/src/tui/hooks/side-effects-types.ts +0 -35
  149. package/src/tui/hooks/useAutocomplete.ts +0 -87
  150. package/src/tui/hooks/usePluginCallbacks.ts +0 -31
  151. package/src/tui/hooks/usePluginScreenData.ts +0 -85
  152. package/src/tui/hooks/useSideEffects.ts +0 -175
  153. package/src/tui/hooks/useSlashRouting.ts +0 -118
  154. package/src/tui/hooks/useStatusLineSettings.ts +0 -37
  155. package/src/tui/hooks/useTuiChannel.ts +0 -95
  156. package/src/tui/index.ts +0 -14
  157. package/src/tui/interactions/CommandConfirm.tsx +0 -36
  158. package/src/tui/interactions/CommandPicker.tsx +0 -77
  159. package/src/tui/interactions/__tests__/CommandConfirm.test.tsx +0 -124
  160. package/src/tui/interactions/__tests__/CommandPicker.test.tsx +0 -138
  161. package/src/tui/plugin-tui-handlers.ts +0 -163
  162. package/src/tui/render-markdown.ts +0 -130
  163. package/src/tui/render.tsx +0 -129
  164. package/src/tui/session-naming.ts +0 -33
  165. package/src/tui/status-activity.ts +0 -63
  166. package/src/tui/tui-cli-adapter-context.tsx +0 -13
  167. package/src/tui/tui-cli-adapter.ts +0 -25
  168. package/src/tui/tui-state-manager.ts +0 -226
  169. package/src/tui/tui-transport.ts +0 -35
  170. package/src/tui/types.ts +0 -15
  171. package/src/tui/utils/__tests__/edit-diff.test.ts +0 -426
  172. package/src/tui/utils/__tests__/paste-detection.test.ts +0 -116
  173. package/src/tui/utils/__tests__/paste-labels.test.ts +0 -46
  174. package/src/tui/utils/__tests__/tool-call-extractor.test.ts +0 -227
  175. package/src/tui/utils/__tests__/tool-diff-summary.test.ts +0 -104
  176. package/src/tui/utils/edit-diff.ts +0 -153
  177. package/src/tui/utils/paste-labels.ts +0 -9
  178. package/src/tui/utils/tool-call-extractor.ts +0 -92
  179. package/src/tui/utils/tool-diff-summary.ts +0 -75
  180. package/src/ws/__tests__/ws-handler.test.ts +0 -409
  181. package/src/ws/__tests__/ws-transport.test.ts +0 -53
  182. package/src/ws/index.ts +0 -13
  183. package/src/ws/ws-background-messages.ts +0 -170
  184. package/src/ws/ws-handler.ts +0 -280
  185. package/src/ws/ws-protocol.ts +0 -78
  186. package/src/ws/ws-transport-configurable.ts +0 -128
  187. 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
- }