@xingular/harness 1.3.1-unstable.2 → 1.3.1-unstable.4

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +3680 -1947
  4. package/dist/sdk/cli.js +2 -0
  5. package/dist/sdk/commands/registry.d.ts +15 -0
  6. package/dist/sdk/commands/registry.js +2 -0
  7. package/dist/sdk/computer/screenshot.d.ts +32 -0
  8. package/dist/sdk/computer/screenshot.js +35 -0
  9. package/dist/sdk/context/attribution.d.ts +18 -0
  10. package/dist/sdk/context/attribution.js +295 -0
  11. package/dist/sdk/context/types.d.ts +94 -0
  12. package/dist/sdk/context/types.js +10 -0
  13. package/dist/sdk/index.d.ts +2 -1
  14. package/dist/sdk/index.js +2 -1
  15. package/dist/sdk/plugins/builtin.js +1 -0
  16. package/dist/sdk/prompt/compile.d.ts +42 -2
  17. package/dist/sdk/prompt/compile.js +87 -15
  18. package/dist/sdk/remote/addresses.d.ts +19 -0
  19. package/dist/sdk/remote/addresses.js +52 -0
  20. package/dist/sdk/remote/pairing.d.ts +59 -0
  21. package/dist/sdk/remote/pairing.js +160 -0
  22. package/dist/sdk/remote/seal.d.ts +28 -0
  23. package/dist/sdk/remote/seal.js +84 -0
  24. package/dist/sdk/remote/server.d.ts +28 -0
  25. package/dist/sdk/remote/server.js +158 -0
  26. package/dist/sdk/remote/service.d.ts +125 -0
  27. package/dist/sdk/remote/service.js +330 -0
  28. package/dist/sdk/remote/webClient.d.ts +4 -0
  29. package/dist/sdk/remote/webClient.js +444 -0
  30. package/dist/sdk/server/methods.js +3 -0
  31. package/dist/sdk/sessions/contracts.d.ts +4 -0
  32. package/dist/sdk/sessions/contracts.js +2 -0
  33. package/dist/sdk/sessions/planMode.d.ts +26 -1
  34. package/dist/sdk/sessions/planMode.js +48 -1
  35. package/dist/sdk/sessions/runner.d.ts +12 -0
  36. package/dist/sdk/sessions/runner.js +34 -2
  37. package/dist/sdk/sessions/sessions.js +1 -0
  38. package/dist/sdk/sessions/subagentService.d.ts +11 -1
  39. package/dist/sdk/sessions/subagentService.js +120 -38
  40. package/dist/sdk/sessions/subagents.d.ts +31 -0
  41. package/dist/sdk/sessions/subagents.js +45 -0
  42. package/dist/sdk/sessions/workSlice.d.ts +5 -9
  43. package/dist/sdk/sessions/workSlice.js +8 -149
  44. package/dist/sdk/terminal/client.d.ts +3 -1
  45. package/dist/sdk/terminal/client.js +15 -6
  46. package/dist/sdk/terminal/control.d.ts +1 -1
  47. package/dist/sdk/terminal/control.js +2 -2
  48. package/dist/sdk/terminal/controller.d.ts +22 -0
  49. package/dist/sdk/terminal/controller.js +104 -13
  50. package/dist/sdk/terminal/piExtension.js +106 -4
  51. package/dist/sdk/terminal/stallWatch.d.ts +13 -0
  52. package/dist/sdk/terminal/stallWatch.js +16 -0
  53. package/dist/sdk/terminal/standalone.d.ts +2 -0
  54. package/dist/sdk/terminal/standalone.js +9 -2
  55. package/dist/sdk/terminal/terminals.d.ts +14 -0
  56. package/dist/sdk/terminal/terminals.js +98 -2
  57. package/dist/sdk/terminal/tui/commandPages.d.ts +4 -1
  58. package/dist/sdk/terminal/tui/commandPages.js +12 -0
  59. package/dist/sdk/terminal/tui/commands.d.ts +3 -2
  60. package/dist/sdk/terminal/tui/commands.js +19 -5
  61. package/dist/sdk/terminal/tui/dialogs.d.ts +3 -2
  62. package/dist/sdk/terminal/tui/dialogs.js +4 -3
  63. package/dist/sdk/terminal/tui/keybindings.d.ts +1 -1
  64. package/dist/sdk/terminal/tui/keybindings.js +1 -1
  65. package/dist/sdk/terminal/tui/markdown.d.ts +16 -0
  66. package/dist/sdk/terminal/tui/markdown.js +55 -0
  67. package/dist/sdk/terminal/tui/modeChoices.d.ts +3 -2
  68. package/dist/sdk/terminal/tui/modeChoices.js +4 -4
  69. package/dist/sdk/terminal/tui/mouseReport.d.ts +30 -0
  70. package/dist/sdk/terminal/tui/mouseReport.js +80 -0
  71. package/dist/sdk/terminal/tui/pages/annotatePage.js +3 -2
  72. package/dist/sdk/terminal/tui/pages/browserPage.d.ts +1 -1
  73. package/dist/sdk/terminal/tui/pages/contextPage.d.ts +62 -0
  74. package/dist/sdk/terminal/tui/pages/contextPage.js +484 -0
  75. package/dist/sdk/terminal/tui/pages/remoteControlPage.d.ts +32 -0
  76. package/dist/sdk/terminal/tui/pages/remoteControlPage.js +170 -0
  77. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +5 -4
  78. package/dist/sdk/terminal/tui/qr.d.ts +4 -0
  79. package/dist/sdk/terminal/tui/qr.js +35 -0
  80. package/dist/sdk/terminal/tui/runtime.js +8 -2
  81. package/dist/sdk/terminal/tui/search.js +3 -2
  82. package/dist/sdk/terminal/tui/sideQuestion.d.ts +1 -0
  83. package/dist/sdk/terminal/tui/sideQuestion.js +8 -3
  84. package/dist/sdk/terminal/tui/skillInvocation.d.ts +21 -2
  85. package/dist/sdk/terminal/tui/skillInvocation.js +31 -11
  86. package/dist/sdk/terminal/tui/status.d.ts +6 -0
  87. package/dist/sdk/terminal/tui/status.js +15 -1
  88. package/dist/sdk/terminal/tui/theme.d.ts +9 -0
  89. package/dist/sdk/terminal/tui/theme.js +9 -0
  90. package/dist/sdk/tools/defaultSkills.d.ts +19 -0
  91. package/dist/sdk/tools/defaultSkills.js +27 -1
  92. package/dist/sdk/tools/dispatch.js +8 -9
  93. package/dist/sdk/tools/spawnSubagent.d.ts +1 -1
  94. package/dist/sdk/tools/spawnSubagent.js +4 -2
  95. package/dist/terminal-opentui.js +1607 -492
  96. package/dist/terminal-pi.js +256 -144
  97. package/docs/README.md +1 -0
  98. package/docs/architecture/02-sessions-and-runs.md +55 -22
  99. package/docs/architecture/04-prompt.md +23 -4
  100. package/docs/architecture/07-tools-and-gate.md +4 -2
  101. package/docs/architecture/10-server-protocol.md +24 -0
  102. package/docs/architecture/11-terminal.md +52 -10
  103. package/docs/architecture/12-storage.md +5 -2
  104. package/docs/architecture/13-world-tools.md +17 -3
  105. package/docs/architecture/14-surfaces-and-parity.md +7 -3
  106. package/docs/architecture/15-remote-control.md +118 -0
  107. package/docs/architecture/README.md +3 -0
  108. package/docs/architecture/proposals/PROP-0049-context-attribution.md +409 -0
  109. package/docs/architecture/proposals/README.md +2 -1
  110. package/docs/decisions/ADR-0039-progress-aware-stall-guards.md +4 -0
  111. package/docs/decisions/ADR-0048-required-skills-from-a-message.md +42 -0
  112. package/docs/decisions/ADR-0050-approved-window-screenshots.md +19 -0
  113. package/docs/decisions/ADR-0051-terminal-decision-wait.md +10 -0
  114. package/docs/decisions/ADR-0052-per-profile-subagent-budgets.md +25 -0
  115. package/docs/decisions/ADR-0053-no-repeated-work-guard.md +45 -0
  116. package/docs/decisions/ADR-0054-terminal-reply-heartbeat-and-stall.md +52 -0
  117. package/docs/decisions/ADR-0055-plan-output-repair.md +51 -0
  118. package/docs/decisions/ADR-0056-remote-control.md +53 -0
  119. package/docs/decisions/ADR-0057-structured-plan-outcomes.md +39 -0
  120. package/docs/decisions/ADR-0058-ask-user-only-plan-clarification.md +36 -0
  121. package/docs/parity.md +14 -10
  122. package/docs/releases/1.3.1-unstable.3.md +54 -0
  123. package/docs/releases/1.3.1-unstable.4.md +56 -0
  124. package/docs/terminal-tui.md +43 -18
  125. package/package.json +3 -1
@@ -0,0 +1,84 @@
1
+ // Sealed frames for remote control. A LAN page is served over plain HTTP, where the browser has no
2
+ // WebCrypto and anyone on the Wi-Fi can read traffic, so every body is a NaCl secretbox
3
+ // (XSalsa20-Poly1305) under a key the phone got from the QR code's fragment, which a browser never
4
+ // sends. A random 24-byte nonce per frame; the counter inside the frame stops replays.
5
+ import { timingSafeEqual } from 'node:crypto';
6
+ import nacl from 'tweetnacl';
7
+ import { HarnessError } from "../core/errors.js";
8
+ export const KEY_BYTES = nacl.secretbox.keyLength;
9
+ /** How far behind the highest counter a frame may arrive, for requests a phone sent at once. */
10
+ export const REPLAY_WINDOW = 256;
11
+ const encoder = new TextEncoder();
12
+ const decoder = new TextDecoder();
13
+ export function newKey() {
14
+ return nacl.randomBytes(KEY_BYTES);
15
+ }
16
+ /** URL-safe base64 without padding: it travels in fragments, query strings and SSE lines. */
17
+ export function encode(bytes) {
18
+ return Buffer.from(bytes).toString('base64url');
19
+ }
20
+ export function decode(text) {
21
+ return new Uint8Array(Buffer.from(text, 'base64url'));
22
+ }
23
+ export function seal(key, n, body) {
24
+ const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
25
+ const box = nacl.secretbox(encoder.encode(JSON.stringify({ n, body })), nonce, key);
26
+ const frame = new Uint8Array(nonce.length + box.length);
27
+ frame.set(nonce);
28
+ frame.set(box, nonce.length);
29
+ return encode(frame);
30
+ }
31
+ const rejected = () => new HarnessError('unauthorized', 'This remote-control frame could not be opened.');
32
+ /**
33
+ * Opens a frame and asks `fresh` whether its counter is new. A frame that fails here is a wrong
34
+ * key, a tampered body, or a replay; callers answer all three the same way.
35
+ */
36
+ export function open(key, frame, fresh) {
37
+ if (typeof frame !== 'string' || frame.length > 1_500_000)
38
+ throw rejected();
39
+ const bytes = decode(frame);
40
+ const nonceLength = nacl.secretbox.nonceLength;
41
+ if (bytes.length <= nonceLength + nacl.secretbox.overheadLength)
42
+ throw rejected();
43
+ const plain = nacl.secretbox.open(bytes.subarray(nonceLength), bytes.subarray(0, nonceLength), key);
44
+ if (!plain)
45
+ throw rejected();
46
+ let value;
47
+ try {
48
+ value = JSON.parse(decoder.decode(plain));
49
+ }
50
+ catch {
51
+ throw rejected();
52
+ }
53
+ if (typeof value.n !== 'number' || !Number.isSafeInteger(value.n) || value.n < 1 || !fresh(value.n))
54
+ throw rejected();
55
+ return { n: value.n, body: value.body };
56
+ }
57
+ /**
58
+ * Remembers the counters a sender used: a counter is fresh when it is above the highest seen, or
59
+ * within the window below it and not seen before.
60
+ */
61
+ export class ReplayGuard {
62
+ highest = 0;
63
+ recent = new Set();
64
+ accept(n) {
65
+ if (n > this.highest) {
66
+ this.highest = n;
67
+ this.recent.add(n);
68
+ for (const seen of this.recent)
69
+ if (seen <= n - REPLAY_WINDOW)
70
+ this.recent.delete(seen);
71
+ return true;
72
+ }
73
+ if (n <= this.highest - REPLAY_WINDOW || this.recent.has(n))
74
+ return false;
75
+ this.recent.add(n);
76
+ return true;
77
+ }
78
+ }
79
+ /** Compares two identifiers without leaking how much of them matched. */
80
+ export function sameSecret(a, b) {
81
+ const left = Buffer.from(a);
82
+ const right = Buffer.from(b);
83
+ return left.length === right.length && timingSafeEqual(left, right);
84
+ }
@@ -0,0 +1,28 @@
1
+ export interface RemoteAsset {
2
+ type: string;
3
+ body: string;
4
+ }
5
+ export interface RemoteStream {
6
+ /** Sends one sealed frame as an SSE message. */
7
+ send(frame: string): void;
8
+ end(): void;
9
+ }
10
+ export interface RemoteRoutes {
11
+ /** The page and its scripts, by path. */
12
+ asset(path: string): RemoteAsset | null;
13
+ hello(body: Record<string, unknown>): Promise<{
14
+ f: string;
15
+ }>;
16
+ call(body: Record<string, unknown>): Promise<{
17
+ f: string;
18
+ }>;
19
+ /** Opens a stream for a paired device; returns what to run when the phone hangs up. */
20
+ stream(deviceId: string, frame: string, stream: RemoteStream): () => void;
21
+ }
22
+ export interface RemoteListening {
23
+ host: string;
24
+ port: number;
25
+ close(): Promise<void>;
26
+ }
27
+ /** Starts the server on one address. `port` 0 picks a free one. */
28
+ export declare function listenRemote(host: string, port: number, routes: RemoteRoutes): Promise<RemoteListening>;
@@ -0,0 +1,158 @@
1
+ // The remote-control HTTP server: the phone page, pairing, sealed calls and a sealed event stream.
2
+ // It listens on exactly one private or Tailscale address (INV-RC-1). Bodies are sealed (seal.ts),
3
+ // so this layer only checks the Host and Origin, caps sizes, and routes.
4
+ import { createServer } from 'node:http';
5
+ import { HarnessError, toErrorShape } from "../core/errors.js";
6
+ const MAX_HELLO = 64_000;
7
+ const MAX_CALL = 1_200_000;
8
+ const PING_MS = 15_000;
9
+ const HEADERS = {
10
+ 'cache-control': 'no-store',
11
+ 'referrer-policy': 'no-referrer',
12
+ 'x-content-type-options': 'nosniff',
13
+ 'x-frame-options': 'DENY',
14
+ 'cross-origin-resource-policy': 'same-origin'
15
+ };
16
+ const CSP = "default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; manifest-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
17
+ const STATUS = { unauthorized: 401, remote_denied: 403, rate_limited: 429, remote_busy: 409, remote_full: 409, session_busy: 409, terminal_has_draft: 409, too_large: 413, not_found: 404 };
18
+ function reply(response, status, body) {
19
+ response.writeHead(status, { ...HEADERS, 'content-type': 'application/json; charset=utf-8' });
20
+ response.end(JSON.stringify(body));
21
+ }
22
+ function failure(response, error) {
23
+ const shape = toErrorShape(error);
24
+ reply(response, STATUS[shape.code] ?? 400, { error: { code: shape.code, message: shape.message } });
25
+ }
26
+ async function readBody(request, limit) {
27
+ const chunks = [];
28
+ let size = 0;
29
+ for await (const chunk of request) {
30
+ size += chunk.length;
31
+ if (size > limit)
32
+ throw new HarnessError('too_large', 'This message is too large.');
33
+ chunks.push(chunk);
34
+ }
35
+ try {
36
+ const value = JSON.parse(Buffer.concat(chunks).toString('utf8'));
37
+ if (value && typeof value === 'object' && !Array.isArray(value))
38
+ return value;
39
+ }
40
+ catch { }
41
+ throw new HarnessError('invalid_request', 'The request is not JSON.');
42
+ }
43
+ /** Starts the server on one address. `port` 0 picks a free one. */
44
+ export async function listenRemote(host, port, routes) {
45
+ const sockets = new Set();
46
+ const streams = new Set();
47
+ let authority = '';
48
+ const handle = async (request, response) => {
49
+ // A page on another site cannot reach this server through DNS it controls (rebinding), and no
50
+ // other origin may post to it.
51
+ if (request.headers.host !== authority)
52
+ return reply(response, 421, { error: { code: 'wrong_host', message: 'Open the link shown in the terminal.' } });
53
+ const origin = request.headers.origin;
54
+ if (origin !== undefined && origin !== `http://${authority}`)
55
+ return reply(response, 403, { error: { code: 'wrong_origin', message: 'Requests from other sites are refused.' } });
56
+ const url = new URL(request.url ?? '/', `http://${authority}`);
57
+ if (request.method === 'GET' && url.pathname === '/rc/events') {
58
+ const deviceId = url.searchParams.get('d') ?? '';
59
+ const frame = url.searchParams.get('f') ?? '';
60
+ let ended = false;
61
+ let opened = false;
62
+ let ping = null;
63
+ // The route sends its first frame as the stream opens, so the headers go out with it.
64
+ const begin = () => {
65
+ if (opened)
66
+ return;
67
+ opened = true;
68
+ response.writeHead(200, { ...HEADERS, 'content-type': 'text/event-stream; charset=utf-8', connection: 'keep-alive' });
69
+ response.write(': open\n\n');
70
+ };
71
+ const stream = {
72
+ send: (data) => {
73
+ if (ended)
74
+ return;
75
+ begin();
76
+ response.write(`data: ${data}\n\n`);
77
+ },
78
+ end: () => {
79
+ if (ended)
80
+ return;
81
+ ended = true;
82
+ streams.delete(stream);
83
+ if (ping)
84
+ clearInterval(ping);
85
+ response.end();
86
+ }
87
+ };
88
+ let release;
89
+ try {
90
+ release = routes.stream(deviceId, frame, stream);
91
+ }
92
+ catch (error) {
93
+ return failure(response, error);
94
+ }
95
+ begin();
96
+ if (ended)
97
+ return release();
98
+ streams.add(stream);
99
+ ping = setInterval(() => { if (!ended)
100
+ response.write(': ping\n\n'); }, PING_MS);
101
+ ping.unref?.();
102
+ request.on('close', () => {
103
+ stream.end();
104
+ release();
105
+ });
106
+ return;
107
+ }
108
+ if (request.method === 'POST' && (url.pathname === '/rc/hello' || url.pathname === '/rc/call')) {
109
+ try {
110
+ const hello = url.pathname === '/rc/hello';
111
+ const body = await readBody(request, hello ? MAX_HELLO : MAX_CALL);
112
+ return reply(response, 200, hello ? await routes.hello(body) : await routes.call(body));
113
+ }
114
+ catch (error) {
115
+ return failure(response, error);
116
+ }
117
+ }
118
+ if (request.method === 'GET') {
119
+ const asset = routes.asset(url.pathname);
120
+ if (asset) {
121
+ response.writeHead(200, { ...HEADERS, 'content-type': asset.type, 'content-security-policy': CSP });
122
+ response.end(asset.body);
123
+ return;
124
+ }
125
+ }
126
+ reply(response, 404, { error: { code: 'not_found', message: 'Not found.' } });
127
+ };
128
+ const server = createServer((request, response) => {
129
+ handle(request, response).catch((error) => { if (!response.headersSent)
130
+ failure(response, error); });
131
+ });
132
+ server.on('connection', (socket) => {
133
+ sockets.add(socket);
134
+ socket.on('close', () => sockets.delete(socket));
135
+ });
136
+ await new Promise((resolve, reject) => {
137
+ server.once('error', reject);
138
+ server.listen({ host, port, exclusive: true }, () => {
139
+ server.off('error', reject);
140
+ resolve();
141
+ });
142
+ });
143
+ const bound = server.address().port;
144
+ authority = `${host.includes(':') ? `[${host}]` : host}:${bound}`;
145
+ return {
146
+ host,
147
+ port: bound,
148
+ close: async () => {
149
+ for (const stream of [...streams])
150
+ stream.end();
151
+ await new Promise((resolve) => {
152
+ server.close(() => resolve());
153
+ for (const socket of sockets)
154
+ socket.destroy();
155
+ });
156
+ }
157
+ };
158
+ }
@@ -0,0 +1,125 @@
1
+ import type { RunEvent } from "../sessions/contracts.js";
2
+ import { type RemoteAddress, type RemoteAddressKind, type RemoteInterfaceChoice } from "./addresses.js";
3
+ import { type PairRequest } from "./pairing.js";
4
+ /** The session remote control drives. Terminals supplies it; tests supply a fake. */
5
+ export interface RemoteSessionPort {
6
+ sessionId: string;
7
+ /** A short title for the phone's header. */
8
+ title(): string;
9
+ send(text: string): Promise<{
10
+ runId: string;
11
+ }>;
12
+ /** One of the terminal controller's decision calls. */
13
+ control(call: 'terminal.interrupt' | 'terminal.approve' | 'terminal.answer' | 'terminal.answers' | 'terminal.pending.wait', params: Record<string, unknown>): Promise<unknown>;
14
+ /** The conversation so far, as saved run events, oldest first. */
15
+ history(): RunEvent[];
16
+ /** Events of this session's own runs, after they are redacted and saved. */
17
+ listen(listener: (event: RunEvent) => void): () => void;
18
+ state(): {
19
+ ready: boolean;
20
+ active: boolean;
21
+ };
22
+ }
23
+ /** What the phone is sent for one run event. Everything else in an event stays on the computer. */
24
+ export type PhoneEvent = {
25
+ kind: 'delta';
26
+ runId: string;
27
+ messageId: string;
28
+ text: string;
29
+ } | {
30
+ kind: 'message';
31
+ runId: string;
32
+ messageId: string;
33
+ role: 'user' | 'assistant';
34
+ text: string;
35
+ } | {
36
+ kind: 'tool';
37
+ runId: string;
38
+ id: string;
39
+ name: string;
40
+ phase: 'start' | 'end';
41
+ summary?: string;
42
+ ok?: boolean;
43
+ } | {
44
+ kind: 'run';
45
+ runId: string;
46
+ status: string;
47
+ } | {
48
+ kind: 'todo';
49
+ runId: string;
50
+ items: Array<{
51
+ text: string;
52
+ status: string;
53
+ }>;
54
+ };
55
+ export declare function projectEvent(event: RunEvent): PhoneEvent | null;
56
+ /** A pending decision as the phone shows it. The approval's full action text is kept whole. */
57
+ export declare function projectRequest(request: Record<string, unknown>): Record<string, unknown>;
58
+ /** What the phone may type. Commands and shell escapes belong to the terminal (INV-RC-6). */
59
+ export declare function checkPrompt(value: unknown): string;
60
+ export interface RemoteStatus {
61
+ running: boolean;
62
+ revision: number;
63
+ url: string | null;
64
+ kind: RemoteAddressKind | null;
65
+ /** Which address kinds this computer has, for switching. */
66
+ kinds: RemoteAddressKind[];
67
+ devices: Array<{
68
+ id: string;
69
+ label: string;
70
+ connected: boolean;
71
+ }>;
72
+ request: PairRequest | null;
73
+ warning: string | null;
74
+ }
75
+ export interface RemoteControlOptions {
76
+ /** Addresses to choose from; the computer's own by default. */
77
+ addresses?: () => RemoteAddress[];
78
+ /** Listen here instead of the chosen address, for tests. */
79
+ bindHost?: string;
80
+ port?: number;
81
+ /** The host's product name for the phone page (`env.productName`). */
82
+ productName?: string;
83
+ }
84
+ export declare class RemoteControl {
85
+ private readonly session;
86
+ private readonly options;
87
+ private listening;
88
+ private address;
89
+ private pairing;
90
+ private readonly connections;
91
+ private revision;
92
+ private readonly waiters;
93
+ private unlisten;
94
+ private pendingLoop;
95
+ private pendingRequests;
96
+ private starting;
97
+ private ended;
98
+ constructor(session: RemoteSessionPort, options?: RemoteControlOptions);
99
+ private changed;
100
+ status(): RemoteStatus;
101
+ /** Returns at once for a changed revision, otherwise after a change or 20 seconds. */
102
+ waitStatus(revision: unknown): Promise<RemoteStatus>;
103
+ private base;
104
+ /** Starts listening, or switches address kind. Starting again with the same kind changes nothing. */
105
+ start(choice?: RemoteInterfaceChoice): Promise<RemoteStatus>;
106
+ private listen;
107
+ /** A new pairing link; every paired phone is disconnected. */
108
+ rotate(): RemoteStatus;
109
+ confirm(requestId: string, allow: boolean): RemoteStatus;
110
+ /** Stops remote control; phones are told the session is no longer reachable. */
111
+ stop(): Promise<RemoteStatus>;
112
+ /** The terminal session ended (INV-RC-5). */
113
+ dispose(): Promise<void>;
114
+ private close;
115
+ private get productName();
116
+ private asset;
117
+ private send;
118
+ private broadcast;
119
+ private drop;
120
+ private openStream;
121
+ /** Follows the controller's decisions while a phone is connected, as the terminal itself does. */
122
+ private watchPending;
123
+ private call;
124
+ private dispatch;
125
+ }