@mono-agent/agent-app 0.6.2 → 0.9.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 (313) hide show
  1. package/README.md +259 -19
  2. package/dist/account-home.d.ts +6 -0
  3. package/dist/account-home.d.ts.map +1 -0
  4. package/dist/account-home.js +9 -0
  5. package/dist/account-home.js.map +1 -0
  6. package/dist/adapter-send-proxy.d.ts +18 -0
  7. package/dist/adapter-send-proxy.d.ts.map +1 -0
  8. package/dist/adapter-send-proxy.js +229 -0
  9. package/dist/adapter-send-proxy.js.map +1 -0
  10. package/dist/adapter-send-tools-main.js +62 -4
  11. package/dist/adapter-send-tools-main.js.map +1 -1
  12. package/dist/adapter-send-tools.d.ts +33 -6
  13. package/dist/adapter-send-tools.d.ts.map +1 -1
  14. package/dist/adapter-send-tools.js +485 -87
  15. package/dist/adapter-send-tools.js.map +1 -1
  16. package/dist/app-config.d.ts +3 -1
  17. package/dist/app-config.d.ts.map +1 -1
  18. package/dist/app-config.js +16 -5
  19. package/dist/app-config.js.map +1 -1
  20. package/dist/app.d.ts +27 -0
  21. package/dist/app.d.ts.map +1 -1
  22. package/dist/app.js +761 -147
  23. package/dist/app.js.map +1 -1
  24. package/dist/background-environment.d.ts +16 -0
  25. package/dist/background-environment.d.ts.map +1 -0
  26. package/dist/background-environment.js +68 -0
  27. package/dist/background-environment.js.map +1 -0
  28. package/dist/background-runtime.d.ts +62 -0
  29. package/dist/background-runtime.d.ts.map +1 -0
  30. package/dist/background-runtime.js +1115 -0
  31. package/dist/background-runtime.js.map +1 -0
  32. package/dist/background-snapshot-key.d.ts +13 -0
  33. package/dist/background-snapshot-key.d.ts.map +1 -0
  34. package/dist/background-snapshot-key.js +194 -0
  35. package/dist/background-snapshot-key.js.map +1 -0
  36. package/dist/background-snapshot.d.ts +96 -0
  37. package/dist/background-snapshot.d.ts.map +1 -0
  38. package/dist/background-snapshot.js +450 -0
  39. package/dist/background-snapshot.js.map +1 -0
  40. package/dist/background-worker-lease.d.ts +39 -0
  41. package/dist/background-worker-lease.d.ts.map +1 -0
  42. package/dist/background-worker-lease.js +356 -0
  43. package/dist/background-worker-lease.js.map +1 -0
  44. package/dist/background.d.ts +74 -2
  45. package/dist/background.d.ts.map +1 -1
  46. package/dist/background.js +815 -73
  47. package/dist/background.js.map +1 -1
  48. package/dist/broadcast-recorder.d.ts.map +1 -1
  49. package/dist/broadcast-recorder.js +24 -2
  50. package/dist/broadcast-recorder.js.map +1 -1
  51. package/dist/channel-plugins.d.ts +2 -0
  52. package/dist/channel-plugins.d.ts.map +1 -1
  53. package/dist/channel-plugins.js +8 -0
  54. package/dist/channel-plugins.js.map +1 -1
  55. package/dist/channels.d.ts +10 -0
  56. package/dist/channels.d.ts.map +1 -1
  57. package/dist/channels.js +87 -9
  58. package/dist/channels.js.map +1 -1
  59. package/dist/cli.d.ts +86 -4
  60. package/dist/cli.d.ts.map +1 -1
  61. package/dist/cli.js +2421 -143
  62. package/dist/cli.js.map +1 -1
  63. package/dist/config-reference.d.ts.map +1 -1
  64. package/dist/config-reference.js +448 -6
  65. package/dist/config-reference.js.map +1 -1
  66. package/dist/configuration-proposal-main.d.ts +3 -0
  67. package/dist/configuration-proposal-main.d.ts.map +1 -0
  68. package/dist/configuration-proposal-main.js +13 -0
  69. package/dist/configuration-proposal-main.js.map +1 -0
  70. package/dist/configuration-proposal-tool.d.ts +33 -0
  71. package/dist/configuration-proposal-tool.d.ts.map +1 -0
  72. package/dist/configuration-proposal-tool.js +188 -0
  73. package/dist/configuration-proposal-tool.js.map +1 -0
  74. package/dist/configured-agent.d.ts +16 -0
  75. package/dist/configured-agent.d.ts.map +1 -1
  76. package/dist/configured-agent.js +165 -18
  77. package/dist/configured-agent.js.map +1 -1
  78. package/dist/consumer-contract.d.ts +3 -0
  79. package/dist/consumer-contract.d.ts.map +1 -1
  80. package/dist/consumer-contract.js +44 -1
  81. package/dist/consumer-contract.js.map +1 -1
  82. package/dist/continuation-command.d.ts +15 -0
  83. package/dist/continuation-command.d.ts.map +1 -0
  84. package/dist/continuation-command.js +170 -0
  85. package/dist/continuation-command.js.map +1 -0
  86. package/dist/continuation-config.d.ts +25 -0
  87. package/dist/continuation-config.d.ts.map +1 -0
  88. package/dist/continuation-config.js +236 -0
  89. package/dist/continuation-config.js.map +1 -0
  90. package/dist/continuation-service.d.ts +99 -0
  91. package/dist/continuation-service.d.ts.map +1 -0
  92. package/dist/continuation-service.js +1313 -0
  93. package/dist/continuation-service.js.map +1 -0
  94. package/dist/continuation-store.d.ts +104 -0
  95. package/dist/continuation-store.d.ts.map +1 -0
  96. package/dist/continuation-store.js +688 -0
  97. package/dist/continuation-store.js.map +1 -0
  98. package/dist/continuations.d.ts +167 -0
  99. package/dist/continuations.d.ts.map +1 -0
  100. package/dist/continuations.js +71 -0
  101. package/dist/continuations.js.map +1 -0
  102. package/dist/doctor.d.ts +29 -5
  103. package/dist/doctor.d.ts.map +1 -1
  104. package/dist/doctor.js +1611 -116
  105. package/dist/doctor.js.map +1 -1
  106. package/dist/first-run-managed-memory.d.ts +39 -0
  107. package/dist/first-run-managed-memory.d.ts.map +1 -0
  108. package/dist/first-run-managed-memory.js +584 -0
  109. package/dist/first-run-managed-memory.js.map +1 -0
  110. package/dist/first-run-readiness.d.ts +111 -0
  111. package/dist/first-run-readiness.d.ts.map +1 -0
  112. package/dist/first-run-readiness.js +728 -0
  113. package/dist/first-run-readiness.js.map +1 -0
  114. package/dist/index.d.ts +17 -3
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +8 -1
  117. package/dist/index.js.map +1 -1
  118. package/dist/init.d.ts +95 -3
  119. package/dist/init.d.ts.map +1 -1
  120. package/dist/init.js +1218 -24
  121. package/dist/init.js.map +1 -1
  122. package/dist/interaction-bridge.d.ts +25 -1
  123. package/dist/interaction-bridge.d.ts.map +1 -1
  124. package/dist/interaction-bridge.js +281 -19
  125. package/dist/interaction-bridge.js.map +1 -1
  126. package/dist/launchd.d.ts +16 -6
  127. package/dist/launchd.d.ts.map +1 -1
  128. package/dist/launchd.js +30 -15
  129. package/dist/launchd.js.map +1 -1
  130. package/dist/local-configuration.d.ts +99 -0
  131. package/dist/local-configuration.d.ts.map +1 -0
  132. package/dist/local-configuration.js +1299 -0
  133. package/dist/local-configuration.js.map +1 -0
  134. package/dist/managed-runtime-packages.d.ts +9 -0
  135. package/dist/managed-runtime-packages.d.ts.map +1 -0
  136. package/dist/managed-runtime-packages.js +56 -0
  137. package/dist/managed-runtime-packages.js.map +1 -0
  138. package/dist/memory-command.d.ts +4 -0
  139. package/dist/memory-command.d.ts.map +1 -1
  140. package/dist/memory-command.js +751 -27
  141. package/dist/memory-command.js.map +1 -1
  142. package/dist/memory-embedding-service.d.ts +33 -0
  143. package/dist/memory-embedding-service.d.ts.map +1 -0
  144. package/dist/memory-embedding-service.js +207 -0
  145. package/dist/memory-embedding-service.js.map +1 -0
  146. package/dist/memory-recall.d.ts +43 -14
  147. package/dist/memory-recall.d.ts.map +1 -1
  148. package/dist/memory-recall.js +103 -33
  149. package/dist/memory-recall.js.map +1 -1
  150. package/dist/memory-retrieval.d.ts +66 -0
  151. package/dist/memory-retrieval.d.ts.map +1 -0
  152. package/dist/memory-retrieval.js +332 -0
  153. package/dist/memory-retrieval.js.map +1 -0
  154. package/dist/modules/base.d.ts +6 -5
  155. package/dist/modules/base.d.ts.map +1 -1
  156. package/dist/modules/base.js +11 -7
  157. package/dist/modules/base.js.map +1 -1
  158. package/dist/modules/catalog.d.ts.map +1 -1
  159. package/dist/modules/catalog.js +141 -25
  160. package/dist/modules/catalog.js.map +1 -1
  161. package/dist/modules/index.d.ts +2 -2
  162. package/dist/modules/index.d.ts.map +1 -1
  163. package/dist/modules/index.js +1 -1
  164. package/dist/modules/index.js.map +1 -1
  165. package/dist/modules/known-tools.d.ts +3 -0
  166. package/dist/modules/known-tools.d.ts.map +1 -1
  167. package/dist/modules/known-tools.js +11 -1
  168. package/dist/modules/known-tools.js.map +1 -1
  169. package/dist/modules/types.d.ts +20 -6
  170. package/dist/modules/types.d.ts.map +1 -1
  171. package/dist/modules/types.js.map +1 -1
  172. package/dist/notify-runtime.d.ts +26 -0
  173. package/dist/notify-runtime.d.ts.map +1 -0
  174. package/dist/notify-runtime.js +27 -0
  175. package/dist/notify-runtime.js.map +1 -0
  176. package/dist/notify-tool.d.ts +51 -0
  177. package/dist/notify-tool.d.ts.map +1 -0
  178. package/dist/notify-tool.js +182 -0
  179. package/dist/notify-tool.js.map +1 -0
  180. package/dist/package-version.d.ts +3 -0
  181. package/dist/package-version.d.ts.map +1 -0
  182. package/dist/package-version.js +14 -0
  183. package/dist/package-version.js.map +1 -0
  184. package/dist/pi-auth-store-inspection.d.ts +21 -0
  185. package/dist/pi-auth-store-inspection.d.ts.map +1 -0
  186. package/dist/pi-auth-store-inspection.js +120 -0
  187. package/dist/pi-auth-store-inspection.js.map +1 -0
  188. package/dist/pi-oauth-login-main.d.ts +3 -0
  189. package/dist/pi-oauth-login-main.d.ts.map +1 -0
  190. package/dist/pi-oauth-login-main.js +14 -0
  191. package/dist/pi-oauth-login-main.js.map +1 -0
  192. package/dist/pi-oauth-login.d.ts +22 -0
  193. package/dist/pi-oauth-login.d.ts.map +1 -0
  194. package/dist/pi-oauth-login.js +79 -0
  195. package/dist/pi-oauth-login.js.map +1 -0
  196. package/dist/proactive-notify.d.ts +2 -0
  197. package/dist/proactive-notify.d.ts.map +1 -1
  198. package/dist/proactive-notify.js +1 -0
  199. package/dist/proactive-notify.js.map +1 -1
  200. package/dist/process-incarnation.d.ts +23 -0
  201. package/dist/process-incarnation.d.ts.map +1 -0
  202. package/dist/process-incarnation.js +208 -0
  203. package/dist/process-incarnation.js.map +1 -0
  204. package/dist/project-skills.d.ts +40 -0
  205. package/dist/project-skills.d.ts.map +1 -0
  206. package/dist/project-skills.js +573 -0
  207. package/dist/project-skills.js.map +1 -0
  208. package/dist/provider-setup.d.ts +117 -2
  209. package/dist/provider-setup.d.ts.map +1 -1
  210. package/dist/provider-setup.js +1505 -49
  211. package/dist/provider-setup.js.map +1 -1
  212. package/dist/readiness-probe-worker.d.ts +12 -0
  213. package/dist/readiness-probe-worker.d.ts.map +1 -0
  214. package/dist/readiness-probe-worker.js +276 -0
  215. package/dist/readiness-probe-worker.js.map +1 -0
  216. package/dist/readiness-probe.d.ts +100 -0
  217. package/dist/readiness-probe.d.ts.map +1 -0
  218. package/dist/readiness-probe.js +780 -0
  219. package/dist/readiness-probe.js.map +1 -0
  220. package/dist/recipes/base.d.ts +15 -0
  221. package/dist/recipes/base.d.ts.map +1 -0
  222. package/dist/recipes/base.js +51 -0
  223. package/dist/recipes/base.js.map +1 -0
  224. package/dist/recipes/catalog.d.ts +4 -0
  225. package/dist/recipes/catalog.d.ts.map +1 -0
  226. package/dist/recipes/catalog.js +525 -0
  227. package/dist/recipes/catalog.js.map +1 -0
  228. package/dist/recipes/index.d.ts +11 -0
  229. package/dist/recipes/index.d.ts.map +1 -0
  230. package/dist/recipes/index.js +14 -0
  231. package/dist/recipes/index.js.map +1 -0
  232. package/dist/recipes/types.d.ts +70 -0
  233. package/dist/recipes/types.d.ts.map +1 -0
  234. package/dist/recipes/types.js +15 -0
  235. package/dist/recipes/types.js.map +1 -0
  236. package/dist/request-model-override.d.ts +43 -1
  237. package/dist/request-model-override.d.ts.map +1 -1
  238. package/dist/request-model-override.js +178 -19
  239. package/dist/request-model-override.js.map +1 -1
  240. package/dist/run-history.d.ts +32 -0
  241. package/dist/run-history.d.ts.map +1 -0
  242. package/dist/run-history.js +859 -0
  243. package/dist/run-history.js.map +1 -0
  244. package/dist/runs-health.d.ts.map +1 -1
  245. package/dist/runs-health.js +12 -4
  246. package/dist/runs-health.js.map +1 -1
  247. package/dist/runtime-option-extensions.d.ts +14 -0
  248. package/dist/runtime-option-extensions.d.ts.map +1 -0
  249. package/dist/runtime-option-extensions.js +98 -0
  250. package/dist/runtime-option-extensions.js.map +1 -0
  251. package/dist/runtime-routes.d.ts +9 -0
  252. package/dist/runtime-routes.d.ts.map +1 -0
  253. package/dist/runtime-routes.js +13 -0
  254. package/dist/runtime-routes.js.map +1 -0
  255. package/dist/sandbox-manager.d.ts +86 -0
  256. package/dist/sandbox-manager.d.ts.map +1 -0
  257. package/dist/sandbox-manager.js +1402 -0
  258. package/dist/sandbox-manager.js.map +1 -0
  259. package/dist/setup.d.ts +29 -0
  260. package/dist/setup.d.ts.map +1 -0
  261. package/dist/setup.js +97 -0
  262. package/dist/setup.js.map +1 -0
  263. package/dist/supermemory-plugin.d.ts +56 -0
  264. package/dist/supermemory-plugin.d.ts.map +1 -0
  265. package/dist/supermemory-plugin.js +142 -0
  266. package/dist/supermemory-plugin.js.map +1 -0
  267. package/dist/tui-command.d.ts +56 -0
  268. package/dist/tui-command.d.ts.map +1 -1
  269. package/dist/tui-command.js +305 -31
  270. package/dist/tui-command.js.map +1 -1
  271. package/dist/web-command.d.ts +8 -0
  272. package/dist/web-command.d.ts.map +1 -1
  273. package/dist/web-command.js +174 -17
  274. package/dist/web-command.js.map +1 -1
  275. package/dist/wizard/answers.d.ts +31 -4
  276. package/dist/wizard/answers.d.ts.map +1 -1
  277. package/dist/wizard/answers.js +141 -29
  278. package/dist/wizard/answers.js.map +1 -1
  279. package/dist/wizard/from-flags.d.ts +7 -0
  280. package/dist/wizard/from-flags.d.ts.map +1 -1
  281. package/dist/wizard/from-flags.js +36 -2
  282. package/dist/wizard/from-flags.js.map +1 -1
  283. package/dist/wizard/index.d.ts +4 -4
  284. package/dist/wizard/index.d.ts.map +1 -1
  285. package/dist/wizard/index.js +2 -2
  286. package/dist/wizard/index.js.map +1 -1
  287. package/dist/wizard/model-discovery.d.ts +33 -2
  288. package/dist/wizard/model-discovery.d.ts.map +1 -1
  289. package/dist/wizard/model-discovery.js +826 -108
  290. package/dist/wizard/model-discovery.js.map +1 -1
  291. package/dist/wizard/presets.d.ts +1 -1
  292. package/dist/wizard/presets.d.ts.map +1 -1
  293. package/dist/wizard/presets.js +7 -12
  294. package/dist/wizard/presets.js.map +1 -1
  295. package/dist/wizard/prompts.d.ts +56 -9
  296. package/dist/wizard/prompts.d.ts.map +1 -1
  297. package/dist/wizard/prompts.js +172 -20
  298. package/dist/wizard/prompts.js.map +1 -1
  299. package/dist/wizard/run.d.ts +59 -3
  300. package/dist/wizard/run.d.ts.map +1 -1
  301. package/dist/wizard/run.js +1290 -191
  302. package/dist/wizard/run.js.map +1 -1
  303. package/package.json +21 -16
  304. package/resources/srt/package-lock.json +66 -0
  305. package/resources/srt/package.json +9 -0
  306. package/schema/mono-agent.config.schema.json +587 -20
  307. package/skills/mono-agent-composer/SKILL.md +6 -6
  308. package/skills/mono-agent-composer/references/config-blueprint.md +11 -10
  309. package/skills/mono-agent-composer/references/discovery-questions.md +33 -18
  310. package/skills/mono-agent-composer/references/feature-coverage.md +14 -12
  311. package/skills/mono-agent-composer/references/package-map.md +2 -2
  312. package/skills/mono-agent-composer/references/playbooks.md +8 -7
  313. package/skills/mono-agent-composer/references/validation.md +3 -3
@@ -0,0 +1,1115 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constants as fsConstants } from "node:fs";
3
+ import { chmod, cp, lstat, mkdir, open, readFile, readlink, readdir, realpath, rename, rm, stat, symlink, writeFile, } from "node:fs/promises";
4
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
+ import process from "node:process";
6
+ import { accountHomeDirectory } from "./account-home.js";
7
+ import { isBackgroundOperationalEnvName } from "./background-environment.js";
8
+ import { currentProcessIncarnation, isSameProcessIncarnation as matchesProcessIncarnation, processIncarnationFromJson, } from "./process-incarnation.js";
9
+ const PACKAGE_NAME = "@mono-agent/agent-app";
10
+ export const MANAGED_BACKGROUND_WORKER_ENV = "MONO_AGENT_MANAGED_WORKER";
11
+ const LOCK_WAIT_TIMEOUT_MS = 30_000;
12
+ const LOCK_STALE_AFTER_MS = 5 * 60_000;
13
+ const LOCK_POLL_INTERVAL_MS = 200;
14
+ export function defaultManagedBackgroundRuntimeDeps() {
15
+ return {
16
+ now: () => Date.now(),
17
+ sleep: (ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms)),
18
+ randomId: () => randomUUID(),
19
+ installPackage: materializeExactExecutionClosure,
20
+ currentProcessIncarnation,
21
+ isSameProcessIncarnation: matchesProcessIncarnation,
22
+ };
23
+ }
24
+ /**
25
+ * launchd can retain a user's historical environment independently of the
26
+ * plist. Strip it before dotenv loading so the worker observes only the
27
+ * reviewed operational allowlist plus the selected dotenv file.
28
+ */
29
+ export function sanitizeManagedBackgroundWorkerEnvironment(env) {
30
+ if (env[MANAGED_BACKGROUND_WORKER_ENV] !== "1")
31
+ return;
32
+ for (const name of Object.keys(env)) {
33
+ if (name !== MANAGED_BACKGROUND_WORKER_ENV && !isBackgroundOperationalEnvName(name)) {
34
+ delete env[name];
35
+ }
36
+ }
37
+ // The marker has served its only purpose and must not enter config loading as
38
+ // an apparent MONO_AGENT_* override.
39
+ delete env[MANAGED_BACKGROUND_WORKER_ENV];
40
+ }
41
+ /**
42
+ * Materialize the currently executing published CLI into a private, immutable
43
+ * runtime outside npm/npx's disposable cache. A runtime is accepted only when
44
+ * the exact installed CLI bytes match the caller, not merely when its package
45
+ * version matches.
46
+ */
47
+ export async function ensureManagedBackgroundRuntime(input, deps = defaultManagedBackgroundRuntimeDeps()) {
48
+ const currentCliPath = resolve(input.currentCliPath);
49
+ await assertRegularFile(currentCliPath, "current mono-agent CLI");
50
+ await assertRegularFile(resolve(input.nodePath), "managed runtime Node executable");
51
+ const currentCli = await readFile(currentCliPath);
52
+ const packageSource = resolve(input.packageSource ?? packageRootForCli(currentCliPath));
53
+ const sourceDetails = await lstat(packageSource);
54
+ if (!sourceDetails.isDirectory() || sourceDetails.isSymbolicLink()) {
55
+ throw new Error(`Managed runtime package source ${packageSource} must be a real directory.`);
56
+ }
57
+ const additionalPackages = await canonicalAdditionalPackages(input.additionalPackages ?? []);
58
+ const sourceClosure = await captureExecutionClosure(packageSource, additionalPackages);
59
+ const packageVersion = input.packageVersion ?? await packageVersionAt(packageSource);
60
+ if (!isExactVersion(packageVersion)) {
61
+ throw new Error(`Cannot install a durable managed runtime for invalid package version ${JSON.stringify(packageVersion)}.`);
62
+ }
63
+ const identity = {
64
+ packageVersion,
65
+ cliSha256: sha256(currentCli),
66
+ sourceClosureSha256: sourceClosure.sourceClosureSha256,
67
+ nodeAbi: input.nodeAbi ?? requiredNodeAbi(),
68
+ platform: input.platform ?? process.platform,
69
+ arch: input.arch ?? process.arch,
70
+ };
71
+ const home = resolve(input.homeDir ?? accountHomeDirectory());
72
+ const id = safeSegment(deps.randomId());
73
+ const layout = runtimeLayout(home, identity, id, deps.now());
74
+ await ensurePrivateRuntimeAncestors(home, layout.versionAbiDir);
75
+ if (await verifyRuntime(layout, identity)) {
76
+ return runtimeResult(layout, identity, input.nodePath);
77
+ }
78
+ const acquired = await acquireRuntimeLock(layout, identity, deps);
79
+ if (!acquired) {
80
+ // Another installer may have completed between the final lock poll and the
81
+ // timeout boundary. Verify one last time before reporting the contention.
82
+ if (await verifyRuntime(layout, identity)) {
83
+ return runtimeResult(layout, identity, input.nodePath);
84
+ }
85
+ throw new Error(`Timed out waiting for the managed runtime installation lock at ${layout.lockDir}.`);
86
+ }
87
+ try {
88
+ if (await verifyRuntime(layout, identity)) {
89
+ return runtimeResult(layout, identity, input.nodePath);
90
+ }
91
+ await assertSourceClosureUnchanged(packageSource, additionalPackages, sourceClosure, "before staging");
92
+ await rm(layout.stagingDir, { recursive: true, force: true });
93
+ await mkdir(layout.stagingDir, { recursive: false, mode: 0o700 });
94
+ await chmod(layout.stagingDir, 0o700);
95
+ await writePrivateJson(join(layout.stagingDir, "package.json"), {
96
+ name: "mono-agent-managed-runtime",
97
+ private: true,
98
+ version: "0.0.0",
99
+ });
100
+ try {
101
+ await deps.installPackage({
102
+ stagingDir: layout.stagingDir,
103
+ packageVersion,
104
+ packageSource,
105
+ nodePath: resolve(input.nodePath),
106
+ additionalPackages,
107
+ expectedSourceClosureSha256: identity.sourceClosureSha256,
108
+ });
109
+ await assertSourceClosureUnchanged(packageSource, additionalPackages, sourceClosure, "while staging");
110
+ const staged = runtimeLayoutForRoot(layout, layout.stagingDir);
111
+ if (!(await verifyInstalledPackage(staged, identity))) {
112
+ throw new Error(`Installed ${PACKAGE_NAME}@${packageVersion} does not match the executing CLI SHA-256 ${identity.cliSha256}.`);
113
+ }
114
+ const closureManifestSha256 = await writeClosureManifest(staged);
115
+ const marker = {
116
+ schema: "mono-agent.managed-runtime.v3",
117
+ packageName: PACKAGE_NAME,
118
+ closureManifestSha256,
119
+ ...identity,
120
+ installedAt: new Date(deps.now()).toISOString(),
121
+ };
122
+ await writePrivateJson(staged.markerPath, marker);
123
+ await chmod(layout.stagingDir, 0o700);
124
+ // Build and verify the replacement completely before moving an invalid
125
+ // runtime aside. This is essential when the executing CLI (and therefore
126
+ // packageSource) lives inside that invalid runtime: quarantine must never
127
+ // make the only repair source disappear halfway through installation.
128
+ await quarantineInvalidRuntime(layout, identity, deps);
129
+ await promoteStaging(layout, staged, identity);
130
+ }
131
+ catch (error) {
132
+ await rm(layout.stagingDir, { recursive: true, force: true }).catch(() => undefined);
133
+ throw error;
134
+ }
135
+ if (!(await verifyRuntime(layout, identity))) {
136
+ throw new Error("The managed runtime failed verification after atomic promotion.");
137
+ }
138
+ return runtimeResult(layout, identity, input.nodePath);
139
+ }
140
+ finally {
141
+ await removeSameRuntimeLockDirectory(layout.lockDir, acquired).catch(() => undefined);
142
+ }
143
+ }
144
+ /**
145
+ * Copy the exact package graph the current CLI is already executing.
146
+ *
147
+ * Deliberately do not invoke npm here. A fresh npm install both re-resolves
148
+ * semver ranges (so it is not the executing dependency closure) and runs
149
+ * lifecycle scripts after the CLI has loaded agent dotenv credentials. Direct
150
+ * graph materialisation also understands pnpm's workspace links without ever
151
+ * handing `workspace:` ranges to npm.
152
+ */
153
+ async function materializeExactExecutionClosure(input) {
154
+ const captured = await captureExecutionClosure(input.packageSource, input.additionalPackages);
155
+ if (captured.sourceClosureSha256 !== input.expectedSourceClosureSha256) {
156
+ throw new Error("The managed runtime package closure changed before staging began.");
157
+ }
158
+ const closure = captured.nodes;
159
+ const root = closure[0];
160
+ if (root === undefined || root.metadata.name !== PACKAGE_NAME || root.metadata.version !== input.packageVersion) {
161
+ throw new Error(`Cannot preserve ${PACKAGE_NAME}@${input.packageVersion}: the executing package closure has an unexpected root.`);
162
+ }
163
+ const destinations = new Map();
164
+ for (const node of closure) {
165
+ const destination = executionClosureDestination(input.stagingDir, node);
166
+ destinations.set(node.id, destination);
167
+ await mkdir(dirname(destination), { recursive: true, mode: 0o700 });
168
+ await cp(node.sourceRoot, destination, {
169
+ recursive: true,
170
+ dereference: false,
171
+ errorOnExist: true,
172
+ force: false,
173
+ verbatimSymlinks: true,
174
+ filter: (source) => shouldCopyPackagePath(node.sourceRoot, source),
175
+ });
176
+ }
177
+ // `cp` is not a transaction: source paths can be replaced while it walks.
178
+ // Compare every copied package against the exact pre-copy package manifest
179
+ // before any staged runtime can receive a marker or be promoted.
180
+ for (const node of closure) {
181
+ const destination = requiredMapValue(destinations, node.id, "package destination");
182
+ const copied = await captureSourcePackage(destination);
183
+ if (JSON.stringify(copied.manifest) !== JSON.stringify(node.sourcePackage)) {
184
+ throw new Error(`Managed runtime source package ${node.metadata.name} changed while it was copied.`);
185
+ }
186
+ }
187
+ for (const node of closure) {
188
+ const destination = requiredMapValue(destinations, node.id, "package destination");
189
+ for (const [dependencyName, dependencyId] of [...node.dependencies].sort(([left], [right]) => left.localeCompare(right))) {
190
+ const dependencyDestination = requiredMapValue(destinations, dependencyId, "dependency destination");
191
+ const linkPath = join(destination, "node_modules", ...packageNameSegments(dependencyName));
192
+ await mkdir(dirname(linkPath), { recursive: true, mode: 0o700 });
193
+ await symlink(relative(dirname(linkPath), dependencyDestination), linkPath, "dir");
194
+ }
195
+ }
196
+ for (const additional of captured.additionalRoots) {
197
+ const destination = requiredMapValue(destinations, additional.nodeId, "additional package destination");
198
+ const linkPath = join(input.stagingDir, "node_modules", ...packageNameSegments(additional.packageName));
199
+ await mkdir(dirname(linkPath), { recursive: true, mode: 0o700 });
200
+ await symlink(relative(dirname(linkPath), destination), linkPath, "dir");
201
+ }
202
+ const packages = Object.fromEntries(closure.map((node) => {
203
+ const destination = requiredMapValue(destinations, node.id, "package destination");
204
+ return [portableRelativePath(input.stagingDir, destination), {
205
+ name: node.metadata.name,
206
+ version: node.metadata.version,
207
+ dependencies: Object.fromEntries([...node.dependencies].sort(([left], [right]) => left.localeCompare(right)).map(([dependencyName, dependencyId]) => [
208
+ dependencyName,
209
+ requiredClosureNode(closure, dependencyId).metadata.version,
210
+ ])),
211
+ }];
212
+ }));
213
+ const additionalLinks = Object.fromEntries(captured.additionalRoots.map((additional) => {
214
+ const destination = requiredMapValue(destinations, additional.nodeId, "additional package destination");
215
+ const linkPath = join(input.stagingDir, "node_modules", ...packageNameSegments(additional.packageName));
216
+ return [portableRelativePath(input.stagingDir, linkPath), {
217
+ link: true,
218
+ resolved: portableRelativePath(input.stagingDir, destination),
219
+ }];
220
+ }));
221
+ await writePrivateJson(join(input.stagingDir, "package-lock.json"), {
222
+ name: "mono-agent-managed-runtime",
223
+ version: "0.0.0",
224
+ lockfileVersion: 3,
225
+ requires: true,
226
+ packages: {
227
+ "": { name: "mono-agent-managed-runtime", version: "0.0.0", private: true },
228
+ ...packages,
229
+ ...additionalLinks,
230
+ },
231
+ });
232
+ const after = await captureExecutionClosure(input.packageSource, input.additionalPackages);
233
+ if (after.sourceClosureSha256 !== captured.sourceClosureSha256
234
+ || after.sourceProofSha256 !== captured.sourceProofSha256) {
235
+ throw new Error("The managed runtime package closure changed while it was staged.");
236
+ }
237
+ }
238
+ async function captureExecutionClosure(packageSource, additionalPackages) {
239
+ const nodes = [];
240
+ const bySourceRoot = new Map();
241
+ const proofs = new Map();
242
+ const visit = async (source) => {
243
+ const sourceRoot = await realpath(source);
244
+ const existing = bySourceRoot.get(sourceRoot);
245
+ if (existing !== undefined)
246
+ return existing;
247
+ const sourcePackage = await captureSourcePackage(sourceRoot);
248
+ const metadata = readInstalledPackageMetadata(sourceRoot, sourcePackage.packageJson);
249
+ const id = nodes.length;
250
+ const node = {
251
+ id,
252
+ sourceRoot,
253
+ metadata,
254
+ dependencies: new Map(),
255
+ sourcePackage: sourcePackage.manifest,
256
+ };
257
+ nodes.push(node);
258
+ proofs.set(id, sourcePackage.proof);
259
+ bySourceRoot.set(sourceRoot, id);
260
+ for (const [dependencyName, optional] of dependencyRequirements(metadata)) {
261
+ const dependencyRoot = await resolveInstalledDependencyPackageRoot(sourceRoot, dependencyName);
262
+ if (dependencyRoot === undefined) {
263
+ if (optional)
264
+ continue;
265
+ throw new Error(`Cannot preserve the executing dependency closure: ${metadata.name}@${metadata.version} ` +
266
+ `cannot resolve required dependency ${dependencyName}. Build or install the package completely before starting it.`);
267
+ }
268
+ node.dependencies.set(dependencyName, await visit(dependencyRoot));
269
+ }
270
+ return id;
271
+ };
272
+ await visit(packageSource);
273
+ const additionalRoots = [];
274
+ for (const additional of additionalPackages) {
275
+ const nodeId = await visit(additional.packageSource);
276
+ const node = requiredClosureNode(nodes, nodeId);
277
+ if (node.metadata.name !== additional.packageName) {
278
+ throw new Error(`Additional managed runtime package ${additional.packageSource} declares ${node.metadata.name}, ` +
279
+ `expected ${additional.packageName}.`);
280
+ }
281
+ additionalRoots.push({ packageName: additional.packageName, nodeId });
282
+ }
283
+ const closure = nodes;
284
+ const stable = {
285
+ schema: "mono-agent.source-execution-closure.v1",
286
+ roots: {
287
+ agentApp: 0,
288
+ additional: additionalRoots,
289
+ },
290
+ packages: closure.map((node) => ({
291
+ id: node.id,
292
+ name: node.metadata.name,
293
+ version: node.metadata.version,
294
+ dependencies: [...node.dependencies]
295
+ .sort(([left], [right]) => left.localeCompare(right))
296
+ .map(([name, nodeId]) => ({ name, nodeId })),
297
+ sourcePackage: node.sourcePackage,
298
+ })),
299
+ };
300
+ const proof = {
301
+ schema: "mono-agent.source-execution-proof.v1",
302
+ stable,
303
+ packages: closure.map((node) => ({
304
+ id: node.id,
305
+ sourceRoot: node.sourceRoot,
306
+ entries: requiredMapValue(proofs, node.id, "source package proof"),
307
+ })),
308
+ };
309
+ return {
310
+ nodes: closure,
311
+ additionalRoots,
312
+ sourceClosureSha256: sha256(Buffer.from(JSON.stringify(stable), "utf8")),
313
+ sourceProofSha256: sha256(Buffer.from(JSON.stringify(proof), "utf8")),
314
+ };
315
+ }
316
+ async function canonicalAdditionalPackages(packages) {
317
+ const byName = new Map();
318
+ for (const entry of packages) {
319
+ packageNameSegments(entry.packageName);
320
+ if (entry.packageName === PACKAGE_NAME) {
321
+ throw new Error(`${PACKAGE_NAME} is already the managed runtime root and cannot be added again.`);
322
+ }
323
+ const lexical = resolve(entry.packageSource);
324
+ const lexicalDetails = await lstat(lexical);
325
+ if (!lexicalDetails.isDirectory() || lexicalDetails.isSymbolicLink()) {
326
+ throw new Error(`Additional managed runtime package source ${lexical} must be a real directory.`);
327
+ }
328
+ const canonical = await realpath(lexical);
329
+ const existing = byName.get(entry.packageName);
330
+ if (existing !== undefined && existing !== canonical) {
331
+ throw new Error(`Additional managed runtime package ${entry.packageName} was resolved to two different sources: ` +
332
+ `${existing} and ${canonical}.`);
333
+ }
334
+ byName.set(entry.packageName, canonical);
335
+ }
336
+ return [...byName]
337
+ .sort(([left], [right]) => left.localeCompare(right))
338
+ .map(([packageName, packageSource]) => ({ packageName, packageSource }));
339
+ }
340
+ async function assertSourceClosureUnchanged(packageSource, additionalPackages, expected, phase) {
341
+ const current = await captureExecutionClosure(packageSource, additionalPackages);
342
+ if (current.sourceClosureSha256 !== expected.sourceClosureSha256
343
+ || current.sourceProofSha256 !== expected.sourceProofSha256) {
344
+ throw new Error(`The managed runtime package closure changed ${phase}; no staged runtime was promoted.`);
345
+ }
346
+ }
347
+ /**
348
+ * Capture exactly the package-owned files copied by `materializeExactExecutionClosure`.
349
+ * `node_modules`, VCS metadata, and dotenv files are deliberately excluded.
350
+ * Relative in-package symlinks are allowed; absolute, escaping, dangling, or
351
+ * excluded-target links are rejected before `cp` can observe them.
352
+ */
353
+ async function captureSourcePackage(packageRoot) {
354
+ const root = await realpath(packageRoot);
355
+ const rootDetails = await lstat(root, { bigint: true });
356
+ if (!rootDetails.isDirectory() || rootDetails.isSymbolicLink()) {
357
+ throw new Error(`Managed runtime source package ${root} must be a real directory.`);
358
+ }
359
+ const entries = [];
360
+ const proof = [];
361
+ let packageJson;
362
+ const visitDirectory = async (directory, pathRelative) => {
363
+ const before = await lstat(directory, { bigint: true });
364
+ if (!before.isDirectory() || before.isSymbolicLink()) {
365
+ throw new Error(`Managed runtime source package directory ${pathRelative} changed type.`);
366
+ }
367
+ entries.push({ path: pathRelative, type: "directory", mode: fileMode(before.mode) });
368
+ proof.push(sourceProofEntry(pathRelative, "directory", before));
369
+ const childNames = (await readdir(directory)).sort((left, right) => left.localeCompare(right));
370
+ for (const name of childNames) {
371
+ const path = join(directory, name);
372
+ const childRelative = sourcePackageRelativePath(root, path);
373
+ if (isExcludedSourcePackagePath(childRelative))
374
+ continue;
375
+ const details = await lstat(path, { bigint: true });
376
+ if (details.isDirectory() && !details.isSymbolicLink()) {
377
+ await visitDirectory(path, childRelative);
378
+ continue;
379
+ }
380
+ if (details.isFile() && !details.isSymbolicLink()) {
381
+ const captured = await captureSourceFile(path, childRelative);
382
+ entries.push(captured.entry);
383
+ proof.push(captured.proof);
384
+ if (childRelative === "package.json")
385
+ packageJson = captured.bytes;
386
+ continue;
387
+ }
388
+ if (details.isSymbolicLink()) {
389
+ const captured = await captureSourceSymlink(root, path, childRelative, details);
390
+ entries.push(captured.entry);
391
+ proof.push(captured.proof);
392
+ continue;
393
+ }
394
+ throw new Error(`Managed runtime source package contains unsupported entry ${childRelative}.`);
395
+ }
396
+ const [after, childNamesAfter] = await Promise.all([
397
+ lstat(directory, { bigint: true }),
398
+ readdir(directory).then((names) => names.sort((left, right) => left.localeCompare(right))),
399
+ ]);
400
+ if (!sameSourceStats(before, after) || JSON.stringify(childNames) !== JSON.stringify(childNamesAfter)) {
401
+ throw new Error(`Managed runtime source package directory ${pathRelative} changed while it was inspected.`);
402
+ }
403
+ };
404
+ await visitDirectory(root, ".");
405
+ if (packageJson === undefined) {
406
+ throw new Error(`Managed runtime source package ${root} has no regular package.json.`);
407
+ }
408
+ entries.sort((left, right) => left.path.localeCompare(right.path));
409
+ proof.sort((left, right) => left.path.localeCompare(right.path));
410
+ return {
411
+ manifest: { schema: "mono-agent.source-package.v1", entries },
412
+ proof,
413
+ packageJson,
414
+ };
415
+ }
416
+ async function captureSourceFile(path, pathRelative) {
417
+ let handle;
418
+ try {
419
+ handle = await open(path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK);
420
+ }
421
+ catch (error) {
422
+ if (["ELOOP", "EMLINK"].includes(error.code ?? "")) {
423
+ throw new Error(`Managed runtime source file ${pathRelative} changed into a symbolic link.`);
424
+ }
425
+ throw error;
426
+ }
427
+ try {
428
+ const before = await handle.stat({ bigint: true });
429
+ if (!before.isFile())
430
+ throw new Error(`Managed runtime source entry ${pathRelative} is not a regular file.`);
431
+ const bytes = await handle.readFile();
432
+ const [after, pathAfter] = await Promise.all([
433
+ handle.stat({ bigint: true }),
434
+ lstat(path, { bigint: true }),
435
+ ]);
436
+ if (!after.isFile() || !pathAfter.isFile() || pathAfter.isSymbolicLink()
437
+ || !sameSourceStats(before, after) || !sameSourceIdentity(after, pathAfter)) {
438
+ throw new Error(`Managed runtime source file ${pathRelative} changed while it was inspected.`);
439
+ }
440
+ return {
441
+ entry: {
442
+ path: pathRelative,
443
+ type: "file",
444
+ mode: fileMode(after.mode),
445
+ sha256: sha256(bytes),
446
+ },
447
+ proof: sourceProofEntry(pathRelative, "file", after),
448
+ bytes,
449
+ };
450
+ }
451
+ finally {
452
+ await handle.close();
453
+ }
454
+ }
455
+ async function captureSourceSymlink(root, path, pathRelative, before) {
456
+ const target = await readlink(path);
457
+ if (isAbsolute(target)) {
458
+ throw new Error(`Managed runtime source symlink ${pathRelative} has an absolute target.`);
459
+ }
460
+ const lexicalTarget = resolve(dirname(path), target);
461
+ assertSourcePathInsidePackage(root, lexicalTarget, pathRelative);
462
+ const lexicalRelative = sourcePackageRelativePath(root, lexicalTarget);
463
+ if (isExcludedSourcePackagePath(lexicalRelative)) {
464
+ throw new Error(`Managed runtime source symlink ${pathRelative} targets excluded package data.`);
465
+ }
466
+ let canonicalTarget;
467
+ try {
468
+ canonicalTarget = await realpath(path);
469
+ }
470
+ catch (error) {
471
+ throw new Error(`Managed runtime source symlink ${pathRelative} must resolve inside its package: ` +
472
+ `${error instanceof Error ? error.message : String(error)}`);
473
+ }
474
+ assertSourcePathInsidePackage(root, canonicalTarget, pathRelative);
475
+ const canonicalRelative = sourcePackageRelativePath(root, canonicalTarget);
476
+ if (isExcludedSourcePackagePath(canonicalRelative)) {
477
+ throw new Error(`Managed runtime source symlink ${pathRelative} resolves to excluded package data.`);
478
+ }
479
+ const [after, targetAfter] = await Promise.all([
480
+ lstat(path, { bigint: true }),
481
+ readlink(path),
482
+ ]);
483
+ if (!after.isSymbolicLink() || targetAfter !== target || !sameSourceStats(before, after)) {
484
+ throw new Error(`Managed runtime source symlink ${pathRelative} changed while it was inspected.`);
485
+ }
486
+ const portableTarget = target.split(sep).join("/");
487
+ return {
488
+ entry: {
489
+ path: pathRelative,
490
+ type: "symlink",
491
+ mode: fileMode(after.mode),
492
+ target: portableTarget,
493
+ },
494
+ proof: sourceProofEntry(pathRelative, "symlink", after, portableTarget),
495
+ };
496
+ }
497
+ function sourceProofEntry(path, type, details, target) {
498
+ return {
499
+ path,
500
+ type,
501
+ dev: details.dev.toString(),
502
+ ino: details.ino.toString(),
503
+ mode: details.mode.toString(),
504
+ size: details.size.toString(),
505
+ mtimeNs: details.mtimeNs.toString(),
506
+ ctimeNs: details.ctimeNs.toString(),
507
+ ...(target === undefined ? {} : { target }),
508
+ };
509
+ }
510
+ function sameSourceIdentity(left, right) {
511
+ return left.dev === right.dev && left.ino === right.ino;
512
+ }
513
+ function sameSourceStats(left, right) {
514
+ return sameSourceIdentity(left, right)
515
+ && left.mode === right.mode
516
+ && left.size === right.size
517
+ && left.mtimeNs === right.mtimeNs
518
+ && left.ctimeNs === right.ctimeNs;
519
+ }
520
+ function sourcePackageRelativePath(root, path) {
521
+ const pathRelative = relative(root, path);
522
+ if (pathRelative === "" || pathRelative === ".")
523
+ return ".";
524
+ if (pathRelative === ".." || pathRelative.startsWith(`..${sep}`) || isAbsolute(pathRelative)) {
525
+ throw new Error(`Managed runtime source path ${path} escapes package root ${root}.`);
526
+ }
527
+ return pathRelative.split(sep).join("/");
528
+ }
529
+ function assertSourcePathInsidePackage(root, path, linkPath) {
530
+ const pathRelative = relative(resolve(root), resolve(path));
531
+ if (pathRelative === ".." || pathRelative.startsWith(`..${sep}`) || isAbsolute(pathRelative)) {
532
+ throw new Error(`Managed runtime source symlink ${linkPath} escapes its package root.`);
533
+ }
534
+ }
535
+ function isExcludedSourcePackagePath(pathRelative) {
536
+ const segments = pathRelative.split("/");
537
+ if (segments.includes("node_modules") || segments.includes(".git"))
538
+ return true;
539
+ const name = segments.at(-1) ?? "";
540
+ return name === ".npmrc" || name === ".env" || name.startsWith(".env.");
541
+ }
542
+ function readInstalledPackageMetadata(packageRoot, packageJson) {
543
+ let parsed;
544
+ try {
545
+ parsed = JSON.parse(packageJson.toString("utf8"));
546
+ }
547
+ catch (error) {
548
+ throw new Error(`Cannot read installed package metadata at ${packageRoot}: ${error instanceof Error ? error.message : String(error)}`);
549
+ }
550
+ if (!isRecord(parsed) || typeof parsed.name !== "string" || typeof parsed.version !== "string") {
551
+ throw new Error(`Installed package metadata at ${packageRoot} must declare string name and version fields.`);
552
+ }
553
+ const peerDependenciesMeta = isRecord(parsed.peerDependenciesMeta) ? parsed.peerDependenciesMeta : {};
554
+ const optionalPeers = new Set(Object.entries(peerDependenciesMeta).flatMap(([name, value]) => isRecord(value) && value.optional === true ? [name] : []));
555
+ return {
556
+ name: parsed.name,
557
+ version: parsed.version,
558
+ dependencies: stringRecord(parsed.dependencies, `${parsed.name} dependencies`),
559
+ optionalDependencies: stringRecord(parsed.optionalDependencies, `${parsed.name} optionalDependencies`),
560
+ peerDependencies: stringRecord(parsed.peerDependencies, `${parsed.name} peerDependencies`),
561
+ optionalPeers,
562
+ };
563
+ }
564
+ function dependencyRequirements(metadata) {
565
+ const requirements = new Map();
566
+ for (const name of Object.keys(metadata.dependencies))
567
+ requirements.set(name, false);
568
+ for (const name of Object.keys(metadata.optionalDependencies))
569
+ requirements.set(name, true);
570
+ for (const name of Object.keys(metadata.peerDependencies)) {
571
+ if (!requirements.has(name))
572
+ requirements.set(name, metadata.optionalPeers.has(name));
573
+ }
574
+ return [...requirements].sort(([left], [right]) => left.localeCompare(right));
575
+ }
576
+ async function resolveInstalledDependencyPackageRoot(packageRoot, dependencyName) {
577
+ const segments = packageNameSegments(dependencyName);
578
+ let cursor = resolve(packageRoot);
579
+ for (;;) {
580
+ const candidate = join(cursor, "node_modules", ...segments);
581
+ try {
582
+ const canonical = await realpath(candidate);
583
+ const details = await lstat(canonical);
584
+ if (!details.isDirectory() || details.isSymbolicLink()) {
585
+ throw new Error(`Installed dependency ${dependencyName} at ${candidate} must resolve to a real directory.`);
586
+ }
587
+ const metadata = readInstalledPackageMetadata(canonical, await readFile(join(canonical, "package.json")));
588
+ if (metadata.name !== dependencyName) {
589
+ throw new Error(`Installed dependency path ${candidate} declares ${metadata.name}, expected ${dependencyName}.`);
590
+ }
591
+ return canonical;
592
+ }
593
+ catch (error) {
594
+ if (!isErrno(error, "ENOENT") && !isErrno(error, "ENOTDIR"))
595
+ throw error;
596
+ }
597
+ const parent = dirname(cursor);
598
+ if (parent === cursor)
599
+ return undefined;
600
+ cursor = parent;
601
+ }
602
+ }
603
+ function executionClosureDestination(stagingDir, node) {
604
+ if (node.id === 0) {
605
+ return join(stagingDir, "node_modules", "@mono-agent", "agent-app");
606
+ }
607
+ const segment = `${String(node.id).padStart(4, "0")}-${safeSegment(node.metadata.name)}-${safeSegment(node.metadata.version)}`;
608
+ return join(stagingDir, "node_modules", ".mono-agent-store", segment, "package");
609
+ }
610
+ function shouldCopyPackagePath(packageRoot, source) {
611
+ const pathRelative = sourcePackageRelativePath(packageRoot, source);
612
+ return pathRelative === "." || !isExcludedSourcePackagePath(pathRelative);
613
+ }
614
+ function packageNameSegments(name) {
615
+ const segments = name.split("/");
616
+ const valid = name.startsWith("@")
617
+ ? segments.length === 2 && segments[0].length > 1 && segments[1].length > 0
618
+ : segments.length === 1 && segments[0].length > 0;
619
+ if (!valid || segments.some((segment) => segment === "." || segment === ".." || segment.includes("\\"))) {
620
+ throw new Error(`Invalid installed package name ${JSON.stringify(name)}.`);
621
+ }
622
+ return segments;
623
+ }
624
+ function stringRecord(value, label) {
625
+ if (value === undefined)
626
+ return {};
627
+ if (!isRecord(value) || Object.values(value).some((entry) => typeof entry !== "string")) {
628
+ throw new Error(`${label} must be an object of package ranges.`);
629
+ }
630
+ return value;
631
+ }
632
+ function portableRelativePath(root, path) {
633
+ const pathRelative = relative(root, path);
634
+ if (pathRelative === "" || pathRelative === ".." || pathRelative.startsWith(`..${sep}`)) {
635
+ throw new Error(`Managed runtime package path ${path} escapes ${root}.`);
636
+ }
637
+ return pathRelative.split(sep).join("/");
638
+ }
639
+ function requiredMapValue(map, key, label) {
640
+ const value = map.get(key);
641
+ if (value === undefined)
642
+ throw new Error(`Missing ${label} for ${String(key)}.`);
643
+ return value;
644
+ }
645
+ function requiredClosureNode(closure, id) {
646
+ const node = closure[id];
647
+ if (node === undefined || node.id !== id)
648
+ throw new Error(`Missing dependency closure node ${id}.`);
649
+ return node;
650
+ }
651
+ function packageRootForCli(cliPath) {
652
+ return resolve(dirname(cliPath), "..");
653
+ }
654
+ async function packageVersionAt(packageRoot) {
655
+ const packageJsonPath = resolve(packageRoot, "package.json");
656
+ await assertRegularFile(packageJsonPath, "mono-agent package.json");
657
+ let parsed;
658
+ try {
659
+ parsed = JSON.parse(await readFile(packageJsonPath, "utf8"));
660
+ }
661
+ catch (error) {
662
+ throw new Error(`Cannot read ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`);
663
+ }
664
+ if (parsed === null || typeof parsed !== "object") {
665
+ throw new Error(`Invalid package metadata at ${packageJsonPath}.`);
666
+ }
667
+ const record = parsed;
668
+ if (record.name !== PACKAGE_NAME || typeof record.version !== "string") {
669
+ throw new Error(`The executing CLI is not inside a valid ${PACKAGE_NAME} package.`);
670
+ }
671
+ return record.version;
672
+ }
673
+ function runtimeLayout(home, identity, id, nowMs) {
674
+ const platformAbi = `${safeSegment(identity.platform)}-${safeSegment(identity.arch)}-abi-${safeSegment(identity.nodeAbi)}`;
675
+ const versionAbiDir = join(home, ".mono-agent", "runtimes", "agent-app", identity.packageVersion, platformAbi);
676
+ const closureId = `${identity.cliSha256}-${identity.sourceClosureSha256}`;
677
+ const installRoot = join(versionAbiDir, closureId);
678
+ const suffix = `${Math.max(0, Math.floor(nowMs))}-${id}`;
679
+ return {
680
+ versionAbiDir,
681
+ installRoot,
682
+ cliPath: join(installRoot, "node_modules", "@mono-agent", "agent-app", "dist", "cli.js"),
683
+ packageJsonPath: join(installRoot, "node_modules", "@mono-agent", "agent-app", "package.json"),
684
+ packageLockPath: join(installRoot, "package-lock.json"),
685
+ closureManifestPath: join(installRoot, ".mono-agent-closure.json"),
686
+ markerPath: join(installRoot, ".mono-agent-runtime.json"),
687
+ lockDir: join(versionAbiDir, `.${closureId}.lock`),
688
+ stagingDir: join(versionAbiDir, `.${closureId}.staging-${suffix}`),
689
+ quarantineDir: join(versionAbiDir, "quarantine", `${closureId}-${suffix}`),
690
+ };
691
+ }
692
+ function runtimeLayoutForRoot(layout, root) {
693
+ return {
694
+ ...layout,
695
+ installRoot: root,
696
+ cliPath: join(root, "node_modules", "@mono-agent", "agent-app", "dist", "cli.js"),
697
+ packageJsonPath: join(root, "node_modules", "@mono-agent", "agent-app", "package.json"),
698
+ packageLockPath: join(root, "package-lock.json"),
699
+ closureManifestPath: join(root, ".mono-agent-closure.json"),
700
+ markerPath: join(root, ".mono-agent-runtime.json"),
701
+ };
702
+ }
703
+ async function acquireRuntimeLock(layout, identity, deps) {
704
+ const deadline = deps.now() + LOCK_WAIT_TIMEOUT_MS;
705
+ const incarnation = await (deps.currentProcessIncarnation ?? currentProcessIncarnation)();
706
+ const isSameProcess = deps.isSameProcessIncarnation ?? matchesProcessIncarnation;
707
+ for (;;) {
708
+ let createdIdentity;
709
+ try {
710
+ await mkdir(layout.lockDir, { mode: 0o700 });
711
+ createdIdentity = runtimeLockIdentity(await assertPrivateRuntimeLockDirectory(layout.lockDir));
712
+ await writePrivateJson(join(layout.lockDir, "owner.json"), {
713
+ pid: process.pid,
714
+ createdAt: new Date(deps.now()).toISOString(),
715
+ incarnation,
716
+ ...identity,
717
+ });
718
+ if (!(await sameRuntimeLockDirectory(layout.lockDir, createdIdentity))) {
719
+ throw new Error(`Managed runtime lock ${layout.lockDir} changed while its owner record was written.`);
720
+ }
721
+ return createdIdentity;
722
+ }
723
+ catch (error) {
724
+ if (createdIdentity !== undefined) {
725
+ await removeSameRuntimeLockDirectory(layout.lockDir, createdIdentity).catch(() => undefined);
726
+ }
727
+ if (!isErrno(error, "EEXIST"))
728
+ throw error;
729
+ }
730
+ if (await verifyRuntime(layout, identity))
731
+ return undefined;
732
+ let stale = false;
733
+ try {
734
+ const lockDetails = await assertPrivateRuntimeLockDirectory(layout.lockDir);
735
+ const observedIdentity = runtimeLockIdentity(lockDetails);
736
+ const owner = await runtimeLockOwner(join(layout.lockDir, "owner.json"));
737
+ stale = owner === undefined
738
+ ? deps.now() - (await stat(layout.lockDir)).mtimeMs >= LOCK_STALE_AFTER_MS
739
+ : !(await isSameProcess(owner.pid, owner.incarnation));
740
+ if (!(await sameRuntimeLockDirectory(layout.lockDir, observedIdentity)))
741
+ continue;
742
+ if (stale) {
743
+ const staleLock = `${layout.quarantineDir}-stale-lock`;
744
+ await mkdir(dirname(staleLock), { recursive: true, mode: 0o700 });
745
+ try {
746
+ await rename(layout.lockDir, staleLock);
747
+ }
748
+ catch (error) {
749
+ if (isErrno(error, "ENOENT") || isErrno(error, "EEXIST"))
750
+ continue;
751
+ throw error;
752
+ }
753
+ const moved = await lstat(staleLock);
754
+ if (!sameRuntimeLockIdentity(moved, observedIdentity)) {
755
+ await rename(staleLock, layout.lockDir).catch(() => undefined);
756
+ return undefined;
757
+ }
758
+ continue;
759
+ }
760
+ }
761
+ catch (error) {
762
+ if (isErrno(error, "ENOENT"))
763
+ continue;
764
+ throw error;
765
+ }
766
+ if (deps.now() >= deadline)
767
+ return undefined;
768
+ await deps.sleep(LOCK_POLL_INTERVAL_MS);
769
+ }
770
+ }
771
+ async function runtimeLockOwner(path) {
772
+ try {
773
+ const owner = JSON.parse(await readFile(path, "utf8"));
774
+ const incarnation = processIncarnationFromJson(owner.incarnation);
775
+ return typeof owner.pid === "number"
776
+ && Number.isSafeInteger(owner.pid)
777
+ && owner.pid > 0
778
+ && incarnation !== undefined
779
+ ? { pid: owner.pid, incarnation }
780
+ : undefined;
781
+ }
782
+ catch {
783
+ return undefined;
784
+ }
785
+ }
786
+ async function assertPrivateRuntimeLockDirectory(path) {
787
+ const details = await lstat(path);
788
+ if (!details.isDirectory() || details.isSymbolicLink() || !ownedPrivately(details)) {
789
+ throw new Error(`Managed runtime lock ${path} is not a private current-user directory.`);
790
+ }
791
+ return details;
792
+ }
793
+ function runtimeLockIdentity(details) {
794
+ return { dev: details.dev, ino: details.ino };
795
+ }
796
+ function sameRuntimeLockIdentity(details, identity) {
797
+ return details.dev === identity.dev && details.ino === identity.ino;
798
+ }
799
+ async function sameRuntimeLockDirectory(path, identity) {
800
+ try {
801
+ return sameRuntimeLockIdentity(await assertPrivateRuntimeLockDirectory(path), identity);
802
+ }
803
+ catch {
804
+ return false;
805
+ }
806
+ }
807
+ async function removeSameRuntimeLockDirectory(path, identity) {
808
+ if (!(await sameRuntimeLockDirectory(path, identity)))
809
+ return;
810
+ const released = `${path}.released-${process.pid}-${randomUUID()}`;
811
+ try {
812
+ await rename(path, released);
813
+ }
814
+ catch (error) {
815
+ if (isErrno(error, "ENOENT"))
816
+ return;
817
+ throw error;
818
+ }
819
+ const moved = await lstat(released);
820
+ if (!sameRuntimeLockIdentity(moved, identity)) {
821
+ await rename(released, path).catch(() => undefined);
822
+ throw new Error(`Managed runtime lock ${path} changed while it was released.`);
823
+ }
824
+ await rm(released, { recursive: true, force: true });
825
+ }
826
+ async function quarantineInvalidRuntime(layout, identity, deps) {
827
+ try {
828
+ await lstat(layout.installRoot);
829
+ }
830
+ catch (error) {
831
+ if (isErrno(error, "ENOENT"))
832
+ return;
833
+ throw error;
834
+ }
835
+ if (await verifyRuntime(layout, identity))
836
+ return;
837
+ await mkdir(dirname(layout.quarantineDir), { recursive: true, mode: 0o700 });
838
+ await rename(layout.installRoot, layout.quarantineDir);
839
+ await chmod(layout.quarantineDir, 0o700).catch(() => undefined);
840
+ // Quarantine is deliberately retained: old verified roots and invalid roots
841
+ // are never silently overwritten, while no plist can point at this location.
842
+ void deps;
843
+ }
844
+ async function promoteStaging(layout, staged, identity) {
845
+ try {
846
+ await rename(staged.installRoot, layout.installRoot);
847
+ }
848
+ catch (error) {
849
+ // A concurrent installer can win after a stale-lock recovery. Its result is
850
+ // acceptable only if it independently verifies against the same identity.
851
+ if (!(await verifyRuntime(layout, identity)))
852
+ throw error;
853
+ await rm(staged.installRoot, { recursive: true, force: true });
854
+ }
855
+ }
856
+ async function verifyRuntime(layout, identity) {
857
+ try {
858
+ const root = await lstat(layout.installRoot);
859
+ if (!root.isDirectory() || root.isSymbolicLink() || !ownedPrivately(root))
860
+ return false;
861
+ if (!(await verifyInstalledPackage(layout, identity)))
862
+ return false;
863
+ await assertRegularFile(layout.markerPath, "managed runtime marker");
864
+ const marker = JSON.parse(await readFile(layout.markerPath, "utf8"));
865
+ return marker.schema === "mono-agent.managed-runtime.v3"
866
+ && marker.packageName === PACKAGE_NAME
867
+ && marker.packageVersion === identity.packageVersion
868
+ && marker.cliSha256 === identity.cliSha256
869
+ && marker.sourceClosureSha256 === identity.sourceClosureSha256
870
+ && marker.nodeAbi === identity.nodeAbi
871
+ && marker.platform === identity.platform
872
+ && marker.arch === identity.arch
873
+ && typeof marker.closureManifestSha256 === "string"
874
+ && await verifyClosureManifest(layout, marker.closureManifestSha256);
875
+ }
876
+ catch {
877
+ return false;
878
+ }
879
+ }
880
+ async function verifyInstalledPackage(layout, identity) {
881
+ try {
882
+ const packageRoot = dirname(layout.packageJsonPath);
883
+ for (const path of [
884
+ join(layout.installRoot, "node_modules"),
885
+ join(layout.installRoot, "node_modules", "@mono-agent"),
886
+ packageRoot,
887
+ join(packageRoot, "dist"),
888
+ ]) {
889
+ const details = await lstat(path);
890
+ if (!details.isDirectory() || details.isSymbolicLink())
891
+ return false;
892
+ }
893
+ await assertRegularFile(layout.packageJsonPath, "installed package.json");
894
+ await assertRegularFile(layout.cliPath, "installed CLI");
895
+ await assertRegularFile(layout.packageLockPath, "managed runtime package-lock.json");
896
+ const pkg = JSON.parse(await readFile(layout.packageJsonPath, "utf8"));
897
+ if (pkg.name !== PACKAGE_NAME || pkg.version !== identity.packageVersion)
898
+ return false;
899
+ const packageLock = JSON.parse(await readFile(layout.packageLockPath, "utf8"));
900
+ const locked = Object.entries(packageLock.packages ?? {}).find(([path]) => path === "node_modules/@mono-agent/agent-app"
901
+ || path.endsWith("/node_modules/@mono-agent/agent-app"))?.[1];
902
+ if (typeof packageLock.lockfileVersion !== "number"
903
+ || locked?.version !== identity.packageVersion)
904
+ return false;
905
+ const [realRoot, realCli] = await Promise.all([realpath(layout.installRoot), realpath(layout.cliPath)]);
906
+ const cliRelative = relative(realRoot, realCli);
907
+ if (cliRelative === "" || cliRelative === ".." || cliRelative.startsWith(`..${sep}`) || resolve(realCli) !== realCli) {
908
+ return false;
909
+ }
910
+ return sha256(await readFile(layout.cliPath)) === identity.cliSha256;
911
+ }
912
+ catch {
913
+ return false;
914
+ }
915
+ }
916
+ async function writeClosureManifest(layout) {
917
+ const manifest = await captureRuntimeClosureManifest(layout);
918
+ const contents = privateJsonContents(manifest);
919
+ await writeFile(layout.closureManifestPath, contents, { encoding: "utf8", mode: 0o600 });
920
+ await chmod(layout.closureManifestPath, 0o600);
921
+ return sha256(Buffer.from(contents, "utf8"));
922
+ }
923
+ async function verifyClosureManifest(layout, expectedSha256) {
924
+ try {
925
+ await assertRegularFile(layout.closureManifestPath, "managed runtime closure manifest");
926
+ const contents = await readFile(layout.closureManifestPath);
927
+ if (sha256(contents) !== expectedSha256)
928
+ return false;
929
+ const parsed = JSON.parse(contents.toString("utf8"));
930
+ if (parsed.schema !== "mono-agent.execution-closure.v1" || !Array.isArray(parsed.entries))
931
+ return false;
932
+ const current = await captureRuntimeClosureManifest(layout);
933
+ return JSON.stringify(parsed) === JSON.stringify(current);
934
+ }
935
+ catch {
936
+ return false;
937
+ }
938
+ }
939
+ async function captureRuntimeClosureManifest(layout) {
940
+ const entries = [];
941
+ const excluded = new Set([resolve(layout.closureManifestPath), resolve(layout.markerPath)]);
942
+ const visit = async (directory) => {
943
+ const children = await readdir(directory, { withFileTypes: true });
944
+ children.sort((left, right) => left.name.localeCompare(right.name));
945
+ for (const child of children) {
946
+ const path = join(directory, child.name);
947
+ if (excluded.has(resolve(path)))
948
+ continue;
949
+ const details = await lstat(path);
950
+ const pathRelative = portableRelativePath(layout.installRoot, path);
951
+ const mode = fileMode(details.mode);
952
+ if (details.isSymbolicLink()) {
953
+ const target = await readlink(path);
954
+ if (isAbsolute(target)) {
955
+ throw new Error(`Managed runtime symlink ${pathRelative} has an absolute target.`);
956
+ }
957
+ assertPathInsideRoot(layout.installRoot, resolve(dirname(path), target), `symlink ${pathRelative}`);
958
+ entries.push({
959
+ path: pathRelative,
960
+ type: "symlink",
961
+ mode,
962
+ target: target.split(sep).join("/"),
963
+ });
964
+ continue;
965
+ }
966
+ if (details.isDirectory()) {
967
+ entries.push({ path: pathRelative, type: "directory", mode });
968
+ await visit(path);
969
+ continue;
970
+ }
971
+ if (details.isFile()) {
972
+ entries.push({
973
+ path: pathRelative,
974
+ type: "file",
975
+ mode,
976
+ sha256: await fingerprintClosureFile(path, pathRelative),
977
+ });
978
+ continue;
979
+ }
980
+ throw new Error(`Managed runtime closure contains unsupported entry ${pathRelative}.`);
981
+ }
982
+ };
983
+ await visit(layout.installRoot);
984
+ entries.sort((left, right) => left.path.localeCompare(right.path));
985
+ return { schema: "mono-agent.execution-closure.v1", entries };
986
+ }
987
+ async function fingerprintClosureFile(path, pathRelative) {
988
+ let handle;
989
+ try {
990
+ handle = await open(path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK);
991
+ }
992
+ catch (error) {
993
+ if (["ELOOP", "EMLINK"].includes(error.code ?? "")) {
994
+ throw new Error(`Managed runtime file ${pathRelative} changed into a symbolic link.`);
995
+ }
996
+ throw error;
997
+ }
998
+ try {
999
+ const before = await handle.stat();
1000
+ if (!before.isFile())
1001
+ throw new Error(`Managed runtime entry ${pathRelative} is not a regular file.`);
1002
+ const bytes = await handle.readFile();
1003
+ const [after, pathAfter] = await Promise.all([handle.stat(), lstat(path)]);
1004
+ if (!after.isFile()
1005
+ || !pathAfter.isFile()
1006
+ || pathAfter.isSymbolicLink()
1007
+ || before.dev !== after.dev
1008
+ || before.ino !== after.ino
1009
+ || before.size !== after.size
1010
+ || before.mtimeMs !== after.mtimeMs
1011
+ || before.ctimeMs !== after.ctimeMs
1012
+ || after.dev !== pathAfter.dev
1013
+ || after.ino !== pathAfter.ino) {
1014
+ throw new Error(`Managed runtime file ${pathRelative} changed while it was fingerprinted.`);
1015
+ }
1016
+ return sha256(bytes);
1017
+ }
1018
+ finally {
1019
+ await handle.close();
1020
+ }
1021
+ }
1022
+ function assertPathInsideRoot(root, path, label) {
1023
+ const pathRelative = relative(resolve(root), resolve(path));
1024
+ if (pathRelative === ".." || pathRelative.startsWith(`..${sep}`) || isAbsolute(pathRelative)) {
1025
+ throw new Error(`Managed runtime ${label} escapes its private root.`);
1026
+ }
1027
+ }
1028
+ function fileMode(mode) {
1029
+ return (Number(mode) & 0o777).toString(8).padStart(4, "0");
1030
+ }
1031
+ async function assertRegularFile(path, label) {
1032
+ let details;
1033
+ try {
1034
+ details = await lstat(path);
1035
+ }
1036
+ catch (error) {
1037
+ throw new Error(`Cannot inspect ${label} at ${path}: ${error instanceof Error ? error.message : String(error)}`);
1038
+ }
1039
+ if (!details.isFile() || details.isSymbolicLink()) {
1040
+ throw new Error(`${label} at ${path} must be a regular, non-symlink file.`);
1041
+ }
1042
+ }
1043
+ async function writePrivateJson(path, value) {
1044
+ await writeFile(path, privateJsonContents(value), { encoding: "utf8", mode: 0o600 });
1045
+ await chmod(path, 0o600);
1046
+ }
1047
+ function privateJsonContents(value) {
1048
+ return `${JSON.stringify(value, undefined, 2)}\n`;
1049
+ }
1050
+ async function ensurePrivateRuntimeAncestors(home, versionAbiDir) {
1051
+ const base = join(home, ".mono-agent");
1052
+ const relative = versionAbiDir.slice(base.length).split(/[\\/]/u).filter(Boolean);
1053
+ const paths = [base];
1054
+ for (const segment of relative)
1055
+ paths.push(join(paths.at(-1), segment));
1056
+ for (const path of paths) {
1057
+ await mkdir(path, { recursive: false, mode: 0o700 }).catch((error) => {
1058
+ if (!isErrno(error, "EEXIST"))
1059
+ throw error;
1060
+ });
1061
+ const details = await lstat(path);
1062
+ if (!details.isDirectory() || details.isSymbolicLink()) {
1063
+ throw new Error(`Managed runtime ancestor ${path} must be a real directory.`);
1064
+ }
1065
+ if (typeof process.getuid === "function" && details.uid !== process.getuid()) {
1066
+ throw new Error(`Managed runtime ancestor ${path} is not owned by the current user.`);
1067
+ }
1068
+ await chmod(path, 0o700);
1069
+ const secured = await lstat(path);
1070
+ if (!ownedPrivately(secured)) {
1071
+ throw new Error(`Managed runtime ancestor ${path} could not be secured to mode 0700.`);
1072
+ }
1073
+ }
1074
+ }
1075
+ function ownedPrivately(details) {
1076
+ const owned = typeof process.getuid !== "function" || Number(details.uid) === process.getuid();
1077
+ return owned && (Number(details.mode) & 0o077) === 0;
1078
+ }
1079
+ function runtimeResult(layout, identity, nodePath) {
1080
+ return {
1081
+ cliPath: layout.cliPath,
1082
+ nodePath: resolve(nodePath),
1083
+ installRoot: layout.installRoot,
1084
+ packageVersion: identity.packageVersion,
1085
+ cliSha256: identity.cliSha256,
1086
+ nodeAbi: identity.nodeAbi,
1087
+ };
1088
+ }
1089
+ function requiredNodeAbi() {
1090
+ const abi = process.versions.modules;
1091
+ if (abi === undefined || abi.trim().length === 0) {
1092
+ throw new Error("Node did not report a module ABI; refusing to create a managed runtime.");
1093
+ }
1094
+ return abi;
1095
+ }
1096
+ function isExactVersion(value) {
1097
+ return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(value);
1098
+ }
1099
+ function safeSegment(value) {
1100
+ const normalized = value.replace(/[^0-9A-Za-z._-]/gu, "-").replace(/-+/gu, "-");
1101
+ if (normalized.length === 0 || normalized === "." || normalized === "..") {
1102
+ throw new Error(`Cannot use ${JSON.stringify(value)} as a managed runtime path segment.`);
1103
+ }
1104
+ return normalized;
1105
+ }
1106
+ function sha256(value) {
1107
+ return createHash("sha256").update(value).digest("hex");
1108
+ }
1109
+ function isRecord(value) {
1110
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1111
+ }
1112
+ function isErrno(error, code) {
1113
+ return typeof error === "object" && error !== null && error.code === code;
1114
+ }
1115
+ //# sourceMappingURL=background-runtime.js.map