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

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 (170) hide show
  1. package/dist/node/headless/index.cjs +1 -1
  2. package/dist/node/headless/index.d.ts +1 -1
  3. package/dist/node/headless/index.js +1 -1
  4. package/dist/node/{headless-DCtHvyVf.cjs → headless-CT2ibQnr.cjs} +4 -3
  5. package/dist/node/{headless-C6tj35h3.js → headless-mRYilLfC.js} +4 -3
  6. package/dist/node/headless-mRYilLfC.js.map +1 -0
  7. package/dist/node/http/index.cjs +1 -1
  8. package/dist/node/http/index.d.ts +1 -1
  9. package/dist/node/http/index.js +1 -1
  10. package/dist/node/{http-Br10Ps8m.js → http-2Jiuflc1.js} +1 -1
  11. package/dist/node/http-2Jiuflc1.js.map +1 -0
  12. package/dist/node/http-CBAvefLw.cjs +1 -0
  13. package/dist/node/{index-BVNhOeeU.d.ts → index-BNccqSpv.d.ts} +13 -3
  14. package/dist/node/index-BNccqSpv.d.ts.map +1 -0
  15. package/dist/node/{index-TMAlNHuM.d.ts → index-BUhHIf7X.d.ts} +13 -3
  16. package/dist/node/index-BUhHIf7X.d.ts.map +1 -0
  17. package/dist/node/{index-C9LWCL4l.d.ts → index-BnAGE-u9.d.ts} +2 -3
  18. package/dist/node/index-BnAGE-u9.d.ts.map +1 -0
  19. package/dist/node/{index-COWvtBa2.d.ts → index-BrQ4gGw0.d.ts} +3 -3
  20. package/dist/node/index-BrQ4gGw0.d.ts.map +1 -0
  21. package/dist/node/{index-27HV5PJB.d.ts → index-CYl7ksS6.d.ts} +18 -3
  22. package/dist/node/index-CYl7ksS6.d.ts.map +1 -0
  23. package/dist/node/{index-X2Zg8FEY.d.ts → index-CoeBF21y.d.ts} +3 -3
  24. package/dist/node/index-CoeBF21y.d.ts.map +1 -0
  25. package/dist/node/{index-BRgV_MPB.d.ts → index-DHt-2VQ-.d.ts} +2 -3
  26. package/dist/node/index-DHt-2VQ-.d.ts.map +1 -0
  27. package/dist/node/{index-nBlMTFkZ.d.ts → index-DMwKN5Le.d.ts} +2 -3
  28. package/dist/node/index-DMwKN5Le.d.ts.map +1 -0
  29. package/dist/node/{index-BRchlFBE.d.ts → index-E8Gx4-lc.d.ts} +18 -3
  30. package/dist/node/index-E8Gx4-lc.d.ts.map +1 -0
  31. package/dist/node/{index-C5KNEBO9.d.ts → index-c0M42fsA.d.ts} +2 -3
  32. package/dist/node/index-c0M42fsA.d.ts.map +1 -0
  33. package/dist/node/index.cjs +1 -1
  34. package/dist/node/index.d.ts +6 -7
  35. package/dist/node/index.d.ts.map +1 -1
  36. package/dist/node/index.js +1 -1
  37. package/dist/node/index.js.map +1 -1
  38. package/dist/node/mcp/index.cjs +1 -1
  39. package/dist/node/mcp/index.d.ts +1 -1
  40. package/dist/node/mcp/index.js +1 -1
  41. package/dist/node/mcp-BOglBJNy.cjs +1 -0
  42. package/dist/node/{mcp-BAujHOMr.js → mcp-D3BBVK7C.js} +1 -1
  43. package/dist/node/mcp-D3BBVK7C.js.map +1 -0
  44. package/dist/node/{chunk-Bmb41Sf3.cjs → rolldown-runtime-CMqjfN_6.cjs} +1 -1
  45. package/dist/node/testing/index.cjs +1 -0
  46. package/dist/node/testing/index.d.ts +21 -0
  47. package/dist/node/testing/index.d.ts.map +1 -0
  48. package/dist/node/testing/index.js +2 -0
  49. package/dist/node/testing/index.js.map +1 -0
  50. package/dist/node/tui/index.cjs +1 -1
  51. package/dist/node/tui/index.d.ts +1 -1
  52. package/dist/node/tui/index.js +1 -1
  53. package/dist/node/{tui-DIdvTeiT.js → tui-CcH5EsQh.js} +4 -4
  54. package/dist/node/tui-CcH5EsQh.js.map +1 -0
  55. package/dist/node/tui-DznRbcku.cjs +24 -0
  56. package/dist/node/ws/index.cjs +1 -1
  57. package/dist/node/ws/index.d.ts +1 -1
  58. package/dist/node/ws/index.js +1 -1
  59. package/dist/node/{ws-BWel8nzl.js → ws-Dc2RUwVs.js} +1 -1
  60. package/dist/node/ws-Dc2RUwVs.js.map +1 -0
  61. package/dist/node/ws-QNMQn5kg.cjs +1 -0
  62. package/package.json +35 -22
  63. package/src/headless/HeadlessInteractionChannel.ts +30 -2
  64. package/src/headless/__tests__/headless-channel-options.test.ts +106 -0
  65. package/src/headless/__tests__/headless-provider-failure.integration.test.ts +143 -0
  66. package/src/headless/__tests__/headless-runner-initialization.test.ts +1 -1
  67. package/src/headless/__tests__/headless-runner.test.ts +24 -3
  68. package/src/headless/__tests__/headless-transport.test.ts +1 -2
  69. package/src/headless/headless-runner.ts +3 -2
  70. package/src/headless/headless-stream-json.ts +5 -5
  71. package/src/headless/headless-transport.ts +1 -2
  72. package/src/http/__tests__/http-transport.test.ts +1 -1
  73. package/src/http/__tests__/routes.test.ts +1 -1
  74. package/src/http/http-transport.ts +1 -2
  75. package/src/http/routes.ts +1 -1
  76. package/src/mcp/__tests__/mcp-server.test.ts +1 -1
  77. package/src/mcp/__tests__/mcp-transport.test.ts +1 -1
  78. package/src/mcp/mcp-server.ts +1 -1
  79. package/src/mcp/mcp-transport.ts +1 -2
  80. package/src/testing/__tests__/scripted-provider.test.ts +73 -0
  81. package/src/testing/index.ts +7 -0
  82. package/src/testing/scripted-provider.ts +73 -0
  83. package/src/transport-registry.ts +1 -1
  84. package/src/tui/App.tsx +25 -11
  85. package/src/tui/BackgroundTaskPanel.tsx +1 -1
  86. package/src/tui/ExecutionWorkspaceDetailPane.tsx +1 -1
  87. package/src/tui/ExecutionWorkspaceSwitcher.tsx +1 -1
  88. package/src/tui/InputArea.tsx +2 -1
  89. package/src/tui/InteractivePrompt.tsx +2 -2
  90. package/src/tui/PluginTUI.tsx +1 -1
  91. package/src/tui/SessionPicker.tsx +1 -1
  92. package/src/tui/SessionStatusBar.tsx +4 -1
  93. package/src/tui/SlashAutocomplete.tsx +1 -1
  94. package/src/tui/StatusBar.tsx +27 -0
  95. package/src/tui/StreamingIndicator.tsx +1 -1
  96. package/src/tui/TransportTUI.tsx +1 -1
  97. package/src/tui/TuiInteractionChannel.ts +72 -38
  98. package/src/tui/UsageSummaryEntry.tsx +1 -1
  99. package/src/tui/__tests__/PluginTUI.test.tsx +1 -1
  100. package/src/tui/__tests__/SlashAutocomplete.test.tsx +1 -1
  101. package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +1 -1
  102. package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +5 -2
  103. package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +1 -1
  104. package/src/tui/__tests__/background-task-panel.test.tsx +1 -1
  105. package/src/tui/__tests__/background-task-row-format.test.ts +1 -1
  106. package/src/tui/__tests__/channel-factory-integration.test.ts +138 -0
  107. package/src/tui/__tests__/execution-workspace-switcher.test.tsx +1 -1
  108. package/src/tui/__tests__/execution-workspace-view-model.test.ts +1 -1
  109. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +1 -1
  110. package/src/tui/__tests__/input-area-flow.test.ts +1 -1
  111. package/src/tui/__tests__/pty/pty-driver.ts +135 -0
  112. package/src/tui/__tests__/pty/tui-pty.ptytest.ts +61 -0
  113. package/src/tui/__tests__/render-channel-options.test.ts +32 -0
  114. package/src/tui/__tests__/session-init-poller.test.ts +102 -0
  115. package/src/tui/__tests__/session-switch-channel.test.tsx +307 -0
  116. package/src/tui/__tests__/slash-routing-effects.test.ts +4 -1
  117. package/src/tui/__tests__/status-activity.test.ts +3 -3
  118. package/src/tui/__tests__/status-bar.test.tsx +25 -5
  119. package/src/tui/__tests__/tui-channel-init-failure.test.ts +57 -0
  120. package/src/tui/__tests__/tui-state-manager.test.ts +1 -1
  121. package/src/tui/background-task-row-format.ts +1 -1
  122. package/src/tui/execution-workspace-view-model.ts +1 -1
  123. package/src/tui/flows/input-area-flow.ts +1 -1
  124. package/src/tui/flows/permission-prompt-flow.ts +1 -1
  125. package/src/tui/flows/session-init-poller.ts +77 -0
  126. package/src/tui/hooks/command-effect-handler.ts +4 -1
  127. package/src/tui/hooks/command-effect-queue.ts +1 -1
  128. package/src/tui/hooks/side-effects-types.ts +2 -2
  129. package/src/tui/hooks/useAutocomplete.ts +3 -2
  130. package/src/tui/hooks/usePluginCallbacks.ts +1 -1
  131. package/src/tui/hooks/usePluginScreenData.ts +1 -1
  132. package/src/tui/hooks/useSideEffects.ts +1 -1
  133. package/src/tui/hooks/useSlashRouting.ts +3 -3
  134. package/src/tui/hooks/useStatusLineSettings.ts +1 -1
  135. package/src/tui/hooks/useTuiChannel.ts +3 -3
  136. package/src/tui/plugin-tui-handlers.ts +1 -1
  137. package/src/tui/render.tsx +50 -25
  138. package/src/tui/status-activity.ts +2 -2
  139. package/src/tui/tui-cli-adapter.ts +3 -3
  140. package/src/tui/tui-state-manager.ts +2 -2
  141. package/src/tui/tui-transport.ts +4 -2
  142. package/src/ws/__tests__/ws-handler.test.ts +6 -4
  143. package/src/ws/__tests__/ws-transport.test.ts +1 -1
  144. package/src/ws/ws-background-messages.ts +1 -1
  145. package/src/ws/ws-handler.ts +4 -4
  146. package/src/ws/ws-protocol.ts +6 -4
  147. package/src/ws/ws-transport-configurable.ts +4 -2
  148. package/src/ws/ws-transport.ts +1 -2
  149. package/dist/node/headless-C6tj35h3.js.map +0 -1
  150. package/dist/node/http-Br10Ps8m.js.map +0 -1
  151. package/dist/node/http-Da6Kw4oy.cjs +0 -1
  152. package/dist/node/index-27HV5PJB.d.ts.map +0 -1
  153. package/dist/node/index-BRchlFBE.d.ts.map +0 -1
  154. package/dist/node/index-BRgV_MPB.d.ts.map +0 -1
  155. package/dist/node/index-BVNhOeeU.d.ts.map +0 -1
  156. package/dist/node/index-C5KNEBO9.d.ts.map +0 -1
  157. package/dist/node/index-C9LWCL4l.d.ts.map +0 -1
  158. package/dist/node/index-COWvtBa2.d.ts.map +0 -1
  159. package/dist/node/index-TMAlNHuM.d.ts.map +0 -1
  160. package/dist/node/index-X2Zg8FEY.d.ts.map +0 -1
  161. package/dist/node/index-nBlMTFkZ.d.ts.map +0 -1
  162. package/dist/node/mcp-BAujHOMr.js.map +0 -1
  163. package/dist/node/mcp-Bl8jUfev.cjs +0 -1
  164. package/dist/node/tui-D30s8S5f.cjs +0 -24
  165. package/dist/node/tui-DIdvTeiT.js.map +0 -1
  166. package/dist/node/ws-BWel8nzl.js.map +0 -1
  167. package/dist/node/ws-tCjj2gPu.cjs +0 -1
  168. package/src/tui/InkTerminal.ts +0 -42
  169. package/src/tui/hooks/use-interactive-session-init.ts +0 -91
  170. package/src/tui/hooks/usePermissionQueue.ts +0 -52
@@ -109,18 +109,19 @@ describe('StatusBar', () => {
109
109
  );
110
110
  const frame = lastFrame()!;
111
111
  expect(frame).not.toContain('Activity:');
112
- expect(frame).toContain('Tools x2');
112
+ expect(frame).toContain('Tools (2)');
113
+ expect(frame).not.toContain('Tools x2');
113
114
  expect(frame).toContain('queued');
114
115
  expect(frame).not.toContain('thinking...');
115
- expect(frame.indexOf('Tools x2')).toBeLessThan(frame.indexOf('test-model'));
116
+ expect(frame.indexOf('Tools (2)')).toBeLessThan(frame.indexOf('test-model'));
116
117
  expect(frame).not.toContain('Thinking...');
117
118
  });
118
119
 
119
120
  it('shows background activity when no foreground execution is active', () => {
120
121
  const { lastFrame } = render(<StatusBar {...baseProps} activeBackgroundTaskCount={3} />);
121
122
  const frame = lastFrame()!;
122
- expect(frame).toContain('Background x3');
123
- expect(frame.indexOf('Background x3')).toBeLessThan(frame.indexOf('test-model'));
123
+ expect(frame).toContain('Background (3)');
124
+ expect(frame.indexOf('Background (3)')).toBeLessThan(frame.indexOf('test-model'));
124
125
  });
125
126
 
126
127
  it('keeps the activity segment compact for narrow terminals', () => {
@@ -137,7 +138,7 @@ describe('StatusBar', () => {
137
138
  const firstLine = frame.split('\n')[0] ?? '';
138
139
  const activityEnd = firstLine.indexOf('test-model');
139
140
  const activitySegment = firstLine.slice(0, activityEnd);
140
- expect(activitySegment).toContain('Tools x12');
141
+ expect(activitySegment).toContain('Tools (12)');
141
142
  expect(activitySegment.length).toBeLessThanOrEqual(40);
142
143
  });
143
144
 
@@ -154,4 +155,23 @@ describe('StatusBar', () => {
154
155
  const frame = lastFrame()!;
155
156
  expect(frame).not.toContain('feat/status-line');
156
157
  });
158
+
159
+ it('TC-03: shows the active preset id when set and non-default', () => {
160
+ const { lastFrame } = render(<StatusBar {...baseProps} activePresetId="autonomous-builder" />);
161
+ const frame = lastFrame()!;
162
+ expect(frame).toContain('Preset:');
163
+ expect(frame).toContain('autonomous-builder');
164
+ });
165
+
166
+ it('TC-03: hides the default active preset', () => {
167
+ const { lastFrame } = render(<StatusBar {...baseProps} activePresetId="default" />);
168
+ const frame = lastFrame()!;
169
+ expect(frame).not.toContain('Preset:');
170
+ });
171
+
172
+ it('TC-03: hides the preset label when no active preset is provided', () => {
173
+ const { lastFrame } = render(<StatusBar {...baseProps} activePresetId={undefined} />);
174
+ const frame = lastFrame()!;
175
+ expect(frame).not.toContain('Preset:');
176
+ });
157
177
  });
@@ -0,0 +1,57 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import type { IAIProvider } from '@robota-sdk/agent-core';
3
+
4
+ vi.mock('@robota-sdk/agent-framework', async (importOriginal) => {
5
+ const mod = await importOriginal<typeof import('@robota-sdk/agent-framework')>();
6
+
7
+ class FakeInteractiveSession {
8
+ on(): void {}
9
+ off(): void {}
10
+ getFullHistory(): unknown[] {
11
+ return [];
12
+ }
13
+ getContextState(): never {
14
+ throw new Error('ENOENT: session store unreadable');
15
+ }
16
+ getName(): string | undefined {
17
+ return undefined;
18
+ }
19
+ getSession(): { getSessionId: () => string } {
20
+ return { getSessionId: () => 'test-session' };
21
+ }
22
+ async shutdown(): Promise<void> {}
23
+ }
24
+
25
+ return { ...mod, InteractiveSession: FakeInteractiveSession };
26
+ });
27
+
28
+ import { TuiInteractionChannel } from '../TuiInteractionChannel.js';
29
+
30
+ describe('TuiInteractionChannel init failure surfacing', () => {
31
+ beforeEach(() => {
32
+ vi.useFakeTimers();
33
+ });
34
+
35
+ afterEach(() => {
36
+ vi.useRealTimers();
37
+ });
38
+
39
+ it('TC-05: a real init error records a session-init-error entry and sets error state', async () => {
40
+ const channel = new TuiInteractionChannel({
41
+ cwd: '/tmp/project',
42
+ provider: {} as IAIProvider,
43
+ });
44
+ await channel.start();
45
+
46
+ vi.advanceTimersByTime(400);
47
+
48
+ const entries = channel.stateManager.history;
49
+ const initError = entries.find((e) => e.type === 'session-init-error');
50
+ expect(initError).toBeDefined();
51
+ const message = (initError?.data as { message?: string } | undefined)?.message ?? '';
52
+ expect(message).toContain('Session initialization failed');
53
+ expect(message).toContain('ENOENT');
54
+
55
+ await channel.stop();
56
+ });
57
+ });
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { describe, it, expect, vi } from 'vitest';
7
7
  import { TuiStateManager } from '../tui-state-manager.js';
8
- import type { IToolState, IExecutionResult } from '@robota-sdk/agent-framework';
8
+ import type { IExecutionResult, IToolState } from '@robota-sdk/agent-interface-transport';
9
9
 
10
10
  function makeResult(overrides?: Partial<IExecutionResult>): IExecutionResult {
11
11
  return {
@@ -1,6 +1,6 @@
1
1
  import { formatExecutionWorkspaceEntryRow } from './execution-workspace-view-model.js';
2
2
 
3
- import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-framework';
3
+ import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-transport';
4
4
 
5
5
  export interface IBackgroundTaskRow {
6
6
  connector: '├' | '└';
@@ -3,7 +3,7 @@ import type {
3
3
  IExecutionWorkspaceEntry,
4
4
  IExecutionWorkspaceSnapshot,
5
5
  TExecutionWorkspaceStatus,
6
- } from '@robota-sdk/agent-framework';
6
+ } from '@robota-sdk/agent-interface-transport';
7
7
 
8
8
  const ACTIVE_STATUSES: readonly TExecutionWorkspaceStatus[] = [
9
9
  'active',
@@ -1,7 +1,7 @@
1
1
  import { isSlashCommand, tokeniseSlashCommand } from '@robota-sdk/agent-framework';
2
2
 
3
3
  import type { IHistoryEntry, TUniversalValue } from '@robota-sdk/agent-core';
4
- import type { ICommand } from '@robota-sdk/agent-framework';
4
+ import type { ICommand } from '@robota-sdk/agent-interface-transport';
5
5
 
6
6
  export interface IAutocompleteInputKey {
7
7
  upArrow?: boolean;
@@ -67,7 +67,7 @@ export function applyPermissionPromptInput(
67
67
  };
68
68
  }
69
69
 
70
- export function getPermissionDecision(index: number): TPermissionPromptDecision {
70
+ function getPermissionDecision(index: number): TPermissionPromptDecision {
71
71
  if (index === 0) return true;
72
72
  if (index === 1) return 'allow-session';
73
73
  if (index === 2) return 'allow-project';
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Bounded polling helper for asynchronous session initialization.
3
+ *
4
+ * Distinguishes benign "not yet initialized" errors (retried until a timeout)
5
+ * from real failures (surfaced immediately), so the TUI can show an error
6
+ * instead of spinning forever.
7
+ */
8
+
9
+ export type TSessionInitFailure =
10
+ | { kind: 'timeout'; lastError: Error | undefined }
11
+ | { kind: 'error'; error: Error };
12
+
13
+ export interface ISessionInitPollerOptions {
14
+ /** Throws while the session is not ready; returns normally when ready. */
15
+ check: () => void;
16
+ intervalMs: number;
17
+ timeoutMs: number;
18
+ /** Returns true when an error means "keep polling". Defaults to /not initialized/i. */
19
+ isBenignError?: (error: Error) => boolean;
20
+ onReady: () => void;
21
+ onFailure: (failure: TSessionInitFailure) => void;
22
+ }
23
+
24
+ export interface ISessionInitPoller {
25
+ start(): void;
26
+ stop(): void;
27
+ }
28
+
29
+ function defaultIsBenignError(error: Error): boolean {
30
+ return /not initialized/i.test(error.message);
31
+ }
32
+
33
+ export function createSessionInitPoller(options: ISessionInitPollerOptions): ISessionInitPoller {
34
+ const isBenign = options.isBenignError ?? defaultIsBenignError;
35
+ let timer: ReturnType<typeof setInterval> | null = null;
36
+ let elapsedMs = 0;
37
+ let lastBenignError: Error | undefined;
38
+
39
+ function stop(): void {
40
+ if (timer !== null) {
41
+ clearInterval(timer);
42
+ timer = null;
43
+ }
44
+ }
45
+
46
+ function tick(): void {
47
+ elapsedMs += options.intervalMs;
48
+ try {
49
+ options.check();
50
+ } catch (raw) {
51
+ // allow-fallback: poller's purpose is to classify and route this error, never to swallow it
52
+ const error = raw instanceof Error ? raw : new Error(String(raw));
53
+ if (!isBenign(error)) {
54
+ stop();
55
+ options.onFailure({ kind: 'error', error });
56
+ return;
57
+ }
58
+ lastBenignError = error;
59
+ if (elapsedMs >= options.timeoutMs) {
60
+ stop();
61
+ options.onFailure({ kind: 'timeout', lastError: lastBenignError });
62
+ }
63
+ return;
64
+ }
65
+ stop();
66
+ options.onReady();
67
+ }
68
+
69
+ return {
70
+ start(): void {
71
+ if (timer !== null) return;
72
+ elapsedMs = 0;
73
+ timer = setInterval(tick, options.intervalMs);
74
+ },
75
+ stop,
76
+ };
77
+ }
@@ -3,7 +3,10 @@ import { isStatusLineCommandSettingsPatch } from '@robota-sdk/agent-framework';
3
3
 
4
4
  import type { ITuiCliAdapter } from '../tui-cli-adapter.js';
5
5
  import type { IHistoryEntry, TSessionEndReason } from '@robota-sdk/agent-core';
6
- import type { TCommandEffect, TStatusLineCommandSettingsPatch } from '@robota-sdk/agent-framework';
6
+ import type {
7
+ TCommandEffect,
8
+ TStatusLineCommandSettingsPatch,
9
+ } from '@robota-sdk/agent-interface-transport';
7
10
 
8
11
  export interface ICommandEffectHandlerDeps {
9
12
  addEntry: (entry: IHistoryEntry) => void;
@@ -1,4 +1,4 @@
1
- import type { ICommandInteraction, TCommandEffect } from '@robota-sdk/agent-framework';
1
+ import type { ICommandInteraction, TCommandEffect } from '@robota-sdk/agent-interface-transport';
2
2
 
3
3
  export type TQueuedCommandState =
4
4
  | {
@@ -2,9 +2,9 @@ import type { ICommandEffectQueue } from './command-effect-queue.js';
2
2
  import type { IHistoryEntry } from '@robota-sdk/agent-core';
3
3
  import type { InteractiveSession } from '@robota-sdk/agent-framework';
4
4
  import type {
5
- TCommandInteractionPrompt as TInteractivePrompt,
6
5
  IStatusLineCommandSettings as TStatusLineSettings,
7
- } from '@robota-sdk/agent-framework';
6
+ TCommandInteractionPrompt as TInteractivePrompt,
7
+ } from '@robota-sdk/agent-interface-transport';
8
8
 
9
9
  export type { TInteractivePrompt, TStatusLineSettings };
10
10
 
@@ -5,10 +5,11 @@
5
5
 
6
6
  import React, { useState, useMemo } from 'react';
7
7
 
8
- import type { CommandRegistry, ICommand } from '@robota-sdk/agent-framework';
8
+ import type { CommandRegistry } from '@robota-sdk/agent-framework';
9
+ import type { ICommand } from '@robota-sdk/agent-interface-transport';
9
10
 
10
11
  /** Parse input to determine autocomplete state */
11
- export function parseSlashInput(value: string): {
12
+ function parseSlashInput(value: string): {
12
13
  isSlash: boolean;
13
14
  parentCommand: string;
14
15
  filter: string;
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { useMemo } from 'react';
10
10
 
11
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
11
+ import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
12
12
 
13
13
  function createNoOpPluginAdapter(): ICommandPluginAdapter {
14
14
  return {
@@ -6,7 +6,7 @@
6
6
  import { useState, useEffect } from 'react';
7
7
 
8
8
  import type { IMenuSelectItem } from '../MenuSelect.js';
9
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
9
+ import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
10
10
 
11
11
  export function usePluginScreenData(
12
12
  screen: string,
@@ -8,7 +8,7 @@ import { useTuiCliAdapter } from '../tui-cli-adapter-context.js';
8
8
  import type { IUseSideEffectsOptions, IUseSideEffectsResult } from './side-effects-types.js';
9
9
  import type { TInteractivePrompt } from './side-effects-types.js';
10
10
  import type { TSessionEndReason } from '@robota-sdk/agent-core';
11
- import type { ICommandInteraction, ICommandResult } from '@robota-sdk/agent-framework';
11
+ import type { ICommandInteraction, ICommandResult } from '@robota-sdk/agent-interface-transport';
12
12
 
13
13
  const EXIT_DELAY_MS = 500;
14
14
 
@@ -8,12 +8,12 @@ import { useCallback } from 'react';
8
8
 
9
9
  import type { TuiStateManager } from '../tui-state-manager.js';
10
10
  import type { ICommandEffectQueue } from './command-effect-queue.js';
11
+ import type { CommandRegistry } from '@robota-sdk/agent-framework';
11
12
  import type {
12
- IInteractiveSession,
13
- CommandRegistry,
14
13
  ICommandResult,
14
+ IInteractiveSession,
15
15
  TCommandEffect,
16
- } from '@robota-sdk/agent-framework';
16
+ } from '@robota-sdk/agent-interface-transport';
17
17
 
18
18
  export function useSlashRouting(
19
19
  interactiveSession: IInteractiveSession,
@@ -4,7 +4,7 @@ import { useState } from 'react';
4
4
  import { useTuiCliAdapter } from '../tui-cli-adapter-context.js';
5
5
 
6
6
  import type { TUniversalValue } from '@robota-sdk/agent-core';
7
- import type { IStatusLineCommandSettings } from '@robota-sdk/agent-framework';
7
+ import type { IStatusLineCommandSettings } from '@robota-sdk/agent-interface-transport';
8
8
 
9
9
  function readStatusLineSettings(
10
10
  settings: Record<string, TUniversalValue>,
@@ -13,10 +13,10 @@ import type { IPermissionRequest } from '../types.js';
13
13
  import type { IHistoryEntry, TSessionEndReason } from '@robota-sdk/agent-core';
14
14
  import type { InteractiveSession, CommandRegistry } from '@robota-sdk/agent-framework';
15
15
  import type {
16
- IToolState,
17
- IExecutionWorkspaceSnapshot,
18
16
  IExecutionDetailPage,
19
- } from '@robota-sdk/agent-framework';
17
+ IExecutionWorkspaceSnapshot,
18
+ IToolState,
19
+ } from '@robota-sdk/agent-interface-transport';
20
20
 
21
21
  export interface IInteractiveSessionState {
22
22
  interactiveSession: InteractiveSession;
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { IMenuSelectItem } from './MenuSelect.js';
7
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
7
+ import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
8
8
 
9
9
  interface IConfirmState {
10
10
  message: string;
@@ -15,13 +15,15 @@ import type {
15
15
  IBackgroundTaskRunner,
16
16
  ICommandHostAdapters,
17
17
  ICommandModule,
18
- IInteractiveSession,
19
- IInteractiveSessionStore,
20
18
  TSubagentRunnerFactory,
21
19
  TShellExecFn,
22
20
  CommandRegistry,
23
21
  } from '@robota-sdk/agent-framework';
24
- import type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';
22
+ import type {
23
+ IInteractiveSession,
24
+ IInteractiveSessionStore,
25
+ ITransportRegistryView,
26
+ } from '@robota-sdk/agent-interface-transport';
25
27
 
26
28
  export interface IRenderOptions {
27
29
  cwd: string;
@@ -32,6 +34,8 @@ export interface IRenderOptions {
32
34
  language?: string;
33
35
  permissionMode?: TPermissionMode;
34
36
  maxTurns?: number;
37
+ allowedTools?: string[];
38
+ deniedTools?: string[];
35
39
  version?: string;
36
40
  sessionStore?: IInteractiveSessionStore;
37
41
  resumeSessionId?: string;
@@ -48,6 +52,46 @@ export interface IRenderOptions {
48
52
  cliAdapter: ITuiCliAdapter;
49
53
  reloadPluginCommandSource?: (registry: CommandRegistry) => void;
50
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
+ };
51
95
  }
52
96
 
53
97
  export async function renderApp(options: IRenderOptions): Promise<void> {
@@ -58,33 +102,14 @@ export async function renderApp(options: IRenderOptions): Promise<void> {
58
102
  }
59
103
  });
60
104
 
105
+ // Single-owner lifecycle (CLI-B12): render.tsx supplies only the factory;
106
+ // App creates, replaces, and stops channels exclusively through React state.
61
107
  const createChannel = (resumeSessionId?: string): TuiInteractionChannel =>
62
- new TuiInteractionChannel({
63
- cwd: options.cwd,
64
- provider: options.provider,
65
- permissionMode: options.permissionMode,
66
- maxTurns: options.maxTurns,
67
- sessionStore: options.sessionStore,
68
- resumeSessionId,
69
- forkSession: options.forkSession,
70
- sessionName: options.sessionName,
71
- backgroundTaskRunners: options.backgroundTaskRunners,
72
- subagentRunnerFactory: options.subagentRunnerFactory,
73
- commandModules: options.commandModules,
74
- commandHostAdapters: options.commandHostAdapters,
75
- shellExec: options.shellExec,
76
- transportRegistry: options.transportRegistry,
77
- language: options.language,
78
- reloadPluginCommandSource: options.reloadPluginCommandSource,
79
- agentName: options.agentName,
80
- });
81
-
82
- const channel = createChannel(options.resumeSessionId);
108
+ new TuiInteractionChannel(toChannelOptions(options, resumeSessionId));
83
109
 
84
110
  const instance = render(
85
111
  <App
86
112
  cwd={options.cwd}
87
- channel={channel}
88
113
  createChannel={createChannel}
89
114
  providerOverride={options.providerOverride}
90
115
  providerType={options.providerType}
@@ -30,7 +30,7 @@ function getPrimaryActivity(
30
30
  if (input.activeToolCount > NO_ACTIVE_ITEMS) {
31
31
  return {
32
32
  kind: 'tools',
33
- label: `Tools x${input.activeToolCount}`,
33
+ label: `Tools (${input.activeToolCount})`,
34
34
  color: 'cyan',
35
35
  };
36
36
  }
@@ -44,7 +44,7 @@ function getPrimaryActivity(
44
44
  if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {
45
45
  return {
46
46
  kind: 'background',
47
- label: `Background x${input.activeBackgroundTaskCount}`,
47
+ label: `Background (${input.activeBackgroundTaskCount})`,
48
48
  color: 'cyan',
49
49
  };
50
50
  }
@@ -1,9 +1,9 @@
1
1
  import type { TUniversalValue } from '@robota-sdk/agent-core';
2
+ import type { CommandRegistry } from '@robota-sdk/agent-framework';
2
3
  import type {
3
- TStatusLineCommandSettingsPatch,
4
4
  IStatusLineCommandSettings,
5
- CommandRegistry,
6
- } from '@robota-sdk/agent-framework';
5
+ TStatusLineCommandSettingsPatch,
6
+ } from '@robota-sdk/agent-interface-transport';
7
7
 
8
8
  export interface ITuiCliAdapter {
9
9
  getUserSettingsPath(): string;
@@ -10,10 +10,10 @@
10
10
 
11
11
  import type { IContextWindowState, IHistoryEntry } from '@robota-sdk/agent-core';
12
12
  import type {
13
- IToolState,
14
13
  IExecutionResult,
15
14
  IExecutionWorkspaceSnapshot,
16
- } from '@robota-sdk/agent-framework';
15
+ IToolState,
16
+ } from '@robota-sdk/agent-interface-transport';
17
17
 
18
18
  /** Max messages kept in rendering state */
19
19
  const MAX_RENDERED_MESSAGES = 100;
@@ -1,8 +1,10 @@
1
1
  import { renderApp, type IRenderOptions } from './render.js';
2
2
 
3
3
  import type { TUniversalValue } from '@robota-sdk/agent-core';
4
- import type { IInteractiveSession } from '@robota-sdk/agent-framework';
5
- import type { IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
4
+ import type {
5
+ IConfigurableTransport,
6
+ IInteractiveSession,
7
+ } from '@robota-sdk/agent-interface-transport';
6
8
 
7
9
  export class TuiTransport implements IConfigurableTransport<IInteractiveSession> {
8
10
  readonly name = 'tui';
@@ -6,15 +6,17 @@ import { describe, it, expect, vi } from 'vitest';
6
6
  import { createWsHandler } from '../ws-handler.js';
7
7
  import type { TServerMessage } from '../ws-protocol.js';
8
8
  import type {
9
- IBackgroundTaskLogPage,
10
- IBackgroundTaskState,
9
+ IBackgroundJobGroupState,
11
10
  IExecutionWorkspaceEvent,
12
11
  IExecutionWorkspaceSnapshot,
13
12
  IInteractiveSession,
14
- IBackgroundJobGroupState,
15
13
  TBackgroundJobGroupEvent,
16
- TBackgroundTaskEvent,
17
14
  TExecutionWorkspaceUpdateCause,
15
+ } from '@robota-sdk/agent-interface-transport';
16
+ import type {
17
+ IBackgroundTaskLogPage,
18
+ IBackgroundTaskState,
19
+ TBackgroundTaskEvent,
18
20
  } from '@robota-sdk/agent-framework';
19
21
 
20
22
  const backgroundTask: IBackgroundTaskState = {
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, vi } from 'vitest';
2
2
  import { createWsTransport } from '../ws-transport.js';
3
- import type { IInteractiveSession } from '@robota-sdk/agent-framework';
3
+ import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
4
4
 
5
5
  function createMockSession(): IInteractiveSession {
6
6
  return {
@@ -1,5 +1,5 @@
1
1
  import type { TBackgroundControlAction, TClientMessage, TServerMessage } from './ws-protocol.js';
2
- import type { IInteractiveSession } from '@robota-sdk/agent-framework';
2
+ import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
3
3
 
4
4
  export function handleBackgroundQueryMessage(
5
5
  session: IInteractiveSession,
@@ -14,14 +14,14 @@ import {
14
14
  } from './ws-background-messages.js';
15
15
 
16
16
  import type { TClientMessage, TServerMessage } from './ws-protocol.js';
17
+ import type { TBackgroundTaskEvent } from '@robota-sdk/agent-framework';
17
18
  import type {
18
- IInteractiveSession,
19
19
  IExecutionResult,
20
20
  IExecutionWorkspaceEvent,
21
- TBackgroundJobGroupEvent,
22
- TBackgroundTaskEvent,
21
+ IInteractiveSession,
23
22
  IToolState,
24
- } from '@robota-sdk/agent-framework';
23
+ TBackgroundJobGroupEvent,
24
+ } from '@robota-sdk/agent-interface-transport';
25
25
 
26
26
  export interface IWsHandlerOptions {
27
27
  /** IInteractiveSession to expose. */
@@ -1,18 +1,20 @@
1
1
  import type {
2
2
  InteractiveSession,
3
- ICommandResult,
4
- IBackgroundJobGroupState,
5
3
  IBackgroundTaskInput,
6
4
  IBackgroundTaskListFilter,
7
5
  IBackgroundTaskLogCursor,
8
6
  IBackgroundTaskLogPage,
9
7
  IBackgroundTaskState,
8
+ TBackgroundTaskEvent,
9
+ } from '@robota-sdk/agent-framework';
10
+ import type {
11
+ IBackgroundJobGroupState,
12
+ ICommandResult,
10
13
  IExecutionResult,
11
14
  IExecutionWorkspaceSnapshot,
12
15
  IToolState,
13
16
  TBackgroundJobGroupEvent,
14
- TBackgroundTaskEvent,
15
- } from '@robota-sdk/agent-framework';
17
+ } from '@robota-sdk/agent-interface-transport';
16
18
 
17
19
  export type TBackgroundControlAction = 'cancel' | 'close' | 'send';
18
20
 
@@ -11,8 +11,10 @@ import { createWsHandler } from './ws-handler.js';
11
11
 
12
12
  import type { TServerMessage } from './ws-protocol.js';
13
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';
14
+ import type {
15
+ IConfigurableTransport,
16
+ IInteractiveSession,
17
+ } from '@robota-sdk/agent-interface-transport';
16
18
 
17
19
  const DEFAULT_PORT = 7070;
18
20
  const DEFAULT_MAX_RETRIES = 20;
@@ -8,8 +8,7 @@
8
8
  import { createWsHandler } from './ws-handler.js';
9
9
 
10
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';
11
+ import type { IInteractiveSession, ITransportAdapter } from '@robota-sdk/agent-interface-transport';
13
12
 
14
13
  export interface IWsTransportOptions {
15
14
  /** Send a JSON message to the connected WebSocket client. */