fix terminal input ids across page re-entry
This commit is contained in:
parent
3d06ee0a19
commit
eb98d9ae8d
@ -31,6 +31,8 @@ class TerminalViewport {
|
||||
}
|
||||
|
||||
class TerminalSessionCoordinator extends ChangeNotifier {
|
||||
static int _inputDispatchInstanceCounter = 0;
|
||||
|
||||
TerminalSessionCoordinator({
|
||||
required this.controller,
|
||||
required this.apiClient,
|
||||
@ -46,7 +48,8 @@ class TerminalSessionCoordinator extends ChangeNotifier {
|
||||
ResizeScheduler? resizeScheduler,
|
||||
}) : baseUri = baseUri ?? _defaultBaseUri,
|
||||
_reconnectScheduler = reconnectScheduler ?? _defaultReconnectScheduler,
|
||||
_resizeScheduler = resizeScheduler ?? _defaultResizeScheduler;
|
||||
_resizeScheduler = resizeScheduler ?? _defaultResizeScheduler,
|
||||
_inputDispatchScope = _buildInputDispatchScope(session.sessionId);
|
||||
|
||||
static final Uri _defaultBaseUri = Uri(
|
||||
scheme: 'https',
|
||||
@ -71,6 +74,7 @@ class TerminalSessionCoordinator extends ChangeNotifier {
|
||||
final TerminalDiagnosticLog? diagnosticLog;
|
||||
final ReconnectScheduler _reconnectScheduler;
|
||||
final ResizeScheduler _resizeScheduler;
|
||||
final String _inputDispatchScope;
|
||||
|
||||
TerminalSocketSession? _socketSession;
|
||||
CancelReconnect? _cancelReconnect;
|
||||
@ -797,7 +801,12 @@ class TerminalSessionCoordinator extends ChangeNotifier {
|
||||
|
||||
String _buildPendingInputId() {
|
||||
_nextInputId += 1;
|
||||
return '${session.sessionId}-input-$_nextInputId';
|
||||
return '$_inputDispatchScope-input-$_nextInputId';
|
||||
}
|
||||
|
||||
static String _buildInputDispatchScope(String sessionId) {
|
||||
_inputDispatchInstanceCounter += 1;
|
||||
return '$sessionId-${_inputDispatchInstanceCounter}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -123,7 +123,11 @@ void main() {
|
||||
|
||||
expect(sessionFactory.createdSessions, hasLength(2));
|
||||
expect(sessionFactory.createdSessions.last.sentInputs, ['dir\r']);
|
||||
expect(sessionFactory.createdSessions.last.sentInputIds, ['abc-input-1']);
|
||||
expect(sessionFactory.createdSessions.last.sentInputIds, hasLength(1));
|
||||
expect(
|
||||
sessionFactory.createdSessions.last.sentInputIds.single,
|
||||
endsWith('-input-1'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@ -154,16 +158,17 @@ void main() {
|
||||
final firstSession = sessionFactory.createdSessions.single;
|
||||
|
||||
coordinator.sendInput('dir\r');
|
||||
expect(firstSession.sentInputIds, ['abc-input-1']);
|
||||
final firstInputId = firstSession.sentInputIds.single;
|
||||
expect(firstInputId, endsWith('-input-1'));
|
||||
|
||||
firstSession.disconnect();
|
||||
await reconnectScheduler.runPending();
|
||||
|
||||
final secondSession = sessionFactory.createdSessions.last;
|
||||
expect(secondSession.sentInputs, ['dir\r']);
|
||||
expect(secondSession.sentInputIds, ['abc-input-1']);
|
||||
expect(secondSession.sentInputIds, [firstInputId]);
|
||||
|
||||
secondSession.ackInput('abc-input-1');
|
||||
secondSession.ackInput(firstInputId);
|
||||
secondSession.disconnect();
|
||||
await reconnectScheduler.runPending();
|
||||
|
||||
@ -172,6 +177,53 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test('re-entering the same session generates fresh input ids', () async {
|
||||
final firstController = TerminalInteractionController();
|
||||
final firstApiClient = _FakeAgentApiClient();
|
||||
final firstSessionFactory = _FakeTerminalSessionFactory();
|
||||
final session = Session(
|
||||
sessionId: 'abc',
|
||||
name: 'codex-main',
|
||||
status: 'idle',
|
||||
);
|
||||
final firstCoordinator = TerminalSessionCoordinator(
|
||||
controller: firstController,
|
||||
apiClient: firstApiClient,
|
||||
session: session,
|
||||
sessionFactory: firstSessionFactory.create,
|
||||
onFrame: (_) {},
|
||||
onRestore: (_) {},
|
||||
viewportProvider: () => const TerminalViewport(columns: 80, rows: 24),
|
||||
);
|
||||
|
||||
await firstCoordinator.start();
|
||||
firstCoordinator.sendInput('dir\r');
|
||||
final firstInputId = firstSessionFactory.createdSessions.single.sentInputIds.single;
|
||||
await firstCoordinator.close();
|
||||
|
||||
final secondController = TerminalInteractionController();
|
||||
final secondApiClient = _FakeAgentApiClient();
|
||||
final secondSessionFactory = _FakeTerminalSessionFactory();
|
||||
final secondCoordinator = TerminalSessionCoordinator(
|
||||
controller: secondController,
|
||||
apiClient: secondApiClient,
|
||||
session: session,
|
||||
sessionFactory: secondSessionFactory.create,
|
||||
onFrame: (_) {},
|
||||
onRestore: (_) {},
|
||||
viewportProvider: () => const TerminalViewport(columns: 80, rows: 24),
|
||||
);
|
||||
|
||||
await secondCoordinator.start();
|
||||
secondCoordinator.sendInput('dir\r');
|
||||
final secondInputId = secondSessionFactory.createdSessions.single.sentInputIds.single;
|
||||
|
||||
expect(secondInputId, isNot(firstInputId));
|
||||
expect(firstInputId, endsWith('-input-1'));
|
||||
expect(secondInputId, endsWith('-input-1'));
|
||||
await secondCoordinator.close();
|
||||
});
|
||||
|
||||
test('restore payloads are treated as authoritative over provisional text', () {
|
||||
final decision = decideTerminalRestore(
|
||||
currentText: 'PS> git status\r\nmodified: file.txt\r\nPS> ',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user