Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/app/TerminalApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ import {liveSessionRows} from '../sessions/LiveSessionView.js';
import {createResumeBrowser, describeArchivedRow, describeLiveRow, LIVE_ROW_LABELS, liveRowAgent, liveRowState, navigateResume, resumeDayLabel, resumeRowCount, resumeSelection,
visibleLiveSessions, visibleResumeSessions, type ResumeBrowserState} from '../sessions/ResumeBrowser.js';
import {dismissSessionNotice, listLiveSessions, listSessionNotices} from '../session/connectSession.js';
import {OLDER_SERVICE_SWITCH, serviceBuildNotice} from '../session/SocketSessionClient.js';
import {OLDER_SERVICE_SWITCH, serviceBuildNotice, serviceInputNotice} from '../session/SocketSessionClient.js';
import {killAndArchive} from '../session/liveSessions.js';
import {recoverEndedSessions} from '../session/recovery.js';
import {AgentSessions, normalizeProfiles, type AgentProfile} from '../agents/sessions/manager.js';
Expand Down Expand Up @@ -1086,6 +1086,8 @@ export class TerminalApp {
if (connection?.notice) this.output.addFrontendInteraction('session', connection.notice, ERROR);
const stale = connection?.mode === 'service' ? serviceBuildNotice(this.session.serviceBuild, this.buildIdentity) : undefined;
if (stale) this.output.addFrontendInteraction('session', stale, INFO);
const routing = connection?.mode === 'service' ? serviceInputNotice(this.session.features) : undefined;
if (routing) this.output.addFrontendInteraction('session', routing, WARNING);
this.beginStartupWatch(connection?.attached);
this.session.start();
void this.loadInstalledPacks();
Expand Down
12 changes: 12 additions & 0 deletions src/session/SocketSessionClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ export function serviceBuildNotice(serviceBuild: string | undefined, own: {versi
+ 'so fixes to how shells start are not in it yet. The service restarts by itself once every NMSh window and live session has ended.';
}

/**
* A session service without this release's look result (an older NMSh's, e.g. v0.20.0's) still decides who gets what
* is typed while a command runs, the way it did then: #426's and #436's protections are not active in its sessions.
* Said once per window, with when it ends; undefined for a service that has them.
*/
export function serviceInputNotice(features: ReadonlySet<string>): string | undefined {
if (features.has('input-look-result')) return undefined;
return 'This session\'s service is from an older NMSh, so what you type while a command runs is routed as that version did: '
+ 'a password typed before a program asks for it can be shown, queued or run as a command. '
+ 'The service restarts by itself once every NMSh window and live session has ended; new sessions then get the current protections.';
}

export const OLDER_SERVICE_SWITCH = 'The session service running this session is an older NMSh build without shell switching. '
+ 'Its live sessions keep running; once they end, the next nmsh launch starts the current service.';

Expand Down
12 changes: 12 additions & 0 deletions tests/sessionService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,18 @@ test('a shell from an older session service is called out, because the service b
assert.match(serviceBuildNotice(undefined, own) ?? '', /build not reported/u, 'a service too old to report its build');
});

test('a session service without this release\'s typing protections says so, and when that ends (#426)', async () => {
const {serviceInputNotice} = await import('../src/session/SocketSessionClient.js');
const {SERVICE_FEATURES} = await import('../src/session/SessionProtocol.js');
assert.equal(serviceInputNotice(new Set(SERVICE_FEATURES)), undefined, 'this release\'s service: nothing to say');
// v0.20.0's service: the same protocol, without the look result #426's protections are built on.
const v020 = new Set(['shell-switch', 'shell-backends', 'notices', 'input-state', 'queue', 'queue-conditions', 'background-tasks', 'input-look', 'queue-timeline', 'queue-history']);
const notice = serviceInputNotice(v020) ?? '';
assert.match(notice, /what you type while a command runs is routed as that version did/u);
assert.match(notice, /password/u, 'says what can happen, plainly');
assert.match(notice, /restarts by itself once every NMSh window and live session has ended/u);
});


test('transported queue reviews expire at manual submission and controller reattachment', async () => {
const runtimeDir = scratch('nmsh-queue-review-');
Expand Down
Loading