@robota-sdk/agent-transport 3.0.0-beta.72 → 3.0.0-beta.74

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 (171) 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-BeHAOlIM.cjs} +4 -3
  5. package/dist/node/{headless-C6tj35h3.js → headless-D02zUEGh.js} +4 -3
  6. package/dist/node/headless-D02zUEGh.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-BQLN_Lc9.d.ts} +5 -3
  14. package/dist/node/index-BQLN_Lc9.d.ts.map +1 -0
  15. package/dist/node/{index-C9LWCL4l.d.ts → index-BnAGE-u9.d.ts} +2 -3
  16. package/dist/node/index-BnAGE-u9.d.ts.map +1 -0
  17. package/dist/node/{index-COWvtBa2.d.ts → index-BrQ4gGw0.d.ts} +3 -3
  18. package/dist/node/index-BrQ4gGw0.d.ts.map +1 -0
  19. package/dist/node/{index-X2Zg8FEY.d.ts → index-CoeBF21y.d.ts} +3 -3
  20. package/dist/node/index-CoeBF21y.d.ts.map +1 -0
  21. package/dist/node/{index-27HV5PJB.d.ts → index-DE3-dHqw.d.ts} +8 -3
  22. package/dist/node/index-DE3-dHqw.d.ts.map +1 -0
  23. package/dist/node/{index-BRgV_MPB.d.ts → index-DHt-2VQ-.d.ts} +2 -3
  24. package/dist/node/index-DHt-2VQ-.d.ts.map +1 -0
  25. package/dist/node/{index-nBlMTFkZ.d.ts → index-DMwKN5Le.d.ts} +2 -3
  26. package/dist/node/index-DMwKN5Le.d.ts.map +1 -0
  27. package/dist/node/{index-TMAlNHuM.d.ts → index-IvYaYY6v.d.ts} +5 -3
  28. package/dist/node/index-IvYaYY6v.d.ts.map +1 -0
  29. package/dist/node/{index-BRchlFBE.d.ts → index-WKTgvhlg.d.ts} +8 -3
  30. package/dist/node/index-WKTgvhlg.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-4hA-SMtS.js → tui-Btb1q88j.js} +5 -5
  54. package/dist/node/tui-Btb1q88j.js.map +1 -0
  55. package/dist/node/tui-SbUT7Zlt.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 +9 -1
  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 +38 -29
  85. package/src/tui/BackgroundTaskPanel.tsx +1 -1
  86. package/src/tui/CjkTextInput.tsx +4 -8
  87. package/src/tui/ExecutionWorkspaceDetailPane.tsx +1 -1
  88. package/src/tui/ExecutionWorkspaceSwitcher.tsx +1 -1
  89. package/src/tui/InputArea.tsx +15 -7
  90. package/src/tui/InteractivePrompt.tsx +2 -2
  91. package/src/tui/PluginTUI.tsx +1 -1
  92. package/src/tui/SessionPicker.tsx +1 -1
  93. package/src/tui/SessionStatusBar.tsx +1 -1
  94. package/src/tui/SlashAutocomplete.tsx +1 -1
  95. package/src/tui/StatusBar.tsx +1 -7
  96. package/src/tui/StreamingIndicator.tsx +1 -1
  97. package/src/tui/TransportTUI.tsx +1 -1
  98. package/src/tui/TuiInteractionChannel.ts +60 -38
  99. package/src/tui/UsageSummaryEntry.tsx +1 -1
  100. package/src/tui/__tests__/PluginTUI.test.tsx +1 -1
  101. package/src/tui/__tests__/SlashAutocomplete.test.tsx +1 -1
  102. package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +1 -1
  103. package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +5 -2
  104. package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +1 -1
  105. package/src/tui/__tests__/background-task-panel.test.tsx +1 -1
  106. package/src/tui/__tests__/background-task-row-format.test.ts +1 -1
  107. package/src/tui/__tests__/channel-factory-integration.test.ts +138 -0
  108. package/src/tui/__tests__/execution-workspace-switcher.test.tsx +1 -1
  109. package/src/tui/__tests__/execution-workspace-view-model.test.ts +1 -1
  110. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +1 -1
  111. package/src/tui/__tests__/input-area-flow.test.ts +1 -1
  112. package/src/tui/__tests__/pty/pty-driver.ts +135 -0
  113. package/src/tui/__tests__/pty/tui-pty.ptytest.ts +61 -0
  114. package/src/tui/__tests__/render-channel-options.test.ts +32 -0
  115. package/src/tui/__tests__/session-init-poller.test.ts +102 -0
  116. package/src/tui/__tests__/session-switch-channel.test.tsx +307 -0
  117. package/src/tui/__tests__/slash-routing-effects.test.ts +4 -1
  118. package/src/tui/__tests__/status-activity.test.ts +3 -3
  119. package/src/tui/__tests__/status-bar.test.tsx +7 -6
  120. package/src/tui/__tests__/tui-channel-init-failure.test.ts +57 -0
  121. package/src/tui/__tests__/tui-state-manager.test.ts +1 -1
  122. package/src/tui/background-task-row-format.ts +1 -1
  123. package/src/tui/execution-workspace-view-model.ts +1 -1
  124. package/src/tui/flows/input-area-flow.ts +1 -1
  125. package/src/tui/flows/permission-prompt-flow.ts +1 -1
  126. package/src/tui/flows/session-init-poller.ts +77 -0
  127. package/src/tui/hooks/command-effect-handler.ts +4 -1
  128. package/src/tui/hooks/command-effect-queue.ts +1 -1
  129. package/src/tui/hooks/side-effects-types.ts +2 -2
  130. package/src/tui/hooks/useAutocomplete.ts +3 -2
  131. package/src/tui/hooks/usePluginCallbacks.ts +1 -1
  132. package/src/tui/hooks/usePluginScreenData.ts +1 -1
  133. package/src/tui/hooks/useSideEffects.ts +1 -1
  134. package/src/tui/hooks/useSlashRouting.ts +3 -3
  135. package/src/tui/hooks/useStatusLineSettings.ts +1 -1
  136. package/src/tui/hooks/useTuiChannel.ts +3 -3
  137. package/src/tui/plugin-tui-handlers.ts +1 -1
  138. package/src/tui/render.tsx +38 -25
  139. package/src/tui/status-activity.ts +2 -2
  140. package/src/tui/tui-cli-adapter.ts +3 -3
  141. package/src/tui/tui-state-manager.ts +2 -2
  142. package/src/tui/tui-transport.ts +4 -2
  143. package/src/ws/__tests__/ws-handler.test.ts +6 -4
  144. package/src/ws/__tests__/ws-transport.test.ts +1 -1
  145. package/src/ws/ws-background-messages.ts +1 -1
  146. package/src/ws/ws-handler.ts +4 -4
  147. package/src/ws/ws-protocol.ts +6 -4
  148. package/src/ws/ws-transport-configurable.ts +4 -2
  149. package/src/ws/ws-transport.ts +1 -2
  150. package/dist/node/headless-C6tj35h3.js.map +0 -1
  151. package/dist/node/http-Br10Ps8m.js.map +0 -1
  152. package/dist/node/http-Da6Kw4oy.cjs +0 -1
  153. package/dist/node/index-27HV5PJB.d.ts.map +0 -1
  154. package/dist/node/index-BRchlFBE.d.ts.map +0 -1
  155. package/dist/node/index-BRgV_MPB.d.ts.map +0 -1
  156. package/dist/node/index-BVNhOeeU.d.ts.map +0 -1
  157. package/dist/node/index-C5KNEBO9.d.ts.map +0 -1
  158. package/dist/node/index-C9LWCL4l.d.ts.map +0 -1
  159. package/dist/node/index-COWvtBa2.d.ts.map +0 -1
  160. package/dist/node/index-TMAlNHuM.d.ts.map +0 -1
  161. package/dist/node/index-X2Zg8FEY.d.ts.map +0 -1
  162. package/dist/node/index-nBlMTFkZ.d.ts.map +0 -1
  163. package/dist/node/mcp-BAujHOMr.js.map +0 -1
  164. package/dist/node/mcp-Bl8jUfev.cjs +0 -1
  165. package/dist/node/tui-4hA-SMtS.js.map +0 -1
  166. package/dist/node/tui-CcLmEJ1r.cjs +0 -24
  167. package/dist/node/ws-BWel8nzl.js.map +0 -1
  168. package/dist/node/ws-tCjj2gPu.cjs +0 -1
  169. package/src/tui/InkTerminal.ts +0 -42
  170. package/src/tui/hooks/use-interactive-session-init.ts +0 -91
  171. package/src/tui/hooks/usePermissionQueue.ts +0 -52
@@ -14,7 +14,7 @@ import {
14
14
  import type {
15
15
  IExecutionWorkspaceEntry,
16
16
  IExecutionWorkspaceSnapshot,
17
- } from '@robota-sdk/agent-framework';
17
+ } from '@robota-sdk/agent-interface-transport';
18
18
 
19
19
  const MAX_VISIBLE_WORKSPACE_ENTRIES = 8;
20
20
 
@@ -25,7 +25,8 @@ import { expandPasteLabels } from './utils/paste-labels.js';
25
25
  import WaveText from './WaveText.js';
26
26
 
27
27
  import type { IHistoryEntry } from '@robota-sdk/agent-core';
28
- import type { CommandRegistry, ICommand } from '@robota-sdk/agent-framework';
28
+ import type { CommandRegistry } from '@robota-sdk/agent-framework';
29
+ import type { ICommand } from '@robota-sdk/agent-interface-transport';
29
30
 
30
31
  interface IProps {
31
32
  onSubmit: (value: string) => void;
@@ -45,14 +46,14 @@ interface IProps {
45
46
  * Reference: https://github.com/anthropics/claude-code/issues/3045
46
47
  */
47
48
  /**
48
- * Layout constants for InputArea border box (columns).
49
+ * Layout constants for InputArea (columns).
49
50
  * Used to compute available text width from terminal columns.
50
51
  *
51
- * Box borderStyle="single" adds 1 column per side (left + right).
52
+ * Side borders removed only top/bottom horizontal lines remain.
52
53
  * paddingLeft={1} adds 1 column inside the box.
53
54
  * Prompt "> " takes 2 columns.
54
55
  */
55
- const BORDER_HORIZONTAL = 2;
56
+ const BORDER_HORIZONTAL = 0;
56
57
  const PADDING_LEFT = 1;
57
58
  const PROMPT_WIDTH = 2;
58
59
  const INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
@@ -237,9 +238,9 @@ export default function InputArea({
237
238
  const label = ` "${sessionName}" `;
238
239
  const rightPad = 2;
239
240
  const leftLen = Math.max(0, innerWidth - label.length - rightPad);
240
- return { left: '┌' + '─'.repeat(leftLen), label, right: '─'.repeat(rightPad) + '┐' };
241
+ return { left: '─'.repeat(leftLen), label, right: '─'.repeat(rightPad) };
241
242
  }
242
- return { left: '┌' + '─'.repeat(innerWidth), label: '', right: '' };
243
+ return { left: '─'.repeat(innerWidth), label: '', right: '' };
243
244
  })();
244
245
 
245
246
  return (
@@ -261,7 +262,14 @@ export default function InputArea({
261
262
  ) : null}
262
263
  {topBorder.right}
263
264
  </Text>
264
- <Box borderStyle="single" borderTop={false} borderColor={borderColor} paddingLeft={1}>
265
+ <Box
266
+ borderStyle="single"
267
+ borderTop={false}
268
+ borderLeft={false}
269
+ borderRight={false}
270
+ borderColor={borderColor}
271
+ paddingLeft={1}
272
+ >
265
273
  {isAborting ? (
266
274
  <Text color="yellow"> Interrupting...</Text>
267
275
  ) : pendingPrompt ? (
@@ -5,9 +5,9 @@ import ListPicker from './ListPicker.js';
5
5
  import TextPrompt from './TextPrompt.js';
6
6
 
7
7
  import type {
8
- TCommandInteractionPrompt as TInteractivePrompt,
9
8
  ICommandChoicePromptOption as IChoicePromptOption,
10
- } from '@robota-sdk/agent-framework';
9
+ TCommandInteractionPrompt as TInteractivePrompt,
10
+ } from '@robota-sdk/agent-interface-transport';
11
11
 
12
12
  interface IInteractivePromptProps {
13
13
  prompt: TInteractivePrompt;
@@ -20,7 +20,7 @@ import {
20
20
  import TextPrompt from './TextPrompt.js';
21
21
 
22
22
  import type { IMenuSelectItem } from './MenuSelect.js';
23
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
23
+ import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
24
24
 
25
25
  type TScreenId =
26
26
  | 'main'
@@ -8,7 +8,7 @@ import React from 'react';
8
8
 
9
9
  import ListPicker from './ListPicker.js';
10
10
 
11
- import type { IResumableSessionSummary } from '@robota-sdk/agent-framework';
11
+ import type { IResumableSessionSummary } from '@robota-sdk/agent-interface-transport';
12
12
 
13
13
  const SESSION_ID_DISPLAY_LENGTH = 8;
14
14
  const SESSION_PREVIEW_DISPLAY_LENGTH = 60;
@@ -4,7 +4,7 @@ import StatusBar from './StatusBar.js';
4
4
  import { useTuiCliAdapter } from './tui-cli-adapter-context.js';
5
5
 
6
6
  import type { TPermissionMode } 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
  interface IProps {
10
10
  cwd: string;
@@ -1,7 +1,7 @@
1
1
  import { Box, Text, useStdout } from 'ink';
2
2
  import React, { useState, useEffect } from 'react';
3
3
 
4
- import type { ICommand } from '@robota-sdk/agent-framework';
4
+ import type { ICommand } from '@robota-sdk/agent-interface-transport';
5
5
 
6
6
  interface IProps {
7
7
  /** Filtered list of commands to display */
@@ -183,13 +183,7 @@ export default function StatusBar({
183
183
  activeAgentLabel,
184
184
  }: IProps): React.ReactElement {
185
185
  return (
186
- <Box
187
- borderStyle="single"
188
- borderColor="gray"
189
- paddingLeft={1}
190
- paddingRight={1}
191
- justifyContent="space-between"
192
- >
186
+ <Box paddingLeft={1} paddingRight={1} justifyContent="space-between">
193
187
  <StatusLeft
194
188
  permissionMode={permissionMode}
195
189
  modelName={modelName}
@@ -9,7 +9,7 @@ import React from 'react';
9
9
  import { renderMarkdown } from './render-markdown.js';
10
10
  import ToolDiffBlock from './ToolDiffBlock.js';
11
11
 
12
- import type { IToolState } from '@robota-sdk/agent-framework';
12
+ import type { IToolState } from '@robota-sdk/agent-interface-transport';
13
13
 
14
14
  function getToolStyle(t: IToolState): {
15
15
  color: string;
@@ -7,8 +7,8 @@
7
7
  import { Box, Text, useInput } from 'ink';
8
8
  import React, { useState, useCallback } from 'react';
9
9
 
10
- import type { IInteractiveSession } from '@robota-sdk/agent-framework';
11
10
  import type {
11
+ IInteractiveSession,
12
12
  ITransportEntry,
13
13
  ITransportRegistryView,
14
14
  } from '@robota-sdk/agent-interface-transport';
@@ -12,37 +12,40 @@ import {
12
12
  } from '@robota-sdk/agent-core';
13
13
  import { InteractiveSession, CommandRegistry } from '@robota-sdk/agent-framework';
14
14
 
15
+ import { createSessionInitPoller } from './flows/session-init-poller.js';
15
16
  import { CommandEffectQueue, type ICommandEffectQueue } from './hooks/command-effect-queue.js';
16
17
  import { applySystemCommandResult } from './hooks/useSlashRouting.js';
17
18
  import { generateSessionName } from './session-naming.js';
18
19
  import { TuiStateManager } from './tui-state-manager.js';
19
20
 
21
+ import type { ISessionInitPoller, TSessionInitFailure } from './flows/session-init-poller.js';
20
22
  import type { IPermissionRequest } from './types.js';
21
23
  import type { IAIProvider, TPermissionMode, TSessionEndReason } from '@robota-sdk/agent-core';
22
24
  import type { TToolArgs } from '@robota-sdk/agent-core';
23
- import type { IInteractionChannel } from '@robota-sdk/agent-framework';
24
- import type {
25
- InteractionEvent,
26
- IActionRequest,
27
- IActionResponse,
28
- ICommandInfo,
29
- } from '@robota-sdk/agent-framework';
30
25
  import type {
31
26
  IBackgroundTaskRunner,
32
27
  ICommandHostAdapters,
33
28
  ICommandModule,
34
- IInteractiveSession,
35
- IInteractiveSessionStore,
36
29
  TSubagentRunnerFactory,
37
- IExecutionWorkspaceEvent,
38
- IExecutionDetailPage,
39
- IExecutionResult,
40
30
  TShellExecFn,
41
31
  } from '@robota-sdk/agent-framework';
42
- import type { TPermissionResultValue } from '@robota-sdk/agent-framework';
43
- import type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';
32
+ import type {
33
+ IActionRequest,
34
+ IActionResponse,
35
+ ICommandInfo,
36
+ IExecutionDetailPage,
37
+ IExecutionResult,
38
+ IExecutionWorkspaceEvent,
39
+ IInteractionChannel,
40
+ IInteractiveSession,
41
+ IInteractiveSessionStore,
42
+ ITransportRegistryView,
43
+ InteractionEvent,
44
+ TPermissionResultValue,
45
+ } from '@robota-sdk/agent-interface-transport';
44
46
 
45
47
  const SESSION_INIT_POLL_MS = 200;
48
+ const SESSION_INIT_TIMEOUT_MS = 15000;
46
49
 
47
50
  export interface ITuiInteractionChannelOptions {
48
51
  cwd: string;
@@ -92,7 +95,7 @@ export class TuiInteractionChannel implements IInteractionChannel {
92
95
 
93
96
  private autoNameTriggered = false;
94
97
  private sessionStarted = false;
95
- private initCheckInterval: ReturnType<typeof setInterval> | null = null;
98
+ private initPoller: ISessionInitPoller | null = null;
96
99
  private permissionQueue: Array<{
97
100
  toolName: string;
98
101
  toolArgs: TToolArgs;
@@ -369,6 +372,9 @@ export class TuiInteractionChannel implements IInteractionChannel {
369
372
  const onSkillActivation = (): void => {
370
373
  manager.syncHistory(session.getFullHistory());
371
374
  };
375
+ const onMemoryEvent = (): void => {
376
+ manager.syncHistory(session.getFullHistory());
377
+ };
372
378
  const onExecutionWorkspaceEvent = (event: IExecutionWorkspaceEvent): void => {
373
379
  manager.syncExecutionWorkspaceSnapshot(event.snapshot);
374
380
  };
@@ -384,6 +390,7 @@ export class TuiInteractionChannel implements IInteractionChannel {
384
390
  session.on('context_update', manager.onContextUpdate);
385
391
  session.on('compact', onCompact);
386
392
  session.on('skill_activation', onSkillActivation);
393
+ session.on('memory_event', onMemoryEvent);
387
394
  session.on('execution_workspace_event', onExecutionWorkspaceEvent);
388
395
  }
389
396
 
@@ -413,36 +420,51 @@ export class TuiInteractionChannel implements IInteractionChannel {
413
420
  }
414
421
 
415
422
  private startInitCheck(): void {
416
- this.initCheckInterval = setInterval(() => {
417
- this.runInitCheck();
418
- }, SESSION_INIT_POLL_MS);
423
+ this.initPoller = createSessionInitPoller({
424
+ check: () => this.runInitCheck(),
425
+ intervalMs: SESSION_INIT_POLL_MS,
426
+ timeoutMs: SESSION_INIT_TIMEOUT_MS,
427
+ onReady: () => undefined,
428
+ onFailure: (failure) => this.onInitFailure(failure),
429
+ });
430
+ this.initPoller.start();
419
431
  }
420
432
 
433
+ /** Throws while the session is not ready; the init poller classifies the error. */
421
434
  private runInitCheck(): void {
422
- try {
423
- const ctx = this.interactiveSession.getContextState();
424
- this.stateManager.setContextState({
425
- percentage: ctx.usedPercentage,
426
- usedTokens: ctx.usedTokens,
427
- maxTokens: ctx.maxTokens,
428
- });
429
- const restored = this.interactiveSession.getFullHistory();
430
- if (restored.length > 0) {
431
- this.stateManager.syncHistory(restored);
432
- }
433
- this.syncExecutionWorkspace();
434
- this.stopInitCheck();
435
- } catch {
436
- // allow-fallback: session initializes asynchronously; poll until ready
437
- /* Not yet initialized */
435
+ const ctx = this.interactiveSession.getContextState();
436
+ this.stateManager.setContextState({
437
+ percentage: ctx.usedPercentage,
438
+ usedTokens: ctx.usedTokens,
439
+ maxTokens: ctx.maxTokens,
440
+ });
441
+ const restored = this.interactiveSession.getFullHistory();
442
+ if (restored.length > 0) {
443
+ this.stateManager.syncHistory(restored);
438
444
  }
445
+ this.syncExecutionWorkspace();
446
+ }
447
+
448
+ private onInitFailure(failure: TSessionInitFailure): void {
449
+ const message =
450
+ failure.kind === 'timeout'
451
+ ? `Session initialization timed out after ${SESSION_INIT_TIMEOUT_MS / 1000}s${
452
+ failure.lastError ? ` (last error: ${failure.lastError.message})` : ''
453
+ }`
454
+ : `Session initialization failed: ${failure.error.message}`;
455
+ this.stateManager.onError();
456
+ this.stateManager.addEntry({
457
+ id: `session-init-error-${Date.now()}`,
458
+ timestamp: new Date(),
459
+ category: 'event',
460
+ type: 'session-init-error',
461
+ data: { message },
462
+ });
439
463
  }
440
464
 
441
465
  private stopInitCheck(): void {
442
- if (this.initCheckInterval !== null) {
443
- clearInterval(this.initCheckInterval);
444
- this.initCheckInterval = null;
445
- }
466
+ this.initPoller?.stop();
467
+ this.initPoller = null;
446
468
  }
447
469
 
448
470
  private syncExecutionWorkspace(): void {
@@ -3,7 +3,7 @@ import { Box, Text } from 'ink';
3
3
  import React from 'react';
4
4
 
5
5
  import type { IHistoryEntry } from '@robota-sdk/agent-core';
6
- import type { IUsageSnapshot } from '@robota-sdk/agent-framework';
6
+ import type { IUsageSnapshot } from '@robota-sdk/agent-interface-transport';
7
7
 
8
8
  const TOKEN_COMPACT_THRESHOLD = 1000;
9
9
 
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import { render } from 'ink-testing-library';
4
4
  import { describe, it, expect, vi } from 'vitest';
5
5
  import PluginTUI from '../PluginTUI.js';
6
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-framework';
6
+ import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
7
7
 
8
8
  function mockCallbacks(): ICommandPluginAdapter {
9
9
  return {
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { render } from 'ink-testing-library';
3
3
  import { describe, it, expect } from 'vitest';
4
4
  import SlashAutocomplete from '../SlashAutocomplete.js';
5
- import type { ICommand } from '@robota-sdk/agent-framework';
5
+ import type { ICommand } from '@robota-sdk/agent-interface-transport';
6
6
 
7
7
  // ink-testing-library fixes stdout.columns = 100
8
8
  // outer box chrome = 4 → rowWidth = 96 in tests
@@ -68,7 +68,7 @@ import {
68
68
  import { TuiInteractionChannel } from '../TuiInteractionChannel.js';
69
69
 
70
70
  import type { IAIProvider, IHistoryEntry } from '@robota-sdk/agent-core';
71
- import type { IExecutionResult } from '@robota-sdk/agent-framework';
71
+ import type { IExecutionResult } from '@robota-sdk/agent-interface-transport';
72
72
 
73
73
  // ── Helpers ───────────────────────────────────────────────────────────────────
74
74
 
@@ -53,8 +53,11 @@ vi.mock('@robota-sdk/agent-framework', async () => {
53
53
  import { TuiInteractionChannel } from '../TuiInteractionChannel.js';
54
54
 
55
55
  import type { IAIProvider } from '@robota-sdk/agent-core';
56
- import type { IExecutionResult, IInteractiveSession } from '@robota-sdk/agent-framework';
57
- import type { ITransportRegistryView } from '@robota-sdk/agent-interface-transport';
56
+ import type {
57
+ IExecutionResult,
58
+ IInteractiveSession,
59
+ ITransportRegistryView,
60
+ } from '@robota-sdk/agent-interface-transport';
58
61
 
59
62
  // ── Helpers ───────────────────────────────────────────────────────────────────
60
63
 
@@ -29,7 +29,7 @@ vi.mock('@robota-sdk/agent-framework', async () => {
29
29
  import { TuiInteractionChannel } from '../TuiInteractionChannel.js';
30
30
 
31
31
  import type { IAIProvider } from '@robota-sdk/agent-core';
32
- import type { IActionRequest } from '@robota-sdk/agent-framework';
32
+ import type { IActionRequest } from '@robota-sdk/agent-interface-transport';
33
33
 
34
34
  function makeChannel(): TuiInteractionChannel {
35
35
  return new TuiInteractionChannel({
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { describe, expect, it } from 'vitest';
3
3
  import { render } from 'ink-testing-library';
4
- import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-framework';
4
+ import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-transport';
5
5
  import BackgroundTaskPanel from '../BackgroundTaskPanel.js';
6
6
 
7
7
  function makeEntry(overrides: Partial<IExecutionWorkspaceEntry>): IExecutionWorkspaceEntry {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-framework';
2
+ import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-transport';
3
3
  import { formatBackgroundTaskRow } from '../background-task-row-format.js';
4
4
 
5
5
  function makeEntry(overrides: Partial<IExecutionWorkspaceEntry>): IExecutionWorkspaceEntry {
@@ -0,0 +1,138 @@
1
+ /**
2
+ * CLI-B11 TC-02: real-store channel factory integration.
3
+ *
4
+ * The official CI equivalent of real-resume-verify-v3.mjs: build the channel
5
+ * exactly the way render.tsx does (toChannelOptions + TuiInteractionChannel)
6
+ * over a REAL project session store with a persisted conversation, and assert
7
+ * the restored model context is non-empty. No store/session mocks.
8
+ */
9
+
10
+ import { mkdtempSync, rmSync } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import { join } from 'node:path';
13
+
14
+ import { createProjectSessionStore } from '@robota-sdk/agent-framework';
15
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
16
+
17
+ import { createScriptedProvider } from '../../testing/scripted-provider.js';
18
+ import { toChannelOptions, type IRenderOptions } from '../render.js';
19
+ import { TuiInteractionChannel } from '../TuiInteractionChannel.js';
20
+
21
+ import type { ITuiCliAdapter } from '../tui-cli-adapter.js';
22
+ import type { TUniversalMessage } from '@robota-sdk/agent-core';
23
+ import type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-transport';
24
+
25
+ const RESTORE_DEADLINE_MS = 10_000;
26
+ const POLL_MS = 50;
27
+
28
+ function fakeCliAdapter(settingsPath: string): ITuiCliAdapter {
29
+ return {
30
+ getUserSettingsPath: () => settingsPath,
31
+ readSettings: () => ({}),
32
+ writeSettings: vi.fn(),
33
+ deleteSettings: vi.fn().mockReturnValue(false),
34
+ applyStatusLineSettings: vi.fn(),
35
+ reloadPluginCommandSource: vi.fn(),
36
+ applyActiveModelChange: vi.fn().mockReturnValue({ applied: true }),
37
+ getGitBranch: vi.fn().mockReturnValue(undefined),
38
+ getProviderDisplayName: vi.fn((type: string) => type),
39
+ };
40
+ }
41
+
42
+ function persistConversation(store: IInteractiveSessionStore, id: string, cwd: string): void {
43
+ const messages: TUniversalMessage[] = [
44
+ { role: 'user', content: 'Remember the number 42.' } as TUniversalMessage,
45
+ { role: 'assistant', content: 'Noted: 42.' } as TUniversalMessage,
46
+ { role: 'user', content: 'And the city is Busan.' } as TUniversalMessage,
47
+ { role: 'assistant', content: 'Noted: Busan.' } as TUniversalMessage,
48
+ ];
49
+ store.save({
50
+ id,
51
+ cwd,
52
+ createdAt: '2026-06-13T00:00:00.000Z',
53
+ updatedAt: '2026-06-13T00:00:00.000Z',
54
+ messages,
55
+ });
56
+ }
57
+
58
+ describe('channel factory restores persisted context (CLI-B11 TC-02)', () => {
59
+ let cwd: string;
60
+ let channel: TuiInteractionChannel | undefined;
61
+
62
+ beforeEach(() => {
63
+ cwd = mkdtempSync(join(tmpdir(), 'robota-b11-int-'));
64
+ });
65
+
66
+ afterEach(async () => {
67
+ await channel?.stop();
68
+ channel = undefined;
69
+ rmSync(cwd, { recursive: true, force: true });
70
+ });
71
+
72
+ it('createChannel(sessionId) over a real FileSessionStore yields usedTokens > 0', async () => {
73
+ const store = createProjectSessionStore(cwd);
74
+ const sessionId = 'b11-restore-session';
75
+ persistConversation(store, sessionId, cwd);
76
+
77
+ // Exactly the render.tsx factory: toChannelOptions(options, resumeSessionId).
78
+ const scripted = createScriptedProvider([{ text: 'unused in this test' }]);
79
+ const options: IRenderOptions = {
80
+ cwd,
81
+ provider: scripted.provider,
82
+ sessionStore: store,
83
+ cliAdapter: fakeCliAdapter(join(cwd, 'settings.json')),
84
+ };
85
+ channel = new TuiInteractionChannel(toChannelOptions(options, sessionId));
86
+ await channel.start();
87
+
88
+ // Restoration is asynchronous (pendingRestoreMessages inject after init).
89
+ const deadline = Date.now() + RESTORE_DEADLINE_MS;
90
+ let usedTokens = 0;
91
+ while (Date.now() < deadline) {
92
+ try {
93
+ // allow-fallback: session init is asynchronous; poll until it is ready
94
+ usedTokens = channel.getSession().getContextState().usedTokens;
95
+ if (usedTokens > 0) break;
96
+ } catch {
97
+ // allow-fallback: session init is asynchronous; poll until it is ready
98
+ }
99
+ await new Promise((resolve) => setTimeout(resolve, POLL_MS));
100
+ }
101
+
102
+ // The persisted messages were injected into the model context — the exact
103
+ // signal that was 0 in the 2026-05-31 bug. (getFullHistory() is the display
104
+ // log restored from record.history, which this record intentionally omits.)
105
+ expect(usedTokens).toBeGreaterThan(0);
106
+ });
107
+
108
+ it('a channel created WITHOUT resumeSessionId starts with an empty context (control)', async () => {
109
+ const store = createProjectSessionStore(cwd);
110
+ persistConversation(store, 'b11-other-session', cwd);
111
+
112
+ const scripted = createScriptedProvider([{ text: 'unused' }]);
113
+ const options: IRenderOptions = {
114
+ cwd,
115
+ provider: scripted.provider,
116
+ sessionStore: store,
117
+ cliAdapter: fakeCliAdapter(join(cwd, 'settings.json')),
118
+ };
119
+ channel = new TuiInteractionChannel(toChannelOptions(options, undefined));
120
+ await channel.start();
121
+
122
+ const deadline = Date.now() + RESTORE_DEADLINE_MS;
123
+ let ready = false;
124
+ while (Date.now() < deadline && !ready) {
125
+ try {
126
+ // allow-fallback: session init is asynchronous; poll until it is ready
127
+ channel.getSession().getContextState();
128
+ ready = true;
129
+ } catch {
130
+ // allow-fallback: session init is asynchronous; poll until it is ready
131
+ await new Promise((resolve) => setTimeout(resolve, POLL_MS));
132
+ }
133
+ }
134
+
135
+ expect(ready).toBe(true);
136
+ expect(channel.getSession().getFullHistory()).toHaveLength(0);
137
+ });
138
+ });
@@ -4,7 +4,7 @@ import { render } from 'ink-testing-library';
4
4
  import type {
5
5
  IExecutionWorkspaceEntry,
6
6
  IExecutionWorkspaceSnapshot,
7
- } from '@robota-sdk/agent-framework';
7
+ } from '@robota-sdk/agent-interface-transport';
8
8
  import ExecutionWorkspaceSwitcher from '../ExecutionWorkspaceSwitcher.js';
9
9
  import ExecutionWorkspaceDetailPane from '../ExecutionWorkspaceDetailPane.js';
10
10
 
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
2
2
  import type {
3
3
  IExecutionWorkspaceEntry,
4
4
  IExecutionWorkspaceSnapshot,
5
- } from '@robota-sdk/agent-framework';
5
+ } from '@robota-sdk/agent-interface-transport';
6
6
  import {
7
7
  countActiveBackgroundWorkspaceEntries,
8
8
  formatExecutionDetailRecord,
@@ -15,7 +15,7 @@ import React from 'react';
15
15
  import InteractivePrompt from '../../InteractivePrompt.js';
16
16
 
17
17
  import type { IAIProvider, IProviderDefinition } from '@robota-sdk/agent-core';
18
- import type { TCommandInteractionPrompt as TInteractivePrompt } from '@robota-sdk/agent-framework';
18
+ import type { TCommandInteractionPrompt as TInteractivePrompt } from '@robota-sdk/agent-interface-transport';
19
19
 
20
20
  const openaiDefaults = {
21
21
  apiKey: '$ENV:OPENAI_API_KEY',
@@ -13,7 +13,7 @@ import {
13
13
  resolveTabCompletion,
14
14
  shouldSubmitInput,
15
15
  } from '../flows/input-area-flow.js';
16
- import type { ICommand } from '@robota-sdk/agent-framework';
16
+ import type { ICommand } from '@robota-sdk/agent-interface-transport';
17
17
  import {
18
18
  createAssistantMessage,
19
19
  createSystemMessage,