@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.
- 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-CT2ibQnr.cjs} +4 -3
- package/dist/node/{headless-C6tj35h3.js → headless-mRYilLfC.js} +4 -3
- package/dist/node/headless-mRYilLfC.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-BNccqSpv.d.ts} +13 -3
- package/dist/node/index-BNccqSpv.d.ts.map +1 -0
- package/dist/node/{index-TMAlNHuM.d.ts → index-BUhHIf7X.d.ts} +13 -3
- package/dist/node/index-BUhHIf7X.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-27HV5PJB.d.ts → index-CYl7ksS6.d.ts} +18 -3
- package/dist/node/index-CYl7ksS6.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-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-BRchlFBE.d.ts → index-E8Gx4-lc.d.ts} +18 -3
- package/dist/node/index-E8Gx4-lc.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-DIdvTeiT.js → tui-CcH5EsQh.js} +4 -4
- package/dist/node/tui-CcH5EsQh.js.map +1 -0
- package/dist/node/tui-DznRbcku.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 +30 -2
- 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 +25 -11
- package/src/tui/BackgroundTaskPanel.tsx +1 -1
- package/src/tui/ExecutionWorkspaceDetailPane.tsx +1 -1
- package/src/tui/ExecutionWorkspaceSwitcher.tsx +1 -1
- package/src/tui/InputArea.tsx +2 -1
- 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 +4 -1
- package/src/tui/SlashAutocomplete.tsx +1 -1
- package/src/tui/StatusBar.tsx +27 -0
- package/src/tui/StreamingIndicator.tsx +1 -1
- package/src/tui/TransportTUI.tsx +1 -1
- package/src/tui/TuiInteractionChannel.ts +72 -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 +25 -5
- 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 +50 -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-D30s8S5f.cjs +0 -24
- package/dist/node/tui-DIdvTeiT.js.map +0 -1
- 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
|
@@ -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;
|
|
@@ -20,6 +20,7 @@ interface IProps {
|
|
|
20
20
|
sessionName?: string;
|
|
21
21
|
settings: IStatusLineCommandSettings;
|
|
22
22
|
activeAgentLabel?: string;
|
|
23
|
+
activePresetId?: string;
|
|
23
24
|
gitRefreshToken?: number;
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -37,6 +38,7 @@ export default function SessionStatusBar({
|
|
|
37
38
|
sessionName,
|
|
38
39
|
settings,
|
|
39
40
|
activeAgentLabel,
|
|
41
|
+
activePresetId,
|
|
40
42
|
gitRefreshToken,
|
|
41
43
|
}: IProps): React.ReactElement | null {
|
|
42
44
|
const cliAdapter = useTuiCliAdapter();
|
|
@@ -65,6 +67,7 @@ export default function SessionStatusBar({
|
|
|
65
67
|
gitBranch={gitBranch}
|
|
66
68
|
showGitBranch={settings.gitBranch}
|
|
67
69
|
activeAgentLabel={activeAgentLabel}
|
|
70
|
+
activePresetId={activePresetId}
|
|
68
71
|
/>
|
|
69
72
|
);
|
|
70
73
|
}
|
|
@@ -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
|
@@ -26,6 +26,7 @@ interface IProps {
|
|
|
26
26
|
gitBranch?: string;
|
|
27
27
|
showGitBranch?: boolean;
|
|
28
28
|
activeAgentLabel?: string;
|
|
29
|
+
activePresetId?: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
interface IStatusLeftProps {
|
|
@@ -42,6 +43,7 @@ interface IStatusLeftProps {
|
|
|
42
43
|
sessionName?: string;
|
|
43
44
|
gitBranch?: string;
|
|
44
45
|
showGitBranch: boolean;
|
|
46
|
+
activePresetId?: string;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
/** Return the color for the context percentage indicator */
|
|
@@ -106,6 +108,21 @@ function shouldShowPermissionMode(permissionMode: TPermissionMode): boolean {
|
|
|
106
108
|
return permissionMode !== 'default';
|
|
107
109
|
}
|
|
108
110
|
|
|
111
|
+
function PresetText({ activePresetId }: { activePresetId: string }): React.ReactElement {
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<Text color="cyan" bold>
|
|
115
|
+
Preset:
|
|
116
|
+
</Text>{' '}
|
|
117
|
+
<Text>{activePresetId}</Text>
|
|
118
|
+
</>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function shouldShowActivePreset(activePresetId: string | undefined): activePresetId is string {
|
|
123
|
+
return activePresetId !== undefined && activePresetId !== 'default';
|
|
124
|
+
}
|
|
125
|
+
|
|
109
126
|
function ProviderText({
|
|
110
127
|
modelName,
|
|
111
128
|
providerDisplayName,
|
|
@@ -127,6 +144,8 @@ function StatusLeft(props: IStatusLeftProps): React.ReactElement {
|
|
|
127
144
|
const shouldShowGitBranch =
|
|
128
145
|
props.showGitBranch && props.gitBranch !== undefined && props.gitBranch.length > 0;
|
|
129
146
|
const showPermissionMode = shouldShowPermissionMode(props.permissionMode);
|
|
147
|
+
const activePresetId = props.activePresetId;
|
|
148
|
+
const showActivePreset = shouldShowActivePreset(activePresetId);
|
|
130
149
|
return (
|
|
131
150
|
<Text>
|
|
132
151
|
<StatusActivityText
|
|
@@ -141,6 +160,12 @@ function StatusLeft(props: IStatusLeftProps): React.ReactElement {
|
|
|
141
160
|
<ModeText permissionMode={props.permissionMode} />
|
|
142
161
|
</>
|
|
143
162
|
)}
|
|
163
|
+
{showActivePreset && (
|
|
164
|
+
<>
|
|
165
|
+
{' | '}
|
|
166
|
+
<PresetText activePresetId={activePresetId} />
|
|
167
|
+
</>
|
|
168
|
+
)}
|
|
144
169
|
{props.sessionName && (
|
|
145
170
|
<>
|
|
146
171
|
{' | '}
|
|
@@ -181,6 +206,7 @@ export default function StatusBar({
|
|
|
181
206
|
gitBranch,
|
|
182
207
|
showGitBranch = true,
|
|
183
208
|
activeAgentLabel,
|
|
209
|
+
activePresetId,
|
|
184
210
|
}: IProps): React.ReactElement {
|
|
185
211
|
return (
|
|
186
212
|
<Box paddingLeft={1} paddingRight={1} justifyContent="space-between">
|
|
@@ -198,6 +224,7 @@ export default function StatusBar({
|
|
|
198
224
|
sessionName={sessionName}
|
|
199
225
|
gitBranch={gitBranch}
|
|
200
226
|
showGitBranch={showGitBranch}
|
|
227
|
+
activePresetId={activePresetId}
|
|
201
228
|
/>
|
|
202
229
|
{activeAgentLabel !== undefined && (
|
|
203
230
|
<Text color="yellow" bold>
|
|
@@ -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;
|
|
@@ -63,10 +66,18 @@ export interface ITuiInteractionChannelOptions {
|
|
|
63
66
|
language?: string;
|
|
64
67
|
reloadPluginCommandSource?: (registry: CommandRegistry) => void;
|
|
65
68
|
agentName?: string;
|
|
69
|
+
/** Active preset id selected at startup (PRESET-011 runtime state). Defaults to 'default'. */
|
|
70
|
+
activePresetId?: string;
|
|
71
|
+
/** Preset persona block composed as a `source: 'persona'` system-prompt section (priority 5). */
|
|
72
|
+
persona?: string;
|
|
66
73
|
systemPrompt?: string;
|
|
67
74
|
appendSystemPrompt?: string;
|
|
68
75
|
allowedTools?: string[];
|
|
69
76
|
deniedTools?: string[];
|
|
77
|
+
/** Preset execution capability: activate agent runtime + subagent/background dispatch. */
|
|
78
|
+
enableParallelSubagents?: boolean;
|
|
79
|
+
/** Preset execution capability: run a post-task self-verification step. */
|
|
80
|
+
selfVerification?: boolean;
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
export class TuiInteractionChannel implements IInteractionChannel {
|
|
@@ -92,7 +103,7 @@ export class TuiInteractionChannel implements IInteractionChannel {
|
|
|
92
103
|
|
|
93
104
|
private autoNameTriggered = false;
|
|
94
105
|
private sessionStarted = false;
|
|
95
|
-
private
|
|
106
|
+
private initPoller: ISessionInitPoller | null = null;
|
|
96
107
|
private permissionQueue: Array<{
|
|
97
108
|
toolName: string;
|
|
98
109
|
toolArgs: TToolArgs;
|
|
@@ -133,10 +144,14 @@ export class TuiInteractionChannel implements IInteractionChannel {
|
|
|
133
144
|
shellExec: opts.shellExec,
|
|
134
145
|
language: opts.language,
|
|
135
146
|
agentName: opts.agentName,
|
|
147
|
+
activePresetId: opts.activePresetId,
|
|
148
|
+
persona: opts.persona,
|
|
136
149
|
systemPrompt: opts.systemPrompt,
|
|
137
150
|
appendSystemPrompt: opts.appendSystemPrompt,
|
|
138
151
|
allowedTools: opts.allowedTools,
|
|
139
152
|
deniedTools: opts.deniedTools,
|
|
153
|
+
enableParallelSubagents: opts.enableParallelSubagents,
|
|
154
|
+
selfVerification: opts.selfVerification,
|
|
140
155
|
});
|
|
141
156
|
}
|
|
142
157
|
|
|
@@ -369,6 +384,9 @@ export class TuiInteractionChannel implements IInteractionChannel {
|
|
|
369
384
|
const onSkillActivation = (): void => {
|
|
370
385
|
manager.syncHistory(session.getFullHistory());
|
|
371
386
|
};
|
|
387
|
+
const onMemoryEvent = (): void => {
|
|
388
|
+
manager.syncHistory(session.getFullHistory());
|
|
389
|
+
};
|
|
372
390
|
const onExecutionWorkspaceEvent = (event: IExecutionWorkspaceEvent): void => {
|
|
373
391
|
manager.syncExecutionWorkspaceSnapshot(event.snapshot);
|
|
374
392
|
};
|
|
@@ -384,6 +402,7 @@ export class TuiInteractionChannel implements IInteractionChannel {
|
|
|
384
402
|
session.on('context_update', manager.onContextUpdate);
|
|
385
403
|
session.on('compact', onCompact);
|
|
386
404
|
session.on('skill_activation', onSkillActivation);
|
|
405
|
+
session.on('memory_event', onMemoryEvent);
|
|
387
406
|
session.on('execution_workspace_event', onExecutionWorkspaceEvent);
|
|
388
407
|
}
|
|
389
408
|
|
|
@@ -413,36 +432,51 @@ export class TuiInteractionChannel implements IInteractionChannel {
|
|
|
413
432
|
}
|
|
414
433
|
|
|
415
434
|
private startInitCheck(): void {
|
|
416
|
-
this.
|
|
417
|
-
this.runInitCheck()
|
|
418
|
-
|
|
435
|
+
this.initPoller = createSessionInitPoller({
|
|
436
|
+
check: () => this.runInitCheck(),
|
|
437
|
+
intervalMs: SESSION_INIT_POLL_MS,
|
|
438
|
+
timeoutMs: SESSION_INIT_TIMEOUT_MS,
|
|
439
|
+
onReady: () => undefined,
|
|
440
|
+
onFailure: (failure) => this.onInitFailure(failure),
|
|
441
|
+
});
|
|
442
|
+
this.initPoller.start();
|
|
419
443
|
}
|
|
420
444
|
|
|
445
|
+
/** Throws while the session is not ready; the init poller classifies the error. */
|
|
421
446
|
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 */
|
|
447
|
+
const ctx = this.interactiveSession.getContextState();
|
|
448
|
+
this.stateManager.setContextState({
|
|
449
|
+
percentage: ctx.usedPercentage,
|
|
450
|
+
usedTokens: ctx.usedTokens,
|
|
451
|
+
maxTokens: ctx.maxTokens,
|
|
452
|
+
});
|
|
453
|
+
const restored = this.interactiveSession.getFullHistory();
|
|
454
|
+
if (restored.length > 0) {
|
|
455
|
+
this.stateManager.syncHistory(restored);
|
|
438
456
|
}
|
|
457
|
+
this.syncExecutionWorkspace();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private onInitFailure(failure: TSessionInitFailure): void {
|
|
461
|
+
const message =
|
|
462
|
+
failure.kind === 'timeout'
|
|
463
|
+
? `Session initialization timed out after ${SESSION_INIT_TIMEOUT_MS / 1000}s${
|
|
464
|
+
failure.lastError ? ` (last error: ${failure.lastError.message})` : ''
|
|
465
|
+
}`
|
|
466
|
+
: `Session initialization failed: ${failure.error.message}`;
|
|
467
|
+
this.stateManager.onError();
|
|
468
|
+
this.stateManager.addEntry({
|
|
469
|
+
id: `session-init-error-${Date.now()}`,
|
|
470
|
+
timestamp: new Date(),
|
|
471
|
+
category: 'event',
|
|
472
|
+
type: 'session-init-error',
|
|
473
|
+
data: { message },
|
|
474
|
+
});
|
|
439
475
|
}
|
|
440
476
|
|
|
441
477
|
private stopInitCheck(): void {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
this.initCheckInterval = null;
|
|
445
|
-
}
|
|
478
|
+
this.initPoller?.stop();
|
|
479
|
+
this.initPoller = null;
|
|
446
480
|
}
|
|
447
481
|
|
|
448
482
|
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,
|