@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,124 +0,0 @@
1
- import React from 'react';
2
- import { render } from 'ink-testing-library';
3
- import { describe, it, expect } from 'vitest';
4
-
5
- import CommandConfirm from '../CommandConfirm.js';
6
-
7
- import type { ITuiConfirmInteraction } from '../../command-interaction.js';
8
-
9
- const delay = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));
10
-
11
- function makeInteraction(message: string): ITuiConfirmInteraction {
12
- return { onMissingArgs: 'confirm', message };
13
- }
14
-
15
- describe('CommandConfirm', () => {
16
- it('renders the message text', () => {
17
- const { lastFrame } = render(
18
- <CommandConfirm
19
- commandName="exit"
20
- interaction={makeInteraction('Exit the session?')}
21
- onConfirm={() => {}}
22
- onCancel={() => {}}
23
- />,
24
- );
25
- expect(lastFrame()).toContain('Exit the session?');
26
- });
27
-
28
- it('calls onConfirm when y is pressed', () => {
29
- let confirmed = false;
30
- const { stdin } = render(
31
- <CommandConfirm
32
- commandName="exit"
33
- interaction={makeInteraction('Exit?')}
34
- onConfirm={() => {
35
- confirmed = true;
36
- }}
37
- onCancel={() => {}}
38
- />,
39
- );
40
- stdin.write('y');
41
- expect(confirmed).toBe(true);
42
- });
43
-
44
- it('calls onConfirm when Y is pressed', () => {
45
- let confirmed = false;
46
- const { stdin } = render(
47
- <CommandConfirm
48
- commandName="exit"
49
- interaction={makeInteraction('Exit?')}
50
- onConfirm={() => {
51
- confirmed = true;
52
- }}
53
- onCancel={() => {}}
54
- />,
55
- );
56
- stdin.write('Y');
57
- expect(confirmed).toBe(true);
58
- });
59
-
60
- it('calls onConfirm on Enter', () => {
61
- let confirmed = false;
62
- const { stdin } = render(
63
- <CommandConfirm
64
- commandName="exit"
65
- interaction={makeInteraction('Exit?')}
66
- onConfirm={() => {
67
- confirmed = true;
68
- }}
69
- onCancel={() => {}}
70
- />,
71
- );
72
- stdin.write('\r');
73
- expect(confirmed).toBe(true);
74
- });
75
-
76
- it('calls onCancel when n is pressed', () => {
77
- let cancelled = false;
78
- const { stdin } = render(
79
- <CommandConfirm
80
- commandName="exit"
81
- interaction={makeInteraction('Exit?')}
82
- onConfirm={() => {}}
83
- onCancel={() => {
84
- cancelled = true;
85
- }}
86
- />,
87
- );
88
- stdin.write('n');
89
- expect(cancelled).toBe(true);
90
- });
91
-
92
- it('calls onCancel when N is pressed', () => {
93
- let cancelled = false;
94
- const { stdin } = render(
95
- <CommandConfirm
96
- commandName="exit"
97
- interaction={makeInteraction('Exit?')}
98
- onConfirm={() => {}}
99
- onCancel={() => {
100
- cancelled = true;
101
- }}
102
- />,
103
- );
104
- stdin.write('N');
105
- expect(cancelled).toBe(true);
106
- });
107
-
108
- it('calls onCancel on Escape', async () => {
109
- let cancelled = false;
110
- const { stdin } = render(
111
- <CommandConfirm
112
- commandName="exit"
113
- interaction={makeInteraction('Exit?')}
114
- onConfirm={() => {}}
115
- onCancel={() => {
116
- cancelled = true;
117
- }}
118
- />,
119
- );
120
- stdin.write('\x1b');
121
- await delay(50);
122
- expect(cancelled).toBe(true);
123
- });
124
- });
@@ -1,138 +0,0 @@
1
- import React from 'react';
2
- import { render } from 'ink-testing-library';
3
- import { describe, it, expect, vi } from 'vitest';
4
-
5
- import CommandPicker from '../CommandPicker.js';
6
-
7
- import type { ITuiPickerInteraction, ITuiPickerItem } from '../../command-interaction.js';
8
-
9
- const delay = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));
10
-
11
- function makeInteraction(items: ITuiPickerItem[]): ITuiPickerInteraction {
12
- return {
13
- onMissingArgs: 'picker',
14
- getItems: () => items,
15
- };
16
- }
17
-
18
- const ITEMS: ITuiPickerItem[] = [
19
- { label: 'plan', value: 'plan', description: 'Plan mode' },
20
- { label: 'default', value: 'default', description: 'Default mode' },
21
- { label: 'auto', value: 'auto' },
22
- ];
23
-
24
- describe('CommandPicker', () => {
25
- it('renders all item labels', () => {
26
- const { lastFrame } = render(
27
- <CommandPicker
28
- commandName="mode"
29
- interaction={makeInteraction(ITEMS)}
30
- onSelect={() => {}}
31
- onCancel={() => {}}
32
- />,
33
- );
34
- const frame = lastFrame()!;
35
- expect(frame).toContain('plan');
36
- expect(frame).toContain('default');
37
- expect(frame).toContain('auto');
38
- });
39
-
40
- it('highlights first item by default', () => {
41
- const { lastFrame } = render(
42
- <CommandPicker
43
- commandName="mode"
44
- interaction={makeInteraction(ITEMS)}
45
- onSelect={() => {}}
46
- onCancel={() => {}}
47
- />,
48
- );
49
- const frame = lastFrame()!;
50
- expect(frame).toContain('▸ plan');
51
- });
52
-
53
- it('calls onSelect with first item on Enter', () => {
54
- const onSelect = vi.fn();
55
- const { stdin } = render(
56
- <CommandPicker
57
- commandName="mode"
58
- interaction={makeInteraction(ITEMS)}
59
- onSelect={onSelect}
60
- onCancel={() => {}}
61
- />,
62
- );
63
- stdin.write('\r');
64
- expect(onSelect).toHaveBeenCalledWith(ITEMS[0]);
65
- });
66
-
67
- it('moves highlight down on arrow-down then selects on Enter', async () => {
68
- let selected: ITuiPickerItem | undefined;
69
- const { stdin } = render(
70
- <CommandPicker
71
- commandName="mode"
72
- interaction={makeInteraction(ITEMS)}
73
- onSelect={(item) => {
74
- selected = item;
75
- }}
76
- onCancel={() => {}}
77
- />,
78
- );
79
- stdin.write('\x1B[B'); // ↓
80
- await delay(50);
81
- stdin.write('\r');
82
- expect(selected).toEqual(ITEMS[1]);
83
- });
84
-
85
- it('calls onCancel on Escape', async () => {
86
- let cancelled = false;
87
- const { stdin } = render(
88
- <CommandPicker
89
- commandName="mode"
90
- interaction={makeInteraction(ITEMS)}
91
- onSelect={() => {}}
92
- onCancel={() => {
93
- cancelled = true;
94
- }}
95
- />,
96
- );
97
- stdin.write('\x1b');
98
- await delay(50);
99
- expect(cancelled).toBe(true);
100
- });
101
-
102
- it('calls onCancel on q', () => {
103
- const onCancel = vi.fn();
104
- const { stdin } = render(
105
- <CommandPicker
106
- commandName="mode"
107
- interaction={makeInteraction(ITEMS)}
108
- onSelect={() => {}}
109
- onCancel={onCancel}
110
- />,
111
- );
112
- stdin.write('q');
113
- expect(onCancel).toHaveBeenCalled();
114
- });
115
-
116
- it('wraps highlight from last to first on arrow-down at bottom', async () => {
117
- let selected: ITuiPickerItem | undefined;
118
- const { stdin } = render(
119
- <CommandPicker
120
- commandName="mode"
121
- interaction={makeInteraction(ITEMS)}
122
- onSelect={(item) => {
123
- selected = item;
124
- }}
125
- onCancel={() => {}}
126
- />,
127
- );
128
- // Navigate to last item (index 2)
129
- stdin.write('\x1B[B'); // ↓ to index 1
130
- await delay(50);
131
- stdin.write('\x1B[B'); // ↓ to index 2
132
- await delay(50);
133
- stdin.write('\x1B[B'); // ↓ wraps to index 0
134
- await delay(50);
135
- stdin.write('\r');
136
- expect(selected).toEqual(ITEMS[0]);
137
- });
138
- });
@@ -1,163 +0,0 @@
1
- /**
2
- * Screen-specific selection handlers for PluginTUI.
3
- * Extracted to keep PluginTUI.tsx under 300 lines.
4
- */
5
-
6
- import type { IMenuSelectItem } from './MenuSelect.js';
7
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
8
-
9
- interface IConfirmState {
10
- message: string;
11
- onConfirm: () => void;
12
- onCancel: () => void;
13
- }
14
-
15
- interface IMenuContext {
16
- marketplace?: string;
17
- pluginId?: string;
18
- }
19
-
20
- interface INavActions {
21
- push: (state: { screen: string; context?: IMenuContext }) => void;
22
- pop: () => void;
23
- popN: (n: number) => void;
24
- notify: (content: string) => void;
25
- setConfirm: (state: IConfirmState | undefined) => void;
26
- refresh: () => void;
27
- }
28
-
29
- export function handleMainSelect(value: string, nav: Pick<INavActions, 'push'>): void {
30
- if (value === 'marketplace') {
31
- nav.push({ screen: 'marketplace-list' });
32
- } else if (value === 'installed') {
33
- nav.push({ screen: 'installed-list' });
34
- }
35
- }
36
-
37
- export function handleMarketplaceListSelect(value: string, nav: Pick<INavActions, 'push'>): void {
38
- if (value === '__add__') {
39
- nav.push({ screen: 'marketplace-add' });
40
- } else {
41
- nav.push({ screen: 'marketplace-action', context: { marketplace: value } });
42
- }
43
- }
44
-
45
- export function handleMarketplaceActionSelect(
46
- value: string,
47
- marketplace: string,
48
- callbacks: ICommandPluginAdapter,
49
- nav: Pick<INavActions, 'push' | 'pop' | 'popN' | 'notify' | 'setConfirm'>,
50
- ): void {
51
- if (value === 'browse') {
52
- nav.push({ screen: 'marketplace-browse', context: { marketplace } });
53
- } else if (value === 'update') {
54
- callbacks
55
- .marketplaceUpdate(marketplace)
56
- .then(() => {
57
- nav.notify(`Updated marketplace "${marketplace}".`);
58
- nav.pop();
59
- })
60
- .catch((err) => {
61
- nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
62
- });
63
- } else if (value === 'remove') {
64
- nav.setConfirm({
65
- message: `Remove marketplace "${marketplace}" and all its plugins?`,
66
- onConfirm: () => {
67
- nav.setConfirm(undefined);
68
- callbacks
69
- .marketplaceRemove(marketplace)
70
- .then(() => {
71
- nav.notify(`Removed marketplace "${marketplace}".`);
72
- nav.popN(2);
73
- })
74
- .catch((err) => {
75
- nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
76
- });
77
- },
78
- onCancel: () => nav.setConfirm(undefined),
79
- });
80
- }
81
- }
82
-
83
- export function handleMarketplaceBrowseSelect(
84
- value: string,
85
- marketplace: string,
86
- items: IMenuSelectItem[],
87
- nav: Pick<INavActions, 'push'>,
88
- ): void {
89
- const fullId = `${value}@${marketplace}`;
90
- const item = items.find((i) => i.value === value);
91
- if (item?.hint === 'installed') {
92
- nav.push({ screen: 'installed-action', context: { pluginId: fullId } });
93
- } else {
94
- nav.push({ screen: 'marketplace-install-scope', context: { marketplace, pluginId: fullId } });
95
- }
96
- }
97
-
98
- export function handleInstallScopeSelect(
99
- value: string,
100
- pluginId: string,
101
- callbacks: ICommandPluginAdapter,
102
- nav: Pick<INavActions, 'popN' | 'notify'>,
103
- ): void {
104
- const scope = value as 'user' | 'project';
105
- callbacks
106
- .install(pluginId, scope)
107
- .then(() => {
108
- nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
109
- nav.popN(2);
110
- })
111
- .catch((err) => {
112
- nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
113
- });
114
- }
115
-
116
- export function handleInstalledListSelect(
117
- value: string,
118
- callbacks: ICommandPluginAdapter,
119
- nav: Pick<INavActions, 'notify' | 'setConfirm' | 'refresh'>,
120
- ): void {
121
- nav.setConfirm({
122
- message: `Uninstall plugin "${value}"?`,
123
- onConfirm: () => {
124
- nav.setConfirm(undefined);
125
- callbacks
126
- .uninstall(value)
127
- .then(() => {
128
- nav.notify(`Uninstalled plugin "${value}".`);
129
- nav.refresh();
130
- })
131
- .catch((err) => {
132
- nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
133
- });
134
- },
135
- onCancel: () => nav.setConfirm(undefined),
136
- });
137
- }
138
-
139
- export function handleInstalledActionSelect(
140
- value: string,
141
- pluginId: string,
142
- callbacks: ICommandPluginAdapter,
143
- nav: Pick<INavActions, 'popN' | 'notify' | 'setConfirm'>,
144
- ): void {
145
- if (value === 'uninstall') {
146
- nav.setConfirm({
147
- message: `Uninstall plugin "${pluginId}"?`,
148
- onConfirm: () => {
149
- nav.setConfirm(undefined);
150
- callbacks
151
- .uninstall(pluginId)
152
- .then(() => {
153
- nav.notify(`Uninstalled plugin "${pluginId}".`);
154
- nav.popN(2);
155
- })
156
- .catch((err) => {
157
- nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
158
- });
159
- },
160
- onCancel: () => nav.setConfirm(undefined),
161
- });
162
- }
163
- }
@@ -1,130 +0,0 @@
1
- import { marked } from 'marked';
2
- // @ts-expect-error — marked-terminal has no type declarations
3
- import TerminalRenderer from 'marked-terminal';
4
-
5
- import type { Renderer } from 'marked';
6
-
7
- const ANSI_LIGHT_RED = '\u001b[38;5;210m';
8
- const ANSI_LIGHT_GREEN = '\u001b[38;5;120m';
9
- const ANSI_CYAN = '\u001b[36m';
10
- const ANSI_DIM = '\u001b[2m';
11
- const ANSI_DARK_RED_BACKGROUND = '\u001b[48;5;52m';
12
- const ANSI_DARK_GREEN_BACKGROUND = '\u001b[48;5;22m';
13
- const ANSI_RESET = '\u001b[0m';
14
- const CODE_BLOCK_INDENT = ' ';
15
- const ZERO_COLOR = '0';
16
-
17
- interface IRenderMarkdownOptions {
18
- color?: boolean;
19
- codeBlockWidth?: number;
20
- }
21
-
22
- interface ITerminalRendererOptions {
23
- code?: (text: string) => string;
24
- }
25
-
26
- interface IHighlightOptions {
27
- ignoreIllegals?: boolean;
28
- }
29
-
30
- type TTerminalRendererConstructor = new (
31
- options?: ITerminalRendererOptions,
32
- highlightOptions?: IHighlightOptions,
33
- ) => Renderer;
34
-
35
- const TerminalRendererConstructor = TerminalRenderer as TTerminalRendererConstructor;
36
-
37
- function shouldUseColor(option: boolean | undefined): boolean {
38
- if (option !== undefined) {
39
- return option;
40
- }
41
- if (process.env.NO_COLOR || process.env.FORCE_COLOR === ZERO_COLOR) {
42
- return false;
43
- }
44
- if (process.env.FORCE_COLOR) {
45
- return true;
46
- }
47
- return Boolean(process.stdout.isTTY);
48
- }
49
-
50
- function isDiffLanguage(language: string | undefined): boolean {
51
- return language?.trim().toLowerCase() === 'diff';
52
- }
53
-
54
- function styleAddedOrRemovedDiffRow(line: string, rowWidth: number, color: boolean): string {
55
- const row = `${CODE_BLOCK_INDENT}${line}`.padEnd(rowWidth);
56
- if (!color) {
57
- return row.trimEnd();
58
- }
59
- if (line.startsWith('+')) {
60
- return `${ANSI_DARK_GREEN_BACKGROUND}${ANSI_LIGHT_GREEN}${row}${ANSI_RESET}`;
61
- }
62
- if (line.startsWith('-')) {
63
- return `${ANSI_DARK_RED_BACKGROUND}${ANSI_LIGHT_RED}${row}${ANSI_RESET}`;
64
- }
65
- return row.trimEnd();
66
- }
67
-
68
- function colorizeDiffLine(line: string, color: boolean, rowWidth: number): string {
69
- if (line.startsWith('+') || line.startsWith('-')) {
70
- return styleAddedOrRemovedDiffRow(line, rowWidth, color);
71
- }
72
- const row = `${CODE_BLOCK_INDENT}${line}`;
73
- if (!color) {
74
- return row;
75
- }
76
- if (line.startsWith('@@')) {
77
- return `${ANSI_CYAN}${row}${ANSI_RESET}`;
78
- }
79
- if (line.startsWith('diff ') || line.startsWith('index ')) {
80
- return `${ANSI_DIM}${row}${ANSI_RESET}`;
81
- }
82
- return row;
83
- }
84
-
85
- function resolveDiffRowWidth(lines: readonly string[], requestedWidth: number | undefined): number {
86
- const minimumWidth = lines.reduce(
87
- (maxWidth, line) => Math.max(maxWidth, CODE_BLOCK_INDENT.length + line.length),
88
- 0,
89
- );
90
- if (requestedWidth === undefined) {
91
- return minimumWidth;
92
- }
93
- return Math.max(minimumWidth, requestedWidth);
94
- }
95
-
96
- function renderDiffCodeBlock(
97
- code: string,
98
- color: boolean,
99
- codeBlockWidth: number | undefined,
100
- ): string {
101
- const lines = code.split('\n');
102
- const rowWidth = resolveDiffRowWidth(lines, codeBlockWidth);
103
- const body = lines.map((line) => colorizeDiffLine(line, color, rowWidth)).join('\n');
104
- return `${body}\n\n`;
105
- }
106
-
107
- function createTerminalRenderer(color: boolean, codeBlockWidth: number | undefined): Renderer {
108
- const renderer = new TerminalRendererConstructor(undefined, { ignoreIllegals: true });
109
- const renderCode = renderer.code.bind(renderer);
110
-
111
- renderer.code = (code: string, language: string | undefined, escaped: boolean): string => {
112
- if (isDiffLanguage(language)) {
113
- return renderDiffCodeBlock(code, color, codeBlockWidth);
114
- }
115
- return renderCode(code, language, escaped);
116
- };
117
-
118
- return renderer;
119
- }
120
-
121
- /**
122
- * Render markdown to a terminal-formatted string with colors, bold, etc.
123
- * Returns the rendered string (may include ANSI escape codes).
124
- */
125
- export function renderMarkdown(md: string, options: IRenderMarkdownOptions = {}): string {
126
- const result = marked.parse(md, {
127
- renderer: createTerminalRenderer(shouldUseColor(options.color), options.codeBlockWidth),
128
- });
129
- return typeof result === 'string' ? result.trimEnd() : md;
130
- }
@@ -1,129 +0,0 @@
1
- /**
2
- * Ink render entry point.
3
- */
4
-
5
- import { render } from 'ink';
6
- import React from 'react';
7
-
8
- import App from './App.js';
9
- import { TuiInteractionChannel } from './TuiInteractionChannel.js';
10
-
11
- import type { ITuiCliAdapter } from './tui-cli-adapter.js';
12
- import type { IAIProvider } from '@robota-sdk/agent-core';
13
- import type { TPermissionMode } from '@robota-sdk/agent-core';
14
- import type {
15
- IBackgroundTaskRunner,
16
- ICommandHostAdapters,
17
- ICommandModule,
18
- TSubagentRunnerFactory,
19
- TShellExecFn,
20
- CommandRegistry,
21
- } from '@robota-sdk/agent-framework';
22
- import type {
23
- IInteractiveSession,
24
- IInteractiveSessionStore,
25
- ITransportRegistryView,
26
- } from '@robota-sdk/agent-interface-transport';
27
-
28
- export interface IRenderOptions {
29
- cwd: string;
30
- provider: IAIProvider;
31
- providerOverride?: string | undefined;
32
- providerType?: string | undefined;
33
- modelId?: string;
34
- language?: string;
35
- permissionMode?: TPermissionMode;
36
- maxTurns?: number;
37
- allowedTools?: string[];
38
- deniedTools?: string[];
39
- version?: string;
40
- sessionStore?: IInteractiveSessionStore;
41
- resumeSessionId?: string;
42
- showSessionPickerOnStart?: boolean;
43
- forkSession?: boolean;
44
- sessionName?: string;
45
- backgroundTaskRunners?: IBackgroundTaskRunner[];
46
- subagentRunnerFactory?: TSubagentRunnerFactory;
47
- commandModules?: readonly ICommandModule[];
48
- commandHostAdapters?: ICommandHostAdapters;
49
- shellExec?: TShellExecFn;
50
- startupUpdateNotice?: Promise<string | undefined>;
51
- transportRegistry?: ITransportRegistryView<IInteractiveSession>;
52
- cliAdapter: ITuiCliAdapter;
53
- reloadPluginCommandSource?: (registry: CommandRegistry) => void;
54
- agentName?: string;
55
- /** Active preset id selected at startup (PRESET-011 runtime state). Defaults to 'default'. */
56
- activePresetId?: string;
57
- /** Preset persona block composed as a `source: 'persona'` system-prompt section (priority 5). */
58
- persona?: string;
59
- /** Preset execution capability: activate agent runtime + subagent/background dispatch. */
60
- enableParallelSubagents?: boolean;
61
- /** Preset execution capability: run a post-task self-verification step. */
62
- selfVerification?: boolean;
63
- }
64
-
65
- /** Map render options to TuiInteractionChannel constructor options. */
66
- export function toChannelOptions(
67
- options: IRenderOptions,
68
- resumeSessionId?: string,
69
- ): ConstructorParameters<typeof TuiInteractionChannel>[0] {
70
- return {
71
- cwd: options.cwd,
72
- provider: options.provider,
73
- permissionMode: options.permissionMode,
74
- maxTurns: options.maxTurns,
75
- allowedTools: options.allowedTools,
76
- deniedTools: options.deniedTools,
77
- sessionStore: options.sessionStore,
78
- resumeSessionId,
79
- forkSession: options.forkSession,
80
- sessionName: options.sessionName,
81
- backgroundTaskRunners: options.backgroundTaskRunners,
82
- subagentRunnerFactory: options.subagentRunnerFactory,
83
- commandModules: options.commandModules,
84
- commandHostAdapters: options.commandHostAdapters,
85
- shellExec: options.shellExec,
86
- transportRegistry: options.transportRegistry,
87
- language: options.language,
88
- reloadPluginCommandSource: options.reloadPluginCommandSource,
89
- agentName: options.agentName,
90
- activePresetId: options.activePresetId,
91
- persona: options.persona,
92
- enableParallelSubagents: options.enableParallelSubagents,
93
- selfVerification: options.selfVerification,
94
- };
95
- }
96
-
97
- export async function renderApp(options: IRenderOptions): Promise<void> {
98
- process.on('unhandledRejection', (reason) => {
99
- process.stderr.write(`\n[UNHANDLED REJECTION] ${reason}\n`);
100
- if (reason instanceof Error) {
101
- process.stderr.write(`${reason.stack}\n`);
102
- }
103
- });
104
-
105
- // Single-owner lifecycle (CLI-B12): render.tsx supplies only the factory;
106
- // App creates, replaces, and stops channels exclusively through React state.
107
- const createChannel = (resumeSessionId?: string): TuiInteractionChannel =>
108
- new TuiInteractionChannel(toChannelOptions(options, resumeSessionId));
109
-
110
- const instance = render(
111
- <App
112
- cwd={options.cwd}
113
- createChannel={createChannel}
114
- providerOverride={options.providerOverride}
115
- providerType={options.providerType}
116
- modelId={options.modelId}
117
- permissionMode={options.permissionMode}
118
- version={options.version}
119
- sessionStore={options.sessionStore}
120
- resumeSessionId={options.resumeSessionId}
121
- showSessionPickerOnStart={options.showSessionPickerOnStart}
122
- startupUpdateNotice={options.startupUpdateNotice}
123
- transportRegistry={options.transportRegistry}
124
- cliAdapter={options.cliAdapter}
125
- />,
126
- { exitOnCtrlC: false },
127
- );
128
- await instance.waitUntilExit();
129
- }