@robota-sdk/agent-ui-terminal 3.0.0-beta.82 → 3.0.0-beta.84
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/CHANGELOG.md +893 -0
- package/README.md +34 -11
- package/dist/node/index.d.ts +205 -55
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +21 -21
- package/dist/node/index.js.map +1 -1
- package/package.json +19 -23
- package/src/App.tsx +14 -3
- package/src/AppPresentation.tsx +1 -1
- package/src/PermissionPrompt.tsx +73 -23
- package/src/SessionPicker.tsx +27 -7
- package/src/SessionStatusBar.tsx +6 -2
- package/src/StatusBar.tsx +24 -0
- package/src/SupervisedSessionView.tsx +568 -161
- package/src/TuiInteractionChannel.ts +17 -54
- package/src/__tests__/SupervisedSessionView-render.test.tsx +28 -0
- package/src/__tests__/SupervisedSessionView.test.tsx +561 -82
- package/src/__tests__/TuiInteractionChannel.display-contract.test.ts +32 -22
- package/src/__tests__/TuiInteractionChannel.lifecycle.test.ts +10 -2
- package/src/__tests__/attached-app-session-switch.test.tsx +317 -0
- package/src/__tests__/checkpoint-store-per-channel.test.ts +81 -0
- package/src/__tests__/command-handoff-pty-e2e.test.ts +2 -2
- package/src/__tests__/default-tui-cli-adapter-host-settings.test.ts +3 -1
- package/src/__tests__/fixtures/command-handoff-driver.tsx +8 -5
- package/src/__tests__/grant-history-reaches-the-session.test.ts +31 -0
- package/src/__tests__/helpers/terminal-profiles.ts +4 -4
- package/src/__tests__/numbered-selection-chunk.test.tsx +41 -0
- package/src/__tests__/palette-consistency.test.ts +0 -1
- package/src/__tests__/permission-prompt-arming.test.tsx +153 -0
- package/src/__tests__/permission-prompt-requester.test.tsx +34 -0
- package/src/__tests__/pty/behavior-2437-git.ptytest.ts +11 -3
- package/src/__tests__/pty/screen-005-prompt-footers.ptytest.ts +2 -1
- package/src/__tests__/pty/screen-006-no-color.ptytest.ts +2 -0
- package/src/__tests__/pty/session-attach.ptytest.ts +144 -0
- package/src/__tests__/pty/trust-prompt.ptytest.ts +134 -0
- package/src/__tests__/render-channel-options.test.ts +6 -6
- package/src/__tests__/sandbox-reaches-the-session.test.ts +37 -0
- package/src/__tests__/screen-reader-menus.test.tsx +1 -1
- package/src/__tests__/session-picker.test.tsx +93 -0
- package/src/__tests__/status-bar.test.tsx +8 -0
- package/src/__tests__/tui-channel-init-failure.test.ts +10 -2
- package/src/__tests__/tui-interaction-queues.test.ts +10 -0
- package/src/__tests__/tui-state-manager.test.ts +31 -0
- package/src/__tests__/wire-tui-channel.test.ts +1457 -0
- package/src/__tests__/wire-tui-client-commands.test.ts +243 -0
- package/src/app-view-model.ts +6 -2
- package/src/attached-session-connection.ts +14 -0
- package/src/hooks/__tests__/use-app-detach-key.test.tsx +72 -0
- package/src/hooks/__tests__/use-app-loop-escape.test.tsx +13 -0
- package/src/hooks/useAppController.ts +6 -2
- package/src/hooks/useAppInputBindings.ts +20 -2
- package/src/hooks/useAppInteractionState.ts +3 -0
- package/src/hooks/useAppOverlays.ts +20 -4
- package/src/hooks/useNumberedSelection.ts +19 -2
- package/src/hooks/useTuiChannel.ts +13 -1
- package/src/index.ts +19 -3
- package/src/render.tsx +218 -27
- package/src/short-session-id.ts +9 -0
- package/src/slash-command-input.ts +11 -0
- package/src/tui-app-channel-port.ts +29 -1
- package/src/tui-channel-options.ts +11 -2
- package/src/tui-interaction-queues.ts +9 -1
- package/src/tui-session-event-projector.ts +12 -3
- package/src/tui-session-events.ts +6 -0
- package/src/tui-session-options.ts +9 -0
- package/src/tui-state-manager.ts +25 -5
- package/src/types.ts +3 -1
- package/src/waiting-loop-stop-notice.ts +23 -0
- package/src/wire-history-sync.ts +159 -0
- package/src/wire-tui-channel.ts +963 -0
- package/src/wire-tui-client-commands.ts +142 -0
- package/src/wire-tui-projection.ts +136 -0
- package/dist/node/index.cjs +0 -32
- package/dist/node/index.d.cts +0 -1066
- package/dist/node/index.d.cts.map +0 -1
|
@@ -6,16 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createSystemMessage, messageToHistoryEntry } from '@robota-sdk/agent-core';
|
|
9
|
-
import {
|
|
10
|
-
CommandRegistry,
|
|
11
|
-
buildRuntimeSession,
|
|
12
|
-
generateSessionName,
|
|
13
|
-
} from '@robota-sdk/agent-framework';
|
|
9
|
+
import { CommandRegistry, buildRuntimeSession } from '@robota-sdk/agent-framework';
|
|
14
10
|
|
|
15
11
|
import { AttentionCoordinator } from './attention/attention-coordinator.js';
|
|
16
12
|
import { MS_PER_SECOND } from './attention/time-units.js';
|
|
17
13
|
import { createSessionInitPoller } from './flows/session-init-poller.js';
|
|
18
14
|
import { applySystemCommandResult } from './hooks/command-result-handler.js';
|
|
15
|
+
import { parseSlashCommandInput } from './slash-command-input.js';
|
|
19
16
|
import {
|
|
20
17
|
TuiChannelLifecycleCoordinator,
|
|
21
18
|
TuiChannelStartRollbackError,
|
|
@@ -24,6 +21,7 @@ import { TuiPermissionQueue, TuiUserActionQueue } from './tui-interaction-queues
|
|
|
24
21
|
import { TuiSessionEventProjector } from './tui-session-event-projector.js';
|
|
25
22
|
import { buildTuiSessionOptions } from './tui-session-options.js';
|
|
26
23
|
import { TuiStateManager } from './tui-state-manager.js';
|
|
24
|
+
import { WAITING_LOOP_STOP_REASON, waitingLoopStopNotice } from './waiting-loop-stop-notice.js';
|
|
27
25
|
|
|
28
26
|
import type { IAttentionSource } from './attention/attention-tracker.js';
|
|
29
27
|
import type { ISessionInitPoller, TSessionInitFailure } from './flows/session-init-poller.js';
|
|
@@ -78,7 +76,6 @@ export class TuiInteractionChannel implements ITuiAppChannelPort {
|
|
|
78
76
|
availableCommands: ICommandInfo[] = [];
|
|
79
77
|
sessionName: string | undefined;
|
|
80
78
|
|
|
81
|
-
private autoNameTriggered = false;
|
|
82
79
|
private initPoller: ISessionInitPoller | null = null;
|
|
83
80
|
|
|
84
81
|
/** TERM-002: the App registers its Ink suspend/resume hooks into this controller. */
|
|
@@ -103,14 +100,21 @@ export class TuiInteractionChannel implements ITuiAppChannelPort {
|
|
|
103
100
|
session: this.interactiveSession,
|
|
104
101
|
manager: this.stateManager,
|
|
105
102
|
...(this.attention ? { attention: this.attention } : {}),
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
requestPermission: (
|
|
104
|
+
toolName,
|
|
105
|
+
toolArgs,
|
|
106
|
+
id,
|
|
107
|
+
canPersistProjectPermission,
|
|
108
|
+
requestedByPeer,
|
|
109
|
+
requester,
|
|
110
|
+
) =>
|
|
108
111
|
this.permissions.enqueue(
|
|
109
112
|
toolName,
|
|
110
113
|
toolArgs,
|
|
111
114
|
id,
|
|
112
115
|
canPersistProjectPermission,
|
|
113
116
|
requestedByPeer,
|
|
117
|
+
requester,
|
|
114
118
|
),
|
|
115
119
|
askUser: (request, id) => this.askUser(request, id),
|
|
116
120
|
dismissPrompt: (id) => this.dismissPromptById(id),
|
|
@@ -324,34 +328,11 @@ export class TuiInteractionChannel implements ITuiAppChannelPort {
|
|
|
324
328
|
this.stateManager.setPendingPrompt(null);
|
|
325
329
|
}
|
|
326
330
|
|
|
331
|
+
/** Esc on an idle prompt: the session decides which waiting loop, if any, stops. */
|
|
327
332
|
async stopWaitingSelfPacedLoop(): Promise<void> {
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (waiting.length === 0) return;
|
|
332
|
-
if (waiting.length > 1) {
|
|
333
|
-
this.addEntry(
|
|
334
|
-
messageToHistoryEntry(
|
|
335
|
-
createSystemMessage(
|
|
336
|
-
'Several self-paced loops are waiting. Use /loop list and /loop stop <id> to choose one.',
|
|
337
|
-
),
|
|
338
|
-
),
|
|
339
|
-
);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
const loopId = waiting[0]!.loopId;
|
|
343
|
-
try {
|
|
344
|
-
await this.interactiveSession.stopSelfPacedLoop(loopId, 'Loop stopped by Esc');
|
|
345
|
-
this.addEntry(messageToHistoryEntry(createSystemMessage(`Loop ${loopId} stopped by Esc.`)));
|
|
346
|
-
} catch (error) {
|
|
347
|
-
this.addEntry(
|
|
348
|
-
messageToHistoryEntry(
|
|
349
|
-
createSystemMessage(
|
|
350
|
-
`Could not stop loop ${loopId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
351
|
-
),
|
|
352
|
-
),
|
|
353
|
-
);
|
|
354
|
-
}
|
|
333
|
+
const outcome = await this.interactiveSession.stopWaitingSelfPacedLoop(WAITING_LOOP_STOP_REASON);
|
|
334
|
+
const notice = waitingLoopStopNotice(outcome);
|
|
335
|
+
if (notice !== undefined) this.addEntry(messageToHistoryEntry(createSystemMessage(notice)));
|
|
355
336
|
}
|
|
356
337
|
|
|
357
338
|
async shutdown(options?: { reason?: TSessionEndReason; timeoutMs?: number }): Promise<void> {
|
|
@@ -407,9 +388,7 @@ export class TuiInteractionChannel implements ITuiAppChannelPort {
|
|
|
407
388
|
}
|
|
408
389
|
|
|
409
390
|
private async handleSlashCommand(input: string): Promise<void> {
|
|
410
|
-
const
|
|
411
|
-
const cmd = parts[0]?.toLowerCase() ?? '';
|
|
412
|
-
const args = parts.slice(1).join(' ');
|
|
391
|
+
const { name: cmd, args } = parseSlashCommandInput(input);
|
|
413
392
|
|
|
414
393
|
const result = await this.interactiveSession.executeCommand(cmd, args);
|
|
415
394
|
if (result) {
|
|
@@ -463,22 +442,6 @@ export class TuiInteractionChannel implements ITuiAppChannelPort {
|
|
|
463
442
|
this.permissions.cancelAll();
|
|
464
443
|
}
|
|
465
444
|
|
|
466
|
-
private handleAutoNaming(content: string): void {
|
|
467
|
-
if (this.autoNameTriggered) return;
|
|
468
|
-
if (this.opts.sessionName || this.interactiveSession.getName()) return;
|
|
469
|
-
this.autoNameTriggered = true;
|
|
470
|
-
generateSessionName(this.opts.provider, content)
|
|
471
|
-
.then((name) => {
|
|
472
|
-
this.interactiveSession.setName(name);
|
|
473
|
-
this.sessionName = name;
|
|
474
|
-
this.opts.onAutoNamed?.(name);
|
|
475
|
-
this.onChange?.();
|
|
476
|
-
})
|
|
477
|
-
.catch(() => {
|
|
478
|
-
this.autoNameTriggered = false;
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
|
|
482
445
|
private syncRestoredHistory(): void {
|
|
483
446
|
if (this.stateManager.history.length === 0) {
|
|
484
447
|
const restored = this.interactiveSession.getFullHistory();
|
|
@@ -21,4 +21,32 @@ describe('production supervised view renderer', () => {
|
|
|
21
21
|
expect(wrapper.props.children.props.onOpenPr).toBe(onOpenPr);
|
|
22
22
|
expect(wrapper.props.children.props.filteredByPr).toBe(true);
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
it('returns the attach the user confirmed so the host can run it and come back', async () => {
|
|
26
|
+
vi.mocked(render).mockClear();
|
|
27
|
+
let exitView: () => void = () => undefined;
|
|
28
|
+
vi.mocked(render).mockImplementationOnce(() => ({
|
|
29
|
+
waitUntilExit: () => new Promise<void>((resolve) => { exitView = resolve; }),
|
|
30
|
+
unmount: () => undefined,
|
|
31
|
+
}) as never);
|
|
32
|
+
const ended = renderSupervisedSessionView({ loadRows: async () => [], screenReader: false });
|
|
33
|
+
const wrapper = vi.mocked(render).mock.calls[0]?.[0] as React.ReactElement<{
|
|
34
|
+
children: React.ReactElement<{ onAttach?: (request: unknown) => void }>;
|
|
35
|
+
}>;
|
|
36
|
+
wrapper.props.children.props.onAttach?.({ id: 'x', generation: 'g', mode: 'observe', groupByDirectory: true });
|
|
37
|
+
exitView();
|
|
38
|
+
expect(await ended).toEqual({ kind: 'attach', id: 'x', generation: 'g', mode: 'observe', groupByDirectory: true });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('prints the screen-reader line only when asked to announce', async () => {
|
|
42
|
+
const out = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
43
|
+
try {
|
|
44
|
+
await renderSupervisedSessionView({ loadRows: async () => [], screenReader: true, screenReaderChannel: 'flag', announce: false });
|
|
45
|
+
expect(out.mock.calls.map(([text]) => String(text)).join('')).not.toContain('Screen reader mode');
|
|
46
|
+
await renderSupervisedSessionView({ loadRows: async () => [], screenReader: true, screenReaderChannel: 'flag' });
|
|
47
|
+
expect(out.mock.calls.map(([text]) => String(text)).join('')).toContain('Screen reader mode: on via flag');
|
|
48
|
+
} finally {
|
|
49
|
+
out.mockRestore();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
24
52
|
});
|