@dharma-ai-labs/agent-fabric 0.2.152 → 0.2.154

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 (309) hide show
  1. package/dist/bootstrapGitScope.test.d.ts +2 -0
  2. package/dist/bootstrapGitScope.test.d.ts.map +1 -0
  3. package/dist/bootstrapGitScope.test.js +115 -0
  4. package/dist/bootstrapGitScope.test.js.map +1 -0
  5. package/dist/bootstrapHostFiles.d.ts +5 -0
  6. package/dist/bootstrapHostFiles.d.ts.map +1 -0
  7. package/dist/bootstrapHostFiles.js +150 -0
  8. package/dist/bootstrapHostFiles.js.map +1 -0
  9. package/dist/bootstrapHostFiles.test.d.ts +2 -0
  10. package/dist/bootstrapHostFiles.test.d.ts.map +1 -0
  11. package/dist/bootstrapHostFiles.test.js +127 -0
  12. package/dist/bootstrapHostFiles.test.js.map +1 -0
  13. package/dist/bootstrapHostScope.d.ts +42 -0
  14. package/dist/bootstrapHostScope.d.ts.map +1 -0
  15. package/dist/bootstrapHostScope.js +224 -0
  16. package/dist/bootstrapHostScope.js.map +1 -0
  17. package/dist/bootstrapHostScope.test.d.ts +2 -0
  18. package/dist/bootstrapHostScope.test.d.ts.map +1 -0
  19. package/dist/bootstrapHostScope.test.js +577 -0
  20. package/dist/bootstrapHostScope.test.js.map +1 -0
  21. package/dist/bootstrapInstallationScope.test.d.ts +2 -0
  22. package/dist/bootstrapInstallationScope.test.d.ts.map +1 -0
  23. package/dist/bootstrapInstallationScope.test.js +68 -0
  24. package/dist/bootstrapInstallationScope.test.js.map +1 -0
  25. package/dist/bootstrapLauncherCaller.test.d.ts +2 -0
  26. package/dist/bootstrapLauncherCaller.test.d.ts.map +1 -0
  27. package/dist/bootstrapLauncherCaller.test.js +91 -0
  28. package/dist/bootstrapLauncherCaller.test.js.map +1 -0
  29. package/dist/bootstrapOwnedChildren.test.d.ts +2 -0
  30. package/dist/bootstrapOwnedChildren.test.d.ts.map +1 -0
  31. package/dist/bootstrapOwnedChildren.test.js +93 -0
  32. package/dist/bootstrapOwnedChildren.test.js.map +1 -0
  33. package/dist/bootstrapPolicyScope.test.d.ts +2 -0
  34. package/dist/bootstrapPolicyScope.test.d.ts.map +1 -0
  35. package/dist/bootstrapPolicyScope.test.js +261 -0
  36. package/dist/bootstrapPolicyScope.test.js.map +1 -0
  37. package/dist/bootstrapProcessCaller.test.d.ts +2 -0
  38. package/dist/bootstrapProcessCaller.test.d.ts.map +1 -0
  39. package/dist/bootstrapProcessCaller.test.js +262 -0
  40. package/dist/bootstrapProcessCaller.test.js.map +1 -0
  41. package/dist/bootstrapRegistryCaller.test.d.ts +2 -0
  42. package/dist/bootstrapRegistryCaller.test.d.ts.map +1 -0
  43. package/dist/bootstrapRegistryCaller.test.js +128 -0
  44. package/dist/bootstrapRegistryCaller.test.js.map +1 -0
  45. package/dist/bootstrapVaultScope.test.d.ts +2 -0
  46. package/dist/bootstrapVaultScope.test.d.ts.map +1 -0
  47. package/dist/bootstrapVaultScope.test.js +105 -0
  48. package/dist/bootstrapVaultScope.test.js.map +1 -0
  49. package/dist/codexBootstrapCompletion.test.d.ts +2 -0
  50. package/dist/codexBootstrapCompletion.test.d.ts.map +1 -0
  51. package/dist/codexBootstrapCompletion.test.js +630 -0
  52. package/dist/codexBootstrapCompletion.test.js.map +1 -0
  53. package/dist/codexBoundSession.d.ts +2 -2
  54. package/dist/codexBoundSession.d.ts.map +1 -1
  55. package/dist/codexBoundSession.js +23 -17
  56. package/dist/codexBoundSession.js.map +1 -1
  57. package/dist/codexBoundSession.test.js +3 -0
  58. package/dist/codexBoundSession.test.js.map +1 -1
  59. package/dist/codexInboxSession.d.ts.map +1 -1
  60. package/dist/codexInboxSession.js +3 -3
  61. package/dist/codexInboxSession.js.map +1 -1
  62. package/dist/codexScopedDispatch.test.d.ts +2 -0
  63. package/dist/codexScopedDispatch.test.d.ts.map +1 -0
  64. package/dist/codexScopedDispatch.test.js +56 -0
  65. package/dist/codexScopedDispatch.test.js.map +1 -0
  66. package/dist/codexSetupAdmission.d.ts +108 -0
  67. package/dist/codexSetupAdmission.d.ts.map +1 -0
  68. package/dist/codexSetupAdmission.js +214 -0
  69. package/dist/codexSetupAdmission.js.map +1 -0
  70. package/dist/codexSetupAdmission.test.d.ts +2 -0
  71. package/dist/codexSetupAdmission.test.d.ts.map +1 -0
  72. package/dist/codexSetupAdmission.test.js +578 -0
  73. package/dist/codexSetupAdmission.test.js.map +1 -0
  74. package/dist/codexSetupChildProvider.test.d.ts +2 -0
  75. package/dist/codexSetupChildProvider.test.d.ts.map +1 -0
  76. package/dist/codexSetupChildProvider.test.js +210 -0
  77. package/dist/codexSetupChildProvider.test.js.map +1 -0
  78. package/dist/codexSetupChildStartup.d.ts +38 -0
  79. package/dist/codexSetupChildStartup.d.ts.map +1 -0
  80. package/dist/codexSetupChildStartup.js +115 -0
  81. package/dist/codexSetupChildStartup.js.map +1 -0
  82. package/dist/codexSetupChildStartup.test.d.ts +2 -0
  83. package/dist/codexSetupChildStartup.test.d.ts.map +1 -0
  84. package/dist/codexSetupChildStartup.test.js +141 -0
  85. package/dist/codexSetupChildStartup.test.js.map +1 -0
  86. package/dist/codexSetupCliDispatch.test.d.ts +2 -0
  87. package/dist/codexSetupCliDispatch.test.d.ts.map +1 -0
  88. package/dist/codexSetupCliDispatch.test.js +138 -0
  89. package/dist/codexSetupCliDispatch.test.js.map +1 -0
  90. package/dist/codexSetupJournalOpen.test.d.ts +2 -0
  91. package/dist/codexSetupJournalOpen.test.d.ts.map +1 -0
  92. package/dist/codexSetupJournalOpen.test.js +101 -0
  93. package/dist/codexSetupJournalOpen.test.js.map +1 -0
  94. package/dist/codexSetupNativeHost.d.ts +31 -0
  95. package/dist/codexSetupNativeHost.d.ts.map +1 -0
  96. package/dist/codexSetupNativeHost.js +208 -0
  97. package/dist/codexSetupNativeHost.js.map +1 -0
  98. package/dist/codexSetupNativeHost.test.d.ts +2 -0
  99. package/dist/codexSetupNativeHost.test.d.ts.map +1 -0
  100. package/dist/codexSetupNativeHost.test.js +456 -0
  101. package/dist/codexSetupNativeHost.test.js.map +1 -0
  102. package/dist/codexSetupNativeWire.test.d.ts +2 -0
  103. package/dist/codexSetupNativeWire.test.d.ts.map +1 -0
  104. package/dist/codexSetupNativeWire.test.js +243 -0
  105. package/dist/codexSetupNativeWire.test.js.map +1 -0
  106. package/dist/codexSetupOwnedConnection.d.ts +14 -0
  107. package/dist/codexSetupOwnedConnection.d.ts.map +1 -0
  108. package/dist/codexSetupOwnedConnection.js +64 -0
  109. package/dist/codexSetupOwnedConnection.js.map +1 -0
  110. package/dist/codexSetupOwnedConnection.test.d.ts +2 -0
  111. package/dist/codexSetupOwnedConnection.test.d.ts.map +1 -0
  112. package/dist/codexSetupOwnedConnection.test.js +192 -0
  113. package/dist/codexSetupOwnedConnection.test.js.map +1 -0
  114. package/dist/codexSetupPortalCommand.test.d.ts +2 -0
  115. package/dist/codexSetupPortalCommand.test.d.ts.map +1 -0
  116. package/dist/codexSetupPortalCommand.test.js +146 -0
  117. package/dist/codexSetupPortalCommand.test.js.map +1 -0
  118. package/dist/codexSetupReadiness.d.ts +23 -0
  119. package/dist/codexSetupReadiness.d.ts.map +1 -0
  120. package/dist/codexSetupReadiness.js +74 -0
  121. package/dist/codexSetupReadiness.js.map +1 -0
  122. package/dist/codexSetupReadiness.test.d.ts +2 -0
  123. package/dist/codexSetupReadiness.test.d.ts.map +1 -0
  124. package/dist/codexSetupReadiness.test.js +145 -0
  125. package/dist/codexSetupReadiness.test.js.map +1 -0
  126. package/dist/codexSetupSessionCaller.test.d.ts +2 -0
  127. package/dist/codexSetupSessionCaller.test.d.ts.map +1 -0
  128. package/dist/codexSetupSessionCaller.test.js +352 -0
  129. package/dist/codexSetupSessionCaller.test.js.map +1 -0
  130. package/dist/codexSetupSessionHandoff.d.ts +48 -0
  131. package/dist/codexSetupSessionHandoff.d.ts.map +1 -0
  132. package/dist/codexSetupSessionHandoff.js +146 -0
  133. package/dist/codexSetupSessionHandoff.js.map +1 -0
  134. package/dist/codexSetupSessionHandoff.test.d.ts +2 -0
  135. package/dist/codexSetupSessionHandoff.test.d.ts.map +1 -0
  136. package/dist/codexSetupSessionHandoff.test.js +289 -0
  137. package/dist/codexSetupSessionHandoff.test.js.map +1 -0
  138. package/dist/codexSetupTransport.test.d.ts +2 -0
  139. package/dist/codexSetupTransport.test.d.ts.map +1 -0
  140. package/dist/codexSetupTransport.test.js +206 -0
  141. package/dist/codexSetupTransport.test.js.map +1 -0
  142. package/dist/codexSetupVaultJournal.d.ts +13 -0
  143. package/dist/codexSetupVaultJournal.d.ts.map +1 -0
  144. package/dist/codexSetupVaultJournal.js +28 -0
  145. package/dist/codexSetupVaultJournal.js.map +1 -0
  146. package/dist/codexSetupVaultJournal.test.d.ts +2 -0
  147. package/dist/codexSetupVaultJournal.test.d.ts.map +1 -0
  148. package/dist/codexSetupVaultJournal.test.js +104 -0
  149. package/dist/codexSetupVaultJournal.test.js.map +1 -0
  150. package/dist/containerChildCleanup.test.d.ts +2 -0
  151. package/dist/containerChildCleanup.test.d.ts.map +1 -0
  152. package/dist/containerChildCleanup.test.js +145 -0
  153. package/dist/containerChildCleanup.test.js.map +1 -0
  154. package/dist/containerRelayLifecycle.d.ts +1 -1
  155. package/dist/containerRelayLifecycle.d.ts.map +1 -1
  156. package/dist/containerRelayLifecycle.js +29 -37
  157. package/dist/containerRelayLifecycle.js.map +1 -1
  158. package/dist/containerRelayLifecycle.test.js +13 -0
  159. package/dist/containerRelayLifecycle.test.js.map +1 -1
  160. package/dist/cooperativeInboxSession.d.ts +2 -2
  161. package/dist/cooperativeInboxSession.d.ts.map +1 -1
  162. package/dist/cooperativeInboxSession.js +21 -13
  163. package/dist/cooperativeInboxSession.js.map +1 -1
  164. package/dist/cooperativeInboxSession.test.js +65 -0
  165. package/dist/cooperativeInboxSession.test.js.map +1 -1
  166. package/dist/demoWatchControl.d.ts +3 -3
  167. package/dist/index.d.ts +55 -14
  168. package/dist/index.d.ts.map +1 -1
  169. package/dist/index.js +1650 -557
  170. package/dist/index.js.map +1 -1
  171. package/dist/namedCodexSkillDiscovery.d.ts +15 -0
  172. package/dist/namedCodexSkillDiscovery.d.ts.map +1 -0
  173. package/dist/namedCodexSkillDiscovery.js +90 -0
  174. package/dist/namedCodexSkillDiscovery.js.map +1 -0
  175. package/dist/namedCodexSkillDiscovery.test.d.ts +2 -0
  176. package/dist/namedCodexSkillDiscovery.test.d.ts.map +1 -0
  177. package/dist/namedCodexSkillDiscovery.test.js +37 -0
  178. package/dist/namedCodexSkillDiscovery.test.js.map +1 -0
  179. package/dist/namedCodexThread.d.ts +2 -0
  180. package/dist/namedCodexThread.d.ts.map +1 -1
  181. package/dist/namedCodexThread.js +13 -4
  182. package/dist/namedCodexThread.js.map +1 -1
  183. package/dist/namedSessionChildOwner.d.ts +17 -0
  184. package/dist/namedSessionChildOwner.d.ts.map +1 -0
  185. package/dist/namedSessionChildOwner.js +122 -0
  186. package/dist/namedSessionChildOwner.js.map +1 -0
  187. package/dist/namedSessionChildOwner.test.d.ts +2 -0
  188. package/dist/namedSessionChildOwner.test.d.ts.map +1 -0
  189. package/dist/namedSessionChildOwner.test.js +203 -0
  190. package/dist/namedSessionChildOwner.test.js.map +1 -0
  191. package/dist/namedSessionEvidence.d.ts +2 -2
  192. package/dist/namedSessionEvidence.d.ts.map +1 -1
  193. package/dist/namedSessionEvidence.js +2 -2
  194. package/dist/namedSessionEvidence.js.map +1 -1
  195. package/dist/namedSessionEvidence.test.js +24 -1
  196. package/dist/namedSessionEvidence.test.js.map +1 -1
  197. package/dist/namedSessionRepositoryState.d.ts +2 -2
  198. package/dist/namedSessionRepositoryState.d.ts.map +1 -1
  199. package/dist/namedSessionService.d.ts +4 -2
  200. package/dist/namedSessionService.d.ts.map +1 -1
  201. package/dist/namedSessionService.js +18 -1
  202. package/dist/namedSessionService.js.map +1 -1
  203. package/dist/namedSessionService.test.js +31 -2
  204. package/dist/namedSessionService.test.js.map +1 -1
  205. package/dist/namedSessionTaskExportSync.d.ts +3 -3
  206. package/dist/namedSessionTaskExportSync.d.ts.map +1 -1
  207. package/dist/namedSessionTaskExportSync.js +6 -6
  208. package/dist/namedSessionTaskExportSync.js.map +1 -1
  209. package/dist/namedSessionTrust.d.ts +2 -2
  210. package/dist/namedSessionTrust.d.ts.map +1 -1
  211. package/dist/namedSessionTrust.js +3 -2
  212. package/dist/namedSessionTrust.js.map +1 -1
  213. package/dist/namedSessionTrust.test.js +2 -1
  214. package/dist/namedSessionTrust.test.js.map +1 -1
  215. package/dist/ownedChildLifecycle.d.ts +13 -0
  216. package/dist/ownedChildLifecycle.d.ts.map +1 -0
  217. package/dist/ownedChildLifecycle.js +87 -0
  218. package/dist/ownedChildLifecycle.js.map +1 -0
  219. package/dist/ownedChildLifecycle.test.d.ts +2 -0
  220. package/dist/ownedChildLifecycle.test.d.ts.map +1 -0
  221. package/dist/ownedChildLifecycle.test.js +133 -0
  222. package/dist/ownedChildLifecycle.test.js.map +1 -0
  223. package/dist/pidLock.test.js +242 -9
  224. package/dist/pidLock.test.js.map +1 -1
  225. package/dist/providerSessionChannel.d.ts +1 -1
  226. package/dist/providerSessionChannel.d.ts.map +1 -1
  227. package/dist/providerSessionChannel.js +7 -3
  228. package/dist/providerSessionChannel.js.map +1 -1
  229. package/dist/providerSessionChannel.test.js +20 -0
  230. package/dist/providerSessionChannel.test.js.map +1 -1
  231. package/dist/providerSessionReplyRecovery.d.ts +2 -2
  232. package/dist/providerSessionReplyRecovery.d.ts.map +1 -1
  233. package/dist/providerSessionReplyRecovery.js +3 -3
  234. package/dist/providerSessionReplyRecovery.js.map +1 -1
  235. package/dist/relayAutostart.d.ts.map +1 -1
  236. package/dist/relayAutostart.js +12 -9
  237. package/dist/relayAutostart.js.map +1 -1
  238. package/dist/relayRuntimeUpgrade.d.ts +1 -1
  239. package/dist/relayRuntimeUpgrade.d.ts.map +1 -1
  240. package/dist/relayRuntimeUpgrade.js +3 -1
  241. package/dist/relayRuntimeUpgrade.js.map +1 -1
  242. package/dist/relayRuntimeUpgrade.test.js +113 -0
  243. package/dist/relayRuntimeUpgrade.test.js.map +1 -1
  244. package/dist/relayRuntimeUpgradeCaller.test.js +12 -1
  245. package/dist/relayRuntimeUpgradeCaller.test.js.map +1 -1
  246. package/dist/relaySupervisor.d.ts.map +1 -1
  247. package/dist/relaySupervisor.js +19 -14
  248. package/dist/relaySupervisor.js.map +1 -1
  249. package/dist/relaySupervisor.test.js +26 -0
  250. package/dist/relaySupervisor.test.js.map +1 -1
  251. package/dist/relaySupervisorCleanup.test.d.ts +2 -0
  252. package/dist/relaySupervisorCleanup.test.d.ts.map +1 -0
  253. package/dist/relaySupervisorCleanup.test.js +69 -0
  254. package/dist/relaySupervisorCleanup.test.js.map +1 -0
  255. package/dist/repositoryInstallerFiles.d.ts +19 -1
  256. package/dist/repositoryInstallerFiles.d.ts.map +1 -1
  257. package/dist/repositoryInstallerFiles.js +205 -29
  258. package/dist/repositoryInstallerFiles.js.map +1 -1
  259. package/dist/repositoryInstallerScope.test.d.ts +2 -0
  260. package/dist/repositoryInstallerScope.test.d.ts.map +1 -0
  261. package/dist/repositoryInstallerScope.test.js +513 -0
  262. package/dist/repositoryInstallerScope.test.js.map +1 -0
  263. package/dist/repositoryKnowledge.d.ts.map +1 -1
  264. package/dist/repositoryKnowledge.js +177 -37
  265. package/dist/repositoryKnowledge.js.map +1 -1
  266. package/dist/repositoryKnowledgeScope.test.d.ts +2 -0
  267. package/dist/repositoryKnowledgeScope.test.d.ts.map +1 -0
  268. package/dist/repositoryKnowledgeScope.test.js +316 -0
  269. package/dist/repositoryKnowledgeScope.test.js.map +1 -0
  270. package/dist/repositoryOnboardingCaller.test.js +61 -4
  271. package/dist/repositoryOnboardingCaller.test.js.map +1 -1
  272. package/dist/repositoryPackage.d.ts.map +1 -1
  273. package/dist/repositoryPackage.js +340 -59
  274. package/dist/repositoryPackage.js.map +1 -1
  275. package/dist/repositoryPackageDelivery.test.js +65 -0
  276. package/dist/repositoryPackageDelivery.test.js.map +1 -1
  277. package/dist/repositoryPackageScope.test.d.ts +2 -0
  278. package/dist/repositoryPackageScope.test.d.ts.map +1 -0
  279. package/dist/repositoryPackageScope.test.js +578 -0
  280. package/dist/repositoryPackageScope.test.js.map +1 -0
  281. package/dist/schemas/codex-setup-child-start.schema.json +12 -0
  282. package/dist/schemas/codex-setup-intent.schema.json +27 -0
  283. package/dist/schemas/local-codex-setup-session.schema.json +39 -0
  284. package/dist/schemas/setup-claim-failure.schema.json +15 -0
  285. package/dist/skillInstallationScope.test.d.ts +2 -0
  286. package/dist/skillInstallationScope.test.d.ts.map +1 -0
  287. package/dist/skillInstallationScope.test.js +64 -0
  288. package/dist/skillInstallationScope.test.js.map +1 -0
  289. package/dist/skillPreparationCache.test.js +12 -4
  290. package/dist/skillPreparationCache.test.js.map +1 -1
  291. package/dist/skillPreparationCaller.test.js +31 -1
  292. package/dist/skillPreparationCaller.test.js.map +1 -1
  293. package/dist/startupCommitScope.test.d.ts +2 -0
  294. package/dist/startupCommitScope.test.d.ts.map +1 -0
  295. package/dist/startupCommitScope.test.js +172 -0
  296. package/dist/startupCommitScope.test.js.map +1 -0
  297. package/dist/workspaceRegistry.d.ts.map +1 -1
  298. package/dist/workspaceRegistry.js +7 -3
  299. package/dist/workspaceRegistry.js.map +1 -1
  300. package/dist/workspaceRegistry.test.js +51 -0
  301. package/dist/workspaceRegistry.test.js.map +1 -1
  302. package/dist/workspaceRegistryRecovery.d.ts.map +1 -1
  303. package/dist/workspaceRegistryRecovery.js +171 -64
  304. package/dist/workspaceRegistryRecovery.js.map +1 -1
  305. package/dist/workspaceRegistryRecoveryScope.test.d.ts +2 -0
  306. package/dist/workspaceRegistryRecoveryScope.test.d.ts.map +1 -0
  307. package/dist/workspaceRegistryRecoveryScope.test.js +416 -0
  308. package/dist/workspaceRegistryRecoveryScope.test.js.map +1 -0
  309. package/package.json +7 -7
@@ -1,8 +1,10 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
2
  import { constants } from 'node:fs';
3
- import { link, lstat, mkdir, open, opendir, realpath, rename, rmdir, unlink } from 'node:fs/promises';
3
+ import { link as nativeLink, lstat as nativeLstat, mkdir as nativeMkdir, open, opendir, realpath as nativeRealpath, rename as nativeRename, rmdir as nativeRmdir, unlink as nativeUnlink } from 'node:fs/promises';
4
4
  import { isAbsolute, posix, relative, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
+ import { types } from 'node:util';
7
+ import { currentBootstrapHostScope } from './bootstrapHostScope.js';
6
8
  import { canonicalize, validateContract } from '@dharma-ai-labs/agent-fabric-contracts';
7
9
  import { redactValue } from '@dharma-ai-labs/agent-fabric-evidence-reduction';
8
10
  import { readRepositoryKnowledgeSource, validateRepositoryKnowledgeCatalog, validateRepositoryKnowledgeRetention, REPOSITORY_KNOWLEDGE_CATALOG_PATH, REPOSITORY_KNOWLEDGE_RELEASE_MANIFEST_PATH } from './repositoryKnowledge.js';
@@ -14,7 +16,130 @@ const DEFAULT_LIMITS = { maximumEntries: 4096, maximumFiles: 512, maximumFileByt
14
16
  maximumScannedSourceEntries: 65_536 };
15
17
  function digest(value) { return `sha256:${createHash('sha256').update(value).digest('hex')}`; }
16
18
  function compare(a, b) { return a < b ? -1 : a > b ? 1 : 0; }
17
- function missing(error) { return ['ENOENT', 'ENOTDIR'].includes(String(error.code)); }
19
+ function errorCode(error) {
20
+ if (!error || typeof error !== 'object' || types.isProxy(error))
21
+ return undefined;
22
+ const field = Object.getOwnPropertyDescriptor(error, 'code');
23
+ return field && Object.hasOwn(field, 'value') && typeof field.value === 'string' ? field.value : undefined;
24
+ }
25
+ function missing(error) { return ['ENOENT', 'ENOTDIR'].includes(errorCode(error) || ''); }
26
+ async function packageEffect(scope, operation) {
27
+ if (!scope)
28
+ return operation();
29
+ try {
30
+ return await scope.step(operation);
31
+ }
32
+ catch (error) {
33
+ if (error && typeof error === 'object' && cleanupFailures.has(error))
34
+ throw error;
35
+ await scope.assert();
36
+ const code = errorCode(error);
37
+ throw Object.assign(new Error('repository_package_storage_unavailable'), ['ENOENT', 'ENOTDIR', 'EACCES', 'EPERM', 'EEXIST', 'ENOTEMPTY', 'ESRCH'].includes(code || '') ? { code } : {});
38
+ }
39
+ }
40
+ function admitted(operation) {
41
+ return ((...args) => packageEffect(currentBootstrapHostScope(), () => operation(...args)));
42
+ }
43
+ const lstat = admitted(nativeLstat), realpath = admitted(nativeRealpath), mkdir = admitted(nativeMkdir), link = admitted(nativeLink), rename = admitted(nativeRename), rmdir = admitted(nativeRmdir), unlink = admitted(nativeUnlink);
44
+ const cleanupFailures = new WeakSet();
45
+ function cleanupFailure() {
46
+ const failure = new Error('repository_package_cleanup_unconfirmed');
47
+ cleanupFailures.add(failure);
48
+ return failure;
49
+ }
50
+ async function closeReadHandle(handle, scope) {
51
+ try {
52
+ await handle?.close();
53
+ }
54
+ catch (error) {
55
+ if (!scope)
56
+ throw error;
57
+ throw cleanupFailure();
58
+ }
59
+ }
60
+ async function* directoryEntries(source) {
61
+ const scope = currentBootstrapHostScope();
62
+ let directory;
63
+ try {
64
+ await packageEffect(scope, async () => { directory = await opendir(source); });
65
+ for (;;) {
66
+ const entry = await packageEffect(scope, () => directory.read());
67
+ if (!entry)
68
+ break;
69
+ yield entry;
70
+ }
71
+ }
72
+ finally {
73
+ await closeReadHandle(directory, scope);
74
+ }
75
+ }
76
+ function snapshotPackageInput(input, scope, keys) {
77
+ if (!scope)
78
+ return input;
79
+ let nodes = 0, bytes = 0;
80
+ const invalid = () => new Error('repository_package_input_invalid');
81
+ const clone = (value, depth) => {
82
+ if (++nodes > 65_536 || depth > 16)
83
+ throw invalid();
84
+ if (value === null || value === undefined || typeof value === 'boolean')
85
+ return value;
86
+ if (typeof value === 'number') {
87
+ if (!Number.isFinite(value))
88
+ throw invalid();
89
+ return value;
90
+ }
91
+ if (typeof value === 'string') {
92
+ bytes += Buffer.byteLength(value);
93
+ if (bytes > 16_777_216)
94
+ throw invalid();
95
+ return value;
96
+ }
97
+ if (typeof value !== 'object' || types.isProxy(value))
98
+ throw invalid();
99
+ if (types.isDate(value)) {
100
+ const time = Date.prototype.getTime.call(value);
101
+ if (!Number.isFinite(time))
102
+ throw invalid();
103
+ return new Date(time);
104
+ }
105
+ if (Buffer.isBuffer(value)) {
106
+ const length = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), 'byteLength').get.call(value);
107
+ bytes += length;
108
+ if (bytes > 16_777_216)
109
+ throw invalid();
110
+ const copy = Buffer.alloc(length);
111
+ Uint8Array.prototype.set.call(copy, value);
112
+ return copy;
113
+ }
114
+ const array = Array.isArray(value), prototype = Object.getPrototypeOf(value);
115
+ if (!array && prototype !== null && (types.isProxy(prototype) || Object.getPrototypeOf(prototype) !== null))
116
+ throw invalid();
117
+ const fields = Object.getOwnPropertyDescriptors(value), result = array ? [] : Object.create(null);
118
+ if (Object.getOwnPropertySymbols(value).length || array && (fields.length.value > 8192
119
+ || Object.keys(fields).length !== fields.length.value + 1))
120
+ throw invalid();
121
+ for (const [key, field] of Object.entries(fields)) {
122
+ if (array && key === 'length')
123
+ continue;
124
+ if (!Object.hasOwn(field, 'value') || array && !/^(?:0|[1-9][0-9]*)$/.test(key))
125
+ throw invalid();
126
+ Object.defineProperty(result, key, { value: clone(field.value, depth + 1), enumerable: true });
127
+ }
128
+ return Object.freeze(result);
129
+ };
130
+ if (!input || typeof input !== 'object' || types.isProxy(input))
131
+ throw invalid();
132
+ const result = {};
133
+ for (const key of keys) {
134
+ const field = Object.getOwnPropertyDescriptor(input, key);
135
+ if (!field)
136
+ continue;
137
+ if (!Object.hasOwn(field, 'value'))
138
+ throw invalid();
139
+ result[key] = clone(field.value, 0);
140
+ }
141
+ return Object.freeze(result);
142
+ }
18
143
  function pathKey(value) {
19
144
  if (!value || value.includes('\\') || isAbsolute(value) || /^[A-Za-z]:/.test(value))
20
145
  throw new Error('Invalid repository package path.');
@@ -96,12 +221,14 @@ async function checkedPath(workspace, path) {
96
221
  return current;
97
222
  }
98
223
  async function readStable(workspace, path, maximumBytes) {
224
+ const scope = currentBootstrapHostScope();
99
225
  const source = await checkedPath(workspace, path);
100
226
  if (!(await lstat(source)).isFile())
101
227
  throw new Error('Repository package requires regular files.');
102
- const handle = await open(source, constants.O_RDONLY | (constants.O_NOFOLLOW || 0) | (constants.O_NONBLOCK || 0));
228
+ let handle;
103
229
  try {
104
- const before = await handle.stat();
230
+ await packageEffect(scope, async () => { handle = await open(source, constants.O_RDONLY | (constants.O_NOFOLLOW || 0) | (constants.O_NONBLOCK || 0)); });
231
+ const before = await packageEffect(scope, () => handle.stat());
105
232
  if (!before.isFile())
106
233
  throw new Error('Repository package requires regular files.');
107
234
  if (before.size > maximumBytes)
@@ -109,12 +236,12 @@ async function readStable(workspace, path, maximumBytes) {
109
236
  const buffer = Buffer.alloc(before.size + 1);
110
237
  let size = 0;
111
238
  while (size < buffer.length) {
112
- const result = await handle.read(buffer, size, buffer.length - size, size);
239
+ const result = await packageEffect(scope, () => handle.read(buffer, size, buffer.length - size, size));
113
240
  if (!result.bytesRead)
114
241
  break;
115
242
  size += result.bytesRead;
116
243
  }
117
- const after = await handle.stat();
244
+ const after = await packageEffect(scope, () => handle.stat());
118
245
  const current = await lstat(await checkedPath(workspace, path));
119
246
  if (size !== before.size || before.size !== after.size || before.mtimeMs !== after.mtimeMs
120
247
  || before.ctimeMs !== after.ctimeMs || current.ino !== before.ino || current.dev !== before.dev
@@ -123,7 +250,7 @@ async function readStable(workspace, path, maximumBytes) {
123
250
  return buffer.subarray(0, size);
124
251
  }
125
252
  finally {
126
- await handle.close();
253
+ await closeReadHandle(handle, scope);
127
254
  }
128
255
  }
129
256
  function dependencies(text) {
@@ -140,6 +267,11 @@ function sourceContentType(path) {
140
267
  || /^(?:README|LICENSE|Makefile|Dockerfile)$/i.test(posix.basename(path));
141
268
  }
142
269
  export async function inventoryRepositoryPackage(input) {
270
+ const scope = currentBootstrapHostScope();
271
+ input = snapshotPackageInput(input, scope, ['workspace', 'organizationId', 'workspaceId', 'repositoryAgentId', 'repositoryBindingId',
272
+ 'sourceAuthorization', 'approvedOutputs', 'observations', 'limits', 'now', 'retainedKnowledge']);
273
+ if (scope)
274
+ await scope.assert();
143
275
  if (![input.organizationId, input.workspaceId].every(value => /^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/.test(value))) {
144
276
  throw new Error('Repository package requires bounded organization and workspace identities.');
145
277
  }
@@ -201,8 +333,7 @@ export async function inventoryRepositoryPackage(input) {
201
333
  const metadata = await lstat(source);
202
334
  if (metadata.isDirectory()) {
203
335
  const names = [];
204
- const directory = await opendir(source);
205
- for await (const entry of directory) {
336
+ for await (const entry of directoryEntries(source)) {
206
337
  const child = `${path}/${entry.name}`;
207
338
  if (generated(child))
208
339
  continue;
@@ -226,6 +357,8 @@ export async function inventoryRepositoryPackage(input) {
226
357
  exclude(path, 'not_regular_file');
227
358
  }
228
359
  catch (error) {
360
+ if (scope)
361
+ await scope.assert();
229
362
  if (missing(error))
230
363
  return;
231
364
  if (String(error).includes('symlink')) {
@@ -234,7 +367,7 @@ export async function inventoryRepositoryPackage(input) {
234
367
  }
235
368
  if (String(error).includes('limit'))
236
369
  throw error;
237
- if (['EACCES', 'EPERM'].includes(String(error.code))) {
370
+ if (['EACCES', 'EPERM'].includes(errorCode(error) || '')) {
238
371
  exclude(path, 'unavailable');
239
372
  return;
240
373
  }
@@ -266,6 +399,8 @@ export async function inventoryRepositoryPackage(input) {
266
399
  source = path === '.' ? workspace : await checkedPath(workspace, path);
267
400
  }
268
401
  catch (error) {
402
+ if (scope)
403
+ await scope.assert();
269
404
  if (missing(error)) {
270
405
  witnesses.set(path, 'missing');
271
406
  return false;
@@ -280,8 +415,7 @@ export async function inventoryRepositoryPackage(input) {
280
415
  const metadata = await lstat(source);
281
416
  if (metadata.isDirectory()) {
282
417
  const names = [];
283
- const directory = await opendir(source);
284
- for await (const entry of directory) {
418
+ for await (const entry of directoryEntries(source)) {
285
419
  if (++scanned > limits.maximumScannedSourceEntries)
286
420
  throw new Error('Repository source scan limit exceeded.');
287
421
  const child = path === '.' ? entry.name : `${path}/${entry.name}`;
@@ -385,6 +519,10 @@ export async function inventoryRepositoryPackage(input) {
385
519
  content = await readStable(workspace, path, limits.maximumFileBytes);
386
520
  }
387
521
  catch (error) {
522
+ if (error && typeof error === 'object' && cleanupFailures.has(error))
523
+ throw error;
524
+ if (scope)
525
+ await scope.assert();
388
526
  if (String(error).includes('limit') || String(error).includes('changed during'))
389
527
  throw error;
390
528
  exclude(path, missing(error) ? 'missing_dependency' : String(error).includes('symlink') ? 'symlink' : 'unavailable');
@@ -531,6 +669,8 @@ export async function inventoryRepositoryPackage(input) {
531
669
  ...(knowledge ? { knowledge } : {}), ...(authorization ? { sourceAuthorization: authorization } : {}) };
532
670
  const fingerprinted = authorization ? { ...base, sourceFingerprint: sourceFingerprint(base) } : base;
533
671
  const snapshotHash = digest(canonicalize(fingerprinted));
672
+ if (scope)
673
+ await scope.assert();
534
674
  return { schema: 'dharma.repository-package-snapshot/v1', capturedAt: (input.now || new Date()).toISOString(),
535
675
  manifest: { ...fingerprinted, snapshotHash, snapshotId: `repository-package-${snapshotHash.slice(7)}` },
536
676
  blobs: [...blobs].sort(([a], [b]) => compare(a, b)).map(([sha256, contentBase64]) => ({ sha256, contentBase64 })) };
@@ -701,8 +841,11 @@ export function serializeRepositoryPackageSnapshot(snapshot) {
701
841
  return `${canonicalize({ manifest: snapshot.manifest, blobs: snapshot.blobs })}\n`;
702
842
  }
703
843
  async function persistSnapshot(input, workspace) {
844
+ const scope = currentBootstrapHostScope();
845
+ if (scope)
846
+ await scope.assert();
704
847
  const serialized = serializeRepositoryPackageSnapshot(input.snapshot);
705
- const validated = await validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${input.snapshot.manifest.schema.split('/')[1]}`, input.snapshot.manifest);
848
+ const validated = await packageEffect(scope, () => validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${input.snapshot.manifest.schema.split('/')[1]}`, input.snapshot.manifest));
706
849
  if (!validated.ok)
707
850
  throw new Error('Repository package manifest schema is invalid.');
708
851
  const activeManifestPath = `${GENERATED_ROOT}/MANIFEST.json`;
@@ -723,6 +866,8 @@ async function persistSnapshot(input, workspace) {
723
866
  marker = JSON.parse((await readStable(workspace, `${GENERATED_ROOT}/.dharma-agent-fabric.json`, 4096)).toString('utf8'));
724
867
  }
725
868
  catch (error) {
869
+ if (scope)
870
+ await scope.assert();
726
871
  if (String(error).includes('symlink'))
727
872
  throw error;
728
873
  throw new Error('Refusing to write into an unmanaged repository skill.');
@@ -748,7 +893,7 @@ async function persistSnapshot(input, workspace) {
748
893
  await mkdir(resolve(workspace, current), { mode: 0o700 });
749
894
  }
750
895
  catch (error) {
751
- if (error.code !== 'EEXIST')
896
+ if (errorCode(error) !== 'EEXIST')
752
897
  throw error;
753
898
  }
754
899
  await checkedPath(workspace, current);
@@ -756,21 +901,16 @@ async function persistSnapshot(input, workspace) {
756
901
  await checkedPath(workspace, posix.dirname(snapshotPath));
757
902
  for (const [path, content] of [[snapshotPath, serialized], [candidateManifestPath, `${canonicalize(input.snapshot.manifest)}\n`]]) {
758
903
  const stagedSnapshot = `${snapshotRoot}/.snapshot-${randomUUID()}.tmp`;
904
+ let identity;
759
905
  try {
760
- const handle = await open(resolve(workspace, stagedSnapshot), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW || 0), 0o600);
761
- try {
762
- await handle.writeFile(content);
763
- await handle.sync();
764
- }
765
- finally {
766
- await handle.close();
767
- }
906
+ identity = await freshFile(workspace, stagedSnapshot, content);
768
907
  await checkedPath(workspace, snapshotRoot);
908
+ await verifyStaging(workspace, stagedSnapshot, identity);
769
909
  try {
770
910
  await link(resolve(workspace, stagedSnapshot), resolve(workspace, path));
771
911
  }
772
912
  catch (error) {
773
- if (error.code !== 'EEXIST')
913
+ if (errorCode(error) !== 'EEXIST')
774
914
  throw error;
775
915
  const existing = await readStable(workspace, path, 8_388_608);
776
916
  if (existing.toString('utf8') !== content)
@@ -778,8 +918,8 @@ async function persistSnapshot(input, workspace) {
778
918
  }
779
919
  }
780
920
  finally {
781
- await unlink(resolve(workspace, stagedSnapshot)).catch(error => { if (!missing(error))
782
- throw error; });
921
+ if (identity)
922
+ await cleanupStaging(workspace, stagedSnapshot, identity);
783
923
  }
784
924
  }
785
925
  await syncDirectory(workspace, posix.dirname(snapshotPath));
@@ -790,6 +930,8 @@ async function persistSnapshot(input, workspace) {
790
930
  await prepareCopies(workspace, input.snapshot, input.onCopyCheckpoint);
791
931
  });
792
932
  }
933
+ if (scope)
934
+ await scope.assert();
793
935
  return { manifestPath, snapshotPath, snapshotHash: input.snapshot.manifest.snapshotHash,
794
936
  managedCopiesPath: candidateOnly ? null : `${GENERATED_ROOT}/skills/source`,
795
937
  disposition: candidateOnly ? 'candidate_only' : 'local_bootstrap_inventory',
@@ -797,9 +939,20 @@ async function persistSnapshot(input, workspace) {
797
939
  }
798
940
  const writers = new Map();
799
941
  export async function writeRepositoryPackageSnapshot(input) {
942
+ const scope = currentBootstrapHostScope();
943
+ if (scope) {
944
+ const copied = snapshotPackageInput(input, scope, ['workspace', 'snapshot', 'candidateOnly']);
945
+ const field = Object.getOwnPropertyDescriptor(input, 'onCopyCheckpoint');
946
+ if (field && (!Object.hasOwn(field, 'value') || field.value !== undefined
947
+ && (typeof field.value !== 'function' || types.isProxy(field.value))))
948
+ throw new Error('repository_package_input_invalid');
949
+ input = Object.freeze({ ...copied, onCopyCheckpoint: field?.value });
950
+ await scope.assert();
951
+ }
800
952
  const workspace = await realpath(input.workspace);
801
953
  const previous = writers.get(workspace) || Promise.resolve();
802
- const operation = previous.catch(() => { }).then(() => persistSnapshot(input, workspace));
954
+ const operation = previous.catch(() => { }).then(async () => { if (scope)
955
+ await scope.assert(); return persistSnapshot(input, workspace); });
803
956
  writers.set(workspace, operation);
804
957
  try {
805
958
  return await operation;
@@ -854,7 +1007,7 @@ async function directories(workspace, path) {
854
1007
  await mkdir(resolve(workspace, current), { mode: 0o700 });
855
1008
  }
856
1009
  catch (error) {
857
- if (error.code !== 'EEXIST')
1010
+ if (errorCode(error) !== 'EEXIST')
858
1011
  throw error;
859
1012
  }
860
1013
  if (!(await lstat(await checkedPath(workspace, current))).isDirectory())
@@ -862,40 +1015,92 @@ async function directories(workspace, path) {
862
1015
  }
863
1016
  }
864
1017
  async function syncDirectory(workspace, path) {
1018
+ const scope = currentBootstrapHostScope();
1019
+ if (scope)
1020
+ await scope.assert();
865
1021
  if (process.platform === 'win32')
866
1022
  return; // Windows does not support opening directories for fsync.
867
- const handle = await open(await checkedPath(workspace, path), constants.O_RDONLY | (constants.O_NOFOLLOW || 0));
1023
+ const source = await checkedPath(workspace, path);
1024
+ let handle;
868
1025
  try {
869
- await handle.sync();
1026
+ await packageEffect(scope, async () => { handle = await open(source, constants.O_RDONLY | (constants.O_NOFOLLOW || 0)); });
1027
+ await packageEffect(scope, () => handle.sync());
870
1028
  }
871
1029
  finally {
872
- await handle.close();
1030
+ await closeReadHandle(handle, scope);
873
1031
  }
874
1032
  }
875
1033
  async function freshFile(workspace, path, bytes) {
1034
+ const scope = currentBootstrapHostScope();
1035
+ const content = Buffer.from(bytes);
876
1036
  await directories(workspace, posix.dirname(path));
877
- const handle = await open(resolve(workspace, path), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW || 0), 0o600);
1037
+ const parent = await lstat(await checkedPath(workspace, posix.dirname(path)), { bigint: true });
1038
+ let handle, identity;
878
1039
  try {
879
- await handle.writeFile(bytes);
880
- await handle.sync();
1040
+ await packageEffect(scope, async () => {
1041
+ handle = await open(resolve(workspace, path), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW || 0), 0o600);
1042
+ });
1043
+ const metadata = await packageEffect(scope, () => handle.stat({ bigint: true }));
1044
+ if (!metadata.isFile() || metadata.ino === 0n || metadata.nlink !== 1n)
1045
+ throw cleanupFailure();
1046
+ identity = { dev: metadata.dev, ino: metadata.ino, parentDev: parent.dev, parentIno: parent.ino,
1047
+ sizeBytes: content.length, sha256: digest(content) };
1048
+ await packageEffect(scope, () => handle.writeFile(content));
1049
+ await packageEffect(scope, () => handle.sync());
881
1050
  }
882
1051
  finally {
883
- await handle.close();
1052
+ await closeReadHandle(handle, scope);
884
1053
  }
885
1054
  await syncDirectory(workspace, posix.dirname(path));
1055
+ return identity;
1056
+ }
1057
+ async function verifyStaging(workspace, path, identity) {
1058
+ const parent = await lstat(await checkedPath(workspace, posix.dirname(path)), { bigint: true });
1059
+ const metadata = await lstat(await checkedPath(workspace, path), { bigint: true });
1060
+ if (!parent.isDirectory() || parent.dev !== identity.parentDev || parent.ino !== identity.parentIno
1061
+ || !ownedLockMetadata(metadata, identity) || metadata.size !== BigInt(identity.sizeBytes))
1062
+ throw cleanupFailure();
1063
+ const bytes = await readStable(workspace, path, identity.sizeBytes);
1064
+ const current = await lstat(await checkedPath(workspace, path), { bigint: true });
1065
+ if (!ownedLockMetadata(current, identity) || bytes.length !== identity.sizeBytes || digest(bytes) !== identity.sha256)
1066
+ throw cleanupFailure();
1067
+ }
1068
+ async function cleanupStaging(workspace, path, identity) {
1069
+ const scope = currentBootstrapHostScope();
1070
+ if (scope && !await scope.current())
1071
+ return; // Interrupted generated evidence is retained.
1072
+ try {
1073
+ const parent = await lstat(await checkedPath(workspace, posix.dirname(path)), { bigint: true });
1074
+ if (parent.dev !== identity.parentDev || parent.ino !== identity.parentIno)
1075
+ throw cleanupFailure();
1076
+ const metadata = await lstat(resolve(workspace, path), { bigint: true }).catch(error => { if (missing(error))
1077
+ return null; throw error; });
1078
+ if (!metadata)
1079
+ return;
1080
+ if (metadata.isSymbolicLink() || !metadata.isFile() || metadata.dev !== identity.dev || metadata.ino !== identity.ino
1081
+ || metadata.nlink < 1n || metadata.nlink > 2n)
1082
+ throw cleanupFailure();
1083
+ await verifyStaging(workspace, path, identity);
1084
+ await unlink(resolve(workspace, path));
1085
+ }
1086
+ catch (error) {
1087
+ if (scope)
1088
+ await scope.assert();
1089
+ throw cleanupFailure();
1090
+ }
886
1091
  }
887
1092
  async function atomicMetadata(workspace, path, value) {
888
1093
  const temporary = `${GENERATED_ROOT}/.metadata-${randomUUID()}.tmp`;
889
- await freshFile(workspace, temporary, `${canonicalize(value)}\n`);
1094
+ const identity = await freshFile(workspace, temporary, `${canonicalize(value)}\n`);
890
1095
  try {
891
1096
  await checkedPath(workspace, GENERATED_ROOT);
892
1097
  await optionalBytes(workspace, path);
1098
+ await verifyStaging(workspace, temporary, identity);
893
1099
  await rename(resolve(workspace, temporary), resolve(workspace, path));
894
1100
  await syncDirectory(workspace, GENERATED_ROOT);
895
1101
  }
896
1102
  finally {
897
- await unlink(resolve(workspace, temporary)).catch(error => { if (!missing(error))
898
- throw error; });
1103
+ await cleanupStaging(workspace, temporary, identity);
899
1104
  }
900
1105
  }
901
1106
  async function snapshotFor(workspace, hash) {
@@ -907,7 +1112,7 @@ async function snapshotFor(workspace, hash) {
907
1112
  if (snapshot.manifest.snapshotHash !== hash || serializeRepositoryPackageSnapshot(snapshot) !== bytes.toString('utf8')) {
908
1113
  throw new Error('Managed copies snapshot integrity failed.');
909
1114
  }
910
- const validated = await validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${snapshot.manifest.schema.split('/')[1]}`, snapshot.manifest);
1115
+ const validated = await packageEffect(currentBootstrapHostScope(), () => validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${snapshot.manifest.schema.split('/')[1]}`, snapshot.manifest));
911
1116
  if (!validated.ok)
912
1117
  throw new Error('Managed copies snapshot schema integrity failed.');
913
1118
  return snapshot;
@@ -1014,7 +1219,7 @@ async function prepareCopies(workspace, snapshot, checkpoint) {
1014
1219
  await freshFile(workspace, staged, blobs.get(operation.after.sha256));
1015
1220
  }
1016
1221
  await atomicMetadata(workspace, JOURNAL_PATH, journal);
1017
- await checkpoint?.('journal_prepared');
1222
+ await copyCheckpoint(checkpoint, 'journal_prepared');
1018
1223
  await applyJournal(workspace, journal, snapshot, checkpoint);
1019
1224
  }
1020
1225
  async function recoverCopies(workspace) {
@@ -1090,7 +1295,7 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
1090
1295
  await rename(resolve(workspace, target), resolve(workspace, backup));
1091
1296
  await syncDirectory(workspace, posix.dirname(target));
1092
1297
  await syncDirectory(workspace, posix.dirname(backup));
1093
- await checkpoint?.('file_backed_up');
1298
+ await copyCheckpoint(checkpoint, 'file_backed_up');
1094
1299
  }
1095
1300
  else if (operation.before && !saved && !installed)
1096
1301
  throw new Error('Managed copies missing backup conflict.');
@@ -1102,21 +1307,21 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
1102
1307
  await link(resolve(workspace, staged), resolve(workspace, target));
1103
1308
  }
1104
1309
  catch (error) {
1105
- if (error.code === 'EEXIST')
1310
+ if (errorCode(error) === 'EEXIST')
1106
1311
  throw new Error('Managed copies unmanaged destination conflict.');
1107
1312
  throw error;
1108
1313
  }
1109
1314
  await syncDirectory(workspace, posix.dirname(target));
1110
- await checkpoint?.('file_installed');
1315
+ await copyCheckpoint(checkpoint, 'file_installed');
1111
1316
  }
1112
1317
  }
1113
1318
  await verifyCopies(workspace, journal);
1114
1319
  await verifyMetadata(workspace, journal, desiredManifest);
1115
1320
  await atomicMetadata(workspace, INDEX_PATH, journal.desired);
1116
- await checkpoint?.('copies_index_written');
1321
+ await copyCheckpoint(checkpoint, 'copies_index_written');
1117
1322
  await verifyMetadata(workspace, journal, desiredManifest);
1118
1323
  await atomicMetadata(workspace, `${GENERATED_ROOT}/MANIFEST.json`, snapshot.manifest);
1119
- await checkpoint?.('manifest_written');
1324
+ await copyCheckpoint(checkpoint, 'manifest_written');
1120
1325
  }
1121
1326
  await verifyCopies(workspace, journal);
1122
1327
  for (const [number, operation] of operations.entries()) {
@@ -1125,7 +1330,7 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
1125
1330
  if (await matches(workspace, path, file)) {
1126
1331
  await unlink(resolve(workspace, path));
1127
1332
  await syncDirectory(workspace, posix.dirname(path));
1128
- await checkpoint?.('cleanup_file_removed');
1333
+ await copyCheckpoint(checkpoint, 'cleanup_file_removed');
1129
1334
  }
1130
1335
  }
1131
1336
  }
@@ -1148,7 +1353,7 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
1148
1353
  await rmdir(await checkedPath(workspace, folder));
1149
1354
  }
1150
1355
  catch (error) {
1151
- if (!missing(error) && !['ENOTEMPTY', 'EEXIST'].includes(String(error.code)))
1356
+ if (!missing(error) && !['ENOTEMPTY', 'EEXIST'].includes(errorCode(error) || ''))
1152
1357
  throw error;
1153
1358
  }
1154
1359
  }
@@ -1158,21 +1363,88 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
1158
1363
  await unlink(resolve(workspace, JOURNAL_PATH));
1159
1364
  await syncDirectory(workspace, GENERATED_ROOT);
1160
1365
  }
1366
+ async function copyCheckpoint(checkpoint, point) {
1367
+ await packageEffect(currentBootstrapHostScope(), async () => { await checkpoint?.(point); });
1368
+ }
1369
+ function ownedLockMetadata(metadata, identity) {
1370
+ return metadata.isFile() && !metadata.isSymbolicLink() && metadata.dev === identity.dev && metadata.ino === identity.ino
1371
+ && metadata.nlink >= 1n && metadata.nlink <= 2n;
1372
+ }
1373
+ // Cooperative release can inspect/delete only this acquired inode and nonce,
1374
+ // even after ordinary setup admission closes. It never resumes journal work.
1375
+ async function releaseLockFile(workspace, path, identity, text) {
1376
+ const scope = currentBootstrapHostScope(), parentPath = resolve(workspace, posix.dirname(path)), source = resolve(workspace, path);
1377
+ try {
1378
+ const parent = await nativeLstat(parentPath, { bigint: true });
1379
+ if (!parent.isDirectory() || parent.isSymbolicLink() || parent.dev !== identity.parentDev || parent.ino !== identity.parentIno)
1380
+ throw cleanupFailure();
1381
+ const metadata = await nativeLstat(source, { bigint: true }).catch(error => { if (missing(error))
1382
+ return null; throw error; });
1383
+ if (!metadata)
1384
+ return;
1385
+ if (!ownedLockMetadata(metadata, identity))
1386
+ throw cleanupFailure();
1387
+ const expected = Buffer.from(text), bytes = Buffer.alloc(expected.length + 1);
1388
+ let handle, before;
1389
+ try {
1390
+ handle = await open(source, constants.O_RDONLY | (constants.O_NOFOLLOW || 0) | (constants.O_NONBLOCK || 0));
1391
+ before = await handle.stat({ bigint: true });
1392
+ if (!ownedLockMetadata(before, identity) || before.size !== BigInt(expected.length))
1393
+ throw cleanupFailure();
1394
+ let size = 0;
1395
+ while (size < bytes.length) {
1396
+ const result = await handle.read(bytes, size, bytes.length - size, size);
1397
+ if (!result.bytesRead)
1398
+ break;
1399
+ size += result.bytesRead;
1400
+ }
1401
+ const after = await handle.stat({ bigint: true });
1402
+ if (!ownedLockMetadata(after, identity) || size !== expected.length || !bytes.subarray(0, size).equals(expected)
1403
+ || before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs)
1404
+ throw cleanupFailure();
1405
+ }
1406
+ finally {
1407
+ await closeReadHandle(handle, scope);
1408
+ }
1409
+ const current = await nativeLstat(source, { bigint: true });
1410
+ if (!ownedLockMetadata(current, identity) || before.size !== current.size
1411
+ || before.mtimeNs !== current.mtimeNs || before.ctimeNs !== current.ctimeNs)
1412
+ throw cleanupFailure();
1413
+ await nativeUnlink(source);
1414
+ if (process.platform !== 'win32') {
1415
+ let directory;
1416
+ try {
1417
+ directory = await open(parentPath, constants.O_RDONLY | (constants.O_NOFOLLOW || 0));
1418
+ const currentParent = await directory.stat({ bigint: true });
1419
+ if (!currentParent.isDirectory() || currentParent.dev !== identity.parentDev || currentParent.ino !== identity.parentIno)
1420
+ throw cleanupFailure();
1421
+ await directory.sync();
1422
+ }
1423
+ finally {
1424
+ await closeReadHandle(directory, scope);
1425
+ }
1426
+ }
1427
+ }
1428
+ catch {
1429
+ throw cleanupFailure();
1430
+ }
1431
+ }
1161
1432
  async function withCopyLock(workspace, action) {
1433
+ const scope = currentBootstrapHostScope();
1162
1434
  const lock = { schema: 'dharma.repository-package-copy-lock/v1', platform: process.platform, pid: process.pid, nonce: randomUUID() };
1163
1435
  const text = `${canonicalize(lock)}\n`;
1164
1436
  const temporary = `${GENERATED_ROOT}/.lock-${lock.nonce}.tmp`;
1165
- await freshFile(workspace, temporary, text);
1437
+ const identity = await freshFile(workspace, temporary, text);
1166
1438
  let acquired = false;
1167
1439
  try {
1440
+ await verifyStaging(workspace, temporary, identity);
1168
1441
  for (let attempt = 0; attempt < 2; attempt++) {
1169
1442
  try {
1170
- await link(resolve(workspace, temporary), resolve(workspace, LOCK_PATH));
1171
- acquired = true;
1443
+ await packageEffect(scope, async () => { await nativeLink(resolve(workspace, temporary), resolve(workspace, LOCK_PATH)); acquired = true; });
1172
1444
  break;
1173
1445
  }
1174
1446
  catch (error) {
1175
- if (error.code !== 'EEXIST')
1447
+ if (errorCode(error) !== 'EEXIST')
1176
1448
  throw error;
1177
1449
  const bytes = await readStable(workspace, LOCK_PATH, 4096);
1178
1450
  const existing = JSON.parse(bytes.toString('utf8'));
@@ -1180,11 +1452,11 @@ async function withCopyLock(workspace, action) {
1180
1452
  throw new Error('Managed copies lock conflict.');
1181
1453
  }
1182
1454
  try {
1183
- process.kill(existing.pid, 0);
1455
+ await packageEffect(scope, async () => { process.kill(existing.pid, 0); });
1184
1456
  throw new Error('Managed copies writer is active; retry later.');
1185
1457
  }
1186
1458
  catch (error) {
1187
- if (error.code !== 'ESRCH')
1459
+ if (errorCode(error) !== 'ESRCH')
1188
1460
  throw error;
1189
1461
  }
1190
1462
  if (!(await readStable(workspace, LOCK_PATH, 4096)).equals(bytes))
@@ -1194,16 +1466,25 @@ async function withCopyLock(workspace, action) {
1194
1466
  }
1195
1467
  if (!acquired)
1196
1468
  throw new Error('Managed copies lock conflict.');
1197
- await action();
1469
+ await packageEffect(scope, action);
1198
1470
  }
1199
1471
  finally {
1200
- if (acquired) {
1201
- if ((await readStable(workspace, LOCK_PATH, 4096)).toString('utf8') !== text)
1202
- throw new Error('Managed copies lock conflict.');
1203
- await unlink(resolve(workspace, LOCK_PATH));
1204
- await syncDirectory(workspace, GENERATED_ROOT);
1472
+ let failed = false;
1473
+ if (acquired)
1474
+ try {
1475
+ await releaseLockFile(workspace, LOCK_PATH, identity, text);
1476
+ }
1477
+ catch {
1478
+ failed = true;
1479
+ }
1480
+ try {
1481
+ await releaseLockFile(workspace, temporary, identity, text);
1482
+ }
1483
+ catch {
1484
+ failed = true;
1205
1485
  }
1206
- await unlink(resolve(workspace, temporary));
1486
+ if (failed)
1487
+ throw cleanupFailure();
1207
1488
  }
1208
1489
  }
1209
1490
  //# sourceMappingURL=repositoryPackage.js.map