@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.
- package/dist/node/headless/index.cjs +1 -1
- package/dist/node/headless/index.d.ts +1 -1
- package/dist/node/headless/index.js +1 -1
- package/dist/node/{headless-DCtHvyVf.cjs → headless-BeHAOlIM.cjs} +4 -3
- package/dist/node/{headless-C6tj35h3.js → headless-D02zUEGh.js} +4 -3
- package/dist/node/headless-D02zUEGh.js.map +1 -0
- package/dist/node/http/index.cjs +1 -1
- package/dist/node/http/index.d.ts +1 -1
- package/dist/node/http/index.js +1 -1
- package/dist/node/{http-Br10Ps8m.js → http-2Jiuflc1.js} +1 -1
- package/dist/node/http-2Jiuflc1.js.map +1 -0
- package/dist/node/http-CBAvefLw.cjs +1 -0
- package/dist/node/{index-BVNhOeeU.d.ts → index-BQLN_Lc9.d.ts} +5 -3
- package/dist/node/index-BQLN_Lc9.d.ts.map +1 -0
- package/dist/node/{index-C9LWCL4l.d.ts → index-BnAGE-u9.d.ts} +2 -3
- package/dist/node/index-BnAGE-u9.d.ts.map +1 -0
- package/dist/node/{index-COWvtBa2.d.ts → index-BrQ4gGw0.d.ts} +3 -3
- package/dist/node/index-BrQ4gGw0.d.ts.map +1 -0
- package/dist/node/{index-X2Zg8FEY.d.ts → index-CoeBF21y.d.ts} +3 -3
- package/dist/node/index-CoeBF21y.d.ts.map +1 -0
- package/dist/node/{index-27HV5PJB.d.ts → index-DE3-dHqw.d.ts} +8 -3
- package/dist/node/index-DE3-dHqw.d.ts.map +1 -0
- package/dist/node/{index-BRgV_MPB.d.ts → index-DHt-2VQ-.d.ts} +2 -3
- package/dist/node/index-DHt-2VQ-.d.ts.map +1 -0
- package/dist/node/{index-nBlMTFkZ.d.ts → index-DMwKN5Le.d.ts} +2 -3
- package/dist/node/index-DMwKN5Le.d.ts.map +1 -0
- package/dist/node/{index-TMAlNHuM.d.ts → index-IvYaYY6v.d.ts} +5 -3
- package/dist/node/index-IvYaYY6v.d.ts.map +1 -0
- package/dist/node/{index-BRchlFBE.d.ts → index-WKTgvhlg.d.ts} +8 -3
- package/dist/node/index-WKTgvhlg.d.ts.map +1 -0
- package/dist/node/{index-C5KNEBO9.d.ts → index-c0M42fsA.d.ts} +2 -3
- package/dist/node/index-c0M42fsA.d.ts.map +1 -0
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.ts +6 -7
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/dist/node/mcp/index.cjs +1 -1
- package/dist/node/mcp/index.d.ts +1 -1
- package/dist/node/mcp/index.js +1 -1
- package/dist/node/mcp-BOglBJNy.cjs +1 -0
- package/dist/node/{mcp-BAujHOMr.js → mcp-D3BBVK7C.js} +1 -1
- package/dist/node/mcp-D3BBVK7C.js.map +1 -0
- package/dist/node/{chunk-Bmb41Sf3.cjs → rolldown-runtime-CMqjfN_6.cjs} +1 -1
- package/dist/node/testing/index.cjs +1 -0
- package/dist/node/testing/index.d.ts +21 -0
- package/dist/node/testing/index.d.ts.map +1 -0
- package/dist/node/testing/index.js +2 -0
- package/dist/node/testing/index.js.map +1 -0
- package/dist/node/tui/index.cjs +1 -1
- package/dist/node/tui/index.d.ts +1 -1
- package/dist/node/tui/index.js +1 -1
- package/dist/node/{tui-4hA-SMtS.js → tui-Btb1q88j.js} +5 -5
- package/dist/node/tui-Btb1q88j.js.map +1 -0
- package/dist/node/tui-SbUT7Zlt.cjs +24 -0
- package/dist/node/ws/index.cjs +1 -1
- package/dist/node/ws/index.d.ts +1 -1
- package/dist/node/ws/index.js +1 -1
- package/dist/node/{ws-BWel8nzl.js → ws-Dc2RUwVs.js} +1 -1
- package/dist/node/ws-Dc2RUwVs.js.map +1 -0
- package/dist/node/ws-QNMQn5kg.cjs +1 -0
- package/package.json +35 -22
- package/src/headless/HeadlessInteractionChannel.ts +9 -1
- package/src/headless/__tests__/headless-channel-options.test.ts +106 -0
- package/src/headless/__tests__/headless-provider-failure.integration.test.ts +143 -0
- package/src/headless/__tests__/headless-runner-initialization.test.ts +1 -1
- package/src/headless/__tests__/headless-runner.test.ts +24 -3
- package/src/headless/__tests__/headless-transport.test.ts +1 -2
- package/src/headless/headless-runner.ts +3 -2
- package/src/headless/headless-stream-json.ts +5 -5
- package/src/headless/headless-transport.ts +1 -2
- package/src/http/__tests__/http-transport.test.ts +1 -1
- package/src/http/__tests__/routes.test.ts +1 -1
- package/src/http/http-transport.ts +1 -2
- package/src/http/routes.ts +1 -1
- package/src/mcp/__tests__/mcp-server.test.ts +1 -1
- package/src/mcp/__tests__/mcp-transport.test.ts +1 -1
- package/src/mcp/mcp-server.ts +1 -1
- package/src/mcp/mcp-transport.ts +1 -2
- package/src/testing/__tests__/scripted-provider.test.ts +73 -0
- package/src/testing/index.ts +7 -0
- package/src/testing/scripted-provider.ts +73 -0
- package/src/transport-registry.ts +1 -1
- package/src/tui/App.tsx +38 -29
- package/src/tui/BackgroundTaskPanel.tsx +1 -1
- package/src/tui/CjkTextInput.tsx +4 -8
- package/src/tui/ExecutionWorkspaceDetailPane.tsx +1 -1
- package/src/tui/ExecutionWorkspaceSwitcher.tsx +1 -1
- package/src/tui/InputArea.tsx +15 -7
- package/src/tui/InteractivePrompt.tsx +2 -2
- package/src/tui/PluginTUI.tsx +1 -1
- package/src/tui/SessionPicker.tsx +1 -1
- package/src/tui/SessionStatusBar.tsx +1 -1
- package/src/tui/SlashAutocomplete.tsx +1 -1
- package/src/tui/StatusBar.tsx +1 -7
- package/src/tui/StreamingIndicator.tsx +1 -1
- package/src/tui/TransportTUI.tsx +1 -1
- package/src/tui/TuiInteractionChannel.ts +60 -38
- package/src/tui/UsageSummaryEntry.tsx +1 -1
- package/src/tui/__tests__/PluginTUI.test.tsx +1 -1
- package/src/tui/__tests__/SlashAutocomplete.test.tsx +1 -1
- package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +1 -1
- package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +5 -2
- package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +1 -1
- package/src/tui/__tests__/background-task-panel.test.tsx +1 -1
- package/src/tui/__tests__/background-task-row-format.test.ts +1 -1
- package/src/tui/__tests__/channel-factory-integration.test.ts +138 -0
- package/src/tui/__tests__/execution-workspace-switcher.test.tsx +1 -1
- package/src/tui/__tests__/execution-workspace-view-model.test.ts +1 -1
- package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +1 -1
- package/src/tui/__tests__/input-area-flow.test.ts +1 -1
- package/src/tui/__tests__/pty/pty-driver.ts +135 -0
- package/src/tui/__tests__/pty/tui-pty.ptytest.ts +61 -0
- package/src/tui/__tests__/render-channel-options.test.ts +32 -0
- package/src/tui/__tests__/session-init-poller.test.ts +102 -0
- package/src/tui/__tests__/session-switch-channel.test.tsx +307 -0
- package/src/tui/__tests__/slash-routing-effects.test.ts +4 -1
- package/src/tui/__tests__/status-activity.test.ts +3 -3
- package/src/tui/__tests__/status-bar.test.tsx +7 -6
- package/src/tui/__tests__/tui-channel-init-failure.test.ts +57 -0
- package/src/tui/__tests__/tui-state-manager.test.ts +1 -1
- package/src/tui/background-task-row-format.ts +1 -1
- package/src/tui/execution-workspace-view-model.ts +1 -1
- package/src/tui/flows/input-area-flow.ts +1 -1
- package/src/tui/flows/permission-prompt-flow.ts +1 -1
- package/src/tui/flows/session-init-poller.ts +77 -0
- package/src/tui/hooks/command-effect-handler.ts +4 -1
- package/src/tui/hooks/command-effect-queue.ts +1 -1
- package/src/tui/hooks/side-effects-types.ts +2 -2
- package/src/tui/hooks/useAutocomplete.ts +3 -2
- package/src/tui/hooks/usePluginCallbacks.ts +1 -1
- package/src/tui/hooks/usePluginScreenData.ts +1 -1
- package/src/tui/hooks/useSideEffects.ts +1 -1
- package/src/tui/hooks/useSlashRouting.ts +3 -3
- package/src/tui/hooks/useStatusLineSettings.ts +1 -1
- package/src/tui/hooks/useTuiChannel.ts +3 -3
- package/src/tui/plugin-tui-handlers.ts +1 -1
- package/src/tui/render.tsx +38 -25
- package/src/tui/status-activity.ts +2 -2
- package/src/tui/tui-cli-adapter.ts +3 -3
- package/src/tui/tui-state-manager.ts +2 -2
- package/src/tui/tui-transport.ts +4 -2
- package/src/ws/__tests__/ws-handler.test.ts +6 -4
- package/src/ws/__tests__/ws-transport.test.ts +1 -1
- package/src/ws/ws-background-messages.ts +1 -1
- package/src/ws/ws-handler.ts +4 -4
- package/src/ws/ws-protocol.ts +6 -4
- package/src/ws/ws-transport-configurable.ts +4 -2
- package/src/ws/ws-transport.ts +1 -2
- package/dist/node/headless-C6tj35h3.js.map +0 -1
- package/dist/node/http-Br10Ps8m.js.map +0 -1
- package/dist/node/http-Da6Kw4oy.cjs +0 -1
- package/dist/node/index-27HV5PJB.d.ts.map +0 -1
- package/dist/node/index-BRchlFBE.d.ts.map +0 -1
- package/dist/node/index-BRgV_MPB.d.ts.map +0 -1
- package/dist/node/index-BVNhOeeU.d.ts.map +0 -1
- package/dist/node/index-C5KNEBO9.d.ts.map +0 -1
- package/dist/node/index-C9LWCL4l.d.ts.map +0 -1
- package/dist/node/index-COWvtBa2.d.ts.map +0 -1
- package/dist/node/index-TMAlNHuM.d.ts.map +0 -1
- package/dist/node/index-X2Zg8FEY.d.ts.map +0 -1
- package/dist/node/index-nBlMTFkZ.d.ts.map +0 -1
- package/dist/node/mcp-BAujHOMr.js.map +0 -1
- package/dist/node/mcp-Bl8jUfev.cjs +0 -1
- package/dist/node/tui-4hA-SMtS.js.map +0 -1
- package/dist/node/tui-CcLmEJ1r.cjs +0 -24
- package/dist/node/ws-BWel8nzl.js.map +0 -1
- package/dist/node/ws-tCjj2gPu.cjs +0 -1
- package/src/tui/InkTerminal.ts +0 -42
- package/src/tui/hooks/use-interactive-session-init.ts +0 -91
- package/src/tui/hooks/usePermissionQueue.ts +0 -52
package/src/tui/InputArea.tsx
CHANGED
|
@@ -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
|
|
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
|
|
49
|
+
* Layout constants for InputArea (columns).
|
|
49
50
|
* Used to compute available text width from terminal columns.
|
|
50
51
|
*
|
|
51
|
-
*
|
|
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 =
|
|
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: '
|
|
241
|
+
return { left: '─'.repeat(leftLen), label, right: '─'.repeat(rightPad) };
|
|
241
242
|
}
|
|
242
|
-
return { left: '
|
|
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
|
|
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
|
-
|
|
9
|
+
TCommandInteractionPrompt as TInteractivePrompt,
|
|
10
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
11
11
|
|
|
12
12
|
interface IInteractivePromptProps {
|
|
13
13
|
prompt: TInteractivePrompt;
|
package/src/tui/PluginTUI.tsx
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
4
|
+
import type { ICommand } from '@robota-sdk/agent-interface-transport';
|
|
5
5
|
|
|
6
6
|
interface IProps {
|
|
7
7
|
/** Filtered list of commands to display */
|
package/src/tui/StatusBar.tsx
CHANGED
|
@@ -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-
|
|
12
|
+
import type { IToolState } from '@robota-sdk/agent-interface-transport';
|
|
13
13
|
|
|
14
14
|
function getToolStyle(t: IToolState): {
|
|
15
15
|
color: string;
|
package/src/tui/TransportTUI.tsx
CHANGED
|
@@ -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 {
|
|
43
|
-
|
|
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
|
|
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.
|
|
417
|
-
this.runInitCheck()
|
|
418
|
-
|
|
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
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
443
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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 {
|
|
57
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
16
|
+
import type { ICommand } from '@robota-sdk/agent-interface-transport';
|
|
17
17
|
import {
|
|
18
18
|
createAssistantMessage,
|
|
19
19
|
createSystemMessage,
|