@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,123 +0,0 @@
1
- import type {
2
- IExecutionDetailRecord,
3
- IExecutionWorkspaceEntry,
4
- IExecutionWorkspaceSnapshot,
5
- TExecutionWorkspaceStatus,
6
- } from '@robota-sdk/agent-interface-transport';
7
-
8
- const ACTIVE_STATUSES: readonly TExecutionWorkspaceStatus[] = [
9
- 'active',
10
- 'queued',
11
- 'running',
12
- 'waiting_permission',
13
- 'sleeping',
14
- ];
15
- const DETAIL_RECORD_TEXT_LIMIT = 160;
16
- const PREVIEW_WHITESPACE = /\s+/g;
17
- const PREVIEW_SEPARATOR = ' ';
18
-
19
- export interface IExecutionWorkspaceEntryRow {
20
- id: string;
21
- radio: '●' | '○';
22
- title: string;
23
- subtitle?: string;
24
- statusLabel: string;
25
- preview?: string;
26
- color: string;
27
- isSelected: boolean;
28
- accessibleText: string;
29
- }
30
-
31
- export interface IExecutionWorkspaceEntryRowOptions {
32
- selectedEntryId?: string;
33
- }
34
-
35
- export function getDefaultBackgroundWorkspaceEntries(
36
- snapshot: IExecutionWorkspaceSnapshot | null,
37
- ): IExecutionWorkspaceEntry[] {
38
- return (snapshot?.entries ?? []).filter(
39
- (entry) => entry.kind === 'background_task' && entry.visibility === 'default',
40
- );
41
- }
42
-
43
- export function countActiveBackgroundWorkspaceEntries(
44
- snapshot: IExecutionWorkspaceSnapshot | null,
45
- ): number {
46
- return getDefaultBackgroundWorkspaceEntries(snapshot).filter((entry) =>
47
- ACTIVE_STATUSES.includes(entry.status),
48
- ).length;
49
- }
50
-
51
- export function formatExecutionWorkspaceEntryRow(
52
- entry: IExecutionWorkspaceEntry,
53
- options: IExecutionWorkspaceEntryRowOptions = {},
54
- ): IExecutionWorkspaceEntryRow {
55
- const isSelected = entry.id === options.selectedEntryId;
56
- const row = {
57
- id: entry.id,
58
- radio: isSelected ? '●' : '○',
59
- title: formatEntryTitle(entry),
60
- subtitle: formatEntrySubtitle(entry),
61
- statusLabel: formatStatusLabel(entry.status),
62
- preview: trimPreview(entry.preview ?? entry.currentAction),
63
- color: getEntryColor(entry),
64
- isSelected,
65
- } satisfies Omit<IExecutionWorkspaceEntryRow, 'accessibleText'>;
66
- return { ...row, accessibleText: formatAccessibleText(row) };
67
- }
68
-
69
- export function formatExecutionDetailRecord(record: IExecutionDetailRecord): string {
70
- const text = record.text.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);
71
- if (!text) return record.kind;
72
- return text.length > DETAIL_RECORD_TEXT_LIMIT
73
- ? `${text.slice(0, DETAIL_RECORD_TEXT_LIMIT)}...`
74
- : text;
75
- }
76
-
77
- function formatEntryTitle(entry: IExecutionWorkspaceEntry): string {
78
- if (entry.kind === 'main_thread') return entry.title;
79
- if (entry.kind === 'background_group') return `${entry.title} group`;
80
- if (entry.taskKind === 'agent') return `${entry.title} agent`;
81
- if (entry.taskKind === 'process') return entry.title || 'Process';
82
- if (entry.taskKind === 'scheduled') return entry.title || 'Scheduled';
83
- return entry.title;
84
- }
85
-
86
- function formatEntrySubtitle(entry: IExecutionWorkspaceEntry): string | undefined {
87
- if (entry.kind === 'main_thread') return entry.subtitle;
88
- const parts = [
89
- entry.taskKind,
90
- entry.subtitle,
91
- entry.attention === 'none' ? undefined : entry.attention,
92
- ];
93
- return (
94
- parts
95
- .filter((part): part is string => typeof part === 'string' && part.length > 0)
96
- .join(' · ') || undefined
97
- );
98
- }
99
-
100
- function formatStatusLabel(status: TExecutionWorkspaceStatus): string {
101
- return status.replace(/_/g, ' ');
102
- }
103
-
104
- function getEntryColor(entry: IExecutionWorkspaceEntry): string {
105
- if (entry.attention === 'failed' || entry.status === 'failed') return 'red';
106
- if (entry.attention === 'permission' || entry.status === 'waiting_permission') return 'yellow';
107
- if (entry.status === 'completed') return 'green';
108
- if (entry.status === 'cancelled') return 'yellow';
109
- if (ACTIVE_STATUSES.includes(entry.status)) return 'cyan';
110
- return 'white';
111
- }
112
-
113
- function trimPreview(value: string | undefined): string | undefined {
114
- const preview = value?.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);
115
- return preview || undefined;
116
- }
117
-
118
- function formatAccessibleText(row: Omit<IExecutionWorkspaceEntryRow, 'accessibleText'>): string {
119
- const parts = [row.radio, row.title, row.statusLabel, row.subtitle, row.preview];
120
- return parts
121
- .filter((part): part is string => typeof part === 'string' && part.length > 0)
122
- .join(' · ');
123
- }
@@ -1,285 +0,0 @@
1
- import stringWidth from 'string-width';
2
-
3
- const PASTE_START = '[200~';
4
- const PASTE_END = '[201~';
5
- const LAST_ASCII_CONTROL_CODE = 0x1f;
6
- const DELETE_CONTROL_CODE = 0x7f;
7
-
8
- export interface ICjkTextInputFlowState {
9
- value: string;
10
- cursor: number;
11
- isPasting: boolean;
12
- pasteBuffer: string;
13
- }
14
-
15
- export interface ICjkTextInputKey {
16
- ctrl?: boolean;
17
- tab?: boolean;
18
- shift?: boolean;
19
- upArrow?: boolean;
20
- downArrow?: boolean;
21
- return?: boolean;
22
- leftArrow?: boolean;
23
- rightArrow?: boolean;
24
- backspace?: boolean;
25
- delete?: boolean;
26
- }
27
-
28
- export interface ICjkTextInputFlowOptions {
29
- availableWidth?: number;
30
- canPaste: boolean;
31
- enableVerticalNavigation?: boolean;
32
- }
33
-
34
- export type TCjkTextInputEffect =
35
- | { type: 'none' }
36
- | { type: 'change'; value: string }
37
- | { type: 'submit'; value: string }
38
- | { type: 'paste'; text: string; cursor: number }
39
- | { type: 'render' };
40
-
41
- interface ICjkTextInputFlowResult {
42
- state: ICjkTextInputFlowState;
43
- effect: TCjkTextInputEffect;
44
- }
45
-
46
- export function createCjkTextInputFlowState(value: string): ICjkTextInputFlowState {
47
- return { value, cursor: value.length, isPasting: false, pasteBuffer: '' };
48
- }
49
-
50
- export function syncCjkTextInputFlowState(
51
- state: ICjkTextInputFlowState,
52
- value: string,
53
- cursorHint: number | null,
54
- ): ICjkTextInputFlowState {
55
- if (value === state.value) {
56
- return state;
57
- }
58
- return {
59
- ...state,
60
- value,
61
- cursor: cursorHint != null ? Math.min(cursorHint, value.length) : value.length,
62
- };
63
- }
64
-
65
- export function applyCjkTextInput(
66
- state: ICjkTextInputFlowState,
67
- input: string,
68
- key: ICjkTextInputKey,
69
- options: ICjkTextInputFlowOptions,
70
- ): ICjkTextInputFlowResult {
71
- const pasteResult = applyPasteBoundaryInput(state, input, options);
72
- if (pasteResult !== undefined) return pasteResult;
73
- const controlResult = applyControlInput(state, input, key, options);
74
- if (controlResult !== undefined) return controlResult;
75
- const cursorResult = applyCursorInput(state, key, options);
76
- if (cursorResult !== undefined) return cursorResult;
77
- return insertPrintableInput(state, input);
78
- }
79
-
80
- export function applyCjkTextPaste(
81
- state: ICjkTextInputFlowState,
82
- text: string,
83
- options: ICjkTextInputFlowOptions,
84
- ): ICjkTextInputFlowResult {
85
- const normalizedText = text.replace(/\r\n?/g, '\n');
86
- if (normalizedText.length === 0) {
87
- return { state, effect: { type: 'none' } };
88
- }
89
- if (normalizedText.includes('\n') && options.canPaste) {
90
- return { state, effect: { type: 'paste', text: normalizedText, cursor: state.cursor } };
91
- }
92
- return insertPrintableInput(state, normalizedText);
93
- }
94
-
95
- function applyPasteBoundaryInput(
96
- state: ICjkTextInputFlowState,
97
- input: string,
98
- options: ICjkTextInputFlowOptions,
99
- ): ICjkTextInputFlowResult | undefined {
100
- if (input === PASTE_START || input.startsWith(PASTE_START)) {
101
- return startBracketedPaste(state, input);
102
- }
103
- if (state.isPasting) {
104
- return continueBracketedPaste(state, input, options);
105
- }
106
- return undefined;
107
- }
108
-
109
- function applyControlInput(
110
- state: ICjkTextInputFlowState,
111
- input: string,
112
- key: ICjkTextInputKey,
113
- options: ICjkTextInputFlowOptions,
114
- ): ICjkTextInputFlowResult | undefined {
115
- if ((key.ctrl === true && input === 'c') || key.tab === true) {
116
- return { state, effect: { type: 'none' } };
117
- }
118
- if (key.return === true) {
119
- return { state, effect: { type: 'submit', value: state.value } };
120
- }
121
- if (input.length > 1 && (input.includes('\n') || input.includes('\r')) && options.canPaste) {
122
- return {
123
- state,
124
- effect: { type: 'paste', text: input.replace(/\r\n?/g, '\n'), cursor: state.cursor },
125
- };
126
- }
127
- return undefined;
128
- }
129
-
130
- function applyCursorInput(
131
- state: ICjkTextInputFlowState,
132
- key: ICjkTextInputKey,
133
- options: ICjkTextInputFlowOptions,
134
- ): ICjkTextInputFlowResult | undefined {
135
- if (key.upArrow === true || key.downArrow === true) {
136
- if (options.enableVerticalNavigation === false) {
137
- return { state, effect: { type: 'none' } };
138
- }
139
- return moveCursorVertically(
140
- state,
141
- key.upArrow === true ? 'up' : 'down',
142
- options.availableWidth,
143
- );
144
- }
145
- if (key.leftArrow === true) {
146
- return moveCursorHorizontally(state, 'left');
147
- }
148
- if (key.rightArrow === true) {
149
- return moveCursorHorizontally(state, 'right');
150
- }
151
- if (key.backspace === true || key.delete === true) {
152
- return deleteBeforeCursor(state);
153
- }
154
- return undefined;
155
- }
156
-
157
- export function filterPrintable(input: string | null | undefined): string {
158
- if (!input || input.length === 0) return '';
159
- let output = '';
160
- for (const char of input) {
161
- const code = char.charCodeAt(0);
162
- if (code > LAST_ASCII_CONTROL_CODE && code !== DELETE_CONTROL_CODE) {
163
- output += char;
164
- }
165
- }
166
- return output;
167
- }
168
-
169
- export function insertAtCursor(
170
- value: string,
171
- cursor: number,
172
- input: string,
173
- ): { value: string; cursor: number } {
174
- const next = value.slice(0, cursor) + input + value.slice(cursor);
175
- return { value: next, cursor: cursor + input.length };
176
- }
177
-
178
- export function displayOffset(chars: string[], charIndex: number, width: number): number {
179
- let offset = 0;
180
- for (let i = 0; i < charIndex && i < chars.length; i++) {
181
- const w = stringWidth(chars[i]!);
182
- const col = offset % width;
183
- if (col + w > width) offset += width - col;
184
- offset += w;
185
- }
186
- return offset;
187
- }
188
-
189
- export function charIndexAtDisplayOffset(chars: string[], target: number, width: number): number {
190
- let offset = 0;
191
- for (let i = 0; i < chars.length; i++) {
192
- if (offset >= target) return i;
193
- const w = stringWidth(chars[i]!);
194
- const col = offset % width;
195
- if (col + w > width) offset += width - col;
196
- offset += w;
197
- }
198
- return chars.length;
199
- }
200
-
201
- function startBracketedPaste(
202
- state: ICjkTextInputFlowState,
203
- input: string,
204
- ): ICjkTextInputFlowResult {
205
- return {
206
- state: { ...state, isPasting: true, pasteBuffer: input.slice(PASTE_START.length) },
207
- effect: { type: 'none' },
208
- };
209
- }
210
-
211
- function continueBracketedPaste(
212
- state: ICjkTextInputFlowState,
213
- input: string,
214
- options: ICjkTextInputFlowOptions,
215
- ): ICjkTextInputFlowResult {
216
- if (input !== PASTE_END && !input.includes(PASTE_END)) {
217
- return {
218
- state: { ...state, pasteBuffer: state.pasteBuffer + input },
219
- effect: { type: 'none' },
220
- };
221
- }
222
- const beforeMarker = input.split(PASTE_END)[0] ?? '';
223
- const nextState = { ...state, isPasting: false, pasteBuffer: '' };
224
- return applyCjkTextPaste(nextState, state.pasteBuffer + beforeMarker, options);
225
- }
226
-
227
- function moveCursorVertically(
228
- state: ICjkTextInputFlowState,
229
- direction: 'up' | 'down',
230
- availableWidth: number | undefined,
231
- ): ICjkTextInputFlowResult {
232
- if (!availableWidth || availableWidth <= 0) {
233
- return { state, effect: { type: 'none' } };
234
- }
235
- const chars = [...state.value];
236
- const offset = displayOffset(chars, state.cursor, availableWidth);
237
- const target = direction === 'up' ? offset - availableWidth : offset + availableWidth;
238
- if (target < 0) {
239
- return { state, effect: { type: 'none' } };
240
- }
241
- const cursor = charIndexAtDisplayOffset(chars, target, availableWidth);
242
- if (cursor === state.cursor) {
243
- return { state, effect: { type: 'none' } };
244
- }
245
- return { state: { ...state, cursor }, effect: { type: 'render' } };
246
- }
247
-
248
- function moveCursorHorizontally(
249
- state: ICjkTextInputFlowState,
250
- direction: 'left' | 'right',
251
- ): ICjkTextInputFlowResult {
252
- if (direction === 'left' && state.cursor > 0) {
253
- return { state: { ...state, cursor: state.cursor - 1 }, effect: { type: 'render' } };
254
- }
255
- if (direction === 'right' && state.cursor < state.value.length) {
256
- return { state: { ...state, cursor: state.cursor + 1 }, effect: { type: 'render' } };
257
- }
258
- return { state, effect: { type: 'none' } };
259
- }
260
-
261
- function deleteBeforeCursor(state: ICjkTextInputFlowState): ICjkTextInputFlowResult {
262
- if (state.cursor === 0) {
263
- return { state, effect: { type: 'none' } };
264
- }
265
- const value = state.value.slice(0, state.cursor - 1) + state.value.slice(state.cursor);
266
- return {
267
- state: { ...state, value, cursor: state.cursor - 1 },
268
- effect: { type: 'change', value },
269
- };
270
- }
271
-
272
- function insertPrintableInput(
273
- state: ICjkTextInputFlowState,
274
- input: string,
275
- ): ICjkTextInputFlowResult {
276
- const printable = filterPrintable(input);
277
- if (printable.length === 0) {
278
- return { state, effect: { type: 'none' } };
279
- }
280
- const result = insertAtCursor(state.value, state.cursor, printable);
281
- return {
282
- state: { ...state, value: result.value, cursor: result.cursor },
283
- effect: { type: 'change', value: result.value },
284
- };
285
- }
@@ -1,45 +0,0 @@
1
- import {
2
- applySelectionInput,
3
- getDirectionalSelectionInputAction,
4
- type ISelectionFlowState,
5
- type ISelectionInputKey,
6
- type TSelectionEffect,
7
- type TSelectionInputAction,
8
- } from './selection-flow.js';
9
-
10
- export type TConfirmPromptInputAction = TSelectionInputAction | { type: 'shortcut'; index: number };
11
-
12
- export function getConfirmPromptInputAction(
13
- input: string,
14
- key: ISelectionInputKey,
15
- optionCount: number,
16
- ): TConfirmPromptInputAction | undefined {
17
- const action = getDirectionalSelectionInputAction({ ...key, escape: false });
18
- if (action !== undefined) {
19
- return action;
20
- }
21
- if (optionCount === 2 && input === 'y') {
22
- return { type: 'shortcut', index: 0 };
23
- }
24
- if (optionCount === 2 && input === 'n') {
25
- return { type: 'shortcut', index: 1 };
26
- }
27
- return undefined;
28
- }
29
-
30
- export function applyConfirmPromptInput(
31
- state: ISelectionFlowState,
32
- action: TConfirmPromptInputAction,
33
- optionCount: number,
34
- ): { state: ISelectionFlowState; effect: TSelectionEffect } {
35
- if (state.resolved) {
36
- return { state, effect: { type: 'none' } };
37
- }
38
- if (typeof action !== 'string') {
39
- return {
40
- state: { ...state, selectedIndex: action.index, resolved: true },
41
- effect: { type: 'select', index: action.index },
42
- };
43
- }
44
- return applySelectionInput(state, action, { itemCount: optionCount });
45
- }
@@ -1,189 +0,0 @@
1
- import { isSlashCommand, tokeniseSlashCommand } from '@robota-sdk/agent-framework';
2
-
3
- import type { IHistoryEntry, TUniversalValue } from '@robota-sdk/agent-core';
4
- import type { ICommand } from '@robota-sdk/agent-interface-transport';
5
-
6
- export interface IAutocompleteInputKey {
7
- upArrow?: boolean;
8
- downArrow?: boolean;
9
- escape?: boolean;
10
- tab?: boolean;
11
- backspace?: boolean;
12
- delete?: boolean;
13
- }
14
-
15
- export type TAutocompletePopupAction = 'previous' | 'next' | 'close' | 'complete';
16
- export type TPendingPromptInputAction = 'cancelQueue';
17
- export type TPromptHistoryInputAction = 'previous' | 'next';
18
-
19
- export type TCommandSelectionResult =
20
- | { type: 'insert'; value: string; selectedIndex?: number }
21
- | { type: 'submit'; value: string };
22
-
23
- export interface IPasteLabelChange {
24
- value: string;
25
- cursorHint: number;
26
- label: string;
27
- lineCount: number;
28
- }
29
-
30
- export interface IPromptHistoryNavigationState {
31
- selectedIndex: number | null;
32
- draft: string;
33
- }
34
-
35
- export interface IPromptHistoryNavigationResult {
36
- value: string;
37
- cursorHint: number;
38
- state: IPromptHistoryNavigationState;
39
- }
40
-
41
- export function getAutocompletePopupAction(
42
- key: IAutocompleteInputKey,
43
- ): TAutocompletePopupAction | undefined {
44
- if (key.upArrow === true) return 'previous';
45
- if (key.downArrow === true) return 'next';
46
- if (key.escape === true) return 'close';
47
- if (key.tab === true) return 'complete';
48
- return undefined;
49
- }
50
-
51
- export function getPendingPromptInputAction(
52
- key: IAutocompleteInputKey,
53
- ): TPendingPromptInputAction | undefined {
54
- if (key.backspace === true || key.delete === true) {
55
- return 'cancelQueue';
56
- }
57
- return undefined;
58
- }
59
-
60
- export function getPromptHistoryInputAction(
61
- key: IAutocompleteInputKey,
62
- ): TPromptHistoryInputAction | undefined {
63
- if (key.upArrow === true) return 'previous';
64
- if (key.downArrow === true) return 'next';
65
- return undefined;
66
- }
67
-
68
- export function createPromptHistoryNavigationState(): IPromptHistoryNavigationState {
69
- return { selectedIndex: null, draft: '' };
70
- }
71
-
72
- export function navigatePromptHistory(
73
- value: string,
74
- history: readonly string[],
75
- state: IPromptHistoryNavigationState,
76
- action: TPromptHistoryInputAction,
77
- ): IPromptHistoryNavigationResult {
78
- if (history.length === 0) {
79
- return { value, cursorHint: value.length, state };
80
- }
81
-
82
- if (action === 'previous') {
83
- const selectedIndex =
84
- state.selectedIndex === null ? history.length - 1 : Math.max(0, state.selectedIndex - 1);
85
- const nextValue = history[selectedIndex] ?? value;
86
- return {
87
- value: nextValue,
88
- cursorHint: nextValue.length,
89
- state: { selectedIndex, draft: state.selectedIndex === null ? value : state.draft },
90
- };
91
- }
92
-
93
- if (state.selectedIndex === null) {
94
- return { value, cursorHint: value.length, state };
95
- }
96
-
97
- if (state.selectedIndex < history.length - 1) {
98
- const selectedIndex = state.selectedIndex + 1;
99
- const nextValue = history[selectedIndex] ?? value;
100
- return {
101
- value: nextValue,
102
- cursorHint: nextValue.length,
103
- state: { ...state, selectedIndex },
104
- };
105
- }
106
-
107
- return {
108
- value: state.draft,
109
- cursorHint: state.draft.length,
110
- state: createPromptHistoryNavigationState(),
111
- };
112
- }
113
-
114
- export function appendPromptHistory(history: readonly string[], value: string): string[] {
115
- const prompt = value.trim();
116
- if (prompt.length === 0) return [...history];
117
- if (history[history.length - 1] === prompt) return [...history];
118
- return [...history, prompt];
119
- }
120
-
121
- export function extractPromptHistory(entries: readonly IHistoryEntry[]): string[] {
122
- let prompts: string[] = [];
123
- for (const entry of entries) {
124
- if (entry.category !== 'chat' || entry.type !== 'user') continue;
125
- const data = entry.data as Record<string, TUniversalValue> | undefined;
126
- if (typeof data?.content !== 'string') continue;
127
- prompts = appendPromptHistory(prompts, data.content);
128
- }
129
- return prompts;
130
- }
131
-
132
- export function moveAutocompleteSelection(
133
- selectedIndex: number,
134
- commandCount: number,
135
- direction: 'previous' | 'next',
136
- ): number {
137
- if (commandCount === 0) return 0;
138
- if (direction === 'previous') {
139
- return selectedIndex > 0 ? selectedIndex - 1 : commandCount - 1;
140
- }
141
- return selectedIndex < commandCount - 1 ? selectedIndex + 1 : 0;
142
- }
143
-
144
- export function resolveTabCompletion(value: string, command: ICommand): TCommandSelectionResult {
145
- // Subcommand mode: '/parent filter' — space present after command name
146
- if (isSlashCommand(value) && value.slice(1).includes(' ')) {
147
- const { name } = tokeniseSlashCommand(value);
148
- return { type: 'insert', value: `/${name} ${command.name} ` };
149
- }
150
- if (command.subcommands && command.subcommands.length > 0) {
151
- return { type: 'insert', value: `/${command.name} `, selectedIndex: 0 };
152
- }
153
- return { type: 'insert', value: `/${command.name} ` };
154
- }
155
-
156
- export function resolveEnterCommandSelection(
157
- value: string,
158
- command: ICommand,
159
- ): TCommandSelectionResult {
160
- // Subcommand mode: '/parent filter' — space present after command name
161
- if (isSlashCommand(value) && value.slice(1).includes(' ')) {
162
- const { name } = tokeniseSlashCommand(value);
163
- return { type: 'submit', value: `/${name} ${command.name}` };
164
- }
165
- if (command.subcommands && command.subcommands.length > 0) {
166
- return { type: 'insert', value: `/${command.name} `, selectedIndex: 0 };
167
- }
168
- return { type: 'submit', value: `/${command.name}` };
169
- }
170
-
171
- export function createPasteLabelChange(
172
- value: string,
173
- cursorPosition: number,
174
- pasteId: number,
175
- text: string,
176
- ): IPasteLabelChange {
177
- const lineCount = text.split('\n').length;
178
- const label = `[Pasted text #${pasteId} +${lineCount} lines]`;
179
- return {
180
- value: value.slice(0, cursorPosition) + label + value.slice(cursorPosition),
181
- cursorHint: cursorPosition + label.length,
182
- label,
183
- lineCount,
184
- };
185
- }
186
-
187
- export function shouldSubmitInput(text: string): boolean {
188
- return text.trim().length > 0;
189
- }