@mono-agent/agent-app 0.12.0 → 0.14.0

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 (200) hide show
  1. package/README.md +190 -33
  2. package/dist/adapter-send-tools-main.js +0 -1
  3. package/dist/adapter-send-tools-main.js.map +1 -1
  4. package/dist/adapter-send-tools.d.ts +8 -5
  5. package/dist/adapter-send-tools.d.ts.map +1 -1
  6. package/dist/adapter-send-tools.js +195 -257
  7. package/dist/adapter-send-tools.js.map +1 -1
  8. package/dist/app-controller-channels.d.ts.map +1 -1
  9. package/dist/app-controller-channels.js +2 -1
  10. package/dist/app-controller-channels.js.map +1 -1
  11. package/dist/app-controller-continuation.d.ts.map +1 -1
  12. package/dist/app-controller-continuation.js +1 -5
  13. package/dist/app-controller-continuation.js.map +1 -1
  14. package/dist/app-controller-lifecycle.d.ts.map +1 -1
  15. package/dist/app-controller-lifecycle.js +2 -6
  16. package/dist/app-controller-lifecycle.js.map +1 -1
  17. package/dist/app-controller-maintenance.d.ts +2 -1
  18. package/dist/app-controller-maintenance.d.ts.map +1 -1
  19. package/dist/app-controller-maintenance.js +66 -2
  20. package/dist/app-controller-maintenance.js.map +1 -1
  21. package/dist/app-controller-memory-health.d.ts +1 -1
  22. package/dist/app-controller-memory-health.d.ts.map +1 -1
  23. package/dist/app-controller-memory-health.js +5 -1
  24. package/dist/app-controller-memory-health.js.map +1 -1
  25. package/dist/app-controller-responder.d.ts.map +1 -1
  26. package/dist/app-controller-responder.js +12 -4
  27. package/dist/app-controller-responder.js.map +1 -1
  28. package/dist/app-controller-traceability.d.ts.map +1 -1
  29. package/dist/app-controller-traceability.js +15 -1
  30. package/dist/app-controller-traceability.js.map +1 -1
  31. package/dist/app-controller-utils.js +1 -1
  32. package/dist/app-controller-utils.js.map +1 -1
  33. package/dist/app-controller.d.ts +14 -1
  34. package/dist/app-controller.d.ts.map +1 -1
  35. package/dist/app-controller.js +61 -9
  36. package/dist/app-controller.js.map +1 -1
  37. package/dist/audit-runs.d.ts.map +1 -1
  38. package/dist/audit-runs.js +12 -2
  39. package/dist/audit-runs.js.map +1 -1
  40. package/dist/background-environment.d.ts +1 -0
  41. package/dist/background-environment.d.ts.map +1 -1
  42. package/dist/background-environment.js +1 -0
  43. package/dist/background-environment.js.map +1 -1
  44. package/dist/background-runtime.d.ts +39 -2
  45. package/dist/background-runtime.d.ts.map +1 -1
  46. package/dist/background-runtime.js +901 -49
  47. package/dist/background-runtime.js.map +1 -1
  48. package/dist/background.d.ts +36 -3
  49. package/dist/background.d.ts.map +1 -1
  50. package/dist/background.js +404 -57
  51. package/dist/background.js.map +1 -1
  52. package/dist/channel-drivers/cron.js +8 -1
  53. package/dist/channel-drivers/cron.js.map +1 -1
  54. package/dist/channel-drivers/live.d.ts +3 -2
  55. package/dist/channel-drivers/live.d.ts.map +1 -1
  56. package/dist/channel-drivers/live.js +3 -2
  57. package/dist/channel-drivers/live.js.map +1 -1
  58. package/dist/channel-drivers/native-notify.d.ts +2 -0
  59. package/dist/channel-drivers/native-notify.d.ts.map +1 -1
  60. package/dist/channel-drivers/native-notify.js +14 -2
  61. package/dist/channel-drivers/native-notify.js.map +1 -1
  62. package/dist/channel-drivers/slack.d.ts.map +1 -1
  63. package/dist/channel-drivers/slack.js +55 -0
  64. package/dist/channel-drivers/slack.js.map +1 -1
  65. package/dist/channel-drivers/telegram.d.ts.map +1 -1
  66. package/dist/channel-drivers/telegram.js +19 -14
  67. package/dist/channel-drivers/telegram.js.map +1 -1
  68. package/dist/channel-drivers/tui.d.ts.map +1 -1
  69. package/dist/channel-drivers/tui.js +8 -0
  70. package/dist/channel-drivers/tui.js.map +1 -1
  71. package/dist/channel-runtime-controls.d.ts +23 -0
  72. package/dist/channel-runtime-controls.d.ts.map +1 -0
  73. package/dist/channel-runtime-controls.js +63 -0
  74. package/dist/channel-runtime-controls.js.map +1 -0
  75. package/dist/channel-status-display.d.ts +15 -0
  76. package/dist/channel-status-display.d.ts.map +1 -0
  77. package/dist/channel-status-display.js +48 -0
  78. package/dist/channel-status-display.js.map +1 -0
  79. package/dist/cli-args.d.ts +23 -22
  80. package/dist/cli-args.d.ts.map +1 -1
  81. package/dist/cli-args.js +136 -83
  82. package/dist/cli-args.js.map +1 -1
  83. package/dist/cli-background-command.d.ts +1 -0
  84. package/dist/cli-background-command.d.ts.map +1 -1
  85. package/dist/cli-background-command.js +158 -27
  86. package/dist/cli-background-command.js.map +1 -1
  87. package/dist/cli-commands.d.ts +2 -2
  88. package/dist/cli-commands.d.ts.map +1 -1
  89. package/dist/cli-commands.js +27 -59
  90. package/dist/cli-commands.js.map +1 -1
  91. package/dist/cli-help.d.ts +19 -1
  92. package/dist/cli-help.d.ts.map +1 -1
  93. package/dist/cli-help.js +203 -67
  94. package/dist/cli-help.js.map +1 -1
  95. package/dist/cli-init-command.d.ts.map +1 -1
  96. package/dist/cli-init-command.js +4 -18
  97. package/dist/cli-init-command.js.map +1 -1
  98. package/dist/cli-install-skill-command.d.ts.map +1 -1
  99. package/dist/cli-install-skill-command.js +32 -3
  100. package/dist/cli-install-skill-command.js.map +1 -1
  101. package/dist/cli-runs-command.d.ts +10 -0
  102. package/dist/cli-runs-command.d.ts.map +1 -0
  103. package/dist/cli-runs-command.js +66 -0
  104. package/dist/cli-runs-command.js.map +1 -0
  105. package/dist/cli-sandbox-command.d.ts +1 -1
  106. package/dist/cli-sandbox-command.d.ts.map +1 -1
  107. package/dist/cli-sandbox-command.js +14 -2
  108. package/dist/cli-sandbox-command.js.map +1 -1
  109. package/dist/cli-validate-config-command.d.ts +24 -5
  110. package/dist/cli-validate-config-command.d.ts.map +1 -1
  111. package/dist/cli-validate-config-command.js +120 -41
  112. package/dist/cli-validate-config-command.js.map +1 -1
  113. package/dist/config-reference.d.ts.map +1 -1
  114. package/dist/config-reference.js +4 -5
  115. package/dist/config-reference.js.map +1 -1
  116. package/dist/docs-mcp-pairing.d.ts +35 -0
  117. package/dist/docs-mcp-pairing.d.ts.map +1 -0
  118. package/dist/docs-mcp-pairing.js +311 -0
  119. package/dist/docs-mcp-pairing.js.map +1 -0
  120. package/dist/doctor.d.ts +6 -0
  121. package/dist/doctor.d.ts.map +1 -1
  122. package/dist/doctor.js +4 -5
  123. package/dist/doctor.js.map +1 -1
  124. package/dist/install-skill.d.ts +2 -0
  125. package/dist/install-skill.d.ts.map +1 -1
  126. package/dist/install-skill.js +10 -4
  127. package/dist/install-skill.js.map +1 -1
  128. package/dist/interaction-bridge.d.ts +5 -20
  129. package/dist/interaction-bridge.d.ts.map +1 -1
  130. package/dist/interaction-bridge.js +255 -100
  131. package/dist/interaction-bridge.js.map +1 -1
  132. package/dist/launchd.d.ts +35 -1
  133. package/dist/launchd.d.ts.map +1 -1
  134. package/dist/launchd.js +131 -2
  135. package/dist/launchd.js.map +1 -1
  136. package/dist/managed-runtime-publication.d.ts +22 -0
  137. package/dist/managed-runtime-publication.d.ts.map +1 -0
  138. package/dist/managed-runtime-publication.js +91 -0
  139. package/dist/managed-runtime-publication.js.map +1 -0
  140. package/dist/managed-startup.d.ts +8 -0
  141. package/dist/managed-startup.d.ts.map +1 -0
  142. package/dist/managed-startup.js +12 -0
  143. package/dist/managed-startup.js.map +1 -0
  144. package/dist/memory-retrieval.d.ts.map +1 -1
  145. package/dist/memory-retrieval.js +31 -18
  146. package/dist/memory-retrieval.js.map +1 -1
  147. package/dist/metrics.d.ts.map +1 -1
  148. package/dist/metrics.js +12 -2
  149. package/dist/metrics.js.map +1 -1
  150. package/dist/modules/catalog.js +1 -1
  151. package/dist/modules/catalog.js.map +1 -1
  152. package/dist/modules/known-tools.d.ts +1 -1
  153. package/dist/modules/known-tools.d.ts.map +1 -1
  154. package/dist/modules/known-tools.js +0 -3
  155. package/dist/modules/known-tools.js.map +1 -1
  156. package/dist/posted-reply-history.d.ts.map +1 -1
  157. package/dist/posted-reply-history.js +7 -0
  158. package/dist/posted-reply-history.js.map +1 -1
  159. package/dist/request-model-override.d.ts +2 -2
  160. package/dist/request-model-override.js +8 -4
  161. package/dist/request-model-override.js.map +1 -1
  162. package/dist/run-history.d.ts +6 -2
  163. package/dist/run-history.d.ts.map +1 -1
  164. package/dist/run-history.js +511 -118
  165. package/dist/run-history.js.map +1 -1
  166. package/dist/runtime-provenance.d.ts.map +1 -1
  167. package/dist/runtime-provenance.js +10 -5
  168. package/dist/runtime-provenance.js.map +1 -1
  169. package/dist/tui-command.d.ts.map +1 -1
  170. package/dist/tui-command.js +2 -1
  171. package/dist/tui-command.js.map +1 -1
  172. package/dist/ui.js +1 -1
  173. package/dist/ui.js.map +1 -1
  174. package/dist/wizard/from-flags.d.ts +0 -1
  175. package/dist/wizard/from-flags.d.ts.map +1 -1
  176. package/dist/wizard/from-flags.js +1 -8
  177. package/dist/wizard/from-flags.js.map +1 -1
  178. package/dist/wizard/index.d.ts +1 -1
  179. package/dist/wizard/index.d.ts.map +1 -1
  180. package/dist/wizard/index.js +1 -1
  181. package/dist/wizard/index.js.map +1 -1
  182. package/dist/wizard/presets.d.ts +3 -12
  183. package/dist/wizard/presets.d.ts.map +1 -1
  184. package/dist/wizard/presets.js +3 -15
  185. package/dist/wizard/presets.js.map +1 -1
  186. package/dist/wizard/prompts.d.ts.map +1 -1
  187. package/dist/wizard/prompts.js +3 -4
  188. package/dist/wizard/prompts.js.map +1 -1
  189. package/dist/wizard/run.js +3 -3
  190. package/dist/wizard/run.js.map +1 -1
  191. package/package.json +15 -16
  192. package/schema/mono-agent.config.schema.json +4 -4
  193. package/skills/mono-agent-composer/SKILL.md +13 -5
  194. package/skills/mono-agent-composer/agents/openai.yaml +1 -1
  195. package/skills/mono-agent-composer/references/config-blueprint.md +18 -10
  196. package/skills/mono-agent-composer/references/discovery-questions.md +8 -5
  197. package/skills/mono-agent-composer/references/feature-coverage.md +10 -9
  198. package/skills/mono-agent-composer/references/package-map.md +10 -4
  199. package/skills/mono-agent-composer/references/playbooks.md +6 -6
  200. package/skills/mono-agent-composer/references/validation.md +24 -2
@@ -4,16 +4,21 @@ import { chmod, cp, lstat, mkdir, open, readFile, readlink, readdir, realpath, r
4
4
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import process from "node:process";
6
6
  import { accountHomeDirectory } from "./account-home.js";
7
- import { isBackgroundOperationalEnvName } from "./background-environment.js";
7
+ import { isBackgroundOperationalEnvName, MANAGED_BACKGROUND_WORKER_ENV, } from "./background-environment.js";
8
+ export { MANAGED_BACKGROUND_WORKER_ENV } from "./background-environment.js";
8
9
  import { currentProcessIncarnation, isSameProcessIncarnation as matchesProcessIncarnation, processIncarnationFromJson, } from "./process-incarnation.js";
9
10
  import { acquireOwnerPrivateLock } from "./owner-private-lock.js";
10
11
  const PACKAGE_NAME = "@mono-agent/agent-app";
11
- export const MANAGED_BACKGROUND_WORKER_ENV = "MONO_AGENT_MANAGED_WORKER";
12
- const LOCK_WAIT_TIMEOUT_MS = 30_000;
12
+ const LOCK_WAIT_TIMEOUT_MS = 5 * 60_000;
13
13
  const LOCK_STALE_AFTER_MS = 5 * 60_000;
14
14
  const LOCK_POLL_INTERVAL_MS = 200;
15
15
  const PROVISIONAL_RUNTIME_INSTALLED_AT = "1970-01-01T00:00:00.000Z";
16
16
  const MAX_RUNTIME_MARKER_BYTES = 16 * 1024;
17
+ const MAX_RUNTIME_REUSE_PROOF_BYTES = 64 * 1024 * 1024;
18
+ const RUNTIME_REUSE_PROOF_SCHEMA = "mono-agent.managed-runtime-reuse-proof.v1";
19
+ const RUNTIME_REUSE_STAT_CONCURRENCY = 64;
20
+ const MANAGED_RUNTIME_LAUNCH_PROOF_SCHEMA = "mono-agent.managed-runtime-launch.v1";
21
+ const MAX_MANAGED_RUNTIME_LAUNCH_PROOF_BYTES = 2 * 1024;
17
22
  export function defaultManagedBackgroundRuntimeDeps() {
18
23
  return {
19
24
  now: () => Date.now(),
@@ -52,47 +57,77 @@ export async function ensureManagedBackgroundRuntime(input, deps = defaultManage
52
57
  await assertRegularFile(currentCliPath, "current mono-agent CLI");
53
58
  await assertRegularFile(resolve(input.nodePath), "managed runtime Node executable");
54
59
  const currentCli = await readFile(currentCliPath);
55
- const packageSource = resolve(input.packageSource ?? packageRootForCli(currentCliPath));
56
- const sourceDetails = await lstat(packageSource);
60
+ const packageSourceLexical = resolve(input.packageSource ?? packageRootForCli(currentCliPath));
61
+ const sourceDetails = await lstat(packageSourceLexical);
57
62
  if (!sourceDetails.isDirectory() || sourceDetails.isSymbolicLink()) {
58
- throw new Error(`Managed runtime package source ${packageSource} must be a real directory.`);
63
+ throw new Error(`Managed runtime package source ${packageSourceLexical} must be a real directory.`);
59
64
  }
65
+ const packageSource = await realpath(packageSourceLexical);
60
66
  const additionalPackages = await canonicalAdditionalPackages(input.additionalPackages ?? []);
61
- const sourceClosure = await captureExecutionClosure(packageSource, additionalPackages);
62
67
  const packageVersion = input.packageVersion ?? await packageVersionAt(packageSource);
63
68
  if (!isExactVersion(packageVersion)) {
64
69
  throw new Error(`Cannot install a durable managed runtime for invalid package version ${JSON.stringify(packageVersion)}.`);
65
70
  }
71
+ const cliSha256 = sha256(currentCli);
72
+ const nodeAbi = input.nodeAbi ?? requiredNodeAbi();
73
+ const platform = input.platform ?? process.platform;
74
+ const arch = input.arch ?? process.arch;
75
+ const home = resolve(input.homeDir ?? accountHomeDirectory());
76
+ const versionAbiDir = runtimeVersionAbiDir(home, packageVersion, nodeAbi, platform, arch);
77
+ await ensurePrivateRuntimeAncestors(home, versionAbiDir);
78
+ const fastRuntime = await findFastReusableRuntime({
79
+ home,
80
+ versionAbiDir,
81
+ packageVersion,
82
+ cliSha256,
83
+ nodeAbi,
84
+ platform,
85
+ arch,
86
+ packageSource,
87
+ additionalPackages,
88
+ });
89
+ if (fastRuntime !== undefined) {
90
+ await waitForManagedRuntimeLaunchBoundary(fastRuntime.layout, fastRuntime.identity, deps);
91
+ return await runtimeResult(fastRuntime.layout, fastRuntime.identity, input.nodePath, "fast-reuse");
92
+ }
93
+ const sourceClosure = await captureExecutionClosure(packageSource, additionalPackages);
66
94
  const identity = {
67
95
  packageVersion,
68
- cliSha256: sha256(currentCli),
96
+ cliSha256,
69
97
  sourceClosureSha256: sourceClosure.sourceClosureSha256,
70
- nodeAbi: input.nodeAbi ?? requiredNodeAbi(),
71
- platform: input.platform ?? process.platform,
72
- arch: input.arch ?? process.arch,
98
+ nodeAbi,
99
+ platform,
100
+ arch,
73
101
  };
74
- const home = resolve(input.homeDir ?? accountHomeDirectory());
75
102
  const id = safeSegment(deps.randomId());
76
103
  const layout = runtimeLayout(home, identity, id, deps.now());
77
- await ensurePrivateRuntimeAncestors(home, layout.versionAbiDir);
78
- if (await verifyRuntime(layout, identity, additionalPackages)) {
79
- await waitForManagedRuntimeLaunchBoundary(layout, identity, deps);
80
- return runtimeResult(layout, identity, input.nodePath);
81
- }
82
- const acquired = await acquireRuntimeLock(layout, identity, additionalPackages, deps);
104
+ const runtimeWasPresent = await pathExists(layout.installRoot);
105
+ const acquired = await acquireRuntimeLock(layout, identity, additionalPackages, deps, () => verifyRuntimeFast(layout, identity, packageSource, additionalPackages));
83
106
  if (!acquired) {
84
- // Another installer may have completed between the final lock poll and the
85
- // timeout boundary. Verify one last time before reporting the contention.
107
+ if (await verifyRuntimeFast(layout, identity, packageSource, additionalPackages)) {
108
+ await waitForManagedRuntimeLaunchBoundary(layout, identity, deps);
109
+ return await runtimeResult(layout, identity, input.nodePath, "fast-reuse");
110
+ }
111
+ // Preserve compatibility with a finalized v4 runtime when another process
112
+ // released the lock before it could publish the v5 warm proof.
86
113
  if (await verifyRuntime(layout, identity, additionalPackages)) {
87
114
  await waitForManagedRuntimeLaunchBoundary(layout, identity, deps);
88
- return runtimeResult(layout, identity, input.nodePath);
115
+ return await runtimeResult(layout, identity, input.nodePath, "full-reuse");
89
116
  }
90
117
  throw new Error(`Timed out waiting for the managed runtime installation lock at ${layout.lockDir}.`);
91
118
  }
92
119
  try {
93
120
  if (await verifyRuntime(layout, identity, additionalPackages)) {
121
+ const marker = await readRuntimeMarker(layout.markerPath, identity);
122
+ if (marker === undefined)
123
+ throw new Error("The managed runtime marker changed before warm-proof publication.");
124
+ const sourceReuseProof = await currentSourceReuseProof(packageSource, additionalPackages, sourceClosure);
125
+ await publishRuntimeReuseProof(layout, identity, packageSource, additionalPackages, sourceReuseProof, marker, deps);
126
+ if (!(await verifyRuntimeFast(layout, identity, packageSource, additionalPackages))) {
127
+ throw new Error("The managed runtime warm-reuse proof failed verification after publication.");
128
+ }
94
129
  await waitForManagedRuntimeLaunchBoundary(layout, identity, deps);
95
- return runtimeResult(layout, identity, input.nodePath);
130
+ return await runtimeResult(layout, identity, input.nodePath, "full-reuse");
96
131
  }
97
132
  await assertSourceClosureUnchanged(packageSource, additionalPackages, sourceClosure, "before staging");
98
133
  await rm(layout.stagingDir, { recursive: true, force: true });
@@ -158,10 +193,11 @@ export async function ensureManagedBackgroundRuntime(input, deps = defaultManage
158
193
  if (promotedClosure.sourceClosureSha256 !== identity.sourceClosureSha256) {
159
194
  throw new Error("The promoted managed runtime execution closure does not match its source identity.");
160
195
  }
161
- await writeFinalizedRuntimeMarker(layout.markerPath, {
196
+ const sourceReuseProof = await currentSourceReuseProof(packageSource, additionalPackages, sourceClosure);
197
+ await publishRuntimeReuseProof(layout, identity, packageSource, additionalPackages, sourceReuseProof, {
162
198
  ...marker,
163
199
  executionProofSha256: promotedClosure.filesystemProofSha256,
164
- }, deps);
200
+ }, deps, true);
165
201
  }
166
202
  }
167
203
  catch (error) {
@@ -171,13 +207,163 @@ export async function ensureManagedBackgroundRuntime(input, deps = defaultManage
171
207
  if (!(await verifyRuntime(layout, identity, additionalPackages))) {
172
208
  throw new Error("The managed runtime failed verification after atomic promotion.");
173
209
  }
210
+ if (!(await verifyRuntimeFast(layout, identity, packageSource, additionalPackages))) {
211
+ const marker = await readRuntimeMarker(layout.markerPath, identity);
212
+ if (marker === undefined)
213
+ throw new Error("The managed runtime marker changed before warm-proof publication.");
214
+ const sourceReuseProof = await currentSourceReuseProof(packageSource, additionalPackages, sourceClosure);
215
+ await publishRuntimeReuseProof(layout, identity, packageSource, additionalPackages, sourceReuseProof, marker, deps);
216
+ }
217
+ if (!(await verifyRuntimeFast(layout, identity, packageSource, additionalPackages))) {
218
+ throw new Error("The managed runtime warm-reuse proof failed verification after atomic promotion.");
219
+ }
174
220
  await waitForManagedRuntimeLaunchBoundary(layout, identity, deps);
175
- return runtimeResult(layout, identity, input.nodePath);
221
+ return await runtimeResult(layout, identity, input.nodePath, runtimeWasPresent ? "repaired" : "installed");
176
222
  }
177
223
  finally {
178
224
  await acquired.release().catch(() => undefined);
179
225
  }
180
226
  }
227
+ /**
228
+ * Inspect the controller CLI as inert installation input. The recovery helper
229
+ * uses this to decide whether launchd owns an older closure without ever
230
+ * executing mutable checkout bytes.
231
+ */
232
+ export async function inspectManagedRuntimeSourceIdentity(currentCliPathInput) {
233
+ const currentCliPath = resolve(currentCliPathInput);
234
+ await assertRegularFile(currentCliPath, "current mono-agent CLI");
235
+ const packageSource = packageRootForCli(currentCliPath);
236
+ const sourceDetails = await lstat(packageSource);
237
+ if (!sourceDetails.isDirectory() || sourceDetails.isSymbolicLink()) {
238
+ throw new Error(`Managed runtime package source ${packageSource} must be a real directory.`);
239
+ }
240
+ const packageVersion = await packageVersionAt(packageSource);
241
+ if (!isExactVersion(packageVersion)) {
242
+ throw new Error(`Cannot inspect a durable managed runtime for invalid package version ${JSON.stringify(packageVersion)}.`);
243
+ }
244
+ return {
245
+ packageVersion,
246
+ cliSha256: sha256(await readFile(currentCliPath)),
247
+ };
248
+ }
249
+ /**
250
+ * Validate the opaque proof carried by a managed LaunchAgent against the exact
251
+ * CLI that is about to start. This is intentionally lighter than controller
252
+ * attestation: it rechecks the private layout, marker, CLI, and stored closure
253
+ * manifest fingerprints without traversing the entire installed dependency
254
+ * tree on every KeepAlive restart.
255
+ */
256
+ export async function verifyManagedRuntimeLaunch(input) {
257
+ const proof = decodeManagedRuntimeLaunchProof(input.launchProof);
258
+ const currentCliPath = resolve(input.currentCliPath);
259
+ const packageRoot = packageRootForCli(currentCliPath);
260
+ const installRoot = dirname(dirname(dirname(packageRoot)));
261
+ const home = resolve(input.homeDir ?? accountHomeDirectory());
262
+ const initialInstallRoot = await lstat(installRoot, { bigint: true });
263
+ if (!privateRuntimeDirectory(initialInstallRoot)) {
264
+ throw new Error("The managed runtime closure root is not a real owner-private directory.");
265
+ }
266
+ const markerPath = join(installRoot, ".mono-agent-runtime.json");
267
+ const initialBytes = await readPrivateRuntimeMarkerBytes(markerPath);
268
+ if (initialBytes === undefined)
269
+ throw new Error("The managed runtime marker is unavailable or unsafe.");
270
+ const markerValue = parseJson(initialBytes, "managed runtime marker");
271
+ const identity = runtimeIdentityFromMarkerJson(markerValue);
272
+ if (identity === undefined)
273
+ throw new Error("The managed runtime marker has an invalid identity.");
274
+ const marker = runtimeMarkerFromJson(markerValue, identity);
275
+ if (marker === undefined)
276
+ throw new Error("The managed runtime marker is provisional or malformed.");
277
+ const layout = runtimeLayout(home, identity, "launch", 0);
278
+ if (layout.installRoot !== installRoot || layout.cliPath !== currentCliPath) {
279
+ throw new Error("The managed worker CLI is outside its canonical runtime closure.");
280
+ }
281
+ if (!(await verifyPrivateRuntimeAncestors(home, layout.versionAbiDir))) {
282
+ throw new Error("The managed runtime has an unsafe private ancestry.");
283
+ }
284
+ if (sha256(initialBytes) !== proof.markerSha256 || marker.installedAt !== proof.installedAt) {
285
+ throw new Error("The managed runtime marker does not match its LaunchAgent proof.");
286
+ }
287
+ if (!(await verifyInstalledPackage(layout, identity))) {
288
+ throw new Error("The managed runtime CLI or package identity failed verification.");
289
+ }
290
+ if (!(await verifyManagedRuntimeCliFingerprint(layout, identity.cliSha256))) {
291
+ throw new Error("The managed runtime CLI fingerprint is unstable.");
292
+ }
293
+ if (!(await verifyClosureManifestFingerprint(layout, marker.closureManifestSha256))) {
294
+ throw new Error("The managed runtime closure manifest fingerprint is invalid.");
295
+ }
296
+ await waitForLaunchBoundary(marker.installedAt, input.now ?? Date.now, input.sleep ?? sleep);
297
+ const finalBytes = await readPrivateRuntimeMarkerBytes(markerPath);
298
+ if (finalBytes === undefined || sha256(finalBytes) !== proof.markerSha256 || !finalBytes.equals(initialBytes)) {
299
+ throw new Error("The managed runtime marker changed during launch verification.");
300
+ }
301
+ if (!(await verifyManagedRuntimeCliFingerprint(layout, identity.cliSha256))) {
302
+ throw new Error("The managed runtime CLI changed during launch verification.");
303
+ }
304
+ if (!(await verifyClosureManifestFingerprint(layout, marker.closureManifestSha256))) {
305
+ throw new Error("The managed runtime closure manifest changed during launch verification.");
306
+ }
307
+ const finalInstallRoot = await lstat(installRoot, { bigint: true });
308
+ if (!privateRuntimeDirectory(finalInstallRoot) || !sameSourceStats(initialInstallRoot, finalInstallRoot)) {
309
+ throw new Error("The managed runtime closure root changed during launch verification.");
310
+ }
311
+ const closureId = `${identity.cliSha256}-${identity.sourceClosureSha256}`;
312
+ return {
313
+ installRoot,
314
+ packageVersion: identity.packageVersion,
315
+ cliSha256: identity.cliSha256,
316
+ provenanceDetail: `Runtime provenance: managed closure ${closureId} (`
317
+ + `${PACKAGE_NAME} ${identity.packageVersion}; ${identity.platform}-${identity.arch}; `
318
+ + `Node ABI ${identity.nodeAbi}; installed ${marker.installedAt}).`,
319
+ };
320
+ }
321
+ function encodeManagedRuntimeLaunchProof(proof) {
322
+ return Buffer.from(JSON.stringify(proof), "utf8").toString("base64url");
323
+ }
324
+ function decodeManagedRuntimeLaunchProof(encoded) {
325
+ if (typeof encoded !== "string"
326
+ || encoded.length === 0
327
+ || encoded.length > MAX_MANAGED_RUNTIME_LAUNCH_PROOF_BYTES * 2
328
+ || !/^[0-9A-Za-z_-]+$/u.test(encoded)) {
329
+ throw new Error("The managed runtime launch proof is malformed.");
330
+ }
331
+ let bytes;
332
+ try {
333
+ bytes = Buffer.from(encoded, "base64url");
334
+ }
335
+ catch {
336
+ throw new Error("The managed runtime launch proof is malformed.");
337
+ }
338
+ if (bytes.length === 0
339
+ || bytes.length > MAX_MANAGED_RUNTIME_LAUNCH_PROOF_BYTES
340
+ || bytes.toString("base64url") !== encoded) {
341
+ throw new Error("The managed runtime launch proof is malformed.");
342
+ }
343
+ const value = parseJson(bytes, "managed runtime launch proof");
344
+ if (!isRecord(value) || Object.keys(value).length !== 3) {
345
+ throw new Error("The managed runtime launch proof has an invalid schema.");
346
+ }
347
+ const installedAtMs = typeof value.installedAt === "string" ? Date.parse(value.installedAt) : Number.NaN;
348
+ if (value.schema !== MANAGED_RUNTIME_LAUNCH_PROOF_SCHEMA
349
+ || typeof value.markerSha256 !== "string"
350
+ || !/^[0-9a-f]{64}$/u.test(value.markerSha256)
351
+ || typeof value.installedAt !== "string"
352
+ || !Number.isFinite(installedAtMs)
353
+ || new Date(installedAtMs).toISOString() !== value.installedAt
354
+ || value.installedAt === PROVISIONAL_RUNTIME_INSTALLED_AT) {
355
+ throw new Error("The managed runtime launch proof has an invalid schema.");
356
+ }
357
+ return value;
358
+ }
359
+ function parseJson(bytes, label) {
360
+ try {
361
+ return JSON.parse(bytes.toString("utf8"));
362
+ }
363
+ catch {
364
+ throw new Error(`The ${label} is not valid JSON.`);
365
+ }
366
+ }
181
367
  /**
182
368
  * Canonically verify the installed package, freshly recomputed execution
183
369
  * closure/filesystem proof, and coherent current manifest behind a provenance
@@ -212,16 +398,8 @@ export async function verifyManagedRuntimeClosureForProvenance(input, deps = {})
212
398
  return false;
213
399
  if (!(await verifyPrivateRuntimeAncestors(home, layout.versionAbiDir)))
214
400
  return false;
215
- const expectedMarker = {
216
- schema: "mono-agent.managed-runtime.v4",
217
- packageName: PACKAGE_NAME,
218
- closureManifestSha256: input.closureManifestSha256,
219
- executionProofSha256: input.executionProofSha256,
220
- ...identity,
221
- installedAt: input.installedAt,
222
- };
223
401
  const markerInitial = await verifiedRuntimeMarker(layout, identity);
224
- if (markerInitial === undefined || !sameRuntimeMarker(markerInitial, expectedMarker))
402
+ if (markerInitial === undefined || !runtimeMarkerMatchesProvenance(markerInitial, input))
225
403
  return false;
226
404
  const additionalPackages = await managedRuntimeAdditionalPackages(layout);
227
405
  if (additionalPackages === undefined)
@@ -242,7 +420,7 @@ export async function verifyManagedRuntimeClosureForProvenance(input, deps = {})
242
420
  const markerFinal = await readRuntimeMarker(layout.markerPath, identity);
243
421
  if (markerFinal === undefined
244
422
  || !sameRuntimeMarker(markerFinal, markerInitial)
245
- || !sameRuntimeMarker(markerFinal, expectedMarker)
423
+ || !runtimeMarkerMatchesProvenance(markerFinal, input)
246
424
  || !(await verifyClosureManifest(layout, markerFinal.closureManifestSha256, deps.beforeFinalManifestProof === undefined
247
425
  ? {}
248
426
  : { beforeFinalProof: deps.beforeFinalManifestProof }))) {
@@ -251,7 +429,7 @@ export async function verifyManagedRuntimeClosureForProvenance(input, deps = {})
251
429
  const markerAfterManifest = await readRuntimeMarker(layout.markerPath, identity);
252
430
  return markerAfterManifest !== undefined
253
431
  && sameRuntimeMarker(markerAfterManifest, markerInitial)
254
- && sameRuntimeMarker(markerAfterManifest, expectedMarker);
432
+ && runtimeMarkerMatchesProvenance(markerAfterManifest, input);
255
433
  }
256
434
  catch {
257
435
  return false;
@@ -569,6 +747,15 @@ async function captureExecutionClosure(packageSource, additionalPackages, filesy
569
747
  sourceClosureSha256: sha256(Buffer.from(JSON.stringify(stable), "utf8")),
570
748
  sourceProofSha256: sha256(Buffer.from(JSON.stringify(proof), "utf8")),
571
749
  filesystemProofSha256: sha256(Buffer.from(JSON.stringify(filesystemProof), "utf8")),
750
+ reuseProof: {
751
+ sourceProofSha256: sha256(Buffer.from(JSON.stringify(proof), "utf8")),
752
+ packages: closure.map((node) => ({
753
+ id: node.id,
754
+ sourceRoot: node.sourceRoot,
755
+ entries: requiredMapValue(proofs, node.id, "source package proof"),
756
+ })),
757
+ links: [...dependencyProofPaths, ...additionalProofPaths],
758
+ },
572
759
  };
573
760
  }
574
761
  /**
@@ -626,6 +813,15 @@ async function assertSourceClosureUnchanged(packageSource, additionalPackages, e
626
813
  throw new Error(`The managed runtime package closure changed ${phase}; no staged runtime was promoted.`);
627
814
  }
628
815
  }
816
+ async function currentSourceReuseProof(packageSource, additionalPackages, expected) {
817
+ if (await verifySourceReuseProofStats(expected.reuseProof))
818
+ return expected.reuseProof;
819
+ const current = await captureExecutionClosure(packageSource, additionalPackages);
820
+ if (current.sourceClosureSha256 !== expected.sourceClosureSha256) {
821
+ throw new Error("The managed runtime source closure changed before warm-proof publication.");
822
+ }
823
+ return current.reuseProof;
824
+ }
629
825
  /**
630
826
  * Capture exactly the package-owned files copied by `materializeExactExecutionClosure`.
631
827
  * `node_modules`, VCS metadata, and dotenv files are deliberately excluded.
@@ -1054,8 +1250,7 @@ async function packageVersionAt(packageRoot) {
1054
1250
  return record.version;
1055
1251
  }
1056
1252
  function runtimeLayout(home, identity, id, nowMs) {
1057
- const platformAbi = `${safeSegment(identity.platform)}-${safeSegment(identity.arch)}-abi-${safeSegment(identity.nodeAbi)}`;
1058
- const versionAbiDir = join(home, ".mono-agent", "runtimes", "agent-app", identity.packageVersion, platformAbi);
1253
+ const versionAbiDir = runtimeVersionAbiDir(home, identity.packageVersion, identity.nodeAbi, identity.platform, identity.arch);
1059
1254
  const closureId = `${identity.cliSha256}-${identity.sourceClosureSha256}`;
1060
1255
  const installRoot = join(versionAbiDir, closureId);
1061
1256
  const suffix = `${Math.max(0, Math.floor(nowMs))}-${id}`;
@@ -1067,11 +1262,17 @@ function runtimeLayout(home, identity, id, nowMs) {
1067
1262
  packageLockPath: join(installRoot, "package-lock.json"),
1068
1263
  closureManifestPath: join(installRoot, ".mono-agent-closure.json"),
1069
1264
  markerPath: join(installRoot, ".mono-agent-runtime.json"),
1265
+ reuseProofDir: join(versionAbiDir, ".reuse-proofs"),
1266
+ reuseProofPath: join(versionAbiDir, ".reuse-proofs", `${closureId}.json`),
1070
1267
  lockDir: join(versionAbiDir, `.${closureId}.lock`),
1071
1268
  stagingDir: join(versionAbiDir, `.${closureId}.staging-${suffix}`),
1072
1269
  quarantineDir: join(versionAbiDir, "quarantine", `${closureId}-${suffix}`),
1073
1270
  };
1074
1271
  }
1272
+ function runtimeVersionAbiDir(home, packageVersion, nodeAbi, platform, arch) {
1273
+ const platformAbi = `${safeSegment(platform)}-${safeSegment(arch)}-abi-${safeSegment(nodeAbi)}`;
1274
+ return join(home, ".mono-agent", "runtimes", "agent-app", packageVersion, platformAbi);
1275
+ }
1075
1276
  function runtimeLayoutForRoot(layout, root) {
1076
1277
  return {
1077
1278
  ...layout,
@@ -1083,7 +1284,552 @@ function runtimeLayoutForRoot(layout, root) {
1083
1284
  markerPath: join(root, ".mono-agent-runtime.json"),
1084
1285
  };
1085
1286
  }
1086
- async function acquireRuntimeLock(layout, identity, additionalPackages, deps) {
1287
+ async function findFastReusableRuntime(input) {
1288
+ let entries;
1289
+ try {
1290
+ entries = await readdir(input.versionAbiDir, { withFileTypes: true });
1291
+ }
1292
+ catch {
1293
+ return undefined;
1294
+ }
1295
+ const prefix = `${input.cliSha256}-`;
1296
+ for (const entry of entries.sort((left, right) => compareCodeUnits(left.name, right.name))) {
1297
+ if (!entry.isDirectory() || !entry.name.startsWith(prefix))
1298
+ continue;
1299
+ const sourceClosureSha256 = entry.name.slice(prefix.length);
1300
+ if (!/^[0-9a-f]{64}$/u.test(sourceClosureSha256))
1301
+ continue;
1302
+ const identity = {
1303
+ packageVersion: input.packageVersion,
1304
+ cliSha256: input.cliSha256,
1305
+ sourceClosureSha256,
1306
+ nodeAbi: input.nodeAbi,
1307
+ platform: input.platform,
1308
+ arch: input.arch,
1309
+ };
1310
+ const layout = runtimeLayout(input.home, identity, "fast-reuse", 0);
1311
+ if (await verifyRuntimeFast(layout, identity, input.packageSource, input.additionalPackages)) {
1312
+ return { layout, identity };
1313
+ }
1314
+ }
1315
+ return undefined;
1316
+ }
1317
+ async function verifyRuntimeFast(layout, identity, packageSource, additionalPackages) {
1318
+ try {
1319
+ const home = dirname(dirname(dirname(dirname(dirname(layout.versionAbiDir)))));
1320
+ if (!(await verifyPrivateRuntimeAncestors(home, layout.versionAbiDir)))
1321
+ return false;
1322
+ const [initialRoot, initialProofDir] = await Promise.all([
1323
+ lstat(layout.installRoot, { bigint: true }),
1324
+ lstat(layout.reuseProofDir, { bigint: true }),
1325
+ ]);
1326
+ if (!privateRuntimeDirectory(initialRoot) || !privateRuntimeDirectory(initialProofDir))
1327
+ return false;
1328
+ const [markerBytes, proofBytes] = await Promise.all([
1329
+ readPrivateRuntimeMarkerBytes(layout.markerPath),
1330
+ readPrivateRuntimeReuseProofBytes(layout.reuseProofPath),
1331
+ ]);
1332
+ if (markerBytes === undefined || proofBytes === undefined)
1333
+ return false;
1334
+ const marker = runtimeMarkerFromJson(parseJson(markerBytes, "managed runtime marker"), identity);
1335
+ if (marker?.schema !== "mono-agent.managed-runtime.v5"
1336
+ || marker.reuseProofSha256 !== sha256(proofBytes)) {
1337
+ return false;
1338
+ }
1339
+ const proof = runtimeReuseProofFromJson(parseJson(proofBytes, "managed runtime reuse proof"), identity, packageSource, additionalPackages, marker);
1340
+ if (proof === undefined)
1341
+ return false;
1342
+ const [installed, cliStable, manifestStable] = await Promise.all([
1343
+ verifyInstalledPackage(layout, identity),
1344
+ verifyManagedRuntimeCliFingerprint(layout, identity.cliSha256),
1345
+ verifyClosureManifestFingerprint(layout, marker.closureManifestSha256),
1346
+ ]);
1347
+ if (!installed
1348
+ || !cliStable
1349
+ || !manifestStable
1350
+ || !(await runtimeReuseProofMatchesManifest(layout, proof.runtime))) {
1351
+ return false;
1352
+ }
1353
+ if (!(await verifyReuseProofStats(layout, proof)))
1354
+ return false;
1355
+ if (!(await verifyReuseProofStats(layout, proof)))
1356
+ return false;
1357
+ const [markerBytesAfter, proofBytesAfter, finalRoot, finalProofDir] = await Promise.all([
1358
+ readPrivateRuntimeMarkerBytes(layout.markerPath),
1359
+ readPrivateRuntimeReuseProofBytes(layout.reuseProofPath),
1360
+ lstat(layout.installRoot, { bigint: true }),
1361
+ lstat(layout.reuseProofDir, { bigint: true }),
1362
+ ]);
1363
+ return markerBytesAfter !== undefined
1364
+ && proofBytesAfter !== undefined
1365
+ && markerBytesAfter.equals(markerBytes)
1366
+ && proofBytesAfter.equals(proofBytes)
1367
+ && privateRuntimeDirectory(finalRoot)
1368
+ && privateRuntimeDirectory(finalProofDir)
1369
+ && sameSourceStats(initialRoot, finalRoot)
1370
+ && sameSourceStats(initialProofDir, finalProofDir);
1371
+ }
1372
+ catch {
1373
+ return false;
1374
+ }
1375
+ }
1376
+ async function publishRuntimeReuseProof(layout, identity, packageSource, additionalPackages, source, marker, deps, finalizeLaunchBoundary = false) {
1377
+ if (!(await verifySourceReuseProofStats(source))) {
1378
+ throw new Error("The managed runtime source closure changed before warm-proof publication.");
1379
+ }
1380
+ const runtime = await captureRuntimeClosureReuseProof(layout, marker);
1381
+ const proof = {
1382
+ schema: RUNTIME_REUSE_PROOF_SCHEMA,
1383
+ identity,
1384
+ packageSource,
1385
+ additionalPackages,
1386
+ source,
1387
+ runtime,
1388
+ };
1389
+ const contents = privateJsonContents(proof);
1390
+ const bytes = Buffer.from(contents, "utf8");
1391
+ if (bytes.length > MAX_RUNTIME_REUSE_PROOF_BYTES) {
1392
+ throw new Error("The managed runtime warm-reuse proof exceeds its safe size limit.");
1393
+ }
1394
+ await ensurePrivateReuseProofDirectory(layout.reuseProofDir);
1395
+ await writeFile(layout.reuseProofPath, contents, { encoding: "utf8", mode: 0o600 });
1396
+ await chmod(layout.reuseProofPath, 0o600);
1397
+ const stored = await readPrivateRuntimeReuseProofBytes(layout.reuseProofPath);
1398
+ if (stored === undefined || !stored.equals(bytes)) {
1399
+ throw new Error("The managed runtime warm-reuse proof changed while it was published.");
1400
+ }
1401
+ const nextMarker = {
1402
+ schema: "mono-agent.managed-runtime.v5",
1403
+ packageName: PACKAGE_NAME,
1404
+ packageVersion: identity.packageVersion,
1405
+ cliSha256: identity.cliSha256,
1406
+ sourceClosureSha256: identity.sourceClosureSha256,
1407
+ nodeAbi: identity.nodeAbi,
1408
+ platform: identity.platform,
1409
+ arch: identity.arch,
1410
+ closureManifestSha256: marker.closureManifestSha256,
1411
+ executionProofSha256: marker.executionProofSha256,
1412
+ reuseProofSha256: sha256(bytes),
1413
+ installedAt: marker.installedAt,
1414
+ };
1415
+ if (finalizeLaunchBoundary) {
1416
+ await writeFinalizedRuntimeMarker(layout.markerPath, nextMarker, deps);
1417
+ }
1418
+ else {
1419
+ await writePrivateJson(layout.markerPath, nextMarker);
1420
+ }
1421
+ if (!(await verifySourceReuseProofStats(source))) {
1422
+ throw new Error("The managed runtime source closure changed during warm-proof publication.");
1423
+ }
1424
+ }
1425
+ async function captureRuntimeClosureReuseProof(layout, marker) {
1426
+ const stored = await fingerprintClosureFile(layout.closureManifestPath, ".mono-agent-closure.json");
1427
+ if (stored.sha256 !== marker.closureManifestSha256) {
1428
+ throw new Error("The managed runtime closure manifest changed before warm-proof capture.");
1429
+ }
1430
+ const manifest = runtimeClosureManifestFromJson(parseJson(stored.bytes, "managed runtime closure manifest"));
1431
+ if (manifest === undefined)
1432
+ throw new Error("The managed runtime closure manifest is malformed.");
1433
+ const excludedRootEntries = new Set([".mono-agent-closure.json", ".mono-agent-runtime.json"]);
1434
+ const rootEntries = (await readdir(layout.installRoot))
1435
+ .filter((name) => !excludedRootEntries.has(name))
1436
+ .sort(compareCodeUnits);
1437
+ const entries = await mapWithConcurrency(manifest.entries, RUNTIME_REUSE_STAT_CONCURRENCY, async (entry) => ({
1438
+ path: entry.path,
1439
+ proof: await captureFilesystemPathProof(runtimePathFromPortableRelative(layout.installRoot, entry.path), entry.path),
1440
+ }));
1441
+ const result = {
1442
+ rootEntries,
1443
+ entries,
1444
+ closureManifestSha256: marker.closureManifestSha256,
1445
+ executionProofSha256: marker.executionProofSha256,
1446
+ };
1447
+ if (!(await verifyRuntimeReuseProofStats(layout, result))) {
1448
+ throw new Error("The managed runtime closure changed during warm-proof capture.");
1449
+ }
1450
+ return result;
1451
+ }
1452
+ async function verifyReuseProofStats(layout, proof) {
1453
+ const [sourceValid, runtimeValid] = await Promise.all([
1454
+ verifySourceReuseProofStats(proof.source),
1455
+ verifyRuntimeReuseProofStats(layout, proof.runtime),
1456
+ ]);
1457
+ return sourceValid && runtimeValid;
1458
+ }
1459
+ async function verifySourceReuseProofStats(proof) {
1460
+ try {
1461
+ const targets = [
1462
+ ...proof.packages.flatMap((pkg) => pkg.entries.map((entry) => ({
1463
+ path: entry.path === "."
1464
+ ? pkg.sourceRoot
1465
+ : runtimePathFromPortableRelative(pkg.sourceRoot, entry.path),
1466
+ logicalPath: entry.path,
1467
+ proof: entry,
1468
+ }))),
1469
+ ...proof.links,
1470
+ ];
1471
+ const matches = await mapWithConcurrency(targets, RUNTIME_REUSE_STAT_CONCURRENCY, async (target) => {
1472
+ const current = await captureFilesystemPathProof(target.path, target.logicalPath);
1473
+ return sameSourceProofEntry(target.proof, current);
1474
+ });
1475
+ return matches.every(Boolean);
1476
+ }
1477
+ catch {
1478
+ return false;
1479
+ }
1480
+ }
1481
+ async function verifyRuntimeReuseProofStats(layout, proof) {
1482
+ try {
1483
+ const excludedRootEntries = new Set([".mono-agent-closure.json", ".mono-agent-runtime.json"]);
1484
+ const rootEntries = (await readdir(layout.installRoot))
1485
+ .filter((name) => !excludedRootEntries.has(name))
1486
+ .sort(compareCodeUnits);
1487
+ if (JSON.stringify(rootEntries) !== JSON.stringify(proof.rootEntries))
1488
+ return false;
1489
+ const matches = await mapWithConcurrency(proof.entries, RUNTIME_REUSE_STAT_CONCURRENCY, async (entry) => {
1490
+ const current = await captureFilesystemPathProof(runtimePathFromPortableRelative(layout.installRoot, entry.path), entry.path);
1491
+ return sameSourceProofEntry(entry.proof, current);
1492
+ });
1493
+ return matches.every(Boolean);
1494
+ }
1495
+ catch {
1496
+ return false;
1497
+ }
1498
+ }
1499
+ async function runtimeReuseProofMatchesManifest(layout, proof) {
1500
+ try {
1501
+ const bytes = await readFile(layout.closureManifestPath);
1502
+ if (sha256(bytes) !== proof.closureManifestSha256)
1503
+ return false;
1504
+ const manifest = runtimeClosureManifestFromJson(parseJson(bytes, "managed runtime closure manifest"));
1505
+ if (manifest === undefined || manifest.entries.length !== proof.entries.length)
1506
+ return false;
1507
+ return manifest.entries.every((entry, index) => {
1508
+ const proven = proof.entries[index];
1509
+ return proven !== undefined
1510
+ && proven.path === entry.path
1511
+ && proven.proof.type === entry.type
1512
+ && fileMode(BigInt(proven.proof.mode)) === entry.mode
1513
+ && (entry.type !== "symlink" || proven.proof.target === entry.target);
1514
+ });
1515
+ }
1516
+ catch {
1517
+ return false;
1518
+ }
1519
+ }
1520
+ async function ensurePrivateReuseProofDirectory(path) {
1521
+ await mkdir(path, { recursive: false, mode: 0o700 }).catch((error) => {
1522
+ if (!isErrno(error, "EEXIST"))
1523
+ throw error;
1524
+ });
1525
+ const details = await lstat(path, { bigint: true });
1526
+ if (!privateRuntimeDirectory(details)) {
1527
+ throw new Error(`Managed runtime reuse-proof directory ${path} must be owner-private.`);
1528
+ }
1529
+ await chmod(path, 0o700);
1530
+ }
1531
+ async function readPrivateRuntimeReuseProofBytes(path) {
1532
+ let handle;
1533
+ try {
1534
+ handle = await open(path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK);
1535
+ const before = await handle.stat({ bigint: true });
1536
+ if (!privateRuntimeReuseProofFile(before))
1537
+ return undefined;
1538
+ const bytes = await handle.readFile();
1539
+ const [after, named] = await Promise.all([
1540
+ handle.stat({ bigint: true }),
1541
+ lstat(path, { bigint: true }),
1542
+ ]);
1543
+ if (!privateRuntimeReuseProofFile(after)
1544
+ || !privateRuntimeReuseProofFile(named)
1545
+ || !sameSourceStats(before, after)
1546
+ || !sameSourceStats(before, named)) {
1547
+ return undefined;
1548
+ }
1549
+ return bytes;
1550
+ }
1551
+ catch {
1552
+ return undefined;
1553
+ }
1554
+ finally {
1555
+ await handle?.close();
1556
+ }
1557
+ }
1558
+ function privateRuntimeReuseProofFile(details) {
1559
+ const owned = typeof process.getuid !== "function" || details.uid === BigInt(process.getuid());
1560
+ return details.isFile()
1561
+ && !details.isSymbolicLink()
1562
+ && details.nlink === 1n
1563
+ && details.size <= BigInt(MAX_RUNTIME_REUSE_PROOF_BYTES)
1564
+ && (details.mode & 63n) === 0n
1565
+ && owned;
1566
+ }
1567
+ function runtimeReuseProofFromJson(value, identity, packageSource, additionalPackages, marker) {
1568
+ if (!hasExactKeys(value, ["schema", "identity", "packageSource", "additionalPackages", "source", "runtime"])) {
1569
+ return undefined;
1570
+ }
1571
+ if (value.schema !== RUNTIME_REUSE_PROOF_SCHEMA
1572
+ || !sameRuntimeIdentityJson(value.identity, identity)
1573
+ || value.packageSource !== packageSource
1574
+ || !Array.isArray(value.additionalPackages)
1575
+ || JSON.stringify(value.additionalPackages) !== JSON.stringify(additionalPackages)) {
1576
+ return undefined;
1577
+ }
1578
+ const source = sourceClosureReuseProofFromJson(value.source, packageSource);
1579
+ const runtime = runtimeClosureReuseProofFromJson(value.runtime, marker);
1580
+ return source === undefined || runtime === undefined
1581
+ ? undefined
1582
+ : {
1583
+ schema: RUNTIME_REUSE_PROOF_SCHEMA,
1584
+ identity,
1585
+ packageSource,
1586
+ additionalPackages,
1587
+ source,
1588
+ runtime,
1589
+ };
1590
+ }
1591
+ function sourceClosureReuseProofFromJson(value, packageSource) {
1592
+ if (!hasExactKeys(value, ["sourceProofSha256", "packages", "links"]))
1593
+ return undefined;
1594
+ if (typeof value.sourceProofSha256 !== "string"
1595
+ || !/^[0-9a-f]{64}$/u.test(value.sourceProofSha256)
1596
+ || !Array.isArray(value.packages)
1597
+ || !Array.isArray(value.links)) {
1598
+ return undefined;
1599
+ }
1600
+ const packages = [];
1601
+ const roots = new Set();
1602
+ for (const [index, rawPackage] of value.packages.entries()) {
1603
+ if (!hasExactKeys(rawPackage, ["id", "sourceRoot", "entries"]))
1604
+ return undefined;
1605
+ if (rawPackage.id !== index
1606
+ || typeof rawPackage.sourceRoot !== "string"
1607
+ || !isCanonicalAbsolutePath(rawPackage.sourceRoot)
1608
+ || roots.has(rawPackage.sourceRoot)
1609
+ || !Array.isArray(rawPackage.entries)) {
1610
+ return undefined;
1611
+ }
1612
+ const entries = proofEntriesFromJson(rawPackage.entries, true);
1613
+ if (entries === undefined || entries[0]?.path !== ".")
1614
+ return undefined;
1615
+ roots.add(rawPackage.sourceRoot);
1616
+ packages.push({ id: index, sourceRoot: rawPackage.sourceRoot, entries });
1617
+ }
1618
+ if (packages[0]?.sourceRoot !== packageSource)
1619
+ return undefined;
1620
+ const links = [];
1621
+ const linkPaths = new Set();
1622
+ for (const rawLink of value.links) {
1623
+ if (!hasExactKeys(rawLink, ["path", "logicalPath", "proof"]))
1624
+ return undefined;
1625
+ if (typeof rawLink.path !== "string"
1626
+ || !isCanonicalAbsolutePath(rawLink.path)
1627
+ || linkPaths.has(rawLink.path)
1628
+ || typeof rawLink.logicalPath !== "string"
1629
+ || rawLink.logicalPath.length === 0) {
1630
+ return undefined;
1631
+ }
1632
+ const proof = sourceProofEntryFromJson(rawLink.proof);
1633
+ if (proof === undefined || proof.path !== rawLink.logicalPath)
1634
+ return undefined;
1635
+ linkPaths.add(rawLink.path);
1636
+ links.push({ path: rawLink.path, logicalPath: rawLink.logicalPath, proof });
1637
+ }
1638
+ return { sourceProofSha256: value.sourceProofSha256, packages, links };
1639
+ }
1640
+ function runtimeClosureReuseProofFromJson(value, marker) {
1641
+ if (!hasExactKeys(value, [
1642
+ "rootEntries",
1643
+ "entries",
1644
+ "closureManifestSha256",
1645
+ "executionProofSha256",
1646
+ ]))
1647
+ return undefined;
1648
+ if (value.closureManifestSha256 !== marker.closureManifestSha256
1649
+ || value.executionProofSha256 !== marker.executionProofSha256
1650
+ || !Array.isArray(value.rootEntries)
1651
+ || !Array.isArray(value.entries)) {
1652
+ return undefined;
1653
+ }
1654
+ const rootEntries = [];
1655
+ const rootNames = new Set();
1656
+ for (const name of value.rootEntries) {
1657
+ if (typeof name !== "string"
1658
+ || name.length === 0
1659
+ || name === "."
1660
+ || name === ".."
1661
+ || name.includes("/")
1662
+ || name.includes("\\")
1663
+ || name.includes("\0")
1664
+ || rootNames.has(name)
1665
+ || name === ".mono-agent-closure.json"
1666
+ || name === ".mono-agent-runtime.json") {
1667
+ return undefined;
1668
+ }
1669
+ rootNames.add(name);
1670
+ rootEntries.push(name);
1671
+ }
1672
+ if (JSON.stringify(rootEntries) !== JSON.stringify([...rootEntries].sort(compareCodeUnits)))
1673
+ return undefined;
1674
+ const entries = [];
1675
+ const entryPaths = new Set();
1676
+ for (const rawEntry of value.entries) {
1677
+ if (!hasExactKeys(rawEntry, ["path", "proof"]))
1678
+ return undefined;
1679
+ if (typeof rawEntry.path !== "string"
1680
+ || !isSafePortableRelativePath(rawEntry.path)
1681
+ || entryPaths.has(rawEntry.path)) {
1682
+ return undefined;
1683
+ }
1684
+ const proof = sourceProofEntryFromJson(rawEntry.proof);
1685
+ if (proof === undefined || proof.path !== rawEntry.path)
1686
+ return undefined;
1687
+ entryPaths.add(rawEntry.path);
1688
+ entries.push({ path: rawEntry.path, proof });
1689
+ }
1690
+ return {
1691
+ rootEntries,
1692
+ entries,
1693
+ closureManifestSha256: marker.closureManifestSha256,
1694
+ executionProofSha256: marker.executionProofSha256,
1695
+ };
1696
+ }
1697
+ function runtimeClosureManifestFromJson(value) {
1698
+ if (!hasExactKeys(value, ["schema", "entries"]) || value.schema !== "mono-agent.execution-closure.v1") {
1699
+ return undefined;
1700
+ }
1701
+ if (!Array.isArray(value.entries))
1702
+ return undefined;
1703
+ const entries = [];
1704
+ const paths = new Set();
1705
+ for (const rawEntry of value.entries) {
1706
+ if (!isRecord(rawEntry) || typeof rawEntry.type !== "string")
1707
+ return undefined;
1708
+ const expectedKeys = rawEntry.type === "file"
1709
+ ? ["path", "type", "mode", "sha256"]
1710
+ : rawEntry.type === "symlink"
1711
+ ? ["path", "type", "mode", "target"]
1712
+ : ["path", "type", "mode"];
1713
+ if (!hasExactKeys(rawEntry, expectedKeys)
1714
+ || typeof rawEntry.path !== "string"
1715
+ || !isSafePortableRelativePath(rawEntry.path)
1716
+ || paths.has(rawEntry.path)
1717
+ || typeof rawEntry.mode !== "string"
1718
+ || !/^0[0-7]{3}$/u.test(rawEntry.mode)) {
1719
+ return undefined;
1720
+ }
1721
+ if (rawEntry.type === "file") {
1722
+ if (typeof rawEntry.sha256 !== "string" || !/^[0-9a-f]{64}$/u.test(rawEntry.sha256))
1723
+ return undefined;
1724
+ entries.push({ path: rawEntry.path, type: "file", mode: rawEntry.mode, sha256: rawEntry.sha256 });
1725
+ }
1726
+ else if (rawEntry.type === "symlink") {
1727
+ if (typeof rawEntry.target !== "string" || rawEntry.target.includes("\0"))
1728
+ return undefined;
1729
+ entries.push({ path: rawEntry.path, type: "symlink", mode: rawEntry.mode, target: rawEntry.target });
1730
+ }
1731
+ else if (rawEntry.type === "directory") {
1732
+ entries.push({ path: rawEntry.path, type: "directory", mode: rawEntry.mode });
1733
+ }
1734
+ else {
1735
+ return undefined;
1736
+ }
1737
+ paths.add(rawEntry.path);
1738
+ }
1739
+ return { schema: "mono-agent.execution-closure.v1", entries };
1740
+ }
1741
+ function proofEntriesFromJson(values, allowRoot) {
1742
+ const entries = [];
1743
+ const paths = new Set();
1744
+ for (const value of values) {
1745
+ const entry = sourceProofEntryFromJson(value);
1746
+ if (entry === undefined
1747
+ || (entry.path !== "." && !isSafePortableRelativePath(entry.path))
1748
+ || (!allowRoot && entry.path === ".")
1749
+ || paths.has(entry.path)) {
1750
+ return undefined;
1751
+ }
1752
+ paths.add(entry.path);
1753
+ entries.push(entry);
1754
+ }
1755
+ return entries;
1756
+ }
1757
+ function sourceProofEntryFromJson(value) {
1758
+ if (!isRecord(value))
1759
+ return undefined;
1760
+ const type = value.type;
1761
+ const expectedKeys = type === "symlink"
1762
+ ? ["path", "type", "dev", "ino", "nlink", "mode", "size", "mtimeNs", "ctimeNs", "target"]
1763
+ : ["path", "type", "dev", "ino", "nlink", "mode", "size", "mtimeNs", "ctimeNs"];
1764
+ if (!hasExactKeys(value, expectedKeys)
1765
+ || typeof value.path !== "string"
1766
+ || (type !== "directory" && type !== "file" && type !== "symlink")
1767
+ || ![value.dev, value.ino, value.nlink, value.mode, value.size, value.mtimeNs, value.ctimeNs]
1768
+ .every((entry) => typeof entry === "string" && /^-?\d+$/u.test(entry))
1769
+ || (type === "symlink" && (typeof value.target !== "string" || value.target.includes("\0")))) {
1770
+ return undefined;
1771
+ }
1772
+ return value;
1773
+ }
1774
+ function sameRuntimeIdentityJson(value, identity) {
1775
+ return hasExactKeys(value, ["packageVersion", "cliSha256", "sourceClosureSha256", "nodeAbi", "platform", "arch"])
1776
+ && value.packageVersion === identity.packageVersion
1777
+ && value.cliSha256 === identity.cliSha256
1778
+ && value.sourceClosureSha256 === identity.sourceClosureSha256
1779
+ && value.nodeAbi === identity.nodeAbi
1780
+ && value.platform === identity.platform
1781
+ && value.arch === identity.arch;
1782
+ }
1783
+ function hasExactKeys(value, keys) {
1784
+ return isRecord(value)
1785
+ && Object.keys(value).length === keys.length
1786
+ && keys.every((key) => Object.hasOwn(value, key));
1787
+ }
1788
+ function isCanonicalAbsolutePath(path) {
1789
+ return isAbsolute(path) && resolve(path) === path && !path.includes("\0");
1790
+ }
1791
+ function isSafePortableRelativePath(path) {
1792
+ if (path.length === 0 || path === "." || path.includes("\\") || path.includes("\0") || isAbsolute(path)) {
1793
+ return false;
1794
+ }
1795
+ const segments = path.split("/");
1796
+ return segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
1797
+ }
1798
+ function runtimePathFromPortableRelative(root, path) {
1799
+ if (!isSafePortableRelativePath(path)) {
1800
+ throw new Error(`Managed runtime proof path ${JSON.stringify(path)} is unsafe.`);
1801
+ }
1802
+ const target = join(root, ...path.split("/"));
1803
+ assertPathInsideRoot(root, target, `proof path ${path}`);
1804
+ return target;
1805
+ }
1806
+ async function mapWithConcurrency(values, concurrency, mapper) {
1807
+ const results = new Array(values.length);
1808
+ let nextIndex = 0;
1809
+ const workers = Array.from({ length: Math.min(Math.max(1, concurrency), values.length) }, async () => {
1810
+ for (;;) {
1811
+ const index = nextIndex;
1812
+ nextIndex += 1;
1813
+ if (index >= values.length)
1814
+ return;
1815
+ results[index] = await mapper(values[index], index);
1816
+ }
1817
+ });
1818
+ await Promise.all(workers);
1819
+ return results;
1820
+ }
1821
+ async function pathExists(path) {
1822
+ try {
1823
+ await lstat(path);
1824
+ return true;
1825
+ }
1826
+ catch (error) {
1827
+ if (isErrno(error, "ENOENT"))
1828
+ return false;
1829
+ throw error;
1830
+ }
1831
+ }
1832
+ async function acquireRuntimeLock(layout, identity, additionalPackages, deps, isSatisfied = () => verifyRuntime(layout, identity, additionalPackages)) {
1087
1833
  const incarnation = await (deps.currentProcessIncarnation ?? currentProcessIncarnation)();
1088
1834
  return acquireOwnerPrivateLock({
1089
1835
  path: layout.lockDir,
@@ -1110,7 +1856,7 @@ async function acquireRuntimeLock(layout, identity, additionalPackages, deps) {
1110
1856
  : undefined;
1111
1857
  },
1112
1858
  invalidOwner: "ownerless",
1113
- isSatisfied: () => verifyRuntime(layout, identity, additionalPackages),
1859
+ isSatisfied,
1114
1860
  sleep: deps.sleep,
1115
1861
  staleRace: "return",
1116
1862
  beforeStaleRename: async () => {
@@ -1231,7 +1977,22 @@ function sameRuntimeMarker(left, right) {
1231
1977
  && left.nodeAbi === right.nodeAbi
1232
1978
  && left.platform === right.platform
1233
1979
  && left.arch === right.arch
1234
- && left.installedAt === right.installedAt;
1980
+ && left.installedAt === right.installedAt
1981
+ && (left.schema !== "mono-agent.managed-runtime.v5"
1982
+ || (right.schema === "mono-agent.managed-runtime.v5"
1983
+ && left.reuseProofSha256 === right.reuseProofSha256));
1984
+ }
1985
+ function runtimeMarkerMatchesProvenance(marker, input) {
1986
+ return marker.packageName === PACKAGE_NAME
1987
+ && marker.closureManifestSha256 === input.closureManifestSha256
1988
+ && marker.executionProofSha256 === input.executionProofSha256
1989
+ && marker.packageVersion === input.packageVersion
1990
+ && marker.cliSha256 === input.cliSha256
1991
+ && marker.sourceClosureSha256 === input.sourceClosureSha256
1992
+ && marker.nodeAbi === input.nodeAbi
1993
+ && marker.platform === input.platform
1994
+ && marker.arch === input.arch
1995
+ && marker.installedAt === input.installedAt;
1235
1996
  }
1236
1997
  async function verifiedRuntimeMarker(layout, identity) {
1237
1998
  try {
@@ -1250,6 +2011,17 @@ async function verifiedRuntimeMarker(layout, identity) {
1250
2011
  }
1251
2012
  }
1252
2013
  async function readRuntimeMarker(path, identity) {
2014
+ const bytes = await readPrivateRuntimeMarkerBytes(path);
2015
+ if (bytes === undefined)
2016
+ return undefined;
2017
+ try {
2018
+ return runtimeMarkerFromJson(JSON.parse(bytes.toString("utf8")), identity);
2019
+ }
2020
+ catch {
2021
+ return undefined;
2022
+ }
2023
+ }
2024
+ async function readPrivateRuntimeMarkerBytes(path) {
1253
2025
  let handle;
1254
2026
  try {
1255
2027
  handle = await open(path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK);
@@ -1267,7 +2039,7 @@ async function readRuntimeMarker(path, identity) {
1267
2039
  || !sameSourceStats(before, named)) {
1268
2040
  return undefined;
1269
2041
  }
1270
- return runtimeMarkerFromJson(JSON.parse(bytes.toString("utf8")), identity);
2042
+ return bytes;
1271
2043
  }
1272
2044
  catch {
1273
2045
  return undefined;
@@ -1285,10 +2057,17 @@ function privateRuntimeMarkerFile(details) {
1285
2057
  && (details.mode & 63n) === 0n
1286
2058
  && owned;
1287
2059
  }
2060
+ function privateRuntimeDirectory(details) {
2061
+ const owned = typeof process.getuid !== "function" || details.uid === BigInt(process.getuid());
2062
+ return details.isDirectory()
2063
+ && !details.isSymbolicLink()
2064
+ && (details.mode & 63n) === 0n
2065
+ && owned;
2066
+ }
1288
2067
  function runtimeMarkerFromJson(value, identity) {
1289
2068
  if (!isRecord(value))
1290
2069
  return undefined;
1291
- const exactKeys = [
2070
+ const commonKeys = [
1292
2071
  "schema",
1293
2072
  "packageName",
1294
2073
  "closureManifestSha256",
@@ -1301,10 +2080,13 @@ function runtimeMarkerFromJson(value, identity) {
1301
2080
  "arch",
1302
2081
  "installedAt",
1303
2082
  ];
2083
+ const exactKeys = value.schema === "mono-agent.managed-runtime.v5"
2084
+ ? [...commonKeys, "reuseProofSha256"]
2085
+ : commonKeys;
1304
2086
  const installedAtMs = typeof value.installedAt === "string" ? Date.parse(value.installedAt) : Number.NaN;
1305
2087
  const valid = Object.keys(value).length === exactKeys.length
1306
2088
  && exactKeys.every((key) => Object.hasOwn(value, key))
1307
- && value.schema === "mono-agent.managed-runtime.v4"
2089
+ && (value.schema === "mono-agent.managed-runtime.v4" || value.schema === "mono-agent.managed-runtime.v5")
1308
2090
  && value.packageName === PACKAGE_NAME
1309
2091
  && value.packageVersion === identity.packageVersion
1310
2092
  && value.cliSha256 === identity.cliSha256
@@ -1316,6 +2098,8 @@ function runtimeMarkerFromJson(value, identity) {
1316
2098
  && /^[0-9a-f]{64}$/u.test(value.closureManifestSha256)
1317
2099
  && typeof value.executionProofSha256 === "string"
1318
2100
  && /^[0-9a-f]{64}$/u.test(value.executionProofSha256)
2101
+ && (value.schema !== "mono-agent.managed-runtime.v5"
2102
+ || (typeof value.reuseProofSha256 === "string" && /^[0-9a-f]{64}$/u.test(value.reuseProofSha256)))
1319
2103
  && typeof value.installedAt === "string"
1320
2104
  && Number.isFinite(installedAtMs)
1321
2105
  && new Date(installedAtMs).toISOString() === value.installedAt
@@ -1325,6 +2109,32 @@ function runtimeMarkerFromJson(value, identity) {
1325
2109
  && value.installedAt !== PROVISIONAL_RUNTIME_INSTALLED_AT;
1326
2110
  return valid ? value : undefined;
1327
2111
  }
2112
+ function runtimeIdentityFromMarkerJson(value) {
2113
+ if (!isRecord(value))
2114
+ return undefined;
2115
+ if (typeof value.packageVersion !== "string"
2116
+ || !isExactVersion(value.packageVersion)
2117
+ || typeof value.cliSha256 !== "string"
2118
+ || !/^[0-9a-f]{64}$/u.test(value.cliSha256)
2119
+ || typeof value.sourceClosureSha256 !== "string"
2120
+ || !/^[0-9a-f]{64}$/u.test(value.sourceClosureSha256)
2121
+ || typeof value.nodeAbi !== "string"
2122
+ || value.nodeAbi.length === 0
2123
+ || typeof value.platform !== "string"
2124
+ || value.platform.length === 0
2125
+ || typeof value.arch !== "string"
2126
+ || value.arch.length === 0) {
2127
+ return undefined;
2128
+ }
2129
+ return {
2130
+ packageVersion: value.packageVersion,
2131
+ cliSha256: value.cliSha256,
2132
+ sourceClosureSha256: value.sourceClosureSha256,
2133
+ nodeAbi: value.nodeAbi,
2134
+ platform: value.platform,
2135
+ arch: value.arch,
2136
+ };
2137
+ }
1328
2138
  async function verifyInstalledPackage(layout, identity) {
1329
2139
  try {
1330
2140
  const packageRoot = dirname(layout.packageJsonPath);
@@ -1386,6 +2196,28 @@ async function verifyClosureManifest(layout, expectedSha256, deps = {}) {
1386
2196
  return false;
1387
2197
  }
1388
2198
  }
2199
+ async function verifyClosureManifestFingerprint(layout, expectedSha256) {
2200
+ try {
2201
+ await assertRegularFile(layout.closureManifestPath, "managed runtime closure manifest");
2202
+ const stored = await fingerprintClosureFile(layout.closureManifestPath, ".mono-agent-closure.json");
2203
+ if (stored.sha256 !== expectedSha256)
2204
+ return false;
2205
+ const parsed = JSON.parse(stored.bytes.toString("utf8"));
2206
+ return parsed.schema === "mono-agent.execution-closure.v1" && Array.isArray(parsed.entries);
2207
+ }
2208
+ catch {
2209
+ return false;
2210
+ }
2211
+ }
2212
+ async function verifyManagedRuntimeCliFingerprint(layout, expectedSha256) {
2213
+ try {
2214
+ const captured = await fingerprintClosureFile(layout.cliPath, "node_modules/@mono-agent/agent-app/dist/cli.js");
2215
+ return captured.sha256 === expectedSha256;
2216
+ }
2217
+ catch {
2218
+ return false;
2219
+ }
2220
+ }
1389
2221
  async function captureRuntimeClosureManifest(layout, deps = {}) {
1390
2222
  const entries = [];
1391
2223
  const proofs = [];
@@ -1544,18 +2376,21 @@ async function writeFinalizedRuntimeMarker(path, marker, deps) {
1544
2376
  throw new Error("Could not publish a stable managed runtime launch boundary.");
1545
2377
  }
1546
2378
  async function waitForManagedRuntimeLaunchBoundary(layout, identity, deps) {
1547
- const marker = await verifiedRuntimeMarker(layout, identity);
2379
+ const marker = await readRuntimeMarker(layout.markerPath, identity);
1548
2380
  if (marker === undefined) {
1549
2381
  throw new Error("The managed runtime marker became invalid before launch.");
1550
2382
  }
1551
- const boundaryMs = Date.parse(marker.installedAt);
2383
+ await waitForLaunchBoundary(marker.installedAt, deps.now, deps.sleep);
2384
+ }
2385
+ async function waitForLaunchBoundary(installedAt, now, sleepFor) {
2386
+ const boundaryMs = Date.parse(installedAt);
1552
2387
  const safeStartMs = (Math.floor(boundaryMs / 1_000) + 1) * 1_000;
1553
- const nowMs = deps.now();
2388
+ const nowMs = now();
1554
2389
  if (safeStartMs - nowMs > 5_000) {
1555
2390
  throw new Error("The managed runtime launch boundary is implausibly far in the future.");
1556
2391
  }
1557
2392
  if (nowMs < safeStartMs)
1558
- await deps.sleep(safeStartMs - nowMs);
2393
+ await sleepFor(safeStartMs - nowMs);
1559
2394
  }
1560
2395
  function privateJsonContents(value) {
1561
2396
  return `${JSON.stringify(value, undefined, 2)}\n`;
@@ -1611,7 +2446,15 @@ function ownedPrivately(details) {
1611
2446
  const owned = typeof process.getuid !== "function" || Number(details.uid) === process.getuid();
1612
2447
  return owned && (Number(details.mode) & 0o077) === 0;
1613
2448
  }
1614
- function runtimeResult(layout, identity, nodePath) {
2449
+ async function runtimeResult(layout, identity, nodePath, verificationMode) {
2450
+ const markerBytes = await readPrivateRuntimeMarkerBytes(layout.markerPath);
2451
+ if (markerBytes === undefined) {
2452
+ throw new Error("The managed runtime marker became unsafe before launch proof publication.");
2453
+ }
2454
+ const marker = runtimeMarkerFromJson(parseJson(markerBytes, "managed runtime marker"), identity);
2455
+ if (marker === undefined) {
2456
+ throw new Error("The managed runtime marker became invalid before launch proof publication.");
2457
+ }
1615
2458
  return {
1616
2459
  cliPath: layout.cliPath,
1617
2460
  nodePath: resolve(nodePath),
@@ -1619,8 +2462,17 @@ function runtimeResult(layout, identity, nodePath) {
1619
2462
  packageVersion: identity.packageVersion,
1620
2463
  cliSha256: identity.cliSha256,
1621
2464
  nodeAbi: identity.nodeAbi,
2465
+ verificationMode,
2466
+ launchProof: encodeManagedRuntimeLaunchProof({
2467
+ schema: MANAGED_RUNTIME_LAUNCH_PROOF_SCHEMA,
2468
+ markerSha256: sha256(markerBytes),
2469
+ installedAt: marker.installedAt,
2470
+ }),
1622
2471
  };
1623
2472
  }
2473
+ async function sleep(milliseconds) {
2474
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
2475
+ }
1624
2476
  function requiredNodeAbi() {
1625
2477
  const abi = process.versions.modules;
1626
2478
  if (abi === undefined || abi.trim().length === 0) {