@robota-sdk/agent-transport 3.0.0-beta.75 → 3.0.0-beta.77

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 (231) hide show
  1. package/LICENSE +661 -21
  2. package/README.md +17 -14
  3. package/dist/node/headless/index.cjs +1 -1
  4. package/dist/node/headless/index.d.ts +1 -1
  5. package/dist/node/headless/index.js +1 -1
  6. package/dist/node/{headless-mRYilLfC.js → headless-DG_MEbf0.js} +7 -4
  7. package/dist/node/headless-DG_MEbf0.js.map +1 -0
  8. package/dist/node/headless-xq77JBq0.cjs +18 -0
  9. package/dist/node/{index-E8Gx4-lc.d.ts → index-CrJ2Lhn3.d.ts} +16 -3
  10. package/dist/node/index-CrJ2Lhn3.d.ts.map +1 -0
  11. package/dist/node/index-JIvzrtaA.d.ts +52 -0
  12. package/dist/node/index-JIvzrtaA.d.ts.map +1 -0
  13. package/dist/node/{index-CYl7ksS6.d.ts → index-JTD_xtEQ.d.ts} +15 -2
  14. package/dist/node/index-JTD_xtEQ.d.ts.map +1 -0
  15. package/dist/node/index-y_HBOfzO.d.ts +52 -0
  16. package/dist/node/index-y_HBOfzO.d.ts.map +1 -0
  17. package/dist/node/index.cjs +1 -1
  18. package/dist/node/index.d.ts +10 -9
  19. package/dist/node/index.d.ts.map +1 -1
  20. package/dist/node/index.js +1 -1
  21. package/dist/node/index.js.map +1 -1
  22. package/dist/node/programmatic/index.cjs +1 -0
  23. package/dist/node/programmatic/index.d.ts +2 -0
  24. package/dist/node/programmatic/index.js +1 -0
  25. package/dist/node/programmatic-6wLf68qJ.cjs +1 -0
  26. package/dist/node/programmatic-70dxIXtS.js +2 -0
  27. package/dist/node/programmatic-70dxIXtS.js.map +1 -0
  28. package/dist/node/testing/index.cjs +1 -1
  29. package/dist/node/testing/index.d.ts +2 -21
  30. package/dist/node/testing/index.js +1 -2
  31. package/package.json +15 -71
  32. package/src/__tests__/programmatic/programmatic-ask-user.test.ts +32 -0
  33. package/src/__tests__/programmatic/programmatic-driver.test.ts +107 -0
  34. package/src/__tests__/programmatic/programmatic-flows.test.ts +140 -0
  35. package/src/__tests__/transport-registry.stop-all.test.ts +53 -0
  36. package/src/headless/HeadlessInteractionChannel.ts +22 -5
  37. package/src/headless/__tests__/headless-goal-runner.test.ts +108 -0
  38. package/src/headless/__tests__/headless-runner.test.ts +2 -1
  39. package/src/headless/headless-runner.ts +67 -1
  40. package/src/headless/headless-stream-json.ts +1 -1
  41. package/src/headless/print-terminal.ts +1 -0
  42. package/src/index.ts +2 -5
  43. package/src/programmatic/ProgrammaticInteractionChannel.ts +82 -0
  44. package/src/programmatic/createProgrammaticAgent.ts +71 -0
  45. package/src/programmatic/index.ts +5 -0
  46. package/src/testing/index.ts +7 -4
  47. package/src/transport-registry.ts +15 -12
  48. package/dist/node/headless-CT2ibQnr.cjs +0 -15
  49. package/dist/node/headless-mRYilLfC.js.map +0 -1
  50. package/dist/node/http/index.cjs +0 -1
  51. package/dist/node/http/index.d.ts +0 -2
  52. package/dist/node/http/index.js +0 -1
  53. package/dist/node/http-2Jiuflc1.js +0 -2
  54. package/dist/node/http-2Jiuflc1.js.map +0 -1
  55. package/dist/node/http-CBAvefLw.cjs +0 -1
  56. package/dist/node/index-BNccqSpv.d.ts +0 -86
  57. package/dist/node/index-BNccqSpv.d.ts.map +0 -1
  58. package/dist/node/index-BUhHIf7X.d.ts +0 -86
  59. package/dist/node/index-BUhHIf7X.d.ts.map +0 -1
  60. package/dist/node/index-BnAGE-u9.d.ts +0 -33
  61. package/dist/node/index-BnAGE-u9.d.ts.map +0 -1
  62. package/dist/node/index-BrQ4gGw0.d.ts +0 -213
  63. package/dist/node/index-BrQ4gGw0.d.ts.map +0 -1
  64. package/dist/node/index-CYl7ksS6.d.ts.map +0 -1
  65. package/dist/node/index-CoeBF21y.d.ts +0 -213
  66. package/dist/node/index-CoeBF21y.d.ts.map +0 -1
  67. package/dist/node/index-DHt-2VQ-.d.ts +0 -46
  68. package/dist/node/index-DHt-2VQ-.d.ts.map +0 -1
  69. package/dist/node/index-DMwKN5Le.d.ts +0 -33
  70. package/dist/node/index-DMwKN5Le.d.ts.map +0 -1
  71. package/dist/node/index-E8Gx4-lc.d.ts.map +0 -1
  72. package/dist/node/index-c0M42fsA.d.ts +0 -46
  73. package/dist/node/index-c0M42fsA.d.ts.map +0 -1
  74. package/dist/node/mcp/index.cjs +0 -1
  75. package/dist/node/mcp/index.d.ts +0 -2
  76. package/dist/node/mcp/index.js +0 -1
  77. package/dist/node/mcp-BOglBJNy.cjs +0 -1
  78. package/dist/node/mcp-D3BBVK7C.js +0 -2
  79. package/dist/node/mcp-D3BBVK7C.js.map +0 -1
  80. package/dist/node/rolldown-runtime-CMqjfN_6.cjs +0 -1
  81. package/dist/node/testing/index.d.ts.map +0 -1
  82. package/dist/node/testing/index.js.map +0 -1
  83. package/dist/node/tui/index.cjs +0 -1
  84. package/dist/node/tui/index.d.ts +0 -2
  85. package/dist/node/tui/index.js +0 -1
  86. package/dist/node/tui-CcH5EsQh.js +0 -25
  87. package/dist/node/tui-CcH5EsQh.js.map +0 -1
  88. package/dist/node/tui-DznRbcku.cjs +0 -24
  89. package/dist/node/ws/index.cjs +0 -1
  90. package/dist/node/ws/index.d.ts +0 -2
  91. package/dist/node/ws/index.js +0 -1
  92. package/dist/node/ws-Dc2RUwVs.js +0 -2
  93. package/dist/node/ws-Dc2RUwVs.js.map +0 -1
  94. package/dist/node/ws-QNMQn5kg.cjs +0 -1
  95. package/src/http/__tests__/http-transport.test.ts +0 -55
  96. package/src/http/__tests__/routes.test.ts +0 -168
  97. package/src/http/http-transport.ts +0 -41
  98. package/src/http/index.ts +0 -4
  99. package/src/http/routes.ts +0 -152
  100. package/src/mcp/__tests__/mcp-server.test.ts +0 -66
  101. package/src/mcp/__tests__/mcp-transport.test.ts +0 -46
  102. package/src/mcp/index.ts +0 -4
  103. package/src/mcp/mcp-server.ts +0 -163
  104. package/src/mcp/mcp-transport.ts +0 -48
  105. package/src/testing/__tests__/scripted-provider.test.ts +0 -73
  106. package/src/testing/scripted-provider.ts +0 -73
  107. package/src/tui/App.tsx +0 -491
  108. package/src/tui/BackgroundTaskPanel.tsx +0 -36
  109. package/src/tui/CjkTextInput.tsx +0 -199
  110. package/src/tui/ConfirmPrompt.tsx +0 -70
  111. package/src/tui/ContextWarningBanner.tsx +0 -34
  112. package/src/tui/ExecutionWorkspaceDetailPane.tsx +0 -64
  113. package/src/tui/ExecutionWorkspaceSwitcher.tsx +0 -187
  114. package/src/tui/InputArea.tsx +0 -310
  115. package/src/tui/InteractivePrompt.tsx +0 -59
  116. package/src/tui/ListPicker.tsx +0 -95
  117. package/src/tui/MenuSelect.tsx +0 -104
  118. package/src/tui/MessageList.tsx +0 -284
  119. package/src/tui/PermissionPrompt.tsx +0 -86
  120. package/src/tui/PluginTUI.tsx +0 -258
  121. package/src/tui/SessionPicker.tsx +0 -68
  122. package/src/tui/SessionStatusBar.tsx +0 -73
  123. package/src/tui/SlashAutocomplete.tsx +0 -110
  124. package/src/tui/StatusBar.tsx +0 -236
  125. package/src/tui/StreamingIndicator.tsx +0 -93
  126. package/src/tui/TextPrompt.tsx +0 -81
  127. package/src/tui/ToolCommandOutput.tsx +0 -39
  128. package/src/tui/ToolDiffBlock.tsx +0 -32
  129. package/src/tui/TransportTUI.tsx +0 -117
  130. package/src/tui/TuiInteractionChannel.ts +0 -495
  131. package/src/tui/UpdateNotice.tsx +0 -14
  132. package/src/tui/UsageSummaryEntry.tsx +0 -39
  133. package/src/tui/WaveText.tsx +0 -44
  134. package/src/tui/__tests__/InteractivePrompt.test.tsx +0 -82
  135. package/src/tui/__tests__/ListPicker.test.tsx +0 -159
  136. package/src/tui/__tests__/MenuSelect.test.tsx +0 -103
  137. package/src/tui/__tests__/PluginTUI.test.tsx +0 -167
  138. package/src/tui/__tests__/SlashAutocomplete.test.tsx +0 -140
  139. package/src/tui/__tests__/TextPrompt.test.tsx +0 -98
  140. package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +0 -239
  141. package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +0 -297
  142. package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +0 -124
  143. package/src/tui/__tests__/UpdateNotice.test.tsx +0 -15
  144. package/src/tui/__tests__/abort-after-permission.test.tsx +0 -169
  145. package/src/tui/__tests__/abort-streaming-e2e.test.tsx +0 -183
  146. package/src/tui/__tests__/background-task-panel.test.tsx +0 -53
  147. package/src/tui/__tests__/background-task-row-format.test.ts +0 -59
  148. package/src/tui/__tests__/channel-factory-integration.test.ts +0 -138
  149. package/src/tui/__tests__/cjk-text-input-flow.test.ts +0 -109
  150. package/src/tui/__tests__/cjk-text-input.test.ts +0 -191
  151. package/src/tui/__tests__/command-effect-handler.test.ts +0 -127
  152. package/src/tui/__tests__/command-output-summary.test.ts +0 -95
  153. package/src/tui/__tests__/compact-event-bridge.test.ts +0 -20
  154. package/src/tui/__tests__/confirm-permission-flow.test.ts +0 -130
  155. package/src/tui/__tests__/confirm-prompt.test.tsx +0 -87
  156. package/src/tui/__tests__/execution-workspace-switcher.test.tsx +0 -110
  157. package/src/tui/__tests__/execution-workspace-view-model.test.ts +0 -93
  158. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +0 -125
  159. package/src/tui/__tests__/input-area-flow.test.ts +0 -164
  160. package/src/tui/__tests__/message-list-rendering.test.tsx +0 -353
  161. package/src/tui/__tests__/prompt-queue.test.tsx +0 -255
  162. package/src/tui/__tests__/provider-setup-pty-e2e.test.ts +0 -233
  163. package/src/tui/__tests__/pty/pty-driver.ts +0 -135
  164. package/src/tui/__tests__/pty/tui-pty.ptytest.ts +0 -61
  165. package/src/tui/__tests__/render-channel-options.test.ts +0 -32
  166. package/src/tui/__tests__/render-markdown.test.ts +0 -72
  167. package/src/tui/__tests__/selection-flow.test.ts +0 -61
  168. package/src/tui/__tests__/session-init-poller.test.ts +0 -102
  169. package/src/tui/__tests__/session-naming.test.ts +0 -64
  170. package/src/tui/__tests__/session-switch-channel.test.tsx +0 -307
  171. package/src/tui/__tests__/slash-routing-effects.test.ts +0 -228
  172. package/src/tui/__tests__/status-activity.test.ts +0 -71
  173. package/src/tui/__tests__/status-bar.test.tsx +0 -177
  174. package/src/tui/__tests__/streaming-indicator.test.tsx +0 -137
  175. package/src/tui/__tests__/text-prompt-flow.test.ts +0 -77
  176. package/src/tui/__tests__/tui-channel-init-failure.test.ts +0 -57
  177. package/src/tui/__tests__/tui-state-manager.test.ts +0 -401
  178. package/src/tui/background-task-row-format.ts +0 -53
  179. package/src/tui/command-interaction.ts +0 -9
  180. package/src/tui/command-output-summary.ts +0 -122
  181. package/src/tui/create-default-tui-cli-adapter.ts +0 -41
  182. package/src/tui/execution-workspace-view-model.ts +0 -123
  183. package/src/tui/flows/cjk-text-input-flow.ts +0 -285
  184. package/src/tui/flows/confirm-prompt-flow.ts +0 -45
  185. package/src/tui/flows/input-area-flow.ts +0 -189
  186. package/src/tui/flows/permission-prompt-flow.ts +0 -85
  187. package/src/tui/flows/selection-flow.ts +0 -126
  188. package/src/tui/flows/session-init-poller.ts +0 -77
  189. package/src/tui/flows/text-prompt-flow.ts +0 -98
  190. package/src/tui/hooks/command-effect-handler.ts +0 -97
  191. package/src/tui/hooks/command-effect-queue.ts +0 -39
  192. package/src/tui/hooks/side-effects-types.ts +0 -35
  193. package/src/tui/hooks/useAutocomplete.ts +0 -87
  194. package/src/tui/hooks/usePluginCallbacks.ts +0 -31
  195. package/src/tui/hooks/usePluginScreenData.ts +0 -85
  196. package/src/tui/hooks/useSideEffects.ts +0 -175
  197. package/src/tui/hooks/useSlashRouting.ts +0 -118
  198. package/src/tui/hooks/useStatusLineSettings.ts +0 -37
  199. package/src/tui/hooks/useTuiChannel.ts +0 -95
  200. package/src/tui/index.ts +0 -14
  201. package/src/tui/interactions/CommandConfirm.tsx +0 -36
  202. package/src/tui/interactions/CommandPicker.tsx +0 -77
  203. package/src/tui/interactions/__tests__/CommandConfirm.test.tsx +0 -124
  204. package/src/tui/interactions/__tests__/CommandPicker.test.tsx +0 -138
  205. package/src/tui/plugin-tui-handlers.ts +0 -163
  206. package/src/tui/render-markdown.ts +0 -130
  207. package/src/tui/render.tsx +0 -129
  208. package/src/tui/session-naming.ts +0 -33
  209. package/src/tui/status-activity.ts +0 -63
  210. package/src/tui/tui-cli-adapter-context.tsx +0 -13
  211. package/src/tui/tui-cli-adapter.ts +0 -25
  212. package/src/tui/tui-state-manager.ts +0 -226
  213. package/src/tui/tui-transport.ts +0 -35
  214. package/src/tui/types.ts +0 -15
  215. package/src/tui/utils/__tests__/edit-diff.test.ts +0 -426
  216. package/src/tui/utils/__tests__/paste-detection.test.ts +0 -116
  217. package/src/tui/utils/__tests__/paste-labels.test.ts +0 -46
  218. package/src/tui/utils/__tests__/tool-call-extractor.test.ts +0 -227
  219. package/src/tui/utils/__tests__/tool-diff-summary.test.ts +0 -104
  220. package/src/tui/utils/edit-diff.ts +0 -153
  221. package/src/tui/utils/paste-labels.ts +0 -9
  222. package/src/tui/utils/tool-call-extractor.ts +0 -92
  223. package/src/tui/utils/tool-diff-summary.ts +0 -75
  224. package/src/ws/__tests__/ws-handler.test.ts +0 -409
  225. package/src/ws/__tests__/ws-transport.test.ts +0 -53
  226. package/src/ws/index.ts +0 -13
  227. package/src/ws/ws-background-messages.ts +0 -170
  228. package/src/ws/ws-handler.ts +0 -280
  229. package/src/ws/ws-protocol.ts +0 -78
  230. package/src/ws/ws-transport-configurable.ts +0 -128
  231. package/src/ws/ws-transport.ts +0 -42
@@ -1,310 +0,0 @@
1
- import React, { useState, useCallback, useRef, useMemo } from 'react';
2
-
3
- const PENDING_PROMPT_DISPLAY_MAX = 50;
4
- const PENDING_PROMPT_TAIL_KEEP = 47;
5
- import { Box, Text, useInput, useWindowSize } from 'ink';
6
-
7
- import CjkTextInput from './CjkTextInput.js';
8
- import {
9
- appendPromptHistory,
10
- createPasteLabelChange,
11
- createPromptHistoryNavigationState,
12
- extractPromptHistory,
13
- getAutocompletePopupAction,
14
- getPendingPromptInputAction,
15
- getPromptHistoryInputAction,
16
- moveAutocompleteSelection,
17
- navigatePromptHistory,
18
- resolveEnterCommandSelection,
19
- resolveTabCompletion,
20
- shouldSubmitInput,
21
- } from './flows/input-area-flow.js';
22
- import { useAutocomplete } from './hooks/useAutocomplete.js';
23
- import SlashAutocomplete from './SlashAutocomplete.js';
24
- import { expandPasteLabels } from './utils/paste-labels.js';
25
- import WaveText from './WaveText.js';
26
-
27
- import type { IHistoryEntry } from '@robota-sdk/agent-core';
28
- import type { CommandRegistry } from '@robota-sdk/agent-framework';
29
- import type { ICommand } from '@robota-sdk/agent-interface-transport';
30
-
31
- interface IProps {
32
- onSubmit: (value: string) => void;
33
- onCancelQueue?: () => void;
34
- isDisabled: boolean;
35
- isAborting?: boolean;
36
- pendingPrompt?: string | null;
37
- registry?: CommandRegistry;
38
- sessionName?: string;
39
- history?: readonly IHistoryEntry[];
40
- }
41
-
42
- /**
43
- * Known limitation: Korean IME last character may be dropped on Enter.
44
- * This is an Ink raw mode limitation — no compositionstart/compositionend
45
- * events are available in terminal raw mode.
46
- * Reference: https://github.com/anthropics/claude-code/issues/3045
47
- */
48
- /**
49
- * Layout constants for InputArea (columns).
50
- * Used to compute available text width from terminal columns.
51
- *
52
- * Side borders removed — only top/bottom horizontal lines remain.
53
- * paddingLeft={1} adds 1 column inside the box.
54
- * Prompt "> " takes 2 columns.
55
- */
56
- const BORDER_HORIZONTAL = 0;
57
- const PADDING_LEFT = 1;
58
- const PROMPT_WIDTH = 2;
59
- const INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
60
- const DEFAULT_TERMINAL_COLUMNS = 80;
61
-
62
- export default function InputArea({
63
- onSubmit,
64
- onCancelQueue,
65
- isDisabled,
66
- isAborting,
67
- pendingPrompt,
68
- registry,
69
- sessionName,
70
- history,
71
- }: IProps): React.ReactElement {
72
- const [value, setValue] = useState('');
73
- const [cursorHint, setCursorHint] = useState<number | null>(null);
74
- const [historyState, setHistoryState] = useState(createPromptHistoryNavigationState);
75
- const [localPromptHistory, setLocalPromptHistory] = useState<string[]>([]);
76
- const restoredPromptHistory = useMemo(() => extractPromptHistory(history ?? []), [history]);
77
- const promptHistory = useMemo(
78
- () =>
79
- localPromptHistory.reduce<string[]>(
80
- (prompts, prompt) => appendPromptHistory(prompts, prompt),
81
- restoredPromptHistory,
82
- ),
83
- [restoredPromptHistory, localPromptHistory],
84
- );
85
- const pasteStore = useRef<Map<number, string>>(new Map());
86
- const { columns } = useWindowSize();
87
- const terminalColumns = columns > 0 ? columns : DEFAULT_TERMINAL_COLUMNS;
88
- const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
89
- const pasteIdRef = useRef(0);
90
-
91
- const {
92
- showPopup,
93
- filteredCommands,
94
- selectedIndex,
95
- setSelectedIndex,
96
- isSubcommandMode,
97
- setShowPopup,
98
- } = useAutocomplete(value, registry);
99
-
100
- const handlePaste = useCallback((text: string, cursorPosition: number) => {
101
- pasteIdRef.current += 1;
102
- const id = pasteIdRef.current;
103
- pasteStore.current.set(id, text);
104
- setValue((prev) => {
105
- const change = createPasteLabelChange(prev, cursorPosition, id, text);
106
- setCursorHint(change.cursorHint);
107
- return change.value;
108
- });
109
- }, []);
110
-
111
- const resetHistoryNavigation = useCallback(() => {
112
- setHistoryState(createPromptHistoryNavigationState());
113
- }, []);
114
-
115
- const recordPromptHistory = useCallback((prompt: string): void => {
116
- setLocalPromptHistory((prev) => appendPromptHistory(prev, prompt));
117
- }, []);
118
-
119
- const submitPrompt = useCallback(
120
- (prompt: string): void => {
121
- recordPromptHistory(prompt);
122
- resetHistoryNavigation();
123
- onSubmit(prompt);
124
- },
125
- [onSubmit, recordPromptHistory, resetHistoryNavigation],
126
- );
127
-
128
- /** Tab: insert command into input field without executing */
129
- const tabCompleteCommand = useCallback(
130
- (cmd: ICommand): void => {
131
- const result = resolveTabCompletion(value, cmd);
132
- if (result.type === 'insert') {
133
- setValue(result.value);
134
- if (result.selectedIndex !== undefined) {
135
- setSelectedIndex(result.selectedIndex);
136
- }
137
- }
138
- },
139
- [value, setSelectedIndex],
140
- );
141
-
142
- /** Enter: insert and execute command immediately */
143
- const enterSelectCommand = useCallback(
144
- (cmd: ICommand): void => {
145
- const result = resolveEnterCommandSelection(value, cmd);
146
- if (result.type === 'insert') {
147
- setValue(result.value);
148
- if (result.selectedIndex !== undefined) {
149
- setSelectedIndex(result.selectedIndex);
150
- }
151
- return;
152
- }
153
- if (result.type === 'submit') {
154
- setValue('');
155
- submitPrompt(result.value);
156
- }
157
- },
158
- [value, submitPrompt, setSelectedIndex],
159
- );
160
-
161
- const handleSubmit = useCallback(
162
- (text: string): void => {
163
- if (!shouldSubmitInput(text)) return;
164
-
165
- if (showPopup && filteredCommands[selectedIndex]) {
166
- enterSelectCommand(filteredCommands[selectedIndex]);
167
- return;
168
- }
169
-
170
- // Expand paste labels before submitting
171
- const expanded = expandPasteLabels(text.trim(), pasteStore.current);
172
-
173
- setValue('');
174
- // Reset paste state
175
- pasteStore.current.clear();
176
- pasteIdRef.current = 0;
177
-
178
- submitPrompt(expanded);
179
- },
180
- [showPopup, filteredCommands, selectedIndex, enterSelectCommand, submitPrompt],
181
- );
182
-
183
- useInput(
184
- (
185
- _input: string,
186
- key: { upArrow: boolean; downArrow: boolean; escape: boolean; tab: boolean },
187
- ) => {
188
- if (!showPopup) return;
189
- const action = getAutocompletePopupAction(key);
190
- if (action === 'previous' || action === 'next') {
191
- setSelectedIndex((prev) =>
192
- moveAutocompleteSelection(prev, filteredCommands.length, action),
193
- );
194
- } else if (action === 'close') {
195
- setShowPopup(false);
196
- } else if (action === 'complete') {
197
- const cmd = filteredCommands[selectedIndex];
198
- if (cmd) tabCompleteCommand(cmd);
199
- }
200
- },
201
- { isActive: showPopup && !isDisabled },
202
- );
203
-
204
- useInput(
205
- (_input, key) => {
206
- const action = getPromptHistoryInputAction(key);
207
- if (!action) return;
208
- const result = navigatePromptHistory(value, promptHistory, historyState, action);
209
- setValue(result.value);
210
- setCursorHint(result.cursorHint);
211
- setHistoryState(result.state);
212
- },
213
- { isActive: !showPopup && !isDisabled && !pendingPrompt },
214
- );
215
-
216
- // Backspace cancels queued prompt
217
- useInput(
218
- (_input, key) => {
219
- if (getPendingPromptInputAction(key) === 'cancelQueue' && pendingPrompt) {
220
- onCancelQueue?.();
221
- }
222
- },
223
- { isActive: !!pendingPrompt },
224
- );
225
-
226
- const borderColor = isAborting
227
- ? 'yellow'
228
- : pendingPrompt
229
- ? 'cyan'
230
- : isDisabled
231
- ? 'gray'
232
- : 'green';
233
- const innerWidth = Math.max(1, terminalColumns - BORDER_HORIZONTAL);
234
-
235
- // Build top border with optional session name title (right-aligned, 2 chars from edge)
236
- const topBorder = (() => {
237
- if (sessionName) {
238
- const label = ` "${sessionName}" `;
239
- const rightPad = 2;
240
- const leftLen = Math.max(0, innerWidth - label.length - rightPad);
241
- return { left: '─'.repeat(leftLen), label, right: '─'.repeat(rightPad) };
242
- }
243
- return { left: '─'.repeat(innerWidth), label: '', right: '' };
244
- })();
245
-
246
- return (
247
- <Box flexDirection="column">
248
- {showPopup && (
249
- <SlashAutocomplete
250
- commands={filteredCommands}
251
- selectedIndex={selectedIndex}
252
- visible={showPopup}
253
- isSubcommandMode={isSubcommandMode}
254
- />
255
- )}
256
- <Text color={borderColor}>
257
- {topBorder.left}
258
- {topBorder.label ? (
259
- <Text backgroundColor={borderColor} color="black" bold>
260
- {topBorder.label}
261
- </Text>
262
- ) : null}
263
- {topBorder.right}
264
- </Text>
265
- <Box
266
- borderStyle="single"
267
- borderTop={false}
268
- borderLeft={false}
269
- borderRight={false}
270
- borderColor={borderColor}
271
- paddingLeft={1}
272
- >
273
- {isAborting ? (
274
- <Text color="yellow"> Interrupting...</Text>
275
- ) : pendingPrompt ? (
276
- <Text color="cyan">
277
- {' '}
278
- Queued:{' '}
279
- {pendingPrompt.length > PENDING_PROMPT_DISPLAY_MAX
280
- ? pendingPrompt.slice(0, PENDING_PROMPT_TAIL_KEEP) + '...'
281
- : pendingPrompt}{' '}
282
- <Text dimColor>(Backspace to cancel)</Text>
283
- </Text>
284
- ) : isDisabled ? (
285
- <WaveText text=" Waiting for response... (ESC to interrupt)" />
286
- ) : (
287
- <Box>
288
- <Text color="green" bold>
289
- {'> '}
290
- </Text>
291
- <CjkTextInput
292
- value={value}
293
- onChange={(v) => {
294
- setValue(v);
295
- resetHistoryNavigation();
296
- setCursorHint(null); // reset after normal typing
297
- }}
298
- onSubmit={handleSubmit}
299
- onPaste={handlePaste}
300
- placeholder="Type a message or /help"
301
- availableWidth={availableWidth}
302
- cursorHint={cursorHint}
303
- enableVerticalNavigation={false}
304
- />
305
- </Box>
306
- )}
307
- </Box>
308
- </Box>
309
- );
310
- }
@@ -1,59 +0,0 @@
1
- import { Box, Text } from 'ink';
2
- import React from 'react';
3
-
4
- import ListPicker from './ListPicker.js';
5
- import TextPrompt from './TextPrompt.js';
6
-
7
- import type {
8
- ICommandChoicePromptOption as IChoicePromptOption,
9
- TCommandInteractionPrompt as TInteractivePrompt,
10
- } from '@robota-sdk/agent-interface-transport';
11
-
12
- interface IInteractivePromptProps {
13
- prompt: TInteractivePrompt;
14
- onSubmit: (value: string) => void;
15
- onCancel: () => void;
16
- }
17
-
18
- export default function InteractivePrompt({
19
- prompt,
20
- onSubmit,
21
- onCancel,
22
- }: IInteractivePromptProps): React.ReactElement {
23
- if (prompt.kind === 'text') {
24
- return (
25
- <TextPrompt
26
- key={`text:${prompt.title}`}
27
- title={prompt.title}
28
- description={prompt.description}
29
- placeholder={prompt.placeholder}
30
- allowEmpty={prompt.allowEmpty}
31
- masked={prompt.masked}
32
- validate={prompt.validate}
33
- onSubmit={onSubmit}
34
- onCancel={onCancel}
35
- />
36
- );
37
- }
38
-
39
- return (
40
- <Box flexDirection="column">
41
- <Text bold>{prompt.title}</Text>
42
- {prompt.description !== undefined && prompt.description.length > 0 && (
43
- <Text dimColor>{prompt.description}</Text>
44
- )}
45
- <ListPicker<IChoicePromptOption>
46
- items={[...prompt.options]}
47
- maxVisible={prompt.maxVisible}
48
- renderItem={(option, isSelected) => (
49
- <Text color={isSelected ? 'cyan' : undefined}>
50
- {isSelected ? '> ' : ' '}
51
- {option.label}
52
- </Text>
53
- )}
54
- onSelect={(option) => onSubmit(option.value)}
55
- onCancel={onCancel}
56
- />
57
- </Box>
58
- );
59
- }
@@ -1,95 +0,0 @@
1
- /**
2
- * Generic list picker with arrow-key navigation and viewport scrolling.
3
- * Renders items via a user-supplied renderItem function.
4
- * Shows a limited number of items at a time; scrolls as the cursor moves.
5
- */
6
-
7
- import { Box, Text, useInput } from 'ink';
8
- import React, { useState, useRef, useCallback } from 'react';
9
-
10
- import {
11
- applySelectionInput,
12
- createSelectionFlowState,
13
- getVerticalSelectionInputAction,
14
- normalizeSelectionState,
15
- type ISelectionFlowState,
16
- type TSelectionInputAction,
17
- } from './flows/selection-flow.js';
18
-
19
- /** Default number of visible items */
20
- const DEFAULT_MAX_VISIBLE = 3;
21
-
22
- export interface IListPickerProps<T> {
23
- /** Items to display in the list */
24
- items: T[];
25
- /** Render function for each item — receives the item and whether it is currently selected */
26
- renderItem: (item: T, isSelected: boolean) => React.ReactElement;
27
- /** Called when the user presses Enter on the highlighted item */
28
- onSelect: (item: T) => void;
29
- /** Called when the user presses Escape */
30
- onCancel: () => void;
31
- /** Maximum number of items visible at once (default: 10) */
32
- maxVisible?: number;
33
- }
34
-
35
- export default function ListPicker<T>({
36
- items,
37
- renderItem,
38
- onSelect,
39
- onCancel,
40
- maxVisible = DEFAULT_MAX_VISIBLE,
41
- }: IListPickerProps<T>): React.ReactElement {
42
- const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());
43
- const stateRef = useRef(state);
44
- const applyAction = useCallback(
45
- (action: TSelectionInputAction): void => {
46
- const result = applySelectionInput(stateRef.current, action, {
47
- itemCount: items.length,
48
- maxVisible,
49
- });
50
- stateRef.current = result.state;
51
- setState(result.state);
52
- if (result.effect.type === 'cancel') {
53
- onCancel();
54
- } else if (result.effect.type === 'select') {
55
- const item = items[result.effect.index];
56
- if (item !== undefined) {
57
- onSelect(item);
58
- }
59
- }
60
- },
61
- [items, maxVisible, onCancel, onSelect],
62
- );
63
-
64
- useInput((_input, key) => {
65
- const action = getVerticalSelectionInputAction(key);
66
- if (action !== undefined) {
67
- applyAction(action);
68
- }
69
- });
70
-
71
- if (items.length === 0) {
72
- return <Box />;
73
- }
74
-
75
- const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
76
- if (normalizedState !== state) {
77
- stateRef.current = normalizedState;
78
- }
79
- const { selectedIndex, scrollOffset } = normalizedState;
80
- const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
81
- const hasMore = scrollOffset + maxVisible < items.length;
82
- const hasLess = scrollOffset > 0;
83
-
84
- return (
85
- <Box flexDirection="column">
86
- {hasLess && <Text dimColor> ↑ {scrollOffset} more above</Text>}
87
- {visibleItems.map((item, index) => (
88
- <Box key={scrollOffset + index} marginBottom={1}>
89
- {renderItem(item, scrollOffset + index === selectedIndex)}
90
- </Box>
91
- ))}
92
- {hasMore && <Text dimColor> ↓ {items.length - scrollOffset - maxVisible} more below</Text>}
93
- </Box>
94
- );
95
- }
@@ -1,104 +0,0 @@
1
- import { Box, Text, useInput } from 'ink';
2
- import React, { useState, useCallback, useRef } from 'react';
3
-
4
- import {
5
- applySelectionInput,
6
- createSelectionFlowState,
7
- getVerticalSelectionInputAction,
8
- normalizeSelectionState,
9
- type ISelectionFlowState,
10
- type TSelectionInputAction,
11
- } from './flows/selection-flow.js';
12
-
13
- export interface IMenuSelectItem {
14
- label: string;
15
- value: string;
16
- hint?: string;
17
- }
18
-
19
- interface IProps {
20
- title: string;
21
- items: IMenuSelectItem[];
22
- onSelect: (value: string) => void;
23
- onBack: () => void;
24
- loading?: boolean;
25
- error?: string;
26
- }
27
-
28
- export default function MenuSelect({
29
- title,
30
- items,
31
- onSelect,
32
- onBack,
33
- loading,
34
- error,
35
- }: IProps): React.ReactElement {
36
- const [state, setState] = useState<ISelectionFlowState>(() => createSelectionFlowState());
37
- const stateRef = useRef(state);
38
- const isEnabled = !loading && !error;
39
- const applyAction = useCallback(
40
- (action: TSelectionInputAction): void => {
41
- const result = applySelectionInput(stateRef.current, action, {
42
- itemCount: items.length,
43
- enabled: isEnabled,
44
- });
45
- stateRef.current = result.state;
46
- setState(result.state);
47
- if (result.effect.type === 'cancel') {
48
- onBack();
49
- } else if (result.effect.type === 'select') {
50
- const item = items[result.effect.index];
51
- if (item !== undefined) {
52
- onSelect(item.value);
53
- }
54
- }
55
- },
56
- [isEnabled, items, onBack, onSelect],
57
- );
58
-
59
- useInput((input, key) => {
60
- const action = getVerticalSelectionInputAction(key);
61
- if (action !== undefined) {
62
- applyAction(action);
63
- }
64
- });
65
-
66
- const normalizedState = normalizeSelectionState(state, { itemCount: items.length });
67
- if (normalizedState !== state) {
68
- stateRef.current = normalizedState;
69
- }
70
- const selected = normalizedState.selectedIndex;
71
-
72
- return (
73
- <Box flexDirection="column" borderStyle="round" borderColor="yellow" paddingX={1}>
74
- <Text color="yellow" bold>
75
- {title}
76
- </Text>
77
- {loading && (
78
- <Box marginTop={1}>
79
- <Text dimColor>Loading...</Text>
80
- </Box>
81
- )}
82
- {error && (
83
- <Box marginTop={1} flexDirection="column">
84
- <Text color="red">{error}</Text>
85
- <Text dimColor>Press Esc to go back</Text>
86
- </Box>
87
- )}
88
- {!loading && !error && (
89
- <Box flexDirection="column" marginTop={1}>
90
- {items.map((item, i) => (
91
- <Box key={item.value}>
92
- <Text color={i === selected ? 'cyan' : undefined} bold={i === selected}>
93
- {i === selected ? '> ' : ' '}
94
- {item.label}
95
- </Text>
96
- {item.hint && <Text dimColor> {item.hint}</Text>}
97
- </Box>
98
- ))}
99
- </Box>
100
- )}
101
- <Text dimColor>{loading || error ? '' : ' ↑↓ Navigate Enter Select Esc Back'}</Text>
102
- </Box>
103
- );
104
- }