@timurproko/a1 0.2.7-dev.792 → 0.2.7-dev.806

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 (110) hide show
  1. package/dist/app/session-shell/clipboard-diagnostics.js +1 -1
  2. package/dist/app/session-shell/response-copy-helper.js +21 -6
  3. package/dist/app/session-shell/response-copy-protocol.d.ts +1 -1
  4. package/dist/app/session-shell/response-copy-transport.d.ts +3 -6
  5. package/dist/app/session-shell/response-copy-transport.js +60 -17
  6. package/dist/app/session-shell/session-shell-root.d.ts +2 -0
  7. package/dist/app/session-shell/session-shell-root.js +6 -1
  8. package/dist/app/session-shell/session-shell.d.ts +1 -0
  9. package/dist/app/session-shell/session-shell.js +22 -19
  10. package/dist/cli/tabs.js +1 -1
  11. package/dist/composition/owned-ui.d.ts +1 -0
  12. package/dist/composition/owned-ui.js +16 -3
  13. package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
  14. package/dist/features/resident-tab/shortcut-declarations.js +1 -0
  15. package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
  16. package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
  17. package/dist/foundation/launch-guardian/main.d.ts +2 -1
  18. package/dist/foundation/launch-guardian/main.js +58 -6
  19. package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
  20. package/dist/foundation/process-containment/addon-containment.js +139 -0
  21. package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
  22. package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
  23. package/dist/foundation/process-containment/addon.d.ts +42 -0
  24. package/dist/foundation/process-containment/addon.js +57 -0
  25. package/dist/foundation/process-containment/artifact.d.ts +1 -1
  26. package/dist/foundation/process-containment/artifact.js +6 -5
  27. package/dist/foundation/process-containment/contracts.d.ts +2 -0
  28. package/dist/foundation/process-containment/index.d.ts +4 -4
  29. package/dist/foundation/process-containment/index.js +3 -3
  30. package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
  31. package/dist/foundation/process-containment/native-process-inspector.js +10 -10
  32. package/dist/foundation/release/bootstrap.js +53 -16
  33. package/dist/foundation/release/index.d.ts +1 -1
  34. package/dist/foundation/release/index.js +1 -1
  35. package/dist/foundation/release/release-store.d.ts +12 -0
  36. package/dist/foundation/release/release-store.js +124 -2
  37. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +3 -1
  38. package/dist/foundation/resident-tabs/bounded-host-command.js +6 -2
  39. package/dist/foundation/resident-tabs/bridge-client.d.ts +2 -0
  40. package/dist/foundation/resident-tabs/bridge-client.js +13 -0
  41. package/dist/foundation/resident-tabs/doctor.d.ts +0 -1
  42. package/dist/foundation/resident-tabs/doctor.js +4 -4
  43. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +7 -2
  44. package/dist/foundation/resident-tabs/ensure-terminal-host.js +15 -11
  45. package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
  46. package/dist/foundation/resident-tabs/host-entry.js +17 -0
  47. package/dist/foundation/resident-tabs/index.d.ts +2 -2
  48. package/dist/foundation/resident-tabs/index.js +1 -1
  49. package/dist/foundation/resident-tabs/maintenance.d.ts +5 -4
  50. package/dist/foundation/resident-tabs/maintenance.js +32 -17
  51. package/dist/foundation/resident-tabs/release-holds.d.ts +3 -2
  52. package/dist/foundation/resident-tabs/release-holds.js +16 -7
  53. package/dist/foundation/resident-tabs/settings.d.ts +0 -1
  54. package/dist/foundation/resident-tabs/settings.js +1 -7
  55. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +11 -15
  56. package/dist/foundation/resident-tabs/terminal-host-artifact.js +57 -32
  57. package/dist/foundation/session-writer-lock/index.d.ts +1 -3
  58. package/dist/foundation/session-writer-lock/index.js +8 -6
  59. package/dist/integrations/pi/components/shell-editor-autocomplete.js +2 -1
  60. package/dist/integrations/pi/components/shell-shared-facade.d.ts +2 -0
  61. package/dist/native/darwin-arm64/manifest.json +8 -6
  62. package/dist/native/darwin-arm64/process-guardian.node +0 -0
  63. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  64. package/dist/native/darwin-arm64/terminal-host.manifest.json +14 -13
  65. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  66. package/dist/native/darwin-x64/manifest.json +9 -7
  67. package/dist/native/darwin-x64/process-guardian.node +0 -0
  68. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  69. package/dist/native/darwin-x64/terminal-host.manifest.json +15 -14
  70. package/dist/native/darwin-x64/terminal-host.node +0 -0
  71. package/dist/native/linux-x64/manifest.json +8 -6
  72. package/dist/native/linux-x64/process-guardian.node +0 -0
  73. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  74. package/dist/native/linux-x64/terminal-host.manifest.json +14 -13
  75. package/dist/native/linux-x64/{terminal-host → terminal-host.node} +0 -0
  76. package/dist/native/win32-x64/manifest.json +8 -6
  77. package/dist/native/win32-x64/process-guardian.node +0 -0
  78. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +1 -1
  79. package/dist/native/win32-x64/terminal-host.manifest.json +14 -13
  80. package/dist/native/win32-x64/{terminal-host.exe → terminal-host.node} +0 -0
  81. package/dist/product-identity.d.ts +1 -1
  82. package/dist/product-identity.js +1 -1
  83. package/dist/product-identity.json +3 -2
  84. package/dist/runtime-payload-inventory.json +1 -0
  85. package/dist/ui/settings/declarations.d.ts +0 -12
  86. package/dist/ui/settings/declarations.js +0 -10
  87. package/docs/architecture/boundaries.md +10 -9
  88. package/docs/architecture/process-guardian-provenance.md +17 -11
  89. package/docs/architecture/resident-tabs.md +36 -34
  90. package/docs/architecture/terminal-host-provenance.md +5 -3
  91. package/docs/architecture/toolchain.md +8 -6
  92. package/docs/ci-release-runbook.md +1 -1
  93. package/docs/openspec-archive-automation.md +23 -9
  94. package/docs/validation.md +6 -2
  95. package/package.json +5 -3
  96. package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
  97. package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
  98. package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
  99. package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
  100. package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
  101. package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
  102. package/dist/native/darwin-arm64/process-guardian +0 -0
  103. package/dist/native/darwin-arm64/terminal-host +0 -0
  104. package/dist/native/darwin-x64/a1_session_lock.node +0 -0
  105. package/dist/native/darwin-x64/process-guardian +0 -0
  106. package/dist/native/darwin-x64/terminal-host +0 -0
  107. package/dist/native/linux-x64/a1_session_lock.node +0 -0
  108. package/dist/native/linux-x64/process-guardian +0 -0
  109. package/dist/native/win32-x64/a1_session_lock.node +0 -0
  110. package/dist/native/win32-x64/process-guardian.exe +0 -0
@@ -12,7 +12,7 @@ import { cleanupProvenIdleOwner, processIsAlive } from "./process-cleanup.js";
12
12
  import { probeOwnership, readEndpointMetadata, removeEndpointArtifacts, sweepDeadEndpoints } from "./endpoints.js";
13
13
  import { UpdateTransactionStore } from "./update-transaction.js";
14
14
  import { selectUpdateLaunchRelease } from "./update-launch.js";
15
- import { consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease } from "./release-store.js";
15
+ import { assertCertifiedFilesPresent, consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, repairMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease } from "./release-store.js";
16
16
  import { scheduleReleaseCleanup } from "./release-gc.js";
17
17
  import { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDocument } from "./restart-certification.js";
18
18
  import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
@@ -61,10 +61,10 @@ async function runBootstrapAttempt(options, retriesRemaining) {
61
61
  });
62
62
  if (resident !== null) {
63
63
  residentContext = resident.status === "ready" ? {
64
- residentRootExecutable: resident.executable,
64
+ residentRootExecutable: resident.executable.executable,
65
65
  residentRootArguments: JSON.stringify([
66
+ resident.executable.entry,
66
67
  "attach", "--endpoint", resident.endpoint, "--cwd", process.cwd(), "--clear-exit-ms", String(CLEAR_EXIT_INTERVAL_MS),
67
- "--tabs-max", String((await residentLaunchSettings).tabsMax),
68
68
  "--profile", environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir,
69
69
  "--settings-file", resolve(agentProfileDir, "settings.json"),
70
70
  "--data-dir", paths.dataDir, "--runtime-dir", paths.runtimeDir,
@@ -126,7 +126,23 @@ async function runBootstrapAttempt(options, retriesRemaining) {
126
126
  const installedVersion = await readInstalledVersion(options.packageRoot);
127
127
  const activeId = state.references.active;
128
128
  const active = activeId === null ? undefined : state.releases[activeId];
129
- const activeIsLaunchable = active === undefined || (options.releaseIsLaunchable?.(active.releaseRoot) ?? true);
129
+ let activeIsLaunchable = active === undefined || (options.releaseIsLaunchable?.(active.releaseRoot) ?? true);
130
+ let excludedRetainedReleaseId;
131
+ if (activeIsLaunchable && active?.approval === "approved" && active.packageVersion === installedVersion) {
132
+ try {
133
+ await assertCertifiedFilesPresent(active.releaseRoot);
134
+ }
135
+ catch {
136
+ try {
137
+ await repairApprovedRelease(options.packageRoot, active, paths.dataDir);
138
+ }
139
+ catch {
140
+ // Security: a damaged certified release is no longer a fallback when package bytes cannot restore it.
141
+ activeIsLaunchable = false;
142
+ excludedRetainedReleaseId = active.releaseId;
143
+ }
144
+ }
145
+ }
130
146
  if (activeIsLaunchable && active?.approval === "approved" && active.packageVersion === installedVersion) {
131
147
  const endpointMatches = endpoint?.releaseId === active.releaseId
132
148
  && endpoint.releaseRoot === active.releaseRoot
@@ -140,18 +156,27 @@ async function runBootstrapAttempt(options, retriesRemaining) {
140
156
  await removeEndpointArtifacts(endpointPaths.endpointMetadataPath, endpointPaths.endpoint);
141
157
  await markStartupPhase(environment, "durable-validation-start");
142
158
  const retained = await readRestartCertifiedRelease(active, paths.dataDir).catch(async () => {
143
- const verified = await readMaterializedRelease(active.releaseRoot, resolve(paths.dataDir, "releases"));
144
- if (verified.releaseId !== active.releaseId || verified.contentDigest !== active.contentDigest || verified.packageVersion !== active.packageVersion) {
145
- throw new Error("completely verified release differs from the approved active record");
159
+ let verified;
160
+ try {
161
+ verified = await readMaterializedRelease(active.releaseRoot, resolve(paths.dataDir, "releases"));
162
+ }
163
+ catch {
164
+ verified = await repairApprovedRelease(options.packageRoot, active, paths.dataDir);
165
+ return verified;
146
166
  }
167
+ assertApprovedReleaseMatches(verified, active);
147
168
  await certifyMaterializedRelease(verified, paths.dataDir);
148
169
  return verified;
149
- });
150
- await markStartupPhase(environment, "durable-validation-complete");
151
- await markStartupPhase(environment, "replacement-supervisor-start");
152
- await ensureSupervisor(retained, environment);
153
- await markStartupPhase(environment, "replacement-supervisor-ready");
154
- return await launchSelected(retained);
170
+ }).catch(() => null);
171
+ if (retained !== null) {
172
+ await markStartupPhase(environment, "durable-validation-complete");
173
+ await markStartupPhase(environment, "replacement-supervisor-start");
174
+ await ensureSupervisor(retained, environment);
175
+ await markStartupPhase(environment, "replacement-supervisor-ready");
176
+ return await launchSelected(retained);
177
+ }
178
+ activeIsLaunchable = false;
179
+ excludedRetainedReleaseId = active.releaseId;
155
180
  }
156
181
  }
157
182
  // Concurrency: an installation being replaced is not a reason to refuse a launch. While
@@ -163,7 +188,7 @@ async function runBootstrapAttempt(options, retriesRemaining) {
163
188
  candidate = await materializeRelease(options.packageRoot, paths.dataDir);
164
189
  }
165
190
  catch (error) {
166
- const fallback = await launchRetainedActive(state, paths, environment, launchSelected);
191
+ const fallback = await launchRetainedActive(state, paths, environment, launchSelected, excludedRetainedReleaseId);
167
192
  if (fallback === null)
168
193
  throw error;
169
194
  return fallback;
@@ -290,10 +315,10 @@ async function runBootstrapAttempt(options, retriesRemaining) {
290
315
  * certifying anything new, and reports no selection when there is nothing safe
291
316
  * to fall back to.
292
317
  */
293
- async function launchRetainedActive(state, paths, environment, launch) {
318
+ async function launchRetainedActive(state, paths, environment, launch, excludedReleaseId) {
294
319
  const activeId = state.references.active;
295
320
  const active = activeId === null ? undefined : state.releases[activeId];
296
- if (!active || active.approval !== "approved")
321
+ if (!active || active.approval !== "approved" || active.releaseId === excludedReleaseId)
297
322
  return null;
298
323
  try {
299
324
  const retained = await readCertifiedReleaseManifest(active, resolve(paths.dataDir, "releases"));
@@ -304,6 +329,18 @@ async function launchRetainedActive(state, paths, environment, launch) {
304
329
  return null;
305
330
  }
306
331
  }
332
+ async function repairApprovedRelease(packageRoot, active, dataDir) {
333
+ const repaired = await repairMaterializedRelease(packageRoot, active.releaseRoot);
334
+ assertApprovedReleaseMatches(repaired, active);
335
+ await recordParentCertifiedRelease(repaired, dataDir);
336
+ return repaired;
337
+ }
338
+ function assertApprovedReleaseMatches(release, active) {
339
+ if (release.releaseId !== active.releaseId || release.contentDigest !== active.contentDigest
340
+ || (active.packageVersion !== undefined && release.packageVersion !== active.packageVersion)) {
341
+ throw new Error("completely verified release differs from the approved active record");
342
+ }
343
+ }
307
344
  async function readInstalledVersion(packageRoot) {
308
345
  const manifest = JSON.parse(await readFile(resolve(packageRoot, "package.json"), "utf8"));
309
346
  if (typeof manifest.version !== "string" || manifest.version.length === 0) {
@@ -15,7 +15,7 @@ export { PRODUCT_PACKAGE_NAME, createReleaseIdentity, deriveReleaseIdentity, dig
15
15
  export type { DiscoverReleasePayloadOptions, DiscoveredReleasePayload, ReleaseFileIdentity, ReleaseIdentity } from "./release.js";
16
16
  export { collectRelease, prepareReleaseCleanup, runBoundedReleaseCleanup, runReleaseCleanupWorker, scheduleReleaseCleanup } from "./release-gc.js";
17
17
  export type { ReleaseCleanupLimits, ReleaseCleanupOperations, ReleaseCleanupOptions, ReleaseCleanupResult, ReleaseCleanupWorkerOptions } from "./release-gc.js";
18
- export { RELEASE_MANIFEST_FILENAME, assertImmutableExecutionRoot, consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease, } from "./release-store.js";
18
+ export { RELEASE_MANIFEST_FILENAME, ReleaseRepairUnavailableError, assertCertifiedFilesPresent, assertImmutableExecutionRoot, consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, repairMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease, } from "./release-store.js";
19
19
  export type { CertifiedReleaseRecord, MaterializeReleaseOptions, MaterializedRelease, ReadCertifiedReleaseManifestOptions, ReleaseContentOperation, ReleaseContentOperationEvent, VerifyMaterializedReleaseOptions, } from "./release-store.js";
20
20
  export { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDocument, restartSealDigest } from "./restart-certification.js";
21
21
  export type { RestartSeal, RestartValidationEvent } from "./restart-certification.js";
@@ -7,7 +7,7 @@ export { listRecordedEndpoints, liveReleaseIds, probeOwnership, readEndpointMeta
7
7
  export { cleanupProvenIdleOwner, cleanupVerifiedOwner, processIsAlive } from "./process-cleanup.js";
8
8
  export { PRODUCT_PACKAGE_NAME, createReleaseIdentity, deriveReleaseIdentity, digestManifestFiles, discoverReleasePayload, packageRootFromModule, releaseFileIdentity, resolveWithin, } from "./release.js";
9
9
  export { collectRelease, prepareReleaseCleanup, runBoundedReleaseCleanup, runReleaseCleanupWorker, scheduleReleaseCleanup } from "./release-gc.js";
10
- export { RELEASE_MANIFEST_FILENAME, assertImmutableExecutionRoot, consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease, } from "./release-store.js";
10
+ export { RELEASE_MANIFEST_FILENAME, ReleaseRepairUnavailableError, assertCertifiedFilesPresent, assertImmutableExecutionRoot, consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, repairMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease, } from "./release-store.js";
11
11
  export { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDocument, restartSealDigest } from "./restart-certification.js";
12
12
  export { STABLE_RELEASE_SCHEMA, createStableReleaseEvidence, verifyStableRegistry } from "./stable-release.js";
13
13
  export { PACKAGE_UNLOCK_PATIENCE_MS, PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, isPackageLockError, lockedPackageDiagnostic, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
@@ -31,12 +31,24 @@ export interface CertifiedReleaseRecord {
31
31
  /** Absent on records written before releases declared a handoff contract. */
32
32
  readonly launchContract?: string;
33
33
  }
34
+ /** Reports that installed package bytes cannot safely reproduce an approved release. */
35
+ export declare class ReleaseRepairUnavailableError extends Error {
36
+ readonly code: "RELEASE_REPAIR_UNAVAILABLE";
37
+ constructor(message: string, options?: ErrorOptions);
38
+ }
34
39
  /** Process-authority compatibility controls for metadata-only release loading. */
35
40
  export type ReadCertifiedReleaseManifestOptions = ReadCertifiedDependencyLayerOptions;
36
41
  export declare function materializeRelease(packageRoot: string, dataDir: string, options?: MaterializeReleaseOptions): Promise<MaterializedRelease>;
37
42
  /** Consume proof that this exact object was freshly materialized or fully verified in this process. */
38
43
  export declare function consumeMaterializationProof(release: MaterializedRelease): boolean;
39
44
  export declare function readMaterializedRelease(releaseRoot: string, selectedStoreRoot?: string): Promise<MaterializedRelease>;
45
+ /** Fast presence check before trusted certification permits a release to skip full verification. */
46
+ export declare function assertCertifiedFilesPresent(releaseRoot: string): Promise<void>;
47
+ /**
48
+ * Restore changed or missing product payload files from the installed package without replacing the
49
+ * immutable release directory. Dependency layers retain their own verification and repair policy.
50
+ */
51
+ export declare function repairMaterializedRelease(packageRoot: string, releaseRoot: string): Promise<MaterializedRelease>;
40
52
  /**
41
53
  * Load metadata for a release whose bytes were already certified by the
42
54
  * current parent process or an authenticated live supervisor. Callers must
@@ -10,6 +10,14 @@ import { dependencyReference, materializeDependencyLayer, readCertifiedDependenc
10
10
  const RELEASE_FILE_IO_CONCURRENCY = 32;
11
11
  const certificationReady = new WeakSet();
12
12
  export const RELEASE_MANIFEST_FILENAME = PRODUCT_IDENTITY.manifest.releaseFilename;
13
+ /** Reports that installed package bytes cannot safely reproduce an approved release. */
14
+ export class ReleaseRepairUnavailableError extends Error {
15
+ code = "RELEASE_REPAIR_UNAVAILABLE";
16
+ constructor(message, options = {}) {
17
+ super(message, options);
18
+ this.name = "ReleaseRepairUnavailableError";
19
+ }
20
+ }
13
21
  export async function materializeRelease(packageRoot, dataDir, options = {}) {
14
22
  const payload = await discoverReleasePayload(packageRoot, {
15
23
  onSourceRead: (path, bytes) => options.onOperation?.({ operation: "source-read", path, bytes }),
@@ -71,7 +79,7 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
71
79
  const releaseRoot = resolveWithin(storeRoot, identity.releaseId);
72
80
  if (await lstat(releaseRoot).catch(() => null)) {
73
81
  await rm(candidate, { recursive: true, force: true });
74
- return certificationReadyRelease(await verifyMaterializedRelease(releaseRoot, identity));
82
+ return certificationReadyRelease(await verifyOrRepairMaterializedRelease(payload.packageRoot, releaseRoot, identity));
75
83
  }
76
84
  await writeFile(resolve(candidate, RELEASE_MANIFEST_FILENAME), JSON.stringify(identity, null, 2), { mode: 0o400, flag: "wx" });
77
85
  try {
@@ -81,7 +89,7 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
81
89
  if (!await lstat(releaseRoot).catch(() => null))
82
90
  throw error;
83
91
  await rm(candidate, { recursive: true, force: true });
84
- return certificationReadyRelease(await verifyMaterializedRelease(releaseRoot, identity));
92
+ return certificationReadyRelease(await verifyOrRepairMaterializedRelease(payload.packageRoot, releaseRoot, identity));
85
93
  }
86
94
  return certificationReadyRelease({ ...identity, releaseRoot: await realpath(releaseRoot) });
87
95
  }
@@ -102,6 +110,61 @@ export async function readMaterializedRelease(releaseRoot, selectedStoreRoot) {
102
110
  const manifest = JSON.parse(await readFile(resolve(canonical, RELEASE_MANIFEST_FILENAME), "utf8"));
103
111
  return certificationReadyRelease(await verifyMaterializedRelease(canonical, manifest, selectedStoreRoot));
104
112
  }
113
+ /** Fast presence check before trusted certification permits a release to skip full verification. */
114
+ export async function assertCertifiedFilesPresent(releaseRoot) {
115
+ const canonical = await realpath(releaseRoot);
116
+ const manifest = await readReleaseManifest(canonical);
117
+ await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
118
+ const metadata = await lstat(resolveWithin(canonical, file.path));
119
+ if (!metadata.isFile() || metadata.isSymbolicLink())
120
+ throw new Error(`certified release file is missing: ${file.path}`);
121
+ });
122
+ }
123
+ /**
124
+ * Restore changed or missing product payload files from the installed package without replacing the
125
+ * immutable release directory. Dependency layers retain their own verification and repair policy.
126
+ */
127
+ export async function repairMaterializedRelease(packageRoot, releaseRoot) {
128
+ const canonical = await realpath(releaseRoot);
129
+ let manifest;
130
+ let payload;
131
+ try {
132
+ manifest = await readReleaseManifest(canonical);
133
+ payload = await discoverReleasePayload(packageRoot);
134
+ assertCurrentLaunchContract(payload);
135
+ }
136
+ catch (error) {
137
+ throw repairUnavailable("release repair metadata is unavailable", error);
138
+ }
139
+ if (payload.packageVersion !== manifest.packageVersion) {
140
+ throw repairUnavailable(`installed package version ${payload.packageVersion} does not match release ${manifest.packageVersion}`);
141
+ }
142
+ const payloadPaths = new Set(payload.paths.filter(path => !path.startsWith("node_modules/")));
143
+ try {
144
+ await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
145
+ const destination = resolveWithin(canonical, file.path);
146
+ if (!await certifiedFileDiffers(destination, file))
147
+ return;
148
+ if (!payloadPaths.has(file.path))
149
+ throw repairUnavailable(`installed package does not carry ${file.path}`);
150
+ const source = resolveWithin(payload.packageRoot, file.path);
151
+ const sourceMetadata = await lstat(source);
152
+ if (!sourceMetadata.isFile() || sourceMetadata.isSymbolicLink())
153
+ throw repairUnavailable(`installed package file is not regular: ${file.path}`);
154
+ const bytes = payload.cachedFiles.get(file.path) ?? await readFile(source);
155
+ if (bytes.length !== file.bytes || createHash("sha256").update(bytes).digest("hex") !== file.sha256) {
156
+ throw repairUnavailable(`installed package bytes do not match release file ${file.path}`);
157
+ }
158
+ await replaceReleaseFile(destination, bytes, file);
159
+ });
160
+ return await verifyMaterializedRelease(canonical, manifest);
161
+ }
162
+ catch (error) {
163
+ if (error instanceof ReleaseRepairUnavailableError)
164
+ throw error;
165
+ throw repairUnavailable("release repair could not restore the certified payload", error);
166
+ }
167
+ }
105
168
  /**
106
169
  * Load metadata for a release whose bytes were already certified by the
107
170
  * current parent process or an authenticated live supervisor. Callers must
@@ -197,6 +260,65 @@ async function verifyFile(root, file, options) {
197
260
  if (digest !== file.sha256)
198
261
  throw new Error(`release file digest mismatch: ${file.path}`);
199
262
  }
263
+ async function verifyOrRepairMaterializedRelease(packageRoot, releaseRoot, expected) {
264
+ try {
265
+ return await verifyMaterializedRelease(releaseRoot, expected);
266
+ }
267
+ catch {
268
+ await repairMaterializedRelease(packageRoot, releaseRoot);
269
+ return await verifyMaterializedRelease(releaseRoot, expected);
270
+ }
271
+ }
272
+ async function readReleaseManifest(releaseRoot) {
273
+ const manifest = JSON.parse(await readFile(resolveWithin(releaseRoot, RELEASE_MANIFEST_FILENAME), "utf8"));
274
+ validateManifest(manifest);
275
+ if (releaseRoot.split(sep).at(-1) !== manifest.releaseId)
276
+ throw new Error(`release directory does not match identity ${manifest.releaseId}`);
277
+ return manifest;
278
+ }
279
+ async function certifiedFileDiffers(path, file) {
280
+ const metadata = await lstat(path).catch((error) => {
281
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
282
+ return null;
283
+ throw error;
284
+ });
285
+ if (metadata === null || !metadata.isFile() || metadata.isSymbolicLink() || metadata.size !== file.bytes)
286
+ return true;
287
+ if ((metadata.mode & 0o222) !== 0 || ((metadata.mode & 0o111) !== 0) !== file.executable)
288
+ return true;
289
+ return createHash("sha256").update(await readFile(path)).digest("hex") !== file.sha256;
290
+ }
291
+ async function replaceReleaseFile(path, bytes, file) {
292
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
293
+ const temporary = resolve(dirname(path), `.a1-repair-${randomUUID()}`);
294
+ const mode = file.executable ? 0o500 : 0o400;
295
+ let existing = null;
296
+ try {
297
+ await writeFile(temporary, bytes, { flag: "wx", mode: 0o600 });
298
+ existing = await lstat(path).catch((error) => {
299
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
300
+ return null;
301
+ throw error;
302
+ });
303
+ if (existing !== null) {
304
+ if (!existing.isFile() || existing.isSymbolicLink())
305
+ throw repairUnavailable(`release file cannot be safely replaced: ${file.path}`);
306
+ // Platform: Windows refuses rename-over for a read-only destination.
307
+ await chmod(path, 0o600);
308
+ }
309
+ await rename(temporary, path);
310
+ await chmod(path, mode);
311
+ }
312
+ catch (error) {
313
+ await rm(temporary, { force: true }).catch(() => { });
314
+ if (existing !== null)
315
+ await chmod(path, mode).catch(() => { });
316
+ throw error;
317
+ }
318
+ }
319
+ function repairUnavailable(message, cause) {
320
+ return new ReleaseRepairUnavailableError(message, cause === undefined ? {} : { cause });
321
+ }
200
322
  function certificationReadyRelease(release) {
201
323
  certificationReady.add(release);
202
324
  return release;
@@ -1,3 +1,4 @@
1
+ import type { TerminalHostCommand } from "./ensure-terminal-host.js";
1
2
  export interface BoundedHostCommandResult {
2
3
  readonly code: number;
3
4
  readonly stdout: string;
@@ -8,9 +9,10 @@ export interface BoundedHostCommandLimits {
8
9
  readonly timeoutMs: number;
9
10
  /** Names the exchange in the timeout error. */
10
11
  readonly label: string;
12
+ readonly environment?: NodeJS.ProcessEnv;
11
13
  }
12
14
  /**
13
15
  * Runs one terminal-host command whose report is small. An oversized report kills the command and
14
16
  * reports a failed exchange; a command that outlives its deadline is killed and rejected.
15
17
  */
16
- export declare function runBoundedHostCommand(executable: string, arguments_: readonly string[], limits: BoundedHostCommandLimits): Promise<BoundedHostCommandResult>;
18
+ export declare function runBoundedHostCommand(command: TerminalHostCommand, arguments_: readonly string[], limits: BoundedHostCommandLimits): Promise<BoundedHostCommandResult>;
@@ -3,9 +3,13 @@ import { spawn } from "node:child_process";
3
3
  * Runs one terminal-host command whose report is small. An oversized report kills the command and
4
4
  * reports a failed exchange; a command that outlives its deadline is killed and rejected.
5
5
  */
6
- export async function runBoundedHostCommand(executable, arguments_, limits) {
6
+ export async function runBoundedHostCommand(command, arguments_, limits) {
7
7
  return await new Promise((resolvePromise, rejectPromise) => {
8
- const child = spawn(executable, [...arguments_], { windowsHide: true, stdio: ["ignore", "pipe", "ignore"] });
8
+ const child = spawn(command.executable, [command.entry, ...arguments_], {
9
+ env: limits.environment,
10
+ windowsHide: true,
11
+ stdio: ["ignore", "pipe", "ignore"],
12
+ });
9
13
  const stdout = [];
10
14
  let size = 0;
11
15
  let overflowed = false;
@@ -42,6 +42,7 @@ export interface TabBridgeOptions {
42
42
  export declare class TabBridgeClient {
43
43
  #private;
44
44
  constructor(options: TabBridgeOptions);
45
+ get tabId(): string;
45
46
  get available(): boolean;
46
47
  captureAttachment(): TabAttachment | null;
47
48
  onCommand(listener: (command: TabBridgeCommand) => void): () => void;
@@ -55,6 +56,7 @@ export declare class TabBridgeClient {
55
56
  shortcuts(assignments: Readonly<Record<string, string | null>>): void;
56
57
  requestDetach(origin?: TabAttachment | null, timeoutMs?: number): Promise<boolean>;
57
58
  requestRestart(origin?: TabAttachment | null, timeoutMs?: number): Promise<boolean>;
59
+ requestClose(origin?: TabAttachment | null, timeoutMs?: number): Promise<boolean>;
58
60
  consumeControllerBoundaryInput(data: string): string;
59
61
  close(): void;
60
62
  }
@@ -20,12 +20,16 @@ export class TabBridgeClient {
20
20
  #pending = new Map();
21
21
  #startWaiter = null;
22
22
  #commandListeners = new Set();
23
+ #pendingCommands = [];
23
24
  #boundaryInputRemainder = "";
24
25
  constructor(options) { this.#options = options; }
26
+ get tabId() { return this.#options.credentials.tabId; }
25
27
  get available() { return this.#available && this.#connected; }
26
28
  captureAttachment() { return this.#attachment === null ? null : { ...this.#attachment }; }
27
29
  onCommand(listener) {
28
30
  this.#commandListeners.add(listener);
31
+ for (const command of this.#pendingCommands.splice(0))
32
+ listener(command);
29
33
  return () => this.#commandListeners.delete(listener);
30
34
  }
31
35
  async start(timeoutMs = 4_000) {
@@ -119,6 +123,9 @@ export class TabBridgeClient {
119
123
  requestRestart(origin = this.captureAttachment(), timeoutMs = 1_000) {
120
124
  return this.#requestAction("restart", origin, timeoutMs);
121
125
  }
126
+ requestClose(origin = this.captureAttachment(), timeoutMs = 1_000) {
127
+ return this.#requestAction("close", origin, timeoutMs);
128
+ }
122
129
  consumeControllerBoundaryInput(data) {
123
130
  const prefix = "\x1bP+a1;controller=";
124
131
  const suffix = "\x1b\\";
@@ -176,6 +183,12 @@ export class TabBridgeClient {
176
183
  this.#fail(false);
177
184
  }
178
185
  #emitCommand(command) {
186
+ if (this.#commandListeners.size === 0) {
187
+ if (this.#pendingCommands.length >= 16)
188
+ this.#pendingCommands.shift();
189
+ this.#pendingCommands.push(command);
190
+ return;
191
+ }
179
192
  for (const listener of this.#commandListeners)
180
193
  listener(command);
181
194
  }
@@ -6,7 +6,6 @@ export interface DoctorOptions {
6
6
  readonly hostStatus: MaintenanceReport;
7
7
  readonly releaseRoots: readonly string[];
8
8
  readonly settings: {
9
- readonly tabsMax: number;
10
9
  readonly tabsAutoName: boolean;
11
10
  };
12
11
  readonly home?: string;
@@ -33,7 +33,7 @@ export async function writeDoctorBundle(options) {
33
33
  node: process.version,
34
34
  detachmentMode: options.hostStatus.result?.detachmentMode ?? null,
35
35
  }),
36
- json("settings.json", { tabsMax: options.settings.tabsMax, tabsAutoName: options.settings.tabsAutoName }),
36
+ json("settings.json", { tabsAutoName: options.settings.tabsAutoName }),
37
37
  ];
38
38
  const crashes = [];
39
39
  for (const name of names.filter(name => CRASH_NAME.test(name)).sort()) {
@@ -153,11 +153,11 @@ async function redactedMarker(path, redact) {
153
153
  async function artifactFacts(roots, platform, architecture, redact) {
154
154
  const facts = [];
155
155
  for (const root of roots) {
156
- const executable = resolveTerminalHostPath(root, {}, platform, architecture);
157
- const manifest = await readFile(resolve(dirname(executable), TERMINAL_HOST_MANIFEST_FILENAME), "utf8")
156
+ const addon = resolveTerminalHostPath(root, {}, platform, architecture);
157
+ const manifest = await readFile(resolve(dirname(addon), TERMINAL_HOST_MANIFEST_FILENAME), "utf8")
158
158
  .then(text => JSON.parse(text))
159
159
  .catch(() => null);
160
- const verification = await verifyTerminalHostArtifact(executable, platform, architecture)
160
+ const verification = await verifyTerminalHostArtifact(addon, platform, architecture)
161
161
  .then(() => "verified")
162
162
  .catch((error) => error instanceof Error && "code" in error ? String(error.code) : "failed");
163
163
  facts.push({ releaseRoot: redact(root), verification, manifest });
@@ -1,7 +1,11 @@
1
1
  export type TerminalHostFallbackReason = "unsupported platform" | "no interactive terminal" | "terminal host not installed" | "terminal host not verified" | "host probe failed" | "incompatible generation" | "registry unreadable" | "recovery blocked" | "launch not verified" | "host startup timed out";
2
+ export interface TerminalHostCommand {
3
+ readonly executable: string;
4
+ readonly entry: string;
5
+ }
2
6
  export type EnsureTerminalHostResult = {
3
7
  readonly status: "ready";
4
- readonly executable: string;
8
+ readonly executable: TerminalHostCommand;
5
9
  readonly endpoint: string;
6
10
  } | {
7
11
  readonly status: "fallback";
@@ -24,8 +28,9 @@ export interface EnsureTerminalHostOptions {
24
28
  readonly maxConcurrentStarts?: number;
25
29
  readonly unresponsiveRestartSeconds?: number;
26
30
  readonly run?: (executable: string, arguments_: readonly string[], environment: NodeJS.ProcessEnv, timeoutMs: number) => Promise<HostCommandResult>;
27
- readonly verify?: (executable: string, platform: NodeJS.Platform, architecture: string) => Promise<unknown>;
31
+ readonly verify?: (addon: string, platform: NodeJS.Platform, architecture: string) => Promise<unknown>;
28
32
  }
29
33
  export declare function resolveTerminalHostPath(releaseRoot: string, environment?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, architecture?: string): string;
34
+ export declare function resolveTerminalHostEntryPath(releaseRoot: string): string;
30
35
  export declare function ensureTerminalHost(options: EnsureTerminalHostOptions): Promise<EnsureTerminalHostResult>;
31
36
  export {};
@@ -8,8 +8,10 @@ export function resolveTerminalHostPath(releaseRoot, environment = process.env,
8
8
  const override = developmentOverride(environment);
9
9
  if (override)
10
10
  return resolve(override);
11
- const executable = platform === "win32" ? `${PRODUCT_IDENTITY.artifacts.nativeExecutable}.exe` : PRODUCT_IDENTITY.artifacts.nativeExecutable;
12
- return resolve(releaseRoot, "dist", "native", `${platform}-${architecture}`, executable);
11
+ return resolve(releaseRoot, "dist", "native", `${platform}-${architecture}`, `${PRODUCT_IDENTITY.artifacts.terminalHostAddon}.node`);
12
+ }
13
+ export function resolveTerminalHostEntryPath(releaseRoot) {
14
+ return resolve(releaseRoot, "dist", "foundation", "resident-tabs", `${PRODUCT_IDENTITY.artifacts.terminalHostEntry}.js`);
13
15
  }
14
16
  /** The terminal-host override is a development aid; installed releases always run their own verified artifact. */
15
17
  function developmentOverride(environment) {
@@ -22,10 +24,11 @@ export async function ensureTerminalHost(options) {
22
24
  const architecture = options.architecture ?? process.arch;
23
25
  if (!supported(platform, architecture))
24
26
  return { status: "fallback", reason: "unsupported platform" };
25
- const executable = resolveTerminalHostPath(options.releaseRoot, options.environment, platform, architecture);
27
+ const addon = resolveTerminalHostPath(options.releaseRoot, options.environment, platform, architecture);
28
+ const command = { executable: process.execPath, entry: resolveTerminalHostEntryPath(options.releaseRoot) };
26
29
  if (developmentOverride(options.environment)) {
27
30
  try {
28
- await access(executable, constants.R_OK | (platform === "win32" ? 0 : constants.X_OK));
31
+ await Promise.all([access(addon, constants.R_OK), access(command.entry, constants.R_OK)]);
29
32
  }
30
33
  catch {
31
34
  return { status: "fallback", reason: "terminal host not installed" };
@@ -33,7 +36,7 @@ export async function ensureTerminalHost(options) {
33
36
  }
34
37
  else {
35
38
  try {
36
- await (options.verify ?? verifyTerminalHostArtifact)(executable, platform, architecture);
39
+ await (options.verify ?? verifyTerminalHostArtifact)(addon, platform, architecture);
37
40
  }
38
41
  catch (error) {
39
42
  return { status: "fallback", reason: verificationReason(error) };
@@ -43,12 +46,13 @@ export async function ensureTerminalHost(options) {
43
46
  const timeoutMs = options.timeoutMs ?? 8_000;
44
47
  const common = ["--profile", options.profileRoot, "--data-dir", options.dataDir, "--runtime-dir", options.runtimeDir];
45
48
  const environment = { ...options.environment };
49
+ const invoke = async (arguments_, commandTimeoutMs) => await run(command.executable, [command.entry, ...arguments_], environment, commandTimeoutMs);
46
50
  try {
47
- const probe = await run(executable, ["probe", ...common], environment, Math.min(4_000, timeoutMs));
51
+ const probe = await invoke(["probe", ...common], Math.min(4_000, timeoutMs));
48
52
  const existing = parseResult(probe.stdout);
49
53
  if (probe.code === 0) {
50
54
  if (existing?.generation === 1 && typeof existing.endpoint === "string") {
51
- return { status: "ready", executable, endpoint: existing.endpoint };
55
+ return { status: "ready", executable: command, endpoint: existing.endpoint };
52
56
  }
53
57
  return {
54
58
  status: "fallback",
@@ -61,11 +65,11 @@ export async function ensureTerminalHost(options) {
61
65
  return { status: "fallback", reason: classifiedReason(existing?.reason, "host probe failed") };
62
66
  const maximum = Math.max(1, Math.min(8, Math.trunc(options.maxConcurrentStarts ?? 2)));
63
67
  const unresponsiveRestartSeconds = Math.max(30, Math.min(600, Math.trunc(options.unresponsiveRestartSeconds ?? 120)));
64
- const start = await run(executable, [
68
+ const start = await invoke([
65
69
  "server", "--detach", ...common,
66
70
  "--max-concurrent-starts", String(maximum),
67
71
  "--unresponsive-restart-seconds", String(unresponsiveRestartSeconds),
68
- ], environment, timeoutMs);
72
+ ], timeoutMs);
69
73
  const started = parseResult(start.stdout);
70
74
  if (start.code !== 0)
71
75
  return { status: "fallback", reason: classifiedReason(started?.reason, "launch not verified") };
@@ -73,11 +77,11 @@ export async function ensureTerminalHost(options) {
73
77
  return { status: "fallback", reason: "launch not verified" };
74
78
  const deadline = Date.now() + timeoutMs;
75
79
  while (Date.now() < deadline) {
76
- const result = await run(executable, ["probe", ...common], environment, Math.min(1_000, Math.max(1, deadline - Date.now())));
80
+ const result = await invoke(["probe", ...common], Math.min(1_000, Math.max(1, deadline - Date.now())));
77
81
  const ready = parseResult(result.stdout);
78
82
  if (result.code === 0) {
79
83
  if (ready?.generation === 1 && typeof ready.endpoint === "string") {
80
- return { status: "ready", executable, endpoint: ready.endpoint };
84
+ return { status: "ready", executable: command, endpoint: ready.endpoint };
81
85
  }
82
86
  return {
83
87
  status: "fallback",
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import { createRequire } from "node:module";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { PRODUCT_IDENTITY } from "../../product-identity.js";
5
+ const filename = fileURLToPath(import.meta.url);
6
+ const target = `${process.platform}-${process.arch}`;
7
+ const development = process.env[PRODUCT_IDENTITY.environment.developmentInstanceId]?.trim();
8
+ const override = development ? process.env[PRODUCT_IDENTITY.environment.terminalHostPath]?.trim() : undefined;
9
+ const addonPath = resolve(override || resolve(dirname(filename), "../../native", target, `${PRODUCT_IDENTITY.artifacts.terminalHostAddon}.node`));
10
+ const addon = createRequire(import.meta.url)(addonPath);
11
+ if (typeof addon.runRole !== "function")
12
+ throw new Error("terminal host addon exports are incompatible");
13
+ // Invariant: runRole blocks Node's main thread, so this entry must not register JavaScript signal
14
+ // listeners whose callbacks cannot run until the native role has already returned. Default process
15
+ // signal handling keeps attach clients and identity-verified resident roles externally stoppable.
16
+ process.env[PRODUCT_IDENTITY.environment.terminalHostPath] = addonPath;
17
+ process.exitCode = addon.runRole(process.argv.slice(2), { executable: process.execPath, entry: filename, artifact: addonPath });
@@ -1,5 +1,5 @@
1
- export { ensureTerminalHost, resolveTerminalHostPath } from "./ensure-terminal-host.js";
2
- export type { EnsureTerminalHostOptions, EnsureTerminalHostResult, TerminalHostFallbackReason } from "./ensure-terminal-host.js";
1
+ export { ensureTerminalHost, resolveTerminalHostEntryPath, resolveTerminalHostPath } from "./ensure-terminal-host.js";
2
+ export type { EnsureTerminalHostOptions, EnsureTerminalHostResult, TerminalHostCommand, TerminalHostFallbackReason } from "./ensure-terminal-host.js";
3
3
  export { readResidentTabLaunchSettings } from "./settings.js";
4
4
  export type { ResidentTabLaunchSettings } from "./settings.js";
5
5
  export { selectResidentHost } from "./routing.js";
@@ -1,4 +1,4 @@
1
- export { ensureTerminalHost, resolveTerminalHostPath } from "./ensure-terminal-host.js";
1
+ export { ensureTerminalHost, resolveTerminalHostEntryPath, resolveTerminalHostPath } from "./ensure-terminal-host.js";
2
2
  export { readResidentTabLaunchSettings } from "./settings.js";
3
3
  export { selectResidentHost } from "./routing.js";
4
4
  export { readResidentReleaseHolds } from "./release-holds.js";
@@ -1,3 +1,4 @@
1
+ import { type TerminalHostCommand } from "./ensure-terminal-host.js";
1
2
  export type MaintenanceAction = "list" | "stop" | "stop-all" | "host-status" | "host-stop";
2
3
  export type MaintenanceHostState = "none" | "running" | "stopped" | "unresponsive" | "incompatible" | "refused" | "unverifiable" | "unavailable";
3
4
  /** The versioned `a1 tabs` report: `a1-tabs-maintenance-v1`, as printed by the terminal host. */
@@ -26,16 +27,16 @@ export interface MaintenanceOptions {
26
27
  readonly platform?: NodeJS.Platform;
27
28
  readonly architecture?: string;
28
29
  readonly timeoutMs?: number;
29
- readonly verify?: (executable: string, platform: NodeJS.Platform, architecture: string) => Promise<unknown>;
30
- readonly run?: (executable: string, arguments_: readonly string[], timeoutMs: number) => Promise<{
30
+ readonly verify?: (addon: string, platform: NodeJS.Platform, architecture: string) => Promise<unknown>;
31
+ readonly run?: (command: TerminalHostCommand, arguments_: readonly string[], timeoutMs: number) => Promise<{
31
32
  readonly code: number;
32
33
  readonly stdout: string;
33
34
  }>;
34
35
  }
35
36
  /**
36
37
  * Runs one maintenance form through a verified terminal host. When the running host speaks
37
- * another protocol generation, it retries once with the binary from the release that host
38
- * recorded, after verifying that release's manifest; it never runs an unverified binary.
38
+ * another protocol generation, it retries once with the addon from the release that host
39
+ * recorded, after verifying that release's manifest; it never loads an unverified addon.
39
40
  */
40
41
  export declare function runTerminalHostMaintenance(options: MaintenanceOptions): Promise<MaintenanceReport>;
41
42
  /** The largest maintenance report accepted from the terminal host. */