@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
|
@@ -109,18 +109,19 @@ describe('StatusBar', () => {
|
|
|
109
109
|
);
|
|
110
110
|
const frame = lastFrame()!;
|
|
111
111
|
expect(frame).not.toContain('Activity:');
|
|
112
|
-
expect(frame).toContain('Tools
|
|
112
|
+
expect(frame).toContain('Tools (2)');
|
|
113
|
+
expect(frame).not.toContain('Tools x2');
|
|
113
114
|
expect(frame).toContain('queued');
|
|
114
115
|
expect(frame).not.toContain('thinking...');
|
|
115
|
-
expect(frame.indexOf('Tools
|
|
116
|
+
expect(frame.indexOf('Tools (2)')).toBeLessThan(frame.indexOf('test-model'));
|
|
116
117
|
expect(frame).not.toContain('Thinking...');
|
|
117
118
|
});
|
|
118
119
|
|
|
119
120
|
it('shows background activity when no foreground execution is active', () => {
|
|
120
121
|
const { lastFrame } = render(<StatusBar {...baseProps} activeBackgroundTaskCount={3} />);
|
|
121
122
|
const frame = lastFrame()!;
|
|
122
|
-
expect(frame).toContain('Background
|
|
123
|
-
expect(frame.indexOf('Background
|
|
123
|
+
expect(frame).toContain('Background (3)');
|
|
124
|
+
expect(frame.indexOf('Background (3)')).toBeLessThan(frame.indexOf('test-model'));
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
it('keeps the activity segment compact for narrow terminals', () => {
|
|
@@ -137,7 +138,7 @@ describe('StatusBar', () => {
|
|
|
137
138
|
const firstLine = frame.split('\n')[0] ?? '';
|
|
138
139
|
const activityEnd = firstLine.indexOf('test-model');
|
|
139
140
|
const activitySegment = firstLine.slice(0, activityEnd);
|
|
140
|
-
expect(activitySegment).toContain('Tools
|
|
141
|
+
expect(activitySegment).toContain('Tools (12)');
|
|
141
142
|
expect(activitySegment.length).toBeLessThanOrEqual(40);
|
|
142
143
|
});
|
|
143
144
|
|
|
@@ -154,4 +155,23 @@ describe('StatusBar', () => {
|
|
|
154
155
|
const frame = lastFrame()!;
|
|
155
156
|
expect(frame).not.toContain('feat/status-line');
|
|
156
157
|
});
|
|
158
|
+
|
|
159
|
+
it('TC-03: shows the active preset id when set and non-default', () => {
|
|
160
|
+
const { lastFrame } = render(<StatusBar {...baseProps} activePresetId="autonomous-builder" />);
|
|
161
|
+
const frame = lastFrame()!;
|
|
162
|
+
expect(frame).toContain('Preset:');
|
|
163
|
+
expect(frame).toContain('autonomous-builder');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('TC-03: hides the default active preset', () => {
|
|
167
|
+
const { lastFrame } = render(<StatusBar {...baseProps} activePresetId="default" />);
|
|
168
|
+
const frame = lastFrame()!;
|
|
169
|
+
expect(frame).not.toContain('Preset:');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('TC-03: hides the preset label when no active preset is provided', () => {
|
|
173
|
+
const { lastFrame } = render(<StatusBar {...baseProps} activePresetId={undefined} />);
|
|
174
|
+
const frame = lastFrame()!;
|
|
175
|
+
expect(frame).not.toContain('Preset:');
|
|
176
|
+
});
|
|
157
177
|
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { IAIProvider } from '@robota-sdk/agent-core';
|
|
3
|
+
|
|
4
|
+
vi.mock('@robota-sdk/agent-framework', async (importOriginal) => {
|
|
5
|
+
const mod = await importOriginal<typeof import('@robota-sdk/agent-framework')>();
|
|
6
|
+
|
|
7
|
+
class FakeInteractiveSession {
|
|
8
|
+
on(): void {}
|
|
9
|
+
off(): void {}
|
|
10
|
+
getFullHistory(): unknown[] {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
getContextState(): never {
|
|
14
|
+
throw new Error('ENOENT: session store unreadable');
|
|
15
|
+
}
|
|
16
|
+
getName(): string | undefined {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
getSession(): { getSessionId: () => string } {
|
|
20
|
+
return { getSessionId: () => 'test-session' };
|
|
21
|
+
}
|
|
22
|
+
async shutdown(): Promise<void> {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { ...mod, InteractiveSession: FakeInteractiveSession };
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
import { TuiInteractionChannel } from '../TuiInteractionChannel.js';
|
|
29
|
+
|
|
30
|
+
describe('TuiInteractionChannel init failure surfacing', () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
vi.useFakeTimers();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
vi.useRealTimers();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('TC-05: a real init error records a session-init-error entry and sets error state', async () => {
|
|
40
|
+
const channel = new TuiInteractionChannel({
|
|
41
|
+
cwd: '/tmp/project',
|
|
42
|
+
provider: {} as IAIProvider,
|
|
43
|
+
});
|
|
44
|
+
await channel.start();
|
|
45
|
+
|
|
46
|
+
vi.advanceTimersByTime(400);
|
|
47
|
+
|
|
48
|
+
const entries = channel.stateManager.history;
|
|
49
|
+
const initError = entries.find((e) => e.type === 'session-init-error');
|
|
50
|
+
expect(initError).toBeDefined();
|
|
51
|
+
const message = (initError?.data as { message?: string } | undefined)?.message ?? '';
|
|
52
|
+
expect(message).toContain('Session initialization failed');
|
|
53
|
+
expect(message).toContain('ENOENT');
|
|
54
|
+
|
|
55
|
+
await channel.stop();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { describe, it, expect, vi } from 'vitest';
|
|
7
7
|
import { TuiStateManager } from '../tui-state-manager.js';
|
|
8
|
-
import type {
|
|
8
|
+
import type { IExecutionResult, IToolState } from '@robota-sdk/agent-interface-transport';
|
|
9
9
|
|
|
10
10
|
function makeResult(overrides?: Partial<IExecutionResult>): IExecutionResult {
|
|
11
11
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatExecutionWorkspaceEntryRow } from './execution-workspace-view-model.js';
|
|
2
2
|
|
|
3
|
-
import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-
|
|
3
|
+
import type { IExecutionWorkspaceEntry } from '@robota-sdk/agent-interface-transport';
|
|
4
4
|
|
|
5
5
|
export interface IBackgroundTaskRow {
|
|
6
6
|
connector: '├' | '└';
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
IExecutionWorkspaceEntry,
|
|
4
4
|
IExecutionWorkspaceSnapshot,
|
|
5
5
|
TExecutionWorkspaceStatus,
|
|
6
|
-
} from '@robota-sdk/agent-
|
|
6
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
7
7
|
|
|
8
8
|
const ACTIVE_STATUSES: readonly TExecutionWorkspaceStatus[] = [
|
|
9
9
|
'active',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isSlashCommand, tokeniseSlashCommand } from '@robota-sdk/agent-framework';
|
|
2
2
|
|
|
3
3
|
import type { IHistoryEntry, TUniversalValue } from '@robota-sdk/agent-core';
|
|
4
|
-
import type { ICommand } from '@robota-sdk/agent-
|
|
4
|
+
import type { ICommand } from '@robota-sdk/agent-interface-transport';
|
|
5
5
|
|
|
6
6
|
export interface IAutocompleteInputKey {
|
|
7
7
|
upArrow?: boolean;
|
|
@@ -67,7 +67,7 @@ export function applyPermissionPromptInput(
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
function getPermissionDecision(index: number): TPermissionPromptDecision {
|
|
71
71
|
if (index === 0) return true;
|
|
72
72
|
if (index === 1) return 'allow-session';
|
|
73
73
|
if (index === 2) return 'allow-project';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded polling helper for asynchronous session initialization.
|
|
3
|
+
*
|
|
4
|
+
* Distinguishes benign "not yet initialized" errors (retried until a timeout)
|
|
5
|
+
* from real failures (surfaced immediately), so the TUI can show an error
|
|
6
|
+
* instead of spinning forever.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type TSessionInitFailure =
|
|
10
|
+
| { kind: 'timeout'; lastError: Error | undefined }
|
|
11
|
+
| { kind: 'error'; error: Error };
|
|
12
|
+
|
|
13
|
+
export interface ISessionInitPollerOptions {
|
|
14
|
+
/** Throws while the session is not ready; returns normally when ready. */
|
|
15
|
+
check: () => void;
|
|
16
|
+
intervalMs: number;
|
|
17
|
+
timeoutMs: number;
|
|
18
|
+
/** Returns true when an error means "keep polling". Defaults to /not initialized/i. */
|
|
19
|
+
isBenignError?: (error: Error) => boolean;
|
|
20
|
+
onReady: () => void;
|
|
21
|
+
onFailure: (failure: TSessionInitFailure) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ISessionInitPoller {
|
|
25
|
+
start(): void;
|
|
26
|
+
stop(): void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function defaultIsBenignError(error: Error): boolean {
|
|
30
|
+
return /not initialized/i.test(error.message);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createSessionInitPoller(options: ISessionInitPollerOptions): ISessionInitPoller {
|
|
34
|
+
const isBenign = options.isBenignError ?? defaultIsBenignError;
|
|
35
|
+
let timer: ReturnType<typeof setInterval> | null = null;
|
|
36
|
+
let elapsedMs = 0;
|
|
37
|
+
let lastBenignError: Error | undefined;
|
|
38
|
+
|
|
39
|
+
function stop(): void {
|
|
40
|
+
if (timer !== null) {
|
|
41
|
+
clearInterval(timer);
|
|
42
|
+
timer = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function tick(): void {
|
|
47
|
+
elapsedMs += options.intervalMs;
|
|
48
|
+
try {
|
|
49
|
+
options.check();
|
|
50
|
+
} catch (raw) {
|
|
51
|
+
// allow-fallback: poller's purpose is to classify and route this error, never to swallow it
|
|
52
|
+
const error = raw instanceof Error ? raw : new Error(String(raw));
|
|
53
|
+
if (!isBenign(error)) {
|
|
54
|
+
stop();
|
|
55
|
+
options.onFailure({ kind: 'error', error });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
lastBenignError = error;
|
|
59
|
+
if (elapsedMs >= options.timeoutMs) {
|
|
60
|
+
stop();
|
|
61
|
+
options.onFailure({ kind: 'timeout', lastError: lastBenignError });
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
stop();
|
|
66
|
+
options.onReady();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
start(): void {
|
|
71
|
+
if (timer !== null) return;
|
|
72
|
+
elapsedMs = 0;
|
|
73
|
+
timer = setInterval(tick, options.intervalMs);
|
|
74
|
+
},
|
|
75
|
+
stop,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -3,7 +3,10 @@ import { isStatusLineCommandSettingsPatch } from '@robota-sdk/agent-framework';
|
|
|
3
3
|
|
|
4
4
|
import type { ITuiCliAdapter } from '../tui-cli-adapter.js';
|
|
5
5
|
import type { IHistoryEntry, TSessionEndReason } from '@robota-sdk/agent-core';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
TCommandEffect,
|
|
8
|
+
TStatusLineCommandSettingsPatch,
|
|
9
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
7
10
|
|
|
8
11
|
export interface ICommandEffectHandlerDeps {
|
|
9
12
|
addEntry: (entry: IHistoryEntry) => void;
|
|
@@ -2,9 +2,9 @@ import type { ICommandEffectQueue } from './command-effect-queue.js';
|
|
|
2
2
|
import type { IHistoryEntry } from '@robota-sdk/agent-core';
|
|
3
3
|
import type { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
4
4
|
import type {
|
|
5
|
-
TCommandInteractionPrompt as TInteractivePrompt,
|
|
6
5
|
IStatusLineCommandSettings as TStatusLineSettings,
|
|
7
|
-
|
|
6
|
+
TCommandInteractionPrompt as TInteractivePrompt,
|
|
7
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
8
8
|
|
|
9
9
|
export type { TInteractivePrompt, TStatusLineSettings };
|
|
10
10
|
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import React, { useState, useMemo } from 'react';
|
|
7
7
|
|
|
8
|
-
import type { CommandRegistry
|
|
8
|
+
import type { CommandRegistry } from '@robota-sdk/agent-framework';
|
|
9
|
+
import type { ICommand } from '@robota-sdk/agent-interface-transport';
|
|
9
10
|
|
|
10
11
|
/** Parse input to determine autocomplete state */
|
|
11
|
-
|
|
12
|
+
function parseSlashInput(value: string): {
|
|
12
13
|
isSlash: boolean;
|
|
13
14
|
parentCommand: string;
|
|
14
15
|
filter: string;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { useMemo } from 'react';
|
|
10
10
|
|
|
11
|
-
import type { ICommandPluginAdapter } from '@robota-sdk/agent-
|
|
11
|
+
import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
|
|
12
12
|
|
|
13
13
|
function createNoOpPluginAdapter(): ICommandPluginAdapter {
|
|
14
14
|
return {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { useState, useEffect } from 'react';
|
|
7
7
|
|
|
8
8
|
import type { IMenuSelectItem } from '../MenuSelect.js';
|
|
9
|
-
import type { ICommandPluginAdapter } from '@robota-sdk/agent-
|
|
9
|
+
import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
|
|
10
10
|
|
|
11
11
|
export function usePluginScreenData(
|
|
12
12
|
screen: string,
|
|
@@ -8,7 +8,7 @@ import { useTuiCliAdapter } from '../tui-cli-adapter-context.js';
|
|
|
8
8
|
import type { IUseSideEffectsOptions, IUseSideEffectsResult } from './side-effects-types.js';
|
|
9
9
|
import type { TInteractivePrompt } from './side-effects-types.js';
|
|
10
10
|
import type { TSessionEndReason } from '@robota-sdk/agent-core';
|
|
11
|
-
import type { ICommandInteraction, ICommandResult } from '@robota-sdk/agent-
|
|
11
|
+
import type { ICommandInteraction, ICommandResult } from '@robota-sdk/agent-interface-transport';
|
|
12
12
|
|
|
13
13
|
const EXIT_DELAY_MS = 500;
|
|
14
14
|
|
|
@@ -8,12 +8,12 @@ import { useCallback } from 'react';
|
|
|
8
8
|
|
|
9
9
|
import type { TuiStateManager } from '../tui-state-manager.js';
|
|
10
10
|
import type { ICommandEffectQueue } from './command-effect-queue.js';
|
|
11
|
+
import type { CommandRegistry } from '@robota-sdk/agent-framework';
|
|
11
12
|
import type {
|
|
12
|
-
IInteractiveSession,
|
|
13
|
-
CommandRegistry,
|
|
14
13
|
ICommandResult,
|
|
14
|
+
IInteractiveSession,
|
|
15
15
|
TCommandEffect,
|
|
16
|
-
} from '@robota-sdk/agent-
|
|
16
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
17
17
|
|
|
18
18
|
export function useSlashRouting(
|
|
19
19
|
interactiveSession: IInteractiveSession,
|
|
@@ -4,7 +4,7 @@ import { useState } from 'react';
|
|
|
4
4
|
import { useTuiCliAdapter } from '../tui-cli-adapter-context.js';
|
|
5
5
|
|
|
6
6
|
import type { TUniversalValue } from '@robota-sdk/agent-core';
|
|
7
|
-
import type { IStatusLineCommandSettings } from '@robota-sdk/agent-
|
|
7
|
+
import type { IStatusLineCommandSettings } from '@robota-sdk/agent-interface-transport';
|
|
8
8
|
|
|
9
9
|
function readStatusLineSettings(
|
|
10
10
|
settings: Record<string, TUniversalValue>,
|
|
@@ -13,10 +13,10 @@ import type { IPermissionRequest } from '../types.js';
|
|
|
13
13
|
import type { IHistoryEntry, TSessionEndReason } from '@robota-sdk/agent-core';
|
|
14
14
|
import type { InteractiveSession, CommandRegistry } from '@robota-sdk/agent-framework';
|
|
15
15
|
import type {
|
|
16
|
-
IToolState,
|
|
17
|
-
IExecutionWorkspaceSnapshot,
|
|
18
16
|
IExecutionDetailPage,
|
|
19
|
-
|
|
17
|
+
IExecutionWorkspaceSnapshot,
|
|
18
|
+
IToolState,
|
|
19
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
20
20
|
|
|
21
21
|
export interface IInteractiveSessionState {
|
|
22
22
|
interactiveSession: InteractiveSession;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { IMenuSelectItem } from './MenuSelect.js';
|
|
7
|
-
import type { ICommandPluginAdapter } from '@robota-sdk/agent-
|
|
7
|
+
import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
|
|
8
8
|
|
|
9
9
|
interface IConfirmState {
|
|
10
10
|
message: string;
|
package/src/tui/render.tsx
CHANGED
|
@@ -15,13 +15,15 @@ import type {
|
|
|
15
15
|
IBackgroundTaskRunner,
|
|
16
16
|
ICommandHostAdapters,
|
|
17
17
|
ICommandModule,
|
|
18
|
-
IInteractiveSession,
|
|
19
|
-
IInteractiveSessionStore,
|
|
20
18
|
TSubagentRunnerFactory,
|
|
21
19
|
TShellExecFn,
|
|
22
20
|
CommandRegistry,
|
|
23
21
|
} from '@robota-sdk/agent-framework';
|
|
24
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
IInteractiveSession,
|
|
24
|
+
IInteractiveSessionStore,
|
|
25
|
+
ITransportRegistryView,
|
|
26
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
25
27
|
|
|
26
28
|
export interface IRenderOptions {
|
|
27
29
|
cwd: string;
|
|
@@ -32,6 +34,8 @@ export interface IRenderOptions {
|
|
|
32
34
|
language?: string;
|
|
33
35
|
permissionMode?: TPermissionMode;
|
|
34
36
|
maxTurns?: number;
|
|
37
|
+
allowedTools?: string[];
|
|
38
|
+
deniedTools?: string[];
|
|
35
39
|
version?: string;
|
|
36
40
|
sessionStore?: IInteractiveSessionStore;
|
|
37
41
|
resumeSessionId?: string;
|
|
@@ -48,6 +52,46 @@ export interface IRenderOptions {
|
|
|
48
52
|
cliAdapter: ITuiCliAdapter;
|
|
49
53
|
reloadPluginCommandSource?: (registry: CommandRegistry) => void;
|
|
50
54
|
agentName?: string;
|
|
55
|
+
/** Active preset id selected at startup (PRESET-011 runtime state). Defaults to 'default'. */
|
|
56
|
+
activePresetId?: string;
|
|
57
|
+
/** Preset persona block composed as a `source: 'persona'` system-prompt section (priority 5). */
|
|
58
|
+
persona?: string;
|
|
59
|
+
/** Preset execution capability: activate agent runtime + subagent/background dispatch. */
|
|
60
|
+
enableParallelSubagents?: boolean;
|
|
61
|
+
/** Preset execution capability: run a post-task self-verification step. */
|
|
62
|
+
selfVerification?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Map render options to TuiInteractionChannel constructor options. */
|
|
66
|
+
export function toChannelOptions(
|
|
67
|
+
options: IRenderOptions,
|
|
68
|
+
resumeSessionId?: string,
|
|
69
|
+
): ConstructorParameters<typeof TuiInteractionChannel>[0] {
|
|
70
|
+
return {
|
|
71
|
+
cwd: options.cwd,
|
|
72
|
+
provider: options.provider,
|
|
73
|
+
permissionMode: options.permissionMode,
|
|
74
|
+
maxTurns: options.maxTurns,
|
|
75
|
+
allowedTools: options.allowedTools,
|
|
76
|
+
deniedTools: options.deniedTools,
|
|
77
|
+
sessionStore: options.sessionStore,
|
|
78
|
+
resumeSessionId,
|
|
79
|
+
forkSession: options.forkSession,
|
|
80
|
+
sessionName: options.sessionName,
|
|
81
|
+
backgroundTaskRunners: options.backgroundTaskRunners,
|
|
82
|
+
subagentRunnerFactory: options.subagentRunnerFactory,
|
|
83
|
+
commandModules: options.commandModules,
|
|
84
|
+
commandHostAdapters: options.commandHostAdapters,
|
|
85
|
+
shellExec: options.shellExec,
|
|
86
|
+
transportRegistry: options.transportRegistry,
|
|
87
|
+
language: options.language,
|
|
88
|
+
reloadPluginCommandSource: options.reloadPluginCommandSource,
|
|
89
|
+
agentName: options.agentName,
|
|
90
|
+
activePresetId: options.activePresetId,
|
|
91
|
+
persona: options.persona,
|
|
92
|
+
enableParallelSubagents: options.enableParallelSubagents,
|
|
93
|
+
selfVerification: options.selfVerification,
|
|
94
|
+
};
|
|
51
95
|
}
|
|
52
96
|
|
|
53
97
|
export async function renderApp(options: IRenderOptions): Promise<void> {
|
|
@@ -58,33 +102,14 @@ export async function renderApp(options: IRenderOptions): Promise<void> {
|
|
|
58
102
|
}
|
|
59
103
|
});
|
|
60
104
|
|
|
105
|
+
// Single-owner lifecycle (CLI-B12): render.tsx supplies only the factory;
|
|
106
|
+
// App creates, replaces, and stops channels exclusively through React state.
|
|
61
107
|
const createChannel = (resumeSessionId?: string): TuiInteractionChannel =>
|
|
62
|
-
new TuiInteractionChannel(
|
|
63
|
-
cwd: options.cwd,
|
|
64
|
-
provider: options.provider,
|
|
65
|
-
permissionMode: options.permissionMode,
|
|
66
|
-
maxTurns: options.maxTurns,
|
|
67
|
-
sessionStore: options.sessionStore,
|
|
68
|
-
resumeSessionId,
|
|
69
|
-
forkSession: options.forkSession,
|
|
70
|
-
sessionName: options.sessionName,
|
|
71
|
-
backgroundTaskRunners: options.backgroundTaskRunners,
|
|
72
|
-
subagentRunnerFactory: options.subagentRunnerFactory,
|
|
73
|
-
commandModules: options.commandModules,
|
|
74
|
-
commandHostAdapters: options.commandHostAdapters,
|
|
75
|
-
shellExec: options.shellExec,
|
|
76
|
-
transportRegistry: options.transportRegistry,
|
|
77
|
-
language: options.language,
|
|
78
|
-
reloadPluginCommandSource: options.reloadPluginCommandSource,
|
|
79
|
-
agentName: options.agentName,
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const channel = createChannel(options.resumeSessionId);
|
|
108
|
+
new TuiInteractionChannel(toChannelOptions(options, resumeSessionId));
|
|
83
109
|
|
|
84
110
|
const instance = render(
|
|
85
111
|
<App
|
|
86
112
|
cwd={options.cwd}
|
|
87
|
-
channel={channel}
|
|
88
113
|
createChannel={createChannel}
|
|
89
114
|
providerOverride={options.providerOverride}
|
|
90
115
|
providerType={options.providerType}
|
|
@@ -30,7 +30,7 @@ function getPrimaryActivity(
|
|
|
30
30
|
if (input.activeToolCount > NO_ACTIVE_ITEMS) {
|
|
31
31
|
return {
|
|
32
32
|
kind: 'tools',
|
|
33
|
-
label: `Tools
|
|
33
|
+
label: `Tools (${input.activeToolCount})`,
|
|
34
34
|
color: 'cyan',
|
|
35
35
|
};
|
|
36
36
|
}
|
|
@@ -44,7 +44,7 @@ function getPrimaryActivity(
|
|
|
44
44
|
if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {
|
|
45
45
|
return {
|
|
46
46
|
kind: 'background',
|
|
47
|
-
label: `Background
|
|
47
|
+
label: `Background (${input.activeBackgroundTaskCount})`,
|
|
48
48
|
color: 'cyan',
|
|
49
49
|
};
|
|
50
50
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { TUniversalValue } from '@robota-sdk/agent-core';
|
|
2
|
+
import type { CommandRegistry } from '@robota-sdk/agent-framework';
|
|
2
3
|
import type {
|
|
3
|
-
TStatusLineCommandSettingsPatch,
|
|
4
4
|
IStatusLineCommandSettings,
|
|
5
|
-
|
|
6
|
-
} from '@robota-sdk/agent-
|
|
5
|
+
TStatusLineCommandSettingsPatch,
|
|
6
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
7
7
|
|
|
8
8
|
export interface ITuiCliAdapter {
|
|
9
9
|
getUserSettingsPath(): string;
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
import type { IContextWindowState, IHistoryEntry } from '@robota-sdk/agent-core';
|
|
12
12
|
import type {
|
|
13
|
-
IToolState,
|
|
14
13
|
IExecutionResult,
|
|
15
14
|
IExecutionWorkspaceSnapshot,
|
|
16
|
-
|
|
15
|
+
IToolState,
|
|
16
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
17
17
|
|
|
18
18
|
/** Max messages kept in rendering state */
|
|
19
19
|
const MAX_RENDERED_MESSAGES = 100;
|
package/src/tui/tui-transport.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { renderApp, type IRenderOptions } from './render.js';
|
|
2
2
|
|
|
3
3
|
import type { TUniversalValue } from '@robota-sdk/agent-core';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
4
|
+
import type {
|
|
5
|
+
IConfigurableTransport,
|
|
6
|
+
IInteractiveSession,
|
|
7
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
6
8
|
|
|
7
9
|
export class TuiTransport implements IConfigurableTransport<IInteractiveSession> {
|
|
8
10
|
readonly name = 'tui';
|
|
@@ -6,15 +6,17 @@ import { describe, it, expect, vi } from 'vitest';
|
|
|
6
6
|
import { createWsHandler } from '../ws-handler.js';
|
|
7
7
|
import type { TServerMessage } from '../ws-protocol.js';
|
|
8
8
|
import type {
|
|
9
|
-
|
|
10
|
-
IBackgroundTaskState,
|
|
9
|
+
IBackgroundJobGroupState,
|
|
11
10
|
IExecutionWorkspaceEvent,
|
|
12
11
|
IExecutionWorkspaceSnapshot,
|
|
13
12
|
IInteractiveSession,
|
|
14
|
-
IBackgroundJobGroupState,
|
|
15
13
|
TBackgroundJobGroupEvent,
|
|
16
|
-
TBackgroundTaskEvent,
|
|
17
14
|
TExecutionWorkspaceUpdateCause,
|
|
15
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
16
|
+
import type {
|
|
17
|
+
IBackgroundTaskLogPage,
|
|
18
|
+
IBackgroundTaskState,
|
|
19
|
+
TBackgroundTaskEvent,
|
|
18
20
|
} from '@robota-sdk/agent-framework';
|
|
19
21
|
|
|
20
22
|
const backgroundTask: IBackgroundTaskState = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { createWsTransport } from '../ws-transport.js';
|
|
3
|
-
import type { IInteractiveSession } from '@robota-sdk/agent-
|
|
3
|
+
import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
|
|
4
4
|
|
|
5
5
|
function createMockSession(): IInteractiveSession {
|
|
6
6
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TBackgroundControlAction, TClientMessage, TServerMessage } from './ws-protocol.js';
|
|
2
|
-
import type { IInteractiveSession } from '@robota-sdk/agent-
|
|
2
|
+
import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
|
|
3
3
|
|
|
4
4
|
export function handleBackgroundQueryMessage(
|
|
5
5
|
session: IInteractiveSession,
|
package/src/ws/ws-handler.ts
CHANGED
|
@@ -14,14 +14,14 @@ import {
|
|
|
14
14
|
} from './ws-background-messages.js';
|
|
15
15
|
|
|
16
16
|
import type { TClientMessage, TServerMessage } from './ws-protocol.js';
|
|
17
|
+
import type { TBackgroundTaskEvent } from '@robota-sdk/agent-framework';
|
|
17
18
|
import type {
|
|
18
|
-
IInteractiveSession,
|
|
19
19
|
IExecutionResult,
|
|
20
20
|
IExecutionWorkspaceEvent,
|
|
21
|
-
|
|
22
|
-
TBackgroundTaskEvent,
|
|
21
|
+
IInteractiveSession,
|
|
23
22
|
IToolState,
|
|
24
|
-
|
|
23
|
+
TBackgroundJobGroupEvent,
|
|
24
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
25
25
|
|
|
26
26
|
export interface IWsHandlerOptions {
|
|
27
27
|
/** IInteractiveSession to expose. */
|
package/src/ws/ws-protocol.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
InteractiveSession,
|
|
3
|
-
ICommandResult,
|
|
4
|
-
IBackgroundJobGroupState,
|
|
5
3
|
IBackgroundTaskInput,
|
|
6
4
|
IBackgroundTaskListFilter,
|
|
7
5
|
IBackgroundTaskLogCursor,
|
|
8
6
|
IBackgroundTaskLogPage,
|
|
9
7
|
IBackgroundTaskState,
|
|
8
|
+
TBackgroundTaskEvent,
|
|
9
|
+
} from '@robota-sdk/agent-framework';
|
|
10
|
+
import type {
|
|
11
|
+
IBackgroundJobGroupState,
|
|
12
|
+
ICommandResult,
|
|
10
13
|
IExecutionResult,
|
|
11
14
|
IExecutionWorkspaceSnapshot,
|
|
12
15
|
IToolState,
|
|
13
16
|
TBackgroundJobGroupEvent,
|
|
14
|
-
|
|
15
|
-
} from '@robota-sdk/agent-framework';
|
|
17
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
16
18
|
|
|
17
19
|
export type TBackgroundControlAction = 'cancel' | 'close' | 'send';
|
|
18
20
|
|
|
@@ -11,8 +11,10 @@ import { createWsHandler } from './ws-handler.js';
|
|
|
11
11
|
|
|
12
12
|
import type { TServerMessage } from './ws-protocol.js';
|
|
13
13
|
import type { TUniversalValue } from '@robota-sdk/agent-core';
|
|
14
|
-
import type {
|
|
15
|
-
|
|
14
|
+
import type {
|
|
15
|
+
IConfigurableTransport,
|
|
16
|
+
IInteractiveSession,
|
|
17
|
+
} from '@robota-sdk/agent-interface-transport';
|
|
16
18
|
|
|
17
19
|
const DEFAULT_PORT = 7070;
|
|
18
20
|
const DEFAULT_MAX_RETRIES = 20;
|
package/src/ws/ws-transport.ts
CHANGED
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
import { createWsHandler } from './ws-handler.js';
|
|
9
9
|
|
|
10
10
|
import type { TServerMessage } from './ws-protocol.js';
|
|
11
|
-
import type { IInteractiveSession } from '@robota-sdk/agent-
|
|
12
|
-
import type { ITransportAdapter } from '@robota-sdk/agent-interface-transport';
|
|
11
|
+
import type { IInteractiveSession, ITransportAdapter } from '@robota-sdk/agent-interface-transport';
|
|
13
12
|
|
|
14
13
|
export interface IWsTransportOptions {
|
|
15
14
|
/** Send a JSON message to the connected WebSocket client. */
|