@sentropic/h2a-runtime 0.98.4 → 0.98.6

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 (72) hide show
  1. package/dist/identity-cull/cull.d.ts.map +1 -1
  2. package/dist/identity-cull/cull.js +35 -89
  3. package/dist/identity-cull/cull.js.map +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +32 -8
  6. package/dist/index.js.map +1 -1
  7. package/dist/llm-mesh-accounts.d.ts +15 -2
  8. package/dist/llm-mesh-accounts.d.ts.map +1 -1
  9. package/dist/llm-mesh-accounts.js +13 -3
  10. package/dist/llm-mesh-accounts.js.map +1 -1
  11. package/dist/native-host.d.ts +3 -1
  12. package/dist/native-host.d.ts.map +1 -1
  13. package/dist/native-host.js +16 -5
  14. package/dist/native-host.js.map +1 -1
  15. package/dist/native-terminal/client.d.ts +2 -2
  16. package/dist/native-terminal/client.d.ts.map +1 -1
  17. package/dist/native-terminal/client.js +2 -2
  18. package/dist/native-terminal/client.js.map +1 -1
  19. package/dist/native-terminal/crash-safe-log.d.ts +17 -0
  20. package/dist/native-terminal/crash-safe-log.d.ts.map +1 -0
  21. package/dist/native-terminal/crash-safe-log.js +79 -0
  22. package/dist/native-terminal/crash-safe-log.js.map +1 -0
  23. package/dist/native-terminal/fault-counters.d.ts +38 -0
  24. package/dist/native-terminal/fault-counters.d.ts.map +1 -0
  25. package/dist/native-terminal/fault-counters.js +46 -0
  26. package/dist/native-terminal/fault-counters.js.map +1 -0
  27. package/dist/native-terminal/host.d.ts +21 -5
  28. package/dist/native-terminal/host.d.ts.map +1 -1
  29. package/dist/native-terminal/host.js +202 -109
  30. package/dist/native-terminal/host.js.map +1 -1
  31. package/dist/native-terminal/journal.d.ts +5 -1
  32. package/dist/native-terminal/journal.d.ts.map +1 -1
  33. package/dist/native-terminal/journal.js +6 -2
  34. package/dist/native-terminal/journal.js.map +1 -1
  35. package/dist/native-terminal/op.d.ts +3 -1
  36. package/dist/native-terminal/op.d.ts.map +1 -1
  37. package/dist/native-terminal/op.js +62 -42
  38. package/dist/native-terminal/op.js.map +1 -1
  39. package/dist/native-terminal/process.d.ts.map +1 -1
  40. package/dist/native-terminal/process.js +41 -12
  41. package/dist/native-terminal/process.js.map +1 -1
  42. package/dist/native-terminal/publication-refusal.d.ts +22 -0
  43. package/dist/native-terminal/publication-refusal.d.ts.map +1 -0
  44. package/dist/native-terminal/publication-refusal.js +26 -0
  45. package/dist/native-terminal/publication-refusal.js.map +1 -0
  46. package/dist/native-terminal/server.d.ts +40 -2
  47. package/dist/native-terminal/server.d.ts.map +1 -1
  48. package/dist/native-terminal/server.js +528 -180
  49. package/dist/native-terminal/server.js.map +1 -1
  50. package/dist/native-terminal/socket-path.d.ts +1 -0
  51. package/dist/native-terminal/socket-path.d.ts.map +1 -1
  52. package/dist/native-terminal/socket-path.js +3 -3
  53. package/dist/native-terminal/socket-path.js.map +1 -1
  54. package/dist/native-terminal/supervisor.d.ts.map +1 -1
  55. package/dist/native-terminal/supervisor.js +69 -1
  56. package/dist/native-terminal/supervisor.js.map +1 -1
  57. package/dist/proc-identity.d.ts +63 -1
  58. package/dist/proc-identity.d.ts.map +1 -1
  59. package/dist/proc-identity.js +98 -1
  60. package/dist/proc-identity.js.map +1 -1
  61. package/dist/pty.d.ts.map +1 -1
  62. package/dist/pty.js +18 -0
  63. package/dist/pty.js.map +1 -1
  64. package/dist/registry.d.ts +15 -0
  65. package/dist/registry.d.ts.map +1 -1
  66. package/dist/registry.js +17 -4
  67. package/dist/registry.js.map +1 -1
  68. package/dist/tmux.d.ts +2 -0
  69. package/dist/tmux.d.ts.map +1 -1
  70. package/dist/tmux.js +14 -2
  71. package/dist/tmux.js.map +1 -1
  72. package/package.json +3 -2
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Named fault counters for supervision. Every fault-tolerant site in the
3
+ * native terminal host bumps its own named counter so an operator can see
4
+ * WHAT is being absorbed and how often — fault containment must never be
5
+ * silent. The contained sites are:
6
+ *
7
+ * - the host log sink (`crash-safe-log.ts`): stderr write failures;
8
+ * - the pty `onData` guard (`host.ts`);
9
+ * - the pty `onExit` guard (`host.ts`);
10
+ * - the pty DSR reply write (`host.ts`);
11
+ * - socket connection errors and client frame destroys (`server.ts`);
12
+ * - the net.Server listener handler (`server.ts`), per classification;
13
+ * - bounded respawn abandonment (`supervisor.ts`);
14
+ * - the process-level diagnostic writes (`process.ts`).
15
+ *
16
+ * Counters are per-process; supervision reads them through
17
+ * `hostFaultCounters()` (tests, an attached debugger, a future ping op).
18
+ */
19
+ export const HOST_FAULT_COUNTERS = {
20
+ // The four contractual R7.5 supervision counters (h2a_host_*), one per
21
+ // NAMED fault site of the spec.
22
+ hostSocketConnectionErrors: "h2a_host_socket_conn_errors",
23
+ hostClientFrameDestroys: "h2a_host_client_frame_destroys",
24
+ hostPtyWriteBreaks: "h2a_host_pty_write_breaks",
25
+ hostPtyCallbackFailures: "h2a_host_pty_callback_failures",
26
+ // Per-site distinct counters kept for finer supervision.
27
+ hostLogStderrSyncWriteFailed: "host-log.stderr-sync-write-failed",
28
+ hostLogStderrStreamError: "host-log.stderr-stream-error",
29
+ ptyDataCallbackContained: "pty.data-callback-contained",
30
+ ptyExitCallbackContained: "pty.exit-callback-contained",
31
+ serverListenerErrorRecoverable: "server.listener-error-recoverable",
32
+ serverListenerErrorExhausted: "server.listener-error-exhausted",
33
+ serverListenerErrorUnusable: "server.listener-error-unusable",
34
+ serverListenerErrorUnknown: "server.listener-error-unknown",
35
+ supervisorRespawnAbandoned: "supervisor.respawn-abandoned",
36
+ processShutdownWriteFailed: "process.shutdown-write-failed",
37
+ processFatalWriteFailed: "process.fatal-write-failed",
38
+ };
39
+ const counts = new Map();
40
+ export function recordHostFault(counter) {
41
+ counts.set(counter, (counts.get(counter) ?? 0) + 1);
42
+ }
43
+ export function hostFaultCounters() {
44
+ return Object.fromEntries(counts);
45
+ }
46
+ //# sourceMappingURL=fault-counters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fault-counters.js","sourceRoot":"","sources":["../../src/native-terminal/fault-counters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,uEAAuE;IACvE,gCAAgC;IAChC,0BAA0B,EAAE,6BAA6B;IACzD,uBAAuB,EAAE,gCAAgC;IACzD,kBAAkB,EAAE,2BAA2B;IAC/C,uBAAuB,EAAE,gCAAgC;IACzD,yDAAyD;IACzD,4BAA4B,EAAE,mCAAmC;IACjE,wBAAwB,EAAE,8BAA8B;IACxD,wBAAwB,EAAE,6BAA6B;IACvD,wBAAwB,EAAE,6BAA6B;IACvD,8BAA8B,EAAE,mCAAmC;IACnE,4BAA4B,EAAE,iCAAiC;IAC/D,2BAA2B,EAAE,gCAAgC;IAC7D,0BAA0B,EAAE,+BAA+B;IAC3D,0BAA0B,EAAE,8BAA8B;IAC1D,0BAA0B,EAAE,+BAA+B;IAC3D,uBAAuB,EAAE,4BAA4B;CAC7C,CAAC;AAEX,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEzC,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC"}
@@ -82,8 +82,13 @@ export declare const H2A_SESSION_TOKEN_ENV_VAR = "H2A_SESSION_TOKEN";
82
82
  * - This enumeration runs ONLY on the leader-absent path (never on every
83
83
  * reap), keeping it rare in practice — the fast leader-start-time check
84
84
  * above it covers the common case.
85
+ *
86
+ * `procRoot` is injected ONLY by tests: a /proc-shaped view that contains
87
+ * exactly the processes a test started (the enumeration is the confinement
88
+ * boundary — the stat/environ reads stay real). Production always reads the
89
+ * real `/proc`.
85
90
  */
86
- export declare function groupCarriesSessionToken(pgid: number, expectedToken: string): boolean;
91
+ export declare function groupCarriesSessionToken(pgid: number, expectedToken: string, procRoot?: string): boolean;
87
92
  /**
88
93
  * True iff two CONSECUTIVE censuses of `pgid` both positively show the SAME,
89
94
  * non-empty set of members and every one of them is a ZOMBIE down to its last
@@ -169,6 +174,16 @@ export type NativeTerminalExit = Readonly<{
169
174
  export type NativeTerminalSessionStatus = "running" | "stopping" | "exited";
170
175
  /** The provenance of an exclusive controller, used only for human-input safety. */
171
176
  export type NativeTerminalControllerActivity = "human" | "automation";
177
+ export type NativeTerminalControllerIdentity = Readonly<{
178
+ pid: number | null;
179
+ tty: string;
180
+ since: string;
181
+ }>;
182
+ export type NativeTerminalControllerOptions = Readonly<{
183
+ takeover?: boolean;
184
+ pid?: number;
185
+ tty?: string;
186
+ }>;
172
187
  export type NativeTerminalSessionState = Readonly<{
173
188
  id: string;
174
189
  generation: string;
@@ -180,11 +195,12 @@ export type NativeTerminalSessionState = Readonly<{
180
195
  stopSignal: string | null;
181
196
  /**
182
197
  * Whether a controller currently holds this session's exclusive input lease.
183
- * Deliberately a BOOLEAN: consumers (restore visibility) only need to know
184
- * that a controlling terminal exists, never who it is — the controller id
185
- * stays private to the host.
198
+ * Restore consumers need only this boolean. Attach rescue also exposes the
199
+ * holder's diagnostic identity and epoch, never its input lease.
186
200
  */
187
201
  controlled: boolean;
202
+ controllerEpoch?: number;
203
+ controller?: NativeTerminalControllerIdentity | null;
188
204
  /**
189
205
  * Most recent human controller/input activity, in epoch milliseconds. A
190
206
  * missing value means an older host did not provide the safety signal and
@@ -391,7 +407,7 @@ export declare class NativeTerminalHost {
391
407
  state(id: string): NativeTerminalSessionState;
392
408
  readOutput(id: string, afterSeq: number): NativeTerminalReplay;
393
409
  attachObserver(id: string): NativeTerminalObserverAttachment;
394
- acquireController(id: string, controllerId: string, activity?: NativeTerminalControllerActivity): NativeTerminalControllerLease;
410
+ acquireController(id: string, controllerId: string, activity?: NativeTerminalControllerActivity, options?: NativeTerminalControllerOptions): NativeTerminalControllerLease;
395
411
  /**
396
412
  * Atomically acquire automation input only when no human activity is recent.
397
413
  * This runs entirely in the host's serialized request handler: a human
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/native-terminal/host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/native-terminal/host.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  import { readFileSync, readdirSync } from "node:fs";
2
2
  import { randomUUID } from "node:crypto";
3
- import { resolve } from "node:path";
3
+ import { join, resolve } from "node:path";
4
+ import { writeHostLogLine } from "./crash-safe-log.js";
5
+ import { HOST_FAULT_COUNTERS, recordHostFault } from "./fault-counters.js";
4
6
  // Durable session identity (incl. pgid) lives in the single registry store by
5
7
  // design — one local store per the owner's durable-state direction (see the
6
8
  // longer rationale on `persistNativeTerminalPgid`/`readNativeTerminalPgid` in
@@ -155,12 +157,12 @@ export const H2A_SESSION_TOKEN_ENV_VAR = "H2A_SESSION_TOKEN";
155
157
  * process that exits mid-scan is silently skipped, never treated as a parse
156
158
  * failure.
157
159
  */
158
- function listProcessGroupMemberPids(pgid) {
160
+ function listProcessGroupMemberPids(pgid, procRoot = "/proc") {
159
161
  if (process.platform !== "linux")
160
162
  return [];
161
163
  let names;
162
164
  try {
163
- names = readdirSync("/proc");
165
+ names = readdirSync(procRoot);
164
166
  }
165
167
  catch {
166
168
  return [];
@@ -170,7 +172,7 @@ function listProcessGroupMemberPids(pgid) {
170
172
  if (!/^\d+$/.test(name))
171
173
  continue;
172
174
  try {
173
- const raw = readFileSync(`/proc/${name}/stat`, "utf8");
175
+ const raw = readFileSync(join(procRoot, name, "stat"), "utf8");
174
176
  const commandEnd = raw.lastIndexOf(")");
175
177
  const fields = raw.slice(commandEnd + 2).split(" ");
176
178
  if (Number(fields[2]) === pgid)
@@ -189,11 +191,11 @@ function listProcessGroupMemberPids(pgid) {
189
191
  * escaping to worry about). Returns undefined on ANY read/parse failure or
190
192
  * a non-Linux platform — same "never guess" contract as `readProcessStartTime`.
191
193
  */
192
- function readProcessSessionToken(pid) {
194
+ function readProcessSessionToken(pid, procRoot = "/proc") {
193
195
  if (process.platform !== "linux")
194
196
  return undefined;
195
197
  try {
196
- const raw = readFileSync(`/proc/${pid}/environ`, "utf8");
198
+ const raw = readFileSync(join(procRoot, String(pid), "environ"), "utf8");
197
199
  const prefix = `${H2A_SESSION_TOKEN_ENV_VAR}=`;
198
200
  for (const entry of raw.split("\0")) {
199
201
  if (entry.startsWith(prefix))
@@ -225,10 +227,15 @@ function readProcessSessionToken(pid) {
225
227
  * - This enumeration runs ONLY on the leader-absent path (never on every
226
228
  * reap), keeping it rare in practice — the fast leader-start-time check
227
229
  * above it covers the common case.
230
+ *
231
+ * `procRoot` is injected ONLY by tests: a /proc-shaped view that contains
232
+ * exactly the processes a test started (the enumeration is the confinement
233
+ * boundary — the stat/environ reads stay real). Production always reads the
234
+ * real `/proc`.
228
235
  */
229
- export function groupCarriesSessionToken(pgid, expectedToken) {
230
- for (const pid of listProcessGroupMemberPids(pgid)) {
231
- if (readProcessSessionToken(pid) === expectedToken)
236
+ export function groupCarriesSessionToken(pgid, expectedToken, procRoot = "/proc") {
237
+ for (const pid of listProcessGroupMemberPids(pgid, procRoot)) {
238
+ if (readProcessSessionToken(pid, procRoot) === expectedToken)
232
239
  return true;
233
240
  }
234
241
  return false;
@@ -587,7 +594,10 @@ export class NativeTerminalHost {
587
594
  this.#log =
588
595
  options.log ??
589
596
  ((line) => {
590
- process.stderr.write(`[h2a-pty-host] ${line}\n`);
597
+ // The default sink must be crash-safe: a plain `process.stderr.write`
598
+ // here reproduces the suspected mechanism of the 2026-10-10/11
599
+ // crash-loop (async EPIPE, unhandled `error`, uncaughtException).
600
+ writeHostLogLine(line);
591
601
  });
592
602
  this.#forceKillTimeoutMs =
593
603
  options.forceKillTimeoutMs ?? NATIVE_TERMINAL_FORCE_KILL_TIMEOUT_MS;
@@ -719,6 +729,7 @@ export class NativeTerminalHost {
719
729
  stopSignal: null,
720
730
  controllerId: null,
721
731
  controllerActivity: null,
732
+ controllerIdentity: null,
722
733
  controllerEpoch: 0,
723
734
  lastHumanActivityAt: null,
724
735
  };
@@ -728,48 +739,88 @@ export class NativeTerminalHost {
728
739
  let dsrTail = "";
729
740
  const resetRelay = new TerminalResetRelay((data) => { record.replay.append(data); }, () => record.replay.resetSequence());
730
741
  record.dataSubscription = record.pty.onData((data) => {
731
- if (record.exit !== null || data.length === 0)
732
- return;
733
- resetRelay.feed(data);
734
- // Answer Device Status Report cursor queries (ESC[6n) like a real
735
- // terminal/tmux: TUIs that require a cursor-position report (muse
736
- // aborts with "cursor position could not be read" without it).
737
- const window = dsrTail + data;
738
- dsrTail = window.slice(-3);
739
- const queries = window.split("\x1b[6n").length - 1;
740
- for (let i = 0; i < queries; i++) {
742
+ try {
743
+ if (record.exit !== null || data.length === 0)
744
+ return;
745
+ resetRelay.feed(data);
746
+ // Answer Device Status Report cursor queries (ESC[6n) like a real
747
+ // terminal/tmux: TUIs that require a cursor-position report (muse
748
+ // aborts with "cursor position could not be read" without it).
749
+ const window = dsrTail + data;
750
+ dsrTail = window.slice(-3);
751
+ const queries = window.split("\x1b[6n").length - 1;
752
+ for (let i = 0; i < queries; i++) {
753
+ try {
754
+ record.pty.write("\x1b[1;1R");
755
+ }
756
+ catch {
757
+ // R7.5 named exemption: expected transport failure, potentially
758
+ // high volume. Count it instead of logging every failed reply.
759
+ recordHostFault(HOST_FAULT_COUNTERS.hostPtyWriteBreaks);
760
+ break;
761
+ }
762
+ }
763
+ }
764
+ catch (error) {
765
+ // node-pty delivers this callback on the event loop shared with every
766
+ // other session; a faulty one must cost at most this session's output
767
+ // relay, never the host. Dispose the subscription so the faulty
768
+ // callback stops firing on every chunk.
769
+ recordHostFault(HOST_FAULT_COUNTERS.ptyDataCallbackContained);
770
+ record.dataSubscription?.dispose();
771
+ this.#reportSessionCallbackFailure("data", record, error);
772
+ }
773
+ });
774
+ record.exitSubscription = record.pty.onExit((event) => {
775
+ try {
776
+ if (record.exit !== null)
777
+ return;
778
+ resetRelay.flush();
779
+ // A durable row exists to make this session reapable after THIS host
780
+ // dies. Once the group is confirmed empty the row can no longer make
781
+ // anything reapable — it is a stale record that a later host death would
782
+ // re-examine against whatever holds that pgid NUMBER by then. Drop it
783
+ // on POSITIVE proof only (see `#pruneDurableRowIfGroupEmpty`), and OFF
784
+ // this callback: this is node-pty's own synchronous exit callback on the
785
+ // host's event loop (see `#schedulePruneOfExitedSession`).
786
+ this.#schedulePruneOfExitedSession(record, "its pty exited");
787
+ const exit = {
788
+ exitCode: event.exitCode,
789
+ };
790
+ if (event.signal !== undefined)
791
+ exit.signal = event.signal;
792
+ record.exit = Object.freeze(exit);
793
+ record.status = "exited";
794
+ this.#invalidateController(record);
795
+ record.dataSubscription?.dispose();
796
+ record.exitSubscription?.dispose();
797
+ }
798
+ catch (error) {
799
+ // Same shared event loop as the data callback; a faulty exit callback
800
+ // must not take the host down. Settle the record from the parts of
801
+ // the event that remain readable so the session cannot linger as
802
+ // "running" with a dead pty, then dispose both subscriptions.
803
+ const exit = { exitCode: -1 };
741
804
  try {
742
- record.pty.write("\x1b[1;1R");
805
+ exit.exitCode = event.exitCode;
806
+ if (event.signal !== undefined)
807
+ exit.signal = event.signal;
743
808
  }
744
809
  catch {
745
- // Child already gone; nothing left to answer.
746
- break;
810
+ // The event's own fields are unreadable; -1 records that the exit
811
+ // was observed but its code was not.
812
+ }
813
+ if (record.exit === null) {
814
+ record.exit = Object.freeze(exit);
815
+ record.status = "exited";
816
+ this.#invalidateController(record);
747
817
  }
818
+ record.dataSubscription?.dispose();
819
+ record.exitSubscription?.dispose();
820
+ recordHostFault(HOST_FAULT_COUNTERS.ptyExitCallbackContained);
821
+ this.#reportSessionCallbackFailure("exit", record, error);
748
822
  }
749
823
  });
750
- record.exitSubscription = record.pty.onExit((event) => {
751
- if (record.exit !== null)
752
- return;
753
- resetRelay.flush();
754
- // A durable row exists to make this session reapable after THIS host
755
- // dies. Once the group is confirmed empty the row can no longer make
756
- // anything reapable — it is a stale record that a later host death would
757
- // re-examine against whatever holds that pgid NUMBER by then. Drop it
758
- // on POSITIVE proof only (see `#pruneDurableRowIfGroupEmpty`), and OFF
759
- // this callback: this is node-pty's own synchronous exit callback on the
760
- // host's event loop (see `#schedulePruneOfExitedSession`).
761
- this.#schedulePruneOfExitedSession(record, "its pty exited");
762
- const exit = {
763
- exitCode: event.exitCode,
764
- };
765
- if (event.signal !== undefined)
766
- exit.signal = event.signal;
767
- record.exit = Object.freeze(exit);
768
- record.status = "exited";
769
- this.#invalidateController(record);
770
- record.dataSubscription?.dispose();
771
- record.exitSubscription?.dispose();
772
- });
773
824
  return this.#snapshot(record);
774
825
  }
775
826
  list() {
@@ -797,21 +848,31 @@ export class NativeTerminalHost {
797
848
  controllerEpoch: record.controllerEpoch,
798
849
  });
799
850
  }
800
- acquireController(id, controllerId, activity = "human") {
851
+ acquireController(id, controllerId, activity = "human", options = {}) {
801
852
  const record = this.#requireControllableSession(id);
802
853
  if (controllerId.trim().length === 0 ||
803
854
  controllerId.length > NATIVE_TERMINAL_MAX_IDENTIFIER_CHARS) {
804
855
  throw new RangeError(`terminal controller id must contain 1-${NATIVE_TERMINAL_MAX_IDENTIFIER_CHARS} characters`);
805
856
  }
806
- if (record.controllerId !== null) {
807
- throw new Error(`terminal session already has a controller: ${id}`);
808
- }
809
857
  if (activity !== "human" && activity !== "automation") {
810
858
  throw new TypeError("terminal controller activity must be human or automation");
811
859
  }
860
+ if (options.pid !== undefined && (!Number.isSafeInteger(options.pid) || options.pid <= 0)) {
861
+ throw new TypeError("terminal controller pid must be a positive safe integer");
862
+ }
863
+ if (options.tty !== undefined && (options.tty.length === 0 || options.tty.length > 256 || /[\p{Cc}\p{Cf}]/u.test(options.tty))) {
864
+ throw new TypeError("terminal controller tty must be a printable name of 1-256 characters");
865
+ }
866
+ if (record.controllerId !== null && options.takeover !== true) {
867
+ const holder = record.controllerIdentity;
868
+ throw new Error(`terminal session already has a controller: ${id}; pid=${holder?.pid ?? "unknown"} tty=${holder?.tty ?? "unknown"} since=${holder?.since ?? "unknown"}; use h2a attach ${id.replace(/^h2a-/, "")} --takeover to detach it`);
869
+ }
870
+ // Serialized with the lease grant: old input and disconnect cleanup are
871
+ // fenced by the new epoch, even if the previous attach is still exiting.
812
872
  record.controllerEpoch += 1;
813
873
  record.controllerId = controllerId;
814
874
  record.controllerActivity = activity;
875
+ record.controllerIdentity = Object.freeze({ pid: options.pid ?? null, tty: options.tty ?? "unknown", since: new Date().toISOString() });
815
876
  if (activity === "human")
816
877
  record.lastHumanActivityAt = Date.now();
817
878
  return Object.freeze({
@@ -1184,66 +1245,10 @@ export class NativeTerminalHost {
1184
1245
  * states.
1185
1246
  */
1186
1247
  #verifyGroupLeaderIdentity(pgid, persistedLeaderStartTime, persistedGroupToken, sessionId, frame = {}) {
1187
- // FRAME CHECK, before any pid-based comparison. Every proof below compares a
1188
- // number persisted earlier with a number read now, and that is meaningful
1189
- // only while both were taken in the same pid namespace and after the same
1190
- // boot (see proc-identity.ts). A row that records a frame which does not
1191
- // match this reader's is unusable in BOTH directions: a start-time mismatch
1192
- // would be read as "recycled" (pruning the row and releasing the socket on a
1193
- // proof that does not hold), and a coincidental MATCH would authorize a
1194
- // group SIGKILL at a group this row never described. Refuse, blocking, which
1195
- // is the behaviour that predates the `recycled` outcome.
1196
- //
1197
- // ONE mismatch is different, and is separated out below: same pid namespace,
1198
- // different BOOT, same MACHINE. That is not an absence of proof, it is the
1199
- // strongest proof available — a reboot ends every process of the boot
1200
- // before it — so it prunes instead of blocking. It is still a refusal:
1201
- // nothing is signalled, because an unrelated live group may hold the pgid
1202
- // number by now.
1203
1248
  const anchoredFrame = frame.pidNamespace !== undefined && frame.bootId !== undefined;
1204
- if (frame.pidNamespace !== undefined || frame.bootId !== undefined) {
1205
- const current = this.#readFrame();
1206
- // The boot proof is admissible only inside ONE pid space, KNOWN on both
1207
- // sides: a pid resolved in another namespace is a different process, and
1208
- // an unknown namespace on either side is no statement at all.
1209
- const sameKnownPidNamespace = frame.pidNamespace !== undefined &&
1210
- current.pidNamespace !== undefined &&
1211
- frame.pidNamespace === current.pidNamespace;
1212
- // ...and only on ONE machine, KNOWN on both sides. The init pid
1213
- // namespace has the same inode on every kernel and boot ids are random
1214
- // per boot, so "same namespace, different boot" is ALSO what a row written
1215
- // by another machine sharing this registry looks like — a machine whose
1216
- // group may be alive right now. Only the machine id tells the two apart.
1217
- const sameKnownMachine = frame.machineId !== undefined &&
1218
- current.machineId !== undefined &&
1219
- frame.machineId === current.machineId;
1220
- const differentKnownBoot = frame.bootId !== undefined &&
1221
- current.bootId !== undefined &&
1222
- frame.bootId !== current.bootId;
1223
- if (sameKnownPidNamespace && sameKnownMachine && differentKnownBoot) {
1224
- // PROOF, not an absence of one: a reboot ends every process of the
1225
- // boot before it, so every process this row describes has ended. The
1226
- // pgid NUMBER may be held by an unrelated live group now, which is
1227
- // exactly why nothing is signalled here and why no diagnostic may
1228
- // point anyone at it.
1229
- this.#pgidGuardCounters.staleBoot += 1;
1230
- this.#log(`NOT signalling process group pgid=${pgid} for session ${sessionId}: this row was written at boot ${String(frame.bootId)} and this reader is at boot ${String(current.bootId)}, in the same pid namespace ${String(current.pidNamespace)} on the same machine ${String(current.machineId)} — the reboot ENDED every process this row describes, so its group is PROVEN gone. The number pgid=${pgid} may belong to an unrelated live group now and must NOT be killed. cause=stale-boot sessionId=${sessionId} pgid=${pgid}`);
1231
- return { proceed: false, cause: "stale-boot" };
1232
- }
1233
- if (frame.pidNamespace !== current.pidNamespace ||
1234
- frame.bootId !== current.bootId) {
1235
- // Says WHY nothing could be proven, because the two shapes call for
1236
- // different checks by whoever reads this: a boot change that only lacks
1237
- // the machine proof is either a reboot of this machine or another
1238
- // machine; anything else is another (or an unknown) pid space.
1239
- const why = sameKnownPidNamespace && differentKnownBoot
1240
- ? `a different boot in the same pid namespace inode is what a previous boot of THIS machine looks like, but also what ANOTHER machine sharing this registry looks like, and the machine ids that would tell them apart are ${frame.machineId === undefined || current.machineId === undefined ? "not known on both sides" : "DIFFERENT"}`
1241
- : "no pid or start-time on it can be re-proven here";
1242
- this.#pgidGuardCounters.foreignFrame += 1;
1243
- this.#log(`REFUSING to act on process group pgid=${pgid} for session ${sessionId}: this row records pid namespace ${String(frame.pidNamespace)}, boot ${String(frame.bootId)} and machine ${String(frame.machineId)}, but this reader is in pid namespace ${String(current.pidNamespace)} at boot ${String(current.bootId)} on machine ${String(current.machineId)} — ${why}. Inspecting pgid=${pgid} HERE proves nothing about this row. PROCESSES MAY SURVIVE UNCOLLECTED. cause=foreign-frame sessionId=${sessionId} pgid=${pgid}`);
1244
- return { proceed: false, cause: "foreign-frame" };
1245
- }
1246
- }
1249
+ const frameRefusal = this.#verifyGroupFrame(pgid, sessionId, frame);
1250
+ if (frameRefusal)
1251
+ return { proceed: false, cause: frameRefusal };
1247
1252
  const currentStartTime = this.#readLeaderStartTime(pgid);
1248
1253
  if (currentStartTime !== undefined) {
1249
1254
  if (persistedLeaderStartTime === undefined) {
@@ -1306,6 +1311,68 @@ export class NativeTerminalHost {
1306
1311
  this.#log(`REFUSING to kill process group pgid=${pgid} for session ${sessionId}: the group leader is UNREADABLE and no surviving member carries the persisted session token — cannot positively prove this group's identity. PROCESSES MAY SURVIVE UNCOLLECTED. cause=membership-unprovable sessionId=${sessionId} pgid=${pgid}`);
1307
1312
  return { proceed: false, cause: "membership-unprovable" };
1308
1313
  }
1314
+ #verifyGroupFrame(pgid, sessionId, frame) {
1315
+ // FRAME CHECK, before any pid-based comparison. Every proof below compares a
1316
+ // number persisted earlier with a number read now, and that is meaningful
1317
+ // only while both were taken in the same pid namespace and after the same
1318
+ // boot (see proc-identity.ts). A row that records a frame which does not
1319
+ // match this reader's is unusable in BOTH directions: a start-time mismatch
1320
+ // would be read as "recycled" (pruning the row and releasing the socket on a
1321
+ // proof that does not hold), and a coincidental MATCH would authorize a
1322
+ // group SIGKILL at a group this row never described. Refuse, blocking, which
1323
+ // is the behaviour that predates the `recycled` outcome.
1324
+ //
1325
+ // ONE mismatch is different, and is separated out below: same pid namespace,
1326
+ // different BOOT, same MACHINE. That is not an absence of proof, it is the
1327
+ // strongest proof available — a reboot ends every process of the boot
1328
+ // before it — so it prunes instead of blocking. It is still a refusal:
1329
+ // nothing is signalled, because an unrelated live group may hold the pgid
1330
+ // number by now.
1331
+ if (frame.pidNamespace !== undefined || frame.bootId !== undefined) {
1332
+ const current = this.#readFrame();
1333
+ // The boot proof is admissible only inside ONE pid space, KNOWN on both
1334
+ // sides: a pid resolved in another namespace is a different process, and
1335
+ // an unknown namespace on either side is no statement at all.
1336
+ const sameKnownPidNamespace = frame.pidNamespace !== undefined &&
1337
+ current.pidNamespace !== undefined &&
1338
+ frame.pidNamespace === current.pidNamespace;
1339
+ // ...and only on ONE machine, KNOWN on both sides. The init pid
1340
+ // namespace has the same inode on every kernel and boot ids are random
1341
+ // per boot, so "same namespace, different boot" is ALSO what a row written
1342
+ // by another machine sharing this registry looks like — a machine whose
1343
+ // group may be alive right now. Only the machine id tells the two apart.
1344
+ const sameKnownMachine = frame.machineId !== undefined &&
1345
+ current.machineId !== undefined &&
1346
+ frame.machineId === current.machineId;
1347
+ const differentKnownBoot = frame.bootId !== undefined &&
1348
+ current.bootId !== undefined &&
1349
+ frame.bootId !== current.bootId;
1350
+ if (sameKnownPidNamespace && sameKnownMachine && differentKnownBoot) {
1351
+ // PROOF, not an absence of one: a reboot ends every process of the
1352
+ // boot before it, so every process this row describes has ended. The
1353
+ // pgid NUMBER may be held by an unrelated live group now, which is
1354
+ // exactly why nothing is signalled here and why no diagnostic may
1355
+ // point anyone at it.
1356
+ this.#pgidGuardCounters.staleBoot += 1;
1357
+ this.#log(`NOT signalling process group pgid=${pgid} for session ${sessionId}: this row was written at boot ${String(frame.bootId)} and this reader is at boot ${String(current.bootId)}, in the same pid namespace ${String(current.pidNamespace)} on the same machine ${String(current.machineId)} — the reboot ENDED every process this row describes, so its group is PROVEN gone. The number pgid=${pgid} may belong to an unrelated live group now and must NOT be killed. cause=stale-boot sessionId=${sessionId} pgid=${pgid}`);
1358
+ return "stale-boot";
1359
+ }
1360
+ if (frame.pidNamespace !== current.pidNamespace ||
1361
+ frame.bootId !== current.bootId) {
1362
+ // Says WHY nothing could be proven, because the two shapes call for
1363
+ // different checks by whoever reads this: a boot change that only lacks
1364
+ // the machine proof is either a reboot of this machine or another
1365
+ // machine; anything else is another (or an unknown) pid space.
1366
+ const why = sameKnownPidNamespace && differentKnownBoot
1367
+ ? `a different boot in the same pid namespace inode is what a previous boot of THIS machine looks like, but also what ANOTHER machine sharing this registry looks like, and the machine ids that would tell them apart are ${frame.machineId === undefined || current.machineId === undefined ? "not known on both sides" : "DIFFERENT"}`
1368
+ : "no pid or start-time on it can be re-proven here";
1369
+ this.#pgidGuardCounters.foreignFrame += 1;
1370
+ this.#log(`REFUSING to act on process group pgid=${pgid} for session ${sessionId}: this row records pid namespace ${String(frame.pidNamespace)}, boot ${String(frame.bootId)} and machine ${String(frame.machineId)}, but this reader is in pid namespace ${String(current.pidNamespace)} at boot ${String(current.bootId)} on machine ${String(current.machineId)} — ${why}. Inspecting pgid=${pgid} HERE proves nothing about this row. PROCESSES MAY SURVIVE UNCOLLECTED. cause=foreign-frame sessionId=${sessionId} pgid=${pgid}`);
1371
+ return "foreign-frame";
1372
+ }
1373
+ }
1374
+ return undefined;
1375
+ }
1309
1376
  /**
1310
1377
  * Core of both `forceStopAll` and `reapOrphan`: emit the group signal from
1311
1378
  * the parent, then poll `isGroupAlive` (never a fixed sleep-and-hope) until
@@ -1334,6 +1401,13 @@ export class NativeTerminalHost {
1334
1401
  this.#log(`skipping group-kill proof for pgid=${pgid} (${label}): process groups are not supported on ${process.platform}`);
1335
1402
  return { status: "dead", elapsedMs: 0 };
1336
1403
  }
1404
+ // A group number has meaning only in the recorded frame. Even a
1405
+ // signal-zero probe in another namespace may inspect an unrelated group.
1406
+ if (verify) {
1407
+ const refusal = this.#verifyGroupFrame(pgid, verify.sessionId, verify.persistedFrame);
1408
+ if (refusal)
1409
+ return { status: "refused", cause: refusal };
1410
+ }
1337
1411
  if (!this.#reaper.isGroupAlive(pgid)) {
1338
1412
  // The group-leader-identity guard exists to protect a LIVE process
1339
1413
  // about to be signalled from an innocent kill. A group the OS already
@@ -1518,8 +1592,25 @@ export class NativeTerminalHost {
1518
1592
  return;
1519
1593
  record.controllerId = null;
1520
1594
  record.controllerActivity = null;
1595
+ record.controllerIdentity = null;
1521
1596
  record.controllerEpoch += 1;
1522
1597
  }
1598
+ /**
1599
+ * Report a contained pty-callback failure WITHOUT being able to throw: the
1600
+ * caller is the catch block that keeps the host alive, and a failing log
1601
+ * sink must not re-arm the very crash the guard just contained. The default
1602
+ * sink is itself crash-safe (`crash-safe-log.ts`); an injected sink that
1603
+ * throws is swallowed here for the same reason.
1604
+ */
1605
+ #reportSessionCallbackFailure(kind, record, error) {
1606
+ recordHostFault(HOST_FAULT_COUNTERS.hostPtyCallbackFailures);
1607
+ try {
1608
+ this.#log(`contained a faulty pty ${kind} callback for terminal session ${record.id} (pgid=${record.pty.pgid}): ${String(error)}`);
1609
+ }
1610
+ catch {
1611
+ // The log sink itself is failing; containment is the priority.
1612
+ }
1613
+ }
1523
1614
  #snapshot(record) {
1524
1615
  return Object.freeze({
1525
1616
  id: record.id,
@@ -1531,6 +1622,8 @@ export class NativeTerminalHost {
1531
1622
  exit: record.exit,
1532
1623
  stopSignal: record.stopSignal,
1533
1624
  controlled: record.controllerId !== null,
1625
+ controllerEpoch: record.controllerEpoch,
1626
+ controller: record.controllerIdentity,
1534
1627
  lastHumanActivityAt: record.lastHumanActivityAt,
1535
1628
  });
1536
1629
  }