@robota-sdk/agent-transport 3.0.0-beta.64 → 3.0.0-beta.66

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 (142) hide show
  1. package/README.md +97 -0
  2. package/dist/node/chunk-Bmb41Sf3.cjs +1 -0
  3. package/dist/node/headless/index.cjs +1 -1
  4. package/dist/node/headless/index.d.ts +2 -2
  5. package/dist/node/headless/index.js +1 -1
  6. package/dist/node/headless-B8yWkXW_.js +15 -0
  7. package/dist/node/headless-B8yWkXW_.js.map +1 -0
  8. package/dist/node/headless-Dp3kQVmo.cjs +14 -0
  9. package/dist/node/http/index.cjs +1 -1
  10. package/dist/node/http/index.d.ts +1 -1
  11. package/dist/node/http/index.js +1 -1
  12. package/dist/node/{http-DwO1AHG-.js → http-Br10Ps8m.js} +1 -1
  13. package/dist/node/http-Br10Ps8m.js.map +1 -0
  14. package/dist/node/http-Da6Kw4oy.cjs +1 -0
  15. package/dist/node/{index-Y0zHb1Bz.d.ts → index-C5KNEBO9.d.ts} +1 -1
  16. package/dist/node/{index-B_rcr14p.d.ts.map → index-C5KNEBO9.d.ts.map} +1 -1
  17. package/dist/node/{index-B_rcr14p.d.ts → index-C7DvsmEg.d.ts} +2 -2
  18. package/dist/node/{index-Y0zHb1Bz.d.ts.map → index-C7DvsmEg.d.ts.map} +1 -1
  19. package/dist/node/index-C9LWCL4l.d.ts.map +1 -1
  20. package/dist/node/{index-D34WUfFH.d.ts → index-CP7kaYMg.d.ts} +17 -2
  21. package/dist/node/index-CP7kaYMg.d.ts.map +1 -0
  22. package/dist/node/{index-CAr3ioVh.d.ts → index-CQsMNXAh.d.ts} +19 -16
  23. package/dist/node/index-CQsMNXAh.d.ts.map +1 -0
  24. package/dist/node/{index-nBlMTFkZ.d.ts → index-DOA2KIYt.d.ts} +2 -2
  25. package/dist/node/{index-nBlMTFkZ.d.ts.map → index-DOA2KIYt.d.ts.map} +1 -1
  26. package/dist/node/{index-k3TUjA-T.d.ts → index-Gby9H4q2.d.ts} +17 -2
  27. package/dist/node/index-Gby9H4q2.d.ts.map +1 -0
  28. package/dist/node/{index-CEs25wVk.d.ts → index-X2Zg8FEY.d.ts} +2 -2
  29. package/dist/node/{index-CEs25wVk.d.ts.map → index-X2Zg8FEY.d.ts.map} +1 -1
  30. package/dist/node/{index--Ti9NzQX.d.ts → index-rGmGTQ9o.d.ts} +19 -16
  31. package/dist/node/index-rGmGTQ9o.d.ts.map +1 -0
  32. package/dist/node/{index-CvXLpjJO.d.ts → index-yvGShbDx.d.ts} +2 -2
  33. package/dist/node/{index-CvXLpjJO.d.ts.map → index-yvGShbDx.d.ts.map} +1 -1
  34. package/dist/node/index.cjs +1 -1
  35. package/dist/node/index.d.ts +28 -6
  36. package/dist/node/index.d.ts.map +1 -0
  37. package/dist/node/index.js +2 -1
  38. package/dist/node/index.js.map +1 -0
  39. package/dist/node/mcp/index.cjs +1 -1
  40. package/dist/node/mcp/index.d.ts +1 -1
  41. package/dist/node/mcp/index.js +1 -1
  42. package/dist/node/{mcp-BXBwF6Wu.js → mcp-BAujHOMr.js} +1 -1
  43. package/dist/node/mcp-BAujHOMr.js.map +1 -0
  44. package/dist/node/mcp-Bl8jUfev.cjs +1 -0
  45. package/dist/node/tui/index.cjs +1 -1
  46. package/dist/node/tui/index.d.ts +2 -2
  47. package/dist/node/tui/index.js +1 -1
  48. package/dist/node/tui-B8G3yHrL.cjs +24 -0
  49. package/dist/node/tui-DUIfVw3G.js +25 -0
  50. package/dist/node/tui-DUIfVw3G.js.map +1 -0
  51. package/dist/node/ws/index.cjs +1 -1
  52. package/dist/node/ws/index.d.ts +1 -1
  53. package/dist/node/ws/index.js +1 -1
  54. package/dist/node/{ws-B-oRccFl.js → ws-BWel8nzl.js} +1 -1
  55. package/dist/node/ws-BWel8nzl.js.map +1 -0
  56. package/dist/node/ws-tCjj2gPu.cjs +1 -0
  57. package/package.json +19 -18
  58. package/src/headless/cli-input.ts +50 -0
  59. package/src/headless/headless-runner.ts +2 -1
  60. package/src/headless/headless-stream-json.ts +1 -0
  61. package/src/headless/headless-transport.ts +3 -2
  62. package/src/headless/index.ts +2 -0
  63. package/src/headless/print-terminal.ts +57 -0
  64. package/src/http/http-transport.ts +3 -3
  65. package/src/http/routes.ts +2 -1
  66. package/src/index.ts +2 -1
  67. package/src/mcp/mcp-server.ts +1 -0
  68. package/src/mcp/mcp-transport.ts +3 -2
  69. package/src/transport-registry.ts +100 -0
  70. package/src/tui/App.tsx +52 -31
  71. package/src/tui/BackgroundTaskPanel.tsx +4 -2
  72. package/src/tui/CjkTextInput.tsx +3 -2
  73. package/src/tui/ConfirmPrompt.tsx +2 -1
  74. package/src/tui/ExecutionWorkspaceDetailPane.tsx +7 -5
  75. package/src/tui/ExecutionWorkspaceSwitcher.tsx +8 -6
  76. package/src/tui/InputArea.tsx +72 -12
  77. package/src/tui/InteractivePrompt.tsx +5 -3
  78. package/src/tui/ListPicker.tsx +2 -1
  79. package/src/tui/MenuSelect.tsx +2 -1
  80. package/src/tui/MessageList.tsx +8 -6
  81. package/src/tui/PermissionPrompt.tsx +5 -3
  82. package/src/tui/PluginTUI.tsx +5 -3
  83. package/src/tui/SessionPicker.tsx +4 -2
  84. package/src/tui/SessionStatusBar.tsx +7 -3
  85. package/src/tui/SlashAutocomplete.tsx +6 -6
  86. package/src/tui/StatusBar.tsx +5 -3
  87. package/src/tui/StreamingIndicator.tsx +4 -2
  88. package/src/tui/TextPrompt.tsx +2 -1
  89. package/src/tui/ToolCommandOutput.tsx +4 -2
  90. package/src/tui/ToolDiffBlock.tsx +4 -2
  91. package/src/tui/TransportTUI.tsx +3 -2
  92. package/src/tui/UpdateNotice.tsx +1 -1
  93. package/src/tui/UsageSummaryEntry.tsx +3 -2
  94. package/src/tui/WaveText.tsx +1 -1
  95. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +7 -4
  96. package/src/tui/__tests__/input-area-flow.test.ts +19 -0
  97. package/src/tui/background-task-row-format.ts +2 -1
  98. package/src/tui/command-interaction-registry.ts +66 -0
  99. package/src/tui/command-interaction.ts +9 -0
  100. package/src/tui/create-default-tui-cli-adapter.ts +42 -0
  101. package/src/tui/flows/input-area-flow.ts +10 -2
  102. package/src/tui/hooks/command-effect-handler.ts +4 -3
  103. package/src/tui/hooks/side-effects-types.ts +1 -1
  104. package/src/tui/hooks/use-interactive-session-init.ts +4 -2
  105. package/src/tui/hooks/useAutocomplete.ts +1 -0
  106. package/src/tui/hooks/useInteractiveSession.ts +22 -19
  107. package/src/tui/hooks/usePermissionQueue.ts +2 -1
  108. package/src/tui/hooks/usePluginCallbacks.ts +1 -0
  109. package/src/tui/hooks/usePluginScreenData.ts +2 -1
  110. package/src/tui/hooks/useSideEffects.ts +8 -6
  111. package/src/tui/hooks/useSlashRouting.ts +4 -3
  112. package/src/tui/hooks/useStatusLineSettings.ts +4 -2
  113. package/src/tui/index.ts +11 -1
  114. package/src/tui/interactions/CommandConfirm.tsx +36 -0
  115. package/src/tui/interactions/CommandPicker.tsx +77 -0
  116. package/src/tui/render-markdown.ts +2 -1
  117. package/src/tui/render.tsx +12 -26
  118. package/src/tui/tui-cli-adapter-context.tsx +1 -0
  119. package/src/tui/tui-cli-adapter.ts +1 -1
  120. package/src/tui/tui-transport.ts +5 -4
  121. package/src/tui/utils/edit-diff.ts +1 -0
  122. package/src/tui/utils/tool-call-extractor.ts +1 -0
  123. package/src/ws/ws-background-messages.ts +1 -1
  124. package/src/ws/ws-handler.ts +6 -5
  125. package/src/ws/ws-transport-configurable.ts +6 -3
  126. package/src/ws/ws-transport.ts +3 -2
  127. package/dist/node/headless-CWEpJXFK.js +0 -7
  128. package/dist/node/headless-CWEpJXFK.js.map +0 -1
  129. package/dist/node/headless-CsZFelG9.cjs +0 -6
  130. package/dist/node/http-CM3TJhrF.cjs +0 -1
  131. package/dist/node/http-DwO1AHG-.js.map +0 -1
  132. package/dist/node/index--Ti9NzQX.d.ts.map +0 -1
  133. package/dist/node/index-CAr3ioVh.d.ts.map +0 -1
  134. package/dist/node/index-D34WUfFH.d.ts.map +0 -1
  135. package/dist/node/index-k3TUjA-T.d.ts.map +0 -1
  136. package/dist/node/mcp-BXBwF6Wu.js.map +0 -1
  137. package/dist/node/mcp-DcHuGokt.cjs +0 -1
  138. package/dist/node/tui-CeD_6rSo.cjs +0 -24
  139. package/dist/node/tui-zmDTPk4b.js +0 -25
  140. package/dist/node/tui-zmDTPk4b.js.map +0 -1
  141. package/dist/node/ws-B-oRccFl.js.map +0 -1
  142. package/dist/node/ws-COnIgnmn.cjs +0 -1
@@ -0,0 +1,9 @@
1
+ export type {
2
+ TOnMissingArgsAction,
3
+ ITuiPickerItem,
4
+ ITuiCommandInteraction,
5
+ ITuiPickerInteraction,
6
+ ITuiConfirmInteraction,
7
+ TAnyTuiCommandInteraction,
8
+ } from '@robota-sdk/agent-interface-tui';
9
+ export { isPickerInteraction, isConfirmInteraction } from '@robota-sdk/agent-interface-tui';
@@ -0,0 +1,42 @@
1
+ import { findProviderDefinition } from '@robota-sdk/agent-core';
2
+ import {
3
+ applyActiveModelChange,
4
+ applyStatusLineSettings,
5
+ deleteSettings,
6
+ getUserSettingsPath,
7
+ readSettings,
8
+ resolveGitBranch,
9
+ writeSettings,
10
+ } from '@robota-sdk/agent-framework';
11
+
12
+ import type { ITuiCliAdapter } from './tui-cli-adapter.js';
13
+ import type { IProviderDefinition } from '@robota-sdk/agent-core';
14
+ import type { CommandRegistry } from '@robota-sdk/agent-framework';
15
+
16
+ export interface IDefaultTuiCliAdapterOptions {
17
+ providerDefinitions: readonly IProviderDefinition[];
18
+ reloadPluginCommandSource: (registry: CommandRegistry) => void;
19
+ }
20
+
21
+ export function createDefaultTuiCliAdapter({
22
+ providerDefinitions,
23
+ reloadPluginCommandSource,
24
+ }: IDefaultTuiCliAdapterOptions): ITuiCliAdapter {
25
+ return {
26
+ getUserSettingsPath: () => getUserSettingsPath(),
27
+ readSettings: (path) => readSettings(path),
28
+ writeSettings: (path, settings) => writeSettings(path, settings),
29
+ deleteSettings: (path) => deleteSettings(path),
30
+ applyStatusLineSettings: (path, patch) => applyStatusLineSettings(path, patch),
31
+ reloadPluginCommandSource: (registry) => {
32
+ reloadPluginCommandSource(registry);
33
+ },
34
+ applyActiveModelChange: (cwd, modelId, options) => {
35
+ applyActiveModelChange(cwd, modelId, options);
36
+ return { applied: true };
37
+ },
38
+ getGitBranch: (cwd) => resolveGitBranch(cwd),
39
+ getProviderDisplayName: (type) =>
40
+ findProviderDefinition(providerDefinitions, type)?.displayName ?? type,
41
+ };
42
+ }
@@ -1,6 +1,8 @@
1
+ import { parseSlashInput } from '../hooks/useAutocomplete.js';
2
+
3
+ import type { ITuiCommandInteraction } from '../command-interaction.js';
1
4
  import type { IHistoryEntry, TUniversalValue } from '@robota-sdk/agent-core';
2
5
  import type { ICommand } from '@robota-sdk/agent-framework';
3
- import { parseSlashInput } from '../hooks/useAutocomplete.js';
4
6
 
5
7
  export interface IAutocompleteInputKey {
6
8
  upArrow?: boolean;
@@ -17,7 +19,8 @@ export type TPromptHistoryInputAction = 'previous' | 'next';
17
19
 
18
20
  export type TCommandSelectionResult =
19
21
  | { type: 'insert'; value: string; selectedIndex?: number }
20
- | { type: 'submit'; value: string };
22
+ | { type: 'submit'; value: string }
23
+ | { type: 'open-interaction'; commandName: string };
21
24
 
22
25
  export interface IPasteLabelChange {
23
26
  value: string;
@@ -154,11 +157,16 @@ export function resolveTabCompletion(value: string, command: ICommand): TCommand
154
157
  export function resolveEnterCommandSelection(
155
158
  value: string,
156
159
  command: ICommand,
160
+ interaction?: ITuiCommandInteraction,
157
161
  ): TCommandSelectionResult {
158
162
  const parsed = parseSlashInput(value);
159
163
  if (parsed.parentCommand) {
160
164
  return { type: 'submit', value: `/${parsed.parentCommand} ${command.name}` };
161
165
  }
166
+ // parentCommand is empty → no args provided beyond the command name itself
167
+ if (interaction?.onMissingArgs) {
168
+ return { type: 'open-interaction', commandName: command.name };
169
+ }
162
170
  if (command.subcommands && command.subcommands.length > 0) {
163
171
  return { type: 'insert', value: `/${command.name} `, selectedIndex: 0 };
164
172
  }
@@ -1,8 +1,9 @@
1
- import type { TCommandEffect, TStatusLineCommandSettingsPatch } from '@robota-sdk/agent-framework';
2
- import { isStatusLineCommandSettingsPatch } from '@robota-sdk/agent-framework';
3
1
  import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';
4
- import type { IHistoryEntry, TSessionEndReason } from '@robota-sdk/agent-core';
2
+ import { isStatusLineCommandSettingsPatch } from '@robota-sdk/agent-framework';
3
+
5
4
  import type { ITuiCliAdapter } from '../tui-cli-adapter.js';
5
+ import type { IHistoryEntry, TSessionEndReason } from '@robota-sdk/agent-core';
6
+ import type { TCommandEffect, TStatusLineCommandSettingsPatch } from '@robota-sdk/agent-framework';
6
7
 
7
8
  export interface ICommandEffectHandlerDeps {
8
9
  addEntry: (entry: IHistoryEntry) => void;
@@ -1,10 +1,10 @@
1
+ import type { ICommandEffectQueue } from './command-effect-queue.js';
1
2
  import type { IHistoryEntry } from '@robota-sdk/agent-core';
2
3
  import type { InteractiveSession } from '@robota-sdk/agent-framework';
3
4
  import type {
4
5
  TCommandInteractionPrompt as TInteractivePrompt,
5
6
  IStatusLineCommandSettings as TStatusLineSettings,
6
7
  } from '@robota-sdk/agent-framework';
7
- import type { ICommandEffectQueue } from './command-effect-queue.js';
8
8
 
9
9
  export type { TInteractivePrompt, TStatusLineSettings };
10
10
 
@@ -1,9 +1,11 @@
1
1
  import { InteractiveSession, CommandRegistry } from '@robota-sdk/agent-framework';
2
- import type { TToolArgs } from '@robota-sdk/agent-core';
3
- import type { TPermissionResultValue } from '@robota-sdk/agent-framework';
2
+
4
3
  import { TuiStateManager } from '../tui-state-manager.js';
5
4
  import { CommandEffectQueue, type ICommandEffectQueue } from './command-effect-queue.js';
5
+
6
6
  import type { IInteractiveSessionProps } from './useInteractiveSession.js';
7
+ import type { TToolArgs } from '@robota-sdk/agent-core';
8
+ import type { TPermissionResultValue } from '@robota-sdk/agent-framework';
7
9
 
8
10
  export interface IInitState {
9
11
  interactiveSession: InteractiveSession;
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import React, { useState, useMemo } from 'react';
7
+
7
8
  import type { CommandRegistry, ICommand } from '@robota-sdk/agent-framework';
8
9
 
9
10
  /** Parse input to determine autocomplete state */
@@ -1,30 +1,34 @@
1
+ import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';
1
2
  import { useState, useCallback, useEffect } from 'react';
2
- import { InteractiveSession, CommandRegistry } from '@robota-sdk/agent-framework';
3
- import type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';
3
+
4
+ import { CommandEffectQueue, type ICommandEffectQueue } from './command-effect-queue.js';
5
+ import { initializeSession, type IInitState } from './use-interactive-session-init.js';
6
+ import { usePermissionQueue } from './usePermissionQueue.js';
7
+ import { useSlashRouting } from './useSlashRouting.js';
8
+
9
+ import type { TuiStateManager } from '../tui-state-manager.js';
10
+ import type { IPermissionRequest } from '../types.js';
11
+ import type {
12
+ IAIProvider,
13
+ TPermissionMode,
14
+ IHistoryEntry,
15
+ TSessionEndReason,
16
+ } from '@robota-sdk/agent-core';
17
+ import type { InteractiveSession, CommandRegistry } from '@robota-sdk/agent-framework';
4
18
  import type {
5
19
  IBackgroundTaskRunner,
6
20
  ICommandHostAdapters,
7
21
  ICommandModule,
8
22
  IInteractiveSession,
9
23
  IInteractiveSessionStore,
24
+ IExecutionWorkspaceEvent,
10
25
  TSubagentRunnerFactory,
11
26
  IExecutionDetailPage,
12
27
  IExecutionWorkspaceSnapshot,
28
+ IToolState,
13
29
  TShellExecFn,
14
30
  } from '@robota-sdk/agent-framework';
15
- import type {
16
- IAIProvider,
17
- TPermissionMode,
18
- IHistoryEntry,
19
- TSessionEndReason,
20
- } from '@robota-sdk/agent-core';
21
- import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';
22
- import type { IPermissionRequest } from '../types.js';
23
- import { TuiStateManager } from '../tui-state-manager.js';
24
- import { useSlashRouting } from './useSlashRouting.js';
25
- import { CommandEffectQueue, type ICommandEffectQueue } from './command-effect-queue.js';
26
- import { usePermissionQueue } from './usePermissionQueue.js';
27
- import { initializeSession, type IInitState } from './use-interactive-session-init.js';
31
+ import type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';
28
32
 
29
33
  const SESSION_INIT_POLL_MS = 200;
30
34
 
@@ -55,7 +59,7 @@ export interface IInteractiveSessionState {
55
59
  history: IHistoryEntry[];
56
60
  addEntry: (entry: IHistoryEntry) => void;
57
61
  streamingText: string;
58
- activeTools: import('@robota-sdk/agent-framework').IToolState[];
62
+ activeTools: IToolState[];
59
63
  isThinking: boolean;
60
64
  isAborting: boolean;
61
65
  isShuttingDown: boolean;
@@ -144,9 +148,8 @@ export function useInteractiveSession(props: IInteractiveSessionProps): IInterac
144
148
  const onCompact = (): void => applyCompactEventToManager(interactiveSession, manager);
145
149
  const onSkillActivation = (): void =>
146
150
  applySkillActivationEventToManager(interactiveSession, manager);
147
- const onExecutionWorkspaceEvent = (
148
- event: import('@robota-sdk/agent-framework').IExecutionWorkspaceEvent,
149
- ): void => manager.syncExecutionWorkspaceSnapshot(event.snapshot);
151
+ const onExecutionWorkspaceEvent = (event: IExecutionWorkspaceEvent): void =>
152
+ manager.syncExecutionWorkspaceSnapshot(event.snapshot);
150
153
 
151
154
  interactiveSession.on('text_delta', manager.onTextDelta);
152
155
  interactiveSession.on('tool_start', manager.onToolStart);
@@ -1,7 +1,8 @@
1
1
  import { useState, useRef, useCallback } from 'react';
2
+
3
+ import type { IPermissionRequest } from '../types.js';
2
4
  import type { TToolArgs } from '@robota-sdk/agent-core';
3
5
  import type { TPermissionResultValue } from '@robota-sdk/agent-framework';
4
- import type { IPermissionRequest } from '../types.js';
5
6
 
6
7
  export function usePermissionQueue(): {
7
8
  permissionHandler: (toolName: string, toolArgs: TToolArgs) => Promise<TPermissionResultValue>;
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { useMemo } from 'react';
10
+
10
11
  import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
11
12
 
12
13
  function createNoOpPluginAdapter(): ICommandPluginAdapter {
@@ -4,8 +4,9 @@
4
4
  */
5
5
 
6
6
  import { useState, useEffect } from 'react';
7
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
7
+
8
8
  import type { IMenuSelectItem } from '../MenuSelect.js';
9
+ import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
9
10
 
10
11
  export function usePluginScreenData(
11
12
  screen: string,
@@ -1,10 +1,7 @@
1
- import { useState, useRef, useCallback } from 'react';
2
- import { useApp } from 'ink';
3
- import type { ICommandInteraction, ICommandResult } from '@robota-sdk/agent-framework';
4
1
  import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';
5
- import type { TSessionEndReason } from '@robota-sdk/agent-core';
6
- import type { TInteractivePrompt } from './side-effects-types.js';
7
- import type { IUseSideEffectsOptions, IUseSideEffectsResult } from './side-effects-types.js';
2
+ import { useApp } from 'ink';
3
+ import { useState, useRef, useCallback } from 'react';
4
+
8
5
  import { applyCommandEffects } from './command-effect-handler.js';
9
6
  import { useTuiCliAdapter } from '../tui-cli-adapter-context.js';
10
7
  import {
@@ -12,6 +9,11 @@ import {
12
9
  applyConfirmedModelChange,
13
10
  } from './model-change-side-effect.js';
14
11
 
12
+ import type { IUseSideEffectsOptions, IUseSideEffectsResult } from './side-effects-types.js';
13
+ import type { TInteractivePrompt } from './side-effects-types.js';
14
+ import type { TSessionEndReason } from '@robota-sdk/agent-core';
15
+ import type { ICommandInteraction, ICommandResult } from '@robota-sdk/agent-framework';
16
+
15
17
  const EXIT_DELAY_MS = 500;
16
18
 
17
19
  export function useSideEffects({
@@ -3,16 +3,17 @@
3
3
  * Extracted from useInteractiveSession for single-responsibility.
4
4
  */
5
5
 
6
+ import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';
6
7
  import { useCallback } from 'react';
8
+
9
+ import type { TuiStateManager } from '../tui-state-manager.js';
10
+ import type { ICommandEffectQueue } from './command-effect-queue.js';
7
11
  import type {
8
12
  IInteractiveSession,
9
13
  CommandRegistry,
10
14
  ICommandResult,
11
15
  TCommandEffect,
12
16
  } from '@robota-sdk/agent-framework';
13
- import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';
14
- import type { TuiStateManager } from '../tui-state-manager.js';
15
- import type { ICommandEffectQueue } from './command-effect-queue.js';
16
17
 
17
18
  export function useSlashRouting(
18
19
  interactiveSession: IInteractiveSession,
@@ -1,8 +1,10 @@
1
- import { useState } from 'react';
2
- import type { IStatusLineCommandSettings } from '@robota-sdk/agent-framework';
3
1
  import { DEFAULT_STATUS_LINE_COMMAND_SETTINGS } from '@robota-sdk/agent-framework';
2
+ import { useState } from 'react';
3
+
4
4
  import { useTuiCliAdapter } from '../tui-cli-adapter-context.js';
5
+
5
6
  import type { TUniversalValue } from '@robota-sdk/agent-core';
7
+ import type { IStatusLineCommandSettings } from '@robota-sdk/agent-framework';
6
8
 
7
9
  function readStatusLineSettings(
8
10
  settings: Record<string, TUniversalValue>,
package/src/tui/index.ts CHANGED
@@ -1,3 +1,13 @@
1
1
  export { TuiTransport } from './tui-transport.js';
2
2
  export type { ITuiCliAdapter } from './tui-cli-adapter.js';
3
- export type { IRenderOptions } from './render.js';
3
+ export type { IDefaultTuiCliAdapterOptions } from './create-default-tui-cli-adapter.js';
4
+ export { createDefaultTuiCliAdapter } from './create-default-tui-cli-adapter.js';
5
+ export type { ITuiRenderOptions } from './render.js';
6
+ export type {
7
+ TOnMissingArgsAction,
8
+ ITuiPickerItem,
9
+ ITuiCommandInteraction,
10
+ ITuiPickerInteraction,
11
+ ITuiConfirmInteraction,
12
+ TAnyTuiCommandInteraction,
13
+ } from './command-interaction.js';
@@ -0,0 +1,36 @@
1
+ import { Box, Text, useInput } from 'ink';
2
+ import React from 'react';
3
+
4
+ import type { ITuiConfirmInteraction } from '../command-interaction.js';
5
+
6
+ interface IProps {
7
+ commandName: string;
8
+ interaction: ITuiConfirmInteraction;
9
+ onConfirm: () => void;
10
+ onCancel: () => void;
11
+ }
12
+
13
+ export default function CommandConfirm({
14
+ commandName,
15
+ interaction,
16
+ onConfirm,
17
+ onCancel,
18
+ }: IProps): React.ReactElement {
19
+ useInput((input, key) => {
20
+ if (key.return || input === 'y' || input === 'Y') {
21
+ onConfirm();
22
+ } else if (key.escape || input === 'n' || input === 'N') {
23
+ onCancel();
24
+ }
25
+ });
26
+
27
+ return (
28
+ <Box paddingX={1}>
29
+ <Text bold color="yellow">
30
+ /{commandName}:{' '}
31
+ </Text>
32
+ <Text>{interaction.message} </Text>
33
+ <Text dimColor>[y/n]</Text>
34
+ </Box>
35
+ );
36
+ }
@@ -0,0 +1,77 @@
1
+ import { Box, Text, useInput, useStdout } from 'ink';
2
+ import React, { useState } from 'react';
3
+
4
+ import type { ITuiPickerInteraction, ITuiPickerItem } from '../command-interaction.js';
5
+
6
+ interface IProps {
7
+ commandName: string;
8
+ interaction: ITuiPickerInteraction;
9
+ onSelect: (item: ITuiPickerItem) => void;
10
+ onCancel: () => void;
11
+ }
12
+
13
+ const MAX_VISIBLE = 8;
14
+ const OUTER_CHROME = 4;
15
+ const MIN_ROW_WIDTH = 40;
16
+
17
+ function useRowWidth(): number {
18
+ const { stdout } = useStdout();
19
+ return Math.max(MIN_ROW_WIDTH, (stdout.columns ?? 80) - OUTER_CHROME);
20
+ }
21
+
22
+ export default function CommandPicker({
23
+ commandName,
24
+ interaction,
25
+ onSelect,
26
+ onCancel,
27
+ }: IProps): React.ReactElement {
28
+ const items = interaction.getItems();
29
+ const [selectedIndex, setSelectedIndex] = useState(0);
30
+ const rowWidth = useRowWidth();
31
+
32
+ useInput((input, key) => {
33
+ if (key.upArrow) {
34
+ setSelectedIndex((i) => (i > 0 ? i - 1 : items.length - 1));
35
+ } else if (key.downArrow) {
36
+ setSelectedIndex((i) => (i < items.length - 1 ? i + 1 : 0));
37
+ } else if (key.return) {
38
+ const item = items[selectedIndex];
39
+ if (item) onSelect(item);
40
+ } else if (key.escape || input === 'q') {
41
+ onCancel();
42
+ }
43
+ });
44
+
45
+ const scrollOffset = (() => {
46
+ if (items.length <= MAX_VISIBLE) return 0;
47
+ if (selectedIndex < MAX_VISIBLE) return 0;
48
+ return Math.min(selectedIndex - MAX_VISIBLE + 1, items.length - MAX_VISIBLE);
49
+ })();
50
+ const visibleItems = items.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
51
+
52
+ return (
53
+ <Box flexDirection="column" borderStyle="round" borderColor="cyan" paddingX={1}>
54
+ <Text bold color="cyan">
55
+ /{commandName}
56
+ </Text>
57
+ {visibleItems.map((item, i) => {
58
+ const isSelected = scrollOffset + i === selectedIndex;
59
+ const indicator = isSelected ? '▸ ' : ' ';
60
+ return (
61
+ <Box key={item.value} width={rowWidth}>
62
+ <Text
63
+ color={isSelected ? 'cyan' : undefined}
64
+ dimColor={!isSelected}
65
+ wrap="truncate-end"
66
+ >
67
+ {indicator}
68
+ {item.label}
69
+ {item.description != null ? ` ${item.description}` : ''}
70
+ </Text>
71
+ </Box>
72
+ );
73
+ })}
74
+ <Text dimColor>↑↓ navigate · Enter select · Esc cancel</Text>
75
+ </Box>
76
+ );
77
+ }
@@ -1,8 +1,9 @@
1
1
  import { marked } from 'marked';
2
- import type { Renderer } from 'marked';
3
2
  // @ts-expect-error — marked-terminal has no type declarations
4
3
  import TerminalRenderer from 'marked-terminal';
5
4
 
5
+ import type { Renderer } from 'marked';
6
+
6
7
  const ANSI_LIGHT_RED = '\u001b[38;5;210m';
7
8
  const ANSI_LIGHT_GREEN = '\u001b[38;5;120m';
8
9
  const ANSI_CYAN = '\u001b[36m';
@@ -2,27 +2,19 @@
2
2
  * Ink render entry point.
3
3
  */
4
4
 
5
- import React from 'react';
6
5
  import { render } from 'ink';
6
+ import React from 'react';
7
+
7
8
  import App from './App.js';
8
- import type { IAIProvider } from '@robota-sdk/agent-core';
9
- import type { TPermissionMode } from '@robota-sdk/agent-core';
10
- import type {
11
- IBackgroundTaskRunner,
12
- ICommandHostAdapters,
13
- ICommandModule,
14
- IInteractiveSession,
15
- IInteractiveSessionStore,
16
- TSubagentRunnerFactory,
17
- TShellExecFn,
18
- CommandRegistry,
19
- } from '@robota-sdk/agent-framework';
20
- import type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';
9
+
21
10
  import type { ITuiCliAdapter } from './tui-cli-adapter.js';
11
+ import type { TPermissionMode } from '@robota-sdk/agent-core';
12
+ import type { IAgentRuntime } from '@robota-sdk/agent-framework';
13
+ import type { TShellExecFn } from '@robota-sdk/agent-framework';
14
+
15
+ export interface ITuiRenderOptions {
16
+ runtime: IAgentRuntime;
22
17
 
23
- export interface IRenderOptions {
24
- cwd: string;
25
- provider: IAIProvider;
26
18
  providerOverride?: string | undefined;
27
19
  providerType?: string | undefined;
28
20
  modelId?: string;
@@ -30,24 +22,17 @@ export interface IRenderOptions {
30
22
  permissionMode?: TPermissionMode;
31
23
  maxTurns?: number;
32
24
  version?: string;
33
- sessionStore?: IInteractiveSessionStore;
34
25
  resumeSessionId?: string;
35
26
  showSessionPickerOnStart?: boolean;
36
27
  forkSession?: boolean;
37
28
  sessionName?: string;
38
- backgroundTaskRunners?: IBackgroundTaskRunner[];
39
- subagentRunnerFactory?: TSubagentRunnerFactory;
40
- commandModules?: readonly ICommandModule[];
41
- commandHostAdapters?: ICommandHostAdapters;
42
29
  shellExec?: TShellExecFn;
43
30
  startupUpdateNotice?: Promise<string | undefined>;
44
- transportRegistry?: ITransportRegistryView<IInteractiveSession>;
45
31
  cliAdapter: ITuiCliAdapter;
46
- reloadPluginCommandSource?: (registry: CommandRegistry) => void;
47
32
  agentName?: string;
48
33
  }
49
34
 
50
- export async function renderApp(options: IRenderOptions): Promise<void> {
35
+ export async function renderApp(options: ITuiRenderOptions): Promise<void> {
51
36
  process.on('unhandledRejection', (reason) => {
52
37
  process.stderr.write(`\n[UNHANDLED REJECTION] ${reason}\n`);
53
38
  if (reason instanceof Error) {
@@ -55,6 +40,7 @@ export async function renderApp(options: IRenderOptions): Promise<void> {
55
40
  }
56
41
  });
57
42
 
58
- const instance = render(<App {...options} />, { exitOnCtrlC: false });
43
+ const { runtime, ...tuiOptions } = options;
44
+ const instance = render(<App {...runtime} {...tuiOptions} />, { exitOnCtrlC: false });
59
45
  await instance.waitUntilExit();
60
46
  }
@@ -1,4 +1,5 @@
1
1
  import { createContext, useContext } from 'react';
2
+
2
3
  import type { ITuiCliAdapter } from './tui-cli-adapter.js';
3
4
 
4
5
  const TuiCliAdapterContext = createContext<ITuiCliAdapter | null>(null);
@@ -1,9 +1,9 @@
1
+ import type { TUniversalValue } from '@robota-sdk/agent-core';
1
2
  import type {
2
3
  TStatusLineCommandSettingsPatch,
3
4
  IStatusLineCommandSettings,
4
5
  CommandRegistry,
5
6
  } from '@robota-sdk/agent-framework';
6
- import type { TUniversalValue } from '@robota-sdk/agent-core';
7
7
 
8
8
  export interface ITuiCliAdapter {
9
9
  getUserSettingsPath(): string;
@@ -1,16 +1,17 @@
1
+ import { renderApp, type ITuiRenderOptions } from './render.js';
2
+
3
+ import type { TUniversalValue } from '@robota-sdk/agent-core';
1
4
  import type { IInteractiveSession } from '@robota-sdk/agent-framework';
2
5
  import type { IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
3
- import type { TUniversalValue } from '@robota-sdk/agent-core';
4
- import { renderApp, type IRenderOptions } from './render.js';
5
6
 
6
7
  export class TuiTransport implements IConfigurableTransport<IInteractiveSession> {
7
8
  readonly name = 'tui';
8
9
  readonly defaultEnabled = true;
9
10
  readonly optionsSchema = {};
10
11
 
11
- private readonly options: IRenderOptions;
12
+ private readonly options: ITuiRenderOptions;
12
13
 
13
- constructor(options: IRenderOptions) {
14
+ constructor(options: ITuiRenderOptions) {
14
15
  this.options = options;
15
16
  }
16
17
 
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { readFileSync } from 'node:fs';
7
+
7
8
  import type { TUniversalValue } from '@robota-sdk/agent-core';
8
9
 
9
10
  export interface IDiffLine {
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { extractEditDiff } from './edit-diff.js';
7
+
7
8
  import type { IDiffLine } from './edit-diff.js';
8
9
  import type { TUniversalValue } from '@robota-sdk/agent-core';
9
10
 
@@ -1,5 +1,5 @@
1
- import type { IInteractiveSession } from '@robota-sdk/agent-framework';
2
1
  import type { TBackgroundControlAction, TClientMessage, TServerMessage } from './ws-protocol.js';
2
+ import type { IInteractiveSession } from '@robota-sdk/agent-framework';
3
3
 
4
4
  export function handleBackgroundQueryMessage(
5
5
  session: IInteractiveSession,
@@ -8,6 +8,12 @@
8
8
  * Server pushes IInteractiveSession events to client in real-time.
9
9
  */
10
10
 
11
+ import {
12
+ handleBackgroundControlMessage,
13
+ handleBackgroundQueryMessage,
14
+ } from './ws-background-messages.js';
15
+
16
+ import type { TClientMessage, TServerMessage } from './ws-protocol.js';
11
17
  import type {
12
18
  IInteractiveSession,
13
19
  IExecutionResult,
@@ -16,11 +22,6 @@ import type {
16
22
  TBackgroundTaskEvent,
17
23
  IToolState,
18
24
  } from '@robota-sdk/agent-framework';
19
- import type { TClientMessage, TServerMessage } from './ws-protocol.js';
20
- import {
21
- handleBackgroundControlMessage,
22
- handleBackgroundQueryMessage,
23
- } from './ws-background-messages.js';
24
25
 
25
26
  export interface IWsHandlerOptions {
26
27
  /** IInteractiveSession to expose. */
@@ -4,12 +4,15 @@
4
4
  */
5
5
 
6
6
  import { createServer, type Server } from 'node:http';
7
+
7
8
  import { WebSocketServer, WebSocket } from 'ws';
8
- import type { IInteractiveSession } from '@robota-sdk/agent-framework';
9
- import type { TUniversalValue } from '@robota-sdk/agent-core';
10
- import type { IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
9
+
11
10
  import { createWsHandler } from './ws-handler.js';
11
+
12
12
  import type { TServerMessage } from './ws-protocol.js';
13
+ import type { TUniversalValue } from '@robota-sdk/agent-core';
14
+ import type { IInteractiveSession } from '@robota-sdk/agent-framework';
15
+ import type { IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
13
16
 
14
17
  const DEFAULT_PORT = 7070;
15
18
  const DEFAULT_MAX_RETRIES = 20;
@@ -5,10 +5,11 @@
5
5
  * After start(), the consumer must wire onMessage to their WebSocket.
6
6
  */
7
7
 
8
- import type { IInteractiveSession } from '@robota-sdk/agent-framework';
9
- import type { ITransportAdapter } from '@robota-sdk/agent-interface-transport';
10
8
  import { createWsHandler } from './ws-handler.js';
9
+
11
10
  import type { TServerMessage } from './ws-protocol.js';
11
+ import type { IInteractiveSession } from '@robota-sdk/agent-framework';
12
+ import type { ITransportAdapter } from '@robota-sdk/agent-interface-transport';
12
13
 
13
14
  export interface IWsTransportOptions {
14
15
  /** Send a JSON message to the connected WebSocket client. */