@cuna_labs/cli 0.1.0 → 0.1.1
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/bin/cuna.js +15 -2
- package/dist/bin/cuna.js.map +1 -1
- package/dist/cli/command-help.js +2 -2
- package/dist/cli/command-help.js.map +1 -1
- package/dist/cli/first-line.d.ts +80 -0
- package/dist/cli/first-line.d.ts.map +1 -0
- package/dist/cli/first-line.js +119 -0
- package/dist/cli/first-line.js.map +1 -0
- package/dist/cli/help.js +1 -1
- package/dist/cli/process-entrypoint.d.ts +3 -0
- package/dist/cli/process-entrypoint.d.ts.map +1 -1
- package/dist/cli/process-entrypoint.js +3 -1
- package/dist/cli/process-entrypoint.js.map +1 -1
- package/dist/cli/progress-line.d.ts +78 -0
- package/dist/cli/progress-line.d.ts.map +1 -0
- package/dist/cli/progress-line.js +94 -0
- package/dist/cli/progress-line.js.map +1 -0
- package/dist/cli/recorded-launch-prompt.d.ts +83 -0
- package/dist/cli/recorded-launch-prompt.d.ts.map +1 -0
- package/dist/cli/recorded-launch-prompt.js +80 -0
- package/dist/cli/recorded-launch-prompt.js.map +1 -0
- package/dist/cli/run.d.ts +19 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +233 -75
- package/dist/cli/run.js.map +1 -1
- package/dist/commands/commands.js +6 -4
- package/dist/commands/commands.js.map +1 -1
- package/dist/journey/account-identity.d.ts +55 -0
- package/dist/journey/account-identity.d.ts.map +1 -0
- package/dist/journey/account-identity.js +69 -0
- package/dist/journey/account-identity.js.map +1 -0
- package/dist/journey/api-effects.d.ts +9 -0
- package/dist/journey/api-effects.d.ts.map +1 -1
- package/dist/journey/api-effects.js +172 -19
- package/dist/journey/api-effects.js.map +1 -1
- package/dist/journey/index.d.ts +3 -1
- package/dist/journey/index.d.ts.map +1 -1
- package/dist/journey/index.js +2 -0
- package/dist/journey/index.js.map +1 -1
- package/dist/journey/orchestrator.d.ts +24 -0
- package/dist/journey/orchestrator.d.ts.map +1 -1
- package/dist/journey/orchestrator.js +21 -3
- package/dist/journey/orchestrator.js.map +1 -1
- package/dist/journey/provider-launch-intent.d.ts +11 -0
- package/dist/journey/provider-launch-intent.d.ts.map +1 -1
- package/dist/journey/provider-launch-intent.js +50 -15
- package/dist/journey/provider-launch-intent.js.map +1 -1
- package/dist/journey/remote-workspace.d.ts +11 -0
- package/dist/journey/remote-workspace.d.ts.map +1 -1
- package/dist/journey/remote-workspace.js +94 -14
- package/dist/journey/remote-workspace.js.map +1 -1
- package/dist/journey/root-entry.d.ts +15 -0
- package/dist/journey/root-entry.d.ts.map +1 -1
- package/dist/journey/root-entry.js +18 -6
- package/dist/journey/root-entry.js.map +1 -1
- package/dist/journey/selection.d.ts +9 -0
- package/dist/journey/selection.d.ts.map +1 -1
- package/dist/journey/selection.js +14 -2
- package/dist/journey/selection.js.map +1 -1
- package/dist/journey/session-disposition.d.ts +45 -0
- package/dist/journey/session-disposition.d.ts.map +1 -0
- package/dist/journey/session-disposition.js +7 -0
- package/dist/journey/session-disposition.js.map +1 -0
- package/dist/journey/wait-policy.d.ts +218 -0
- package/dist/journey/wait-policy.d.ts.map +1 -0
- package/dist/journey/wait-policy.js +233 -0
- package/dist/journey/wait-policy.js.map +1 -0
- package/dist/journey/workspace-effects.d.ts +7 -1
- package/dist/journey/workspace-effects.d.ts.map +1 -1
- package/dist/journey/workspace-effects.js +131 -14
- package/dist/journey/workspace-effects.js.map +1 -1
- package/dist/machines/explorer.js +2 -2
- package/dist/machines/explorer.js.map +1 -1
- package/dist/machines/machine-first.d.ts.map +1 -1
- package/dist/machines/machine-first.js +2 -0
- package/dist/machines/machine-first.js.map +1 -1
- package/dist/machines/session-actionability.js +10 -0
- package/dist/machines/session-actionability.js.map +1 -1
- package/dist/machines/session-visibility.d.ts +2 -0
- package/dist/machines/session-visibility.d.ts.map +1 -1
- package/dist/machines/session-visibility.js +5 -0
- package/dist/machines/session-visibility.js.map +1 -1
- package/dist/platform/process-lock.d.ts +21 -0
- package/dist/platform/process-lock.d.ts.map +1 -0
- package/dist/platform/process-lock.js +114 -0
- package/dist/platform/process-lock.js.map +1 -0
- package/dist/runtime/boundary.d.ts +2 -0
- package/dist/runtime/boundary.d.ts.map +1 -1
- package/dist/runtime/boundary.js +155 -8
- package/dist/runtime/boundary.js.map +1 -1
- package/dist/runtime/node-foreground-session.d.ts +10 -0
- package/dist/runtime/node-foreground-session.d.ts.map +1 -1
- package/dist/runtime/node-foreground-session.js +112 -4
- package/dist/runtime/node-foreground-session.js.map +1 -1
- package/dist/runtime/terminal-client-identity.d.ts +66 -0
- package/dist/runtime/terminal-client-identity.d.ts.map +1 -0
- package/dist/runtime/terminal-client-identity.js +184 -0
- package/dist/runtime/terminal-client-identity.js.map +1 -0
- package/dist/runtime/terminal-transport.js +2 -2
- package/dist/runtime/terminal-transport.js.map +1 -1
- package/dist/sync/continuous-sync-supervisor.d.ts +64 -1
- package/dist/sync/continuous-sync-supervisor.d.ts.map +1 -1
- package/dist/sync/continuous-sync-supervisor.js +247 -28
- package/dist/sync/continuous-sync-supervisor.js.map +1 -1
- package/dist/sync/journal.js +9 -39
- package/dist/sync/journal.js.map +1 -1
- package/dist/sync/workspace-sync-product-service.d.ts +83 -1
- package/dist/sync/workspace-sync-product-service.d.ts.map +1 -1
- package/dist/sync/workspace-sync-product-service.js +324 -12
- package/dist/sync/workspace-sync-product-service.js.map +1 -1
- package/dist/terminal/foreground.d.ts.map +1 -1
- package/dist/terminal/foreground.js +82 -15
- package/dist/terminal/foreground.js.map +1 -1
- package/dist/terminal/workbench.d.ts +7 -0
- package/dist/terminal/workbench.d.ts.map +1 -1
- package/dist/terminal/workbench.js +24 -6
- package/dist/terminal/workbench.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { chmod, lstat, mkdir, realpath, rename, unlink } from "node:fs/promises";
|
|
3
|
+
import { createConnection, createServer } from "node:net";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
/**
|
|
7
|
+
* Take the lock named by `(scope, key)`, or answer `undefined` when another
|
|
8
|
+
* live process holds it. `undefined` is an ordinary answer, not a fault.
|
|
9
|
+
*/
|
|
10
|
+
export async function tryAcquireProcessLock(scope, key) {
|
|
11
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/u.test(scope))
|
|
12
|
+
throw new TypeError("A process lock scope must be a short lowercase name.");
|
|
13
|
+
const endpoint = await lockEndpoint(scope, key);
|
|
14
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
15
|
+
const server = createServer((socket) => socket.destroy());
|
|
16
|
+
try {
|
|
17
|
+
await listen(server, endpoint);
|
|
18
|
+
return heldLock(server);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
await closeUnbound(server);
|
|
22
|
+
if (error.code !== "EADDRINUSE")
|
|
23
|
+
throw error;
|
|
24
|
+
if (process.platform === "win32" || await socketIsActive(endpoint))
|
|
25
|
+
return undefined;
|
|
26
|
+
await removeStaleSocket(endpoint);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
async function lockEndpoint(scope, key) {
|
|
32
|
+
const digest = createHash("sha256").update(`cuna-process-lock-v1\0${scope}\0`).update(key).digest("hex");
|
|
33
|
+
if (process.platform === "win32")
|
|
34
|
+
return `\\\\.\\pipe\\cuna-${scope}-${digest}`;
|
|
35
|
+
const uid = process.getuid?.();
|
|
36
|
+
if (uid === undefined)
|
|
37
|
+
throw new Error("The process lock needs a user identity on this platform.");
|
|
38
|
+
const directory = join(await realpath(tmpdir()), `.cuna-${uid}`);
|
|
39
|
+
try {
|
|
40
|
+
await mkdir(directory, { mode: 0o700 });
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error.code !== "EEXIST")
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
const entry = await lstat(directory);
|
|
47
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || entry.uid !== uid) {
|
|
48
|
+
throw new Error("The process lock directory is not a private directory of this user.");
|
|
49
|
+
}
|
|
50
|
+
await chmod(directory, 0o700);
|
|
51
|
+
return join(directory, `${scope}-${digest.slice(0, 32)}.sock`);
|
|
52
|
+
}
|
|
53
|
+
async function listen(server, endpoint) {
|
|
54
|
+
await new Promise((resolveListen, rejectListen) => {
|
|
55
|
+
const onError = (error) => {
|
|
56
|
+
server.removeListener("listening", onListening);
|
|
57
|
+
rejectListen(error);
|
|
58
|
+
};
|
|
59
|
+
const onListening = () => {
|
|
60
|
+
server.removeListener("error", onError);
|
|
61
|
+
resolveListen();
|
|
62
|
+
};
|
|
63
|
+
server.once("error", onError);
|
|
64
|
+
server.once("listening", onListening);
|
|
65
|
+
server.listen(endpoint);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function heldLock(server) {
|
|
69
|
+
// A lock must not keep the process alive on its own.
|
|
70
|
+
server.unref();
|
|
71
|
+
let released = false;
|
|
72
|
+
return Object.freeze({
|
|
73
|
+
release: async () => {
|
|
74
|
+
if (released)
|
|
75
|
+
return;
|
|
76
|
+
released = true;
|
|
77
|
+
// Node removes a Unix socket's pathname while closing. Never unlink
|
|
78
|
+
// after: another process may already have bound the name again.
|
|
79
|
+
await new Promise((resolveClose) => server.close(() => resolveClose()));
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async function closeUnbound(server) {
|
|
84
|
+
if (server.listening)
|
|
85
|
+
await new Promise((resolveClose) => server.close(() => resolveClose()));
|
|
86
|
+
}
|
|
87
|
+
async function socketIsActive(endpoint) {
|
|
88
|
+
return new Promise((resolveProbe) => {
|
|
89
|
+
const socket = createConnection(endpoint);
|
|
90
|
+
socket.once("connect", () => {
|
|
91
|
+
socket.destroy();
|
|
92
|
+
resolveProbe(true);
|
|
93
|
+
});
|
|
94
|
+
socket.once("error", (error) => {
|
|
95
|
+
socket.destroy();
|
|
96
|
+
resolveProbe(error.code !== "ECONNREFUSED" && error.code !== "ENOENT");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async function removeStaleSocket(endpoint) {
|
|
101
|
+
const stale = `${endpoint}.stale-${process.pid}-${randomUUID()}`;
|
|
102
|
+
try {
|
|
103
|
+
const entry = await lstat(endpoint);
|
|
104
|
+
if (!entry.isSocket() || entry.isSymbolicLink())
|
|
105
|
+
throw new Error("The stale process lock is not a socket.");
|
|
106
|
+
await rename(endpoint, stale);
|
|
107
|
+
await unlink(stale);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (error.code !== "ENOENT")
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=process-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-lock.js","sourceRoot":"","sources":["../../src/platform/process-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -93,6 +93,8 @@ export interface RuntimeBoundaryOptions {
|
|
|
93
93
|
readonly readyTimeoutMs?: number;
|
|
94
94
|
readonly outputDeliveryTimeoutMs?: number;
|
|
95
95
|
readonly heartbeatTimeoutMs?: number;
|
|
96
|
+
/** How long sent input may wait for its acknowledgement; see `INPUT_ACKNOWLEDGEMENT_DEADLINE_MS`. */
|
|
97
|
+
readonly inputAcknowledgementTimeoutMs?: number;
|
|
96
98
|
readonly onTerminalReady?: (snapshot: RuntimeTerminalSnapshot) => void | Promise<void>;
|
|
97
99
|
readonly onTerminalGeometry?: (event: {
|
|
98
100
|
readonly snapshot: RuntimeTerminalSnapshot;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/runtime/boundary.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAGvF,OAAO,EACL,mBAAmB,EAEnB,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAQL,KAAK,uBAAuB,EAM5B,KAAK,aAAa,EAClB,KAAK,uBAAuB,EAE7B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAA6C,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGhH,OAAO,EAOL,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAE1B,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/runtime/boundary.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/boundary.js
CHANGED
|
@@ -8,6 +8,62 @@ import { admitCapability, writerTransferCapability } from "./capability-gate.js"
|
|
|
8
8
|
import { RuntimeBoundaryError, runtimeFailure, terminalHistoryGap } from "./errors.js";
|
|
9
9
|
import { assertReadyPayloadMatches, assertRemoteAgentSessionEvidence, validateTerminalGrant, } from "./terminal-transport.js";
|
|
10
10
|
const MAX_RUNTIME_EVIDENCE_TTL_MS = 5 * 60_000;
|
|
11
|
+
/**
|
|
12
|
+
* How long a terminal attach waits for the server to prove the terminal
|
|
13
|
+
* capability before reporting the refusal it saw last. Two supervisor
|
|
14
|
+
* heartbeat periods (the slowest measured was 19 s) plus the lease renewal
|
|
15
|
+
* they carry; long enough for a lapsed lease or a not-yet-attested PTY to
|
|
16
|
+
* turn into a proof, short enough that a person is not left staring.
|
|
17
|
+
*/
|
|
18
|
+
const TERMINAL_ADMISSION_WAIT_MS = 45_000;
|
|
19
|
+
const TERMINAL_ADMISSION_POLL_MS = 3_000;
|
|
20
|
+
/**
|
|
21
|
+
* Refusals a later capability read can turn into an admission. Everything
|
|
22
|
+
* else is reported at once: a gone process, a supervisor that needs an
|
|
23
|
+
* update, a capability this server does not offer, a snapshot for another
|
|
24
|
+
* resource.
|
|
25
|
+
*/
|
|
26
|
+
const TERMINAL_ADMISSION_TRANSIENT_REASONS = new Set([
|
|
27
|
+
"runtime_lease_expired",
|
|
28
|
+
"supervisor_registry_unavailable",
|
|
29
|
+
]);
|
|
30
|
+
function terminalAdmissionMayResolveByWaiting(error) {
|
|
31
|
+
if (!(error instanceof RuntimeBoundaryError))
|
|
32
|
+
return false;
|
|
33
|
+
if (error.code !== "capability_unavailable" && error.code !== "capability_unknown")
|
|
34
|
+
return false;
|
|
35
|
+
const reason = error.safeDetails?.reason_code;
|
|
36
|
+
// `temporarily_unavailable` with no named reason, or `unknown` availability
|
|
37
|
+
// with no named reason, are the server's own "not yet"; a named reason is
|
|
38
|
+
// waited on only when it is one that renews itself.
|
|
39
|
+
return reason === undefined || TERMINAL_ADMISSION_TRANSIENT_REASONS.has(String(reason));
|
|
40
|
+
}
|
|
41
|
+
function waitForTerminalAdmission(milliseconds, signal) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
if (signal?.aborted === true) {
|
|
44
|
+
reject(signal.reason);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const timer = setTimeout(() => { signal?.removeEventListener("abort", onAbort); resolve(); }, milliseconds);
|
|
48
|
+
const onAbort = () => { clearTimeout(timer); reject(signal?.reason); };
|
|
49
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* How long a sent key may go unacknowledged before the CLI treats the
|
|
54
|
+
* connection as stalled, says so, and reconnects.
|
|
55
|
+
*
|
|
56
|
+
* Measured 2026-09-22 17:44Z on qa5 (R12): keys i7–i9 were sent and never
|
|
57
|
+
* acknowledged, and the screen said nothing for 19.2 s, until Windows gave up
|
|
58
|
+
* retransmitting and aborted the socket. The only Cuna bound on that path was
|
|
59
|
+
* the 45 s heartbeat, so a stalled path owned the terminal for up to 45 s.
|
|
60
|
+
*
|
|
61
|
+
* 5 000 ms is 1.75x the slowest acknowledgement seen on a healthy path, 2 847 ms
|
|
62
|
+
* (r12rep1; the rest 134–532 ms), and a quarter of the 18.9 s Windows needs to
|
|
63
|
+
* abort. A deadline that fires on a slow but live path costs one reconnect; the
|
|
64
|
+
* input is never resent either way (`retireInputAcceptance`).
|
|
65
|
+
*/
|
|
66
|
+
const INPUT_ACKNOWLEDGEMENT_DEADLINE_MS = 5_000;
|
|
11
67
|
export class CunaRuntimeBoundary {
|
|
12
68
|
#options;
|
|
13
69
|
#clock;
|
|
@@ -182,7 +238,7 @@ export class CunaRuntimeBoundary {
|
|
|
182
238
|
inputSequence: 0n,
|
|
183
239
|
acknowledgedInputSequence: 0n,
|
|
184
240
|
inputContinuity: "none",
|
|
185
|
-
pendingInputSequences: new
|
|
241
|
+
pendingInputSequences: new Map(),
|
|
186
242
|
historicalInputUncertainty: false,
|
|
187
243
|
retiredInputSequence: 0n,
|
|
188
244
|
outputSequence: 0n,
|
|
@@ -408,8 +464,9 @@ export class CunaRuntimeBoundary {
|
|
|
408
464
|
});
|
|
409
465
|
entry.wireSequence = inputSequence;
|
|
410
466
|
entry.inputSequence = inputSequence;
|
|
411
|
-
entry.pendingInputSequences.
|
|
467
|
+
entry.pendingInputSequences.set(entry.inputSequence, this.#clock());
|
|
412
468
|
entry.inputContinuity = "uncertain";
|
|
469
|
+
this.#armInputAcknowledgementDeadline(entry, authority.connection, authority.connectionRevision);
|
|
413
470
|
await authority.connection.send(frame);
|
|
414
471
|
this.#publish(entry);
|
|
415
472
|
});
|
|
@@ -1093,14 +1150,43 @@ export class CunaRuntimeBoundary {
|
|
|
1093
1150
|
throw new AggregateError(failures, "The Cuna runtime stopped with cleanup failures.");
|
|
1094
1151
|
}
|
|
1095
1152
|
async #admitRemoteTerminal(agentSessionId, signal) {
|
|
1096
|
-
const
|
|
1097
|
-
const capability = admitCapability(snapshot, {
|
|
1153
|
+
const requirement = {
|
|
1098
1154
|
id: this.#options.terminalCapabilityId,
|
|
1099
1155
|
scope: "agent_session",
|
|
1100
1156
|
subjectId: agentSessionId,
|
|
1101
1157
|
surface: "cli",
|
|
1102
1158
|
interaction: "native",
|
|
1103
|
-
}
|
|
1159
|
+
};
|
|
1160
|
+
/*
|
|
1161
|
+
* The terminal capability is a moving fact: the server proves it only
|
|
1162
|
+
* while the AgentSession's runtime lease is fresh and its PTY is attested,
|
|
1163
|
+
* and both are renewed by a supervisor heartbeat every 10-19 s. Read once,
|
|
1164
|
+
* a fresh session is refused at two ordinary moments — the first seconds
|
|
1165
|
+
* after launch, before the attachment is attested, and the last seconds of
|
|
1166
|
+
* a lease window, before the next heartbeat renews it. Measured 2026-09-21
|
|
1167
|
+
* on Machine bd94a624: "could not verify live terminal control" 107 s after
|
|
1168
|
+
* launch, then "needs a fresh runtime check" on a running session whose
|
|
1169
|
+
* lease had 0.7 s left. A person reading either is told to retry something
|
|
1170
|
+
* that resolves itself within a heartbeat, so the CLI waits for it here,
|
|
1171
|
+
* bounded, and only then reports the refusal it saw last. A reason that
|
|
1172
|
+
* cannot change by waiting (a gone process, a supervisor that needs an
|
|
1173
|
+
* update) is reported at once.
|
|
1174
|
+
*/
|
|
1175
|
+
const deadline = this.#clock() + TERMINAL_ADMISSION_WAIT_MS;
|
|
1176
|
+
let snapshot;
|
|
1177
|
+
let capability;
|
|
1178
|
+
for (;;) {
|
|
1179
|
+
snapshot = await this.#options.controlPlane.discoverCapabilities("agent_session", agentSessionId, signal);
|
|
1180
|
+
try {
|
|
1181
|
+
capability = admitCapability(snapshot, requirement, this.#clock());
|
|
1182
|
+
break;
|
|
1183
|
+
}
|
|
1184
|
+
catch (error) {
|
|
1185
|
+
if (!terminalAdmissionMayResolveByWaiting(error) || this.#clock() >= deadline || signal?.aborted === true)
|
|
1186
|
+
throw error;
|
|
1187
|
+
await waitForTerminalAdmission(TERMINAL_ADMISSION_POLL_MS, signal);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1104
1190
|
const observation = assertRemoteAgentSessionEvidence({
|
|
1105
1191
|
evidence: await this.#options.controlPlane.observeAgentSession(agentSessionId, signal),
|
|
1106
1192
|
expectedAgentSessionId: agentSessionId,
|
|
@@ -1237,10 +1323,10 @@ export class CunaRuntimeBoundary {
|
|
|
1237
1323
|
#requireGrantCapability(entry, name) {
|
|
1238
1324
|
const matches = entry.capabilities.filter((capability) => capability.name === name);
|
|
1239
1325
|
if (matches.length !== 1 || matches[0]?.availability === "unknown") {
|
|
1240
|
-
throw runtimeFailure("capability_unknown", `Cuna cannot prove terminal capability ${name}
|
|
1326
|
+
throw runtimeFailure("capability_unknown", `Cuna cannot prove terminal capability ${name}.`, { safeDetails: { capability: name } });
|
|
1241
1327
|
}
|
|
1242
1328
|
if (matches[0]?.availability !== "supported") {
|
|
1243
|
-
throw runtimeFailure("capability_unsupported", `Terminal capability ${name} is unsupported
|
|
1329
|
+
throw runtimeFailure("capability_unsupported", `Terminal capability ${name} is unsupported.`, { safeDetails: { capability: name } });
|
|
1244
1330
|
}
|
|
1245
1331
|
}
|
|
1246
1332
|
async #pump(entry, connection, connectionRevision, iterator) {
|
|
@@ -1501,7 +1587,7 @@ export class CunaRuntimeBoundary {
|
|
|
1501
1587
|
payload.meaning !== "durably_accepted_not_executed") {
|
|
1502
1588
|
throw runtimeFailure("terminal_protocol_error", "Terminal input acknowledgement is invalid.");
|
|
1503
1589
|
}
|
|
1504
|
-
for (const sequence of entry.pendingInputSequences) {
|
|
1590
|
+
for (const sequence of entry.pendingInputSequences.keys()) {
|
|
1505
1591
|
if (sequence <= acknowledged)
|
|
1506
1592
|
entry.pendingInputSequences.delete(sequence);
|
|
1507
1593
|
}
|
|
@@ -1684,6 +1770,63 @@ export class CunaRuntimeBoundary {
|
|
|
1684
1770
|
this.#publish(entry);
|
|
1685
1771
|
void connection.close({ code: 1001, reason: "cuna_heartbeat_expired" }).catch(() => undefined);
|
|
1686
1772
|
}
|
|
1773
|
+
#inputAcknowledgementTimeoutMs() {
|
|
1774
|
+
const timeoutMs = this.#options.inputAcknowledgementTimeoutMs ?? INPUT_ACKNOWLEDGEMENT_DEADLINE_MS;
|
|
1775
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 120_000) {
|
|
1776
|
+
throw runtimeFailure("terminal_protocol_error", "The terminal input acknowledgement deadline is invalid.");
|
|
1777
|
+
}
|
|
1778
|
+
return timeoutMs;
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* One timer per attachment, due when the OLDEST unacknowledged input reaches
|
|
1782
|
+
* the deadline. An acknowledgement does not clear it: when it fires it looks
|
|
1783
|
+
* at whichever input is oldest then, and either interrupts or re-arms for it,
|
|
1784
|
+
* so a later key is measured from its own send time, not from the first one.
|
|
1785
|
+
*/
|
|
1786
|
+
#armInputAcknowledgementDeadline(entry, connection, connectionRevision) {
|
|
1787
|
+
if (entry.inputAcknowledgementTimer !== undefined)
|
|
1788
|
+
return;
|
|
1789
|
+
const oldestSentAt = entry.pendingInputSequences.values().next().value;
|
|
1790
|
+
if (oldestSentAt === undefined)
|
|
1791
|
+
return;
|
|
1792
|
+
const deadlineMs = this.#inputAcknowledgementTimeoutMs();
|
|
1793
|
+
entry.inputAcknowledgementTimer = setTimeout(() => {
|
|
1794
|
+
delete entry.inputAcknowledgementTimer;
|
|
1795
|
+
if (this.#closed ||
|
|
1796
|
+
entry.connection !== connection ||
|
|
1797
|
+
entry.connectionRevision !== connectionRevision ||
|
|
1798
|
+
entry.state !== "active")
|
|
1799
|
+
return;
|
|
1800
|
+
const oldest = entry.pendingInputSequences.values().next().value;
|
|
1801
|
+
if (oldest === undefined)
|
|
1802
|
+
return;
|
|
1803
|
+
if (this.#clock() - oldest < deadlineMs) {
|
|
1804
|
+
this.#armInputAcknowledgementDeadline(entry, connection, connectionRevision);
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
this.#expireInputAcknowledgement(entry, connection, connectionRevision);
|
|
1808
|
+
}, Math.max(1, oldestSentAt + deadlineMs - this.#clock()));
|
|
1809
|
+
entry.inputAcknowledgementTimer.unref();
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* The same interruption as an expired heartbeat, reached sooner and named
|
|
1813
|
+
* for its cause. `retireInputAcceptance` marks the unacknowledged keys as
|
|
1814
|
+
* uncertain history, which is what keeps them from ever being resent.
|
|
1815
|
+
*/
|
|
1816
|
+
#expireInputAcknowledgement(entry, connection, connectionRevision) {
|
|
1817
|
+
if (entry.connection !== connection ||
|
|
1818
|
+
entry.connectionRevision !== connectionRevision ||
|
|
1819
|
+
entry.state !== "active")
|
|
1820
|
+
return;
|
|
1821
|
+
this.#clearHeartbeatWatchdog(entry);
|
|
1822
|
+
entry.outputAbort.abort(runtimeFailure("terminal_disconnected", "Terminal input was not acknowledged before its deadline."));
|
|
1823
|
+
entry.state = "interrupted";
|
|
1824
|
+
retireInputAcceptance(entry);
|
|
1825
|
+
entry.outputContinuity = "unknown";
|
|
1826
|
+
entry.reason = "input_ack_timeout";
|
|
1827
|
+
this.#publish(entry);
|
|
1828
|
+
void connection.close({ code: 1001, reason: "cuna_input_ack_timeout" }).catch(() => undefined);
|
|
1829
|
+
}
|
|
1687
1830
|
#nextActiveTab(excluding) {
|
|
1688
1831
|
return [...this.#terminals.values()].find((entry) => entry.tabId !== excluding && entry.state === "active")?.tabId;
|
|
1689
1832
|
}
|
|
@@ -1818,6 +1961,10 @@ function retireInputAcceptance(entry) {
|
|
|
1818
1961
|
if (entry.inputSequence > entry.retiredInputSequence)
|
|
1819
1962
|
entry.retiredInputSequence = entry.inputSequence;
|
|
1820
1963
|
entry.pendingInputSequences.clear();
|
|
1964
|
+
// Retired input has no deadline left to miss; the next scope arms its own.
|
|
1965
|
+
if (entry.inputAcknowledgementTimer !== undefined)
|
|
1966
|
+
clearTimeout(entry.inputAcknowledgementTimer);
|
|
1967
|
+
delete entry.inputAcknowledgementTimer;
|
|
1821
1968
|
if (entry.historicalInputUncertainty)
|
|
1822
1969
|
entry.inputContinuity = "uncertain";
|
|
1823
1970
|
}
|