agentera 3.0.0-dev.23 → 3.0.0-dev.26

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 (376) hide show
  1. package/README.md +18 -22
  2. package/bundle/CHANGELOG.md +25 -27
  3. package/bundle/README.md +22 -42
  4. package/bundle/UPGRADE.md +71 -179
  5. package/bundle/references/adapters/package-manifest-interface-model.yaml +41 -240
  6. package/bundle/references/adapters/package-registry.yaml +9 -145
  7. package/bundle/references/adapters/package-surface-characterization.md +13 -38
  8. package/bundle/references/adapters/runtime-lifecycle-adapters.yaml +5 -219
  9. package/bundle/references/adapters/runtime-lifecycle-authority.yaml +2 -29
  10. package/bundle/references/adapters/runtime-lifecycle-operation-contract.yaml +1 -1
  11. package/bundle/references/artifacts/state-storage-authority.yaml +729 -412
  12. package/bundle/references/cli/agent-ready-state-contract.yaml +12 -12
  13. package/bundle/references/cli/app-lifecycle-vocabulary.yaml +3 -4
  14. package/bundle/references/cli/bundle-skill-vocabulary.yaml +9 -18
  15. package/bundle/references/cli/capability-tool-classification.yaml +2 -2
  16. package/bundle/references/cli/prime-consumer-compatibility.yaml +28 -98
  17. package/bundle/references/cli/routing-execution-vocabulary.yaml +4 -22
  18. package/bundle/references/cli/update-channels.yaml +4 -2
  19. package/bundle/references/cli/upgrade-repair-wording.md +8 -28
  20. package/bundle/references/cli/v3-handoff-manifest.schema.yaml +2 -3
  21. package/bundle/references/cli/vocabulary.md +22 -27
  22. package/bundle/references/meta/documentation-inventory.md +1 -1
  23. package/bundle/skills/agentera/SKILL.md +28 -14
  24. package/bundle/skills/agentera/capabilities/audit/schemas/artifacts.yaml +12 -12
  25. package/bundle/skills/agentera/capabilities/audit/schemas/validation.yaml +2 -2
  26. package/bundle/skills/agentera/capabilities/build/schemas/artifacts.yaml +12 -12
  27. package/bundle/skills/agentera/capabilities/build/schemas/validation.yaml +8 -8
  28. package/bundle/skills/agentera/capabilities/design/schemas/artifacts.yaml +6 -6
  29. package/bundle/skills/agentera/capabilities/discuss/schemas/artifacts.yaml +7 -7
  30. package/bundle/skills/agentera/capabilities/document/schemas/artifacts.yaml +12 -12
  31. package/bundle/skills/agentera/capabilities/optimize/schemas/artifacts.yaml +9 -9
  32. package/bundle/skills/agentera/capabilities/orchestrate/schemas/artifacts.yaml +11 -11
  33. package/bundle/skills/agentera/capabilities/plan/schemas/artifacts.yaml +9 -9
  34. package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +2 -2
  35. package/bundle/skills/agentera/capabilities/research/schemas/artifacts.yaml +3 -3
  36. package/bundle/skills/agentera/capabilities/status/schemas/artifacts.yaml +11 -11
  37. package/bundle/skills/agentera/capabilities/vision/schemas/artifacts.yaml +8 -8
  38. package/bundle/skills/agentera/schemas/artifacts/decisions.yaml +15 -2
  39. package/bundle/skills/agentera/schemas/artifacts/docs.yaml +7 -12
  40. package/bundle/skills/agentera/schemas/artifacts/experiments.yaml +21 -21
  41. package/bundle/skills/agentera/schemas/artifacts/health.yaml +15 -2
  42. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +36 -15
  43. package/bundle/skills/agentera/schemas/artifacts/progress.yaml +15 -2
  44. package/bundle/skills/agentera/schemas/artifacts/todo.yaml +4 -4
  45. package/dist/capabilities/audit/instructions.js +13 -1
  46. package/dist/capabilities/audit/instructions.js.map +1 -1
  47. package/dist/capabilities/build/instructions.js +14 -1
  48. package/dist/capabilities/build/instructions.js.map +1 -1
  49. package/dist/capabilities/discuss/instructions.js +11 -1
  50. package/dist/capabilities/discuss/instructions.js.map +1 -1
  51. package/dist/capabilities/document/instructions.js +9 -1
  52. package/dist/capabilities/document/instructions.js.map +1 -1
  53. package/dist/capabilities/index.js +7 -7
  54. package/dist/capabilities/index.js.map +1 -1
  55. package/dist/capabilities/optimize/instructions.js +10 -1
  56. package/dist/capabilities/optimize/instructions.js.map +1 -1
  57. package/dist/capabilities/orchestrate/instructions.js +13 -2
  58. package/dist/capabilities/orchestrate/instructions.js.map +1 -1
  59. package/dist/capabilities/plan/instructions.js +10 -1
  60. package/dist/capabilities/plan/instructions.js.map +1 -1
  61. package/dist/cli/capabilityContext/bespoke.js +8 -2
  62. package/dist/cli/capabilityContext/bespoke.js.map +1 -1
  63. package/dist/cli/capabilityContext/closeout.js +3 -3
  64. package/dist/cli/capabilityContext/closeout.js.map +1 -1
  65. package/dist/cli/capabilityContext/contract.js +10 -15
  66. package/dist/cli/capabilityContext/contract.js.map +1 -1
  67. package/dist/cli/capabilityContext/evidence.js +55 -43
  68. package/dist/cli/capabilityContext/evidence.js.map +1 -1
  69. package/dist/cli/capabilityContext/planState.js +5 -5
  70. package/dist/cli/capabilityContext/planState.js.map +1 -1
  71. package/dist/cli/capabilityContext/progress.js +1 -1
  72. package/dist/cli/capabilityContext/progress.js.map +1 -1
  73. package/dist/cli/capabilityContext/shared.js +8 -2
  74. package/dist/cli/capabilityContext/shared.js.map +1 -1
  75. package/dist/cli/capabilityContext/slim.js +2 -1
  76. package/dist/cli/capabilityContext/slim.js.map +1 -1
  77. package/dist/cli/capabilityContext/startup.js +19 -8
  78. package/dist/cli/capabilityContext/startup.js.map +1 -1
  79. package/dist/cli/capabilityContext/types.js +4 -4
  80. package/dist/cli/capabilityContext/types.js.map +1 -1
  81. package/dist/cli/commands/doctor.js +6 -64
  82. package/dist/cli/commands/doctor.js.map +1 -1
  83. package/dist/cli/commands/durability.js +27 -30
  84. package/dist/cli/commands/durability.js.map +1 -1
  85. package/dist/cli/commands/entityMigrate.js +128 -0
  86. package/dist/cli/commands/entityMigrate.js.map +1 -0
  87. package/dist/cli/commands/prime/briefOrientation.js +7 -22
  88. package/dist/cli/commands/prime/briefOrientation.js.map +1 -1
  89. package/dist/cli/commands/prime/bundleStatus.js +8 -2
  90. package/dist/cli/commands/prime/bundleStatus.js.map +1 -1
  91. package/dist/cli/commands/prime/collectEntityOrientation.js +161 -0
  92. package/dist/cli/commands/prime/collectEntityOrientation.js.map +1 -0
  93. package/dist/cli/commands/prime/collectOrientationState.js +32 -59
  94. package/dist/cli/commands/prime/collectOrientationState.js.map +1 -1
  95. package/dist/cli/commands/prime/orientationOutput.js +4 -12
  96. package/dist/cli/commands/prime/orientationOutput.js.map +1 -1
  97. package/dist/cli/commands/prime.js +2 -5
  98. package/dist/cli/commands/prime.js.map +1 -1
  99. package/dist/cli/commands/query.js +43 -49
  100. package/dist/cli/commands/query.js.map +1 -1
  101. package/dist/cli/commands/schema.js +26 -83
  102. package/dist/cli/commands/schema.js.map +1 -1
  103. package/dist/cli/commands/state/decisions.js +14 -47
  104. package/dist/cli/commands/state/decisions.js.map +1 -1
  105. package/dist/cli/commands/state/docs.js +58 -84
  106. package/dist/cli/commands/state/docs.js.map +1 -1
  107. package/dist/cli/commands/state/experimentRecords.js +27 -44
  108. package/dist/cli/commands/state/experimentRecords.js.map +1 -1
  109. package/dist/cli/commands/state/experiments.js +11 -64
  110. package/dist/cli/commands/state/experiments.js.map +1 -1
  111. package/dist/cli/commands/state/get.js +74 -88
  112. package/dist/cli/commands/state/get.js.map +1 -1
  113. package/dist/cli/commands/state/health.js +11 -112
  114. package/dist/cli/commands/state/health.js.map +1 -1
  115. package/dist/cli/commands/state/index.js +11 -2
  116. package/dist/cli/commands/state/index.js.map +1 -1
  117. package/dist/cli/commands/state/list.js +30 -13
  118. package/dist/cli/commands/state/list.js.map +1 -1
  119. package/dist/cli/commands/state/objective.js +21 -64
  120. package/dist/cli/commands/state/objective.js.map +1 -1
  121. package/dist/cli/commands/state/plan.js +11 -376
  122. package/dist/cli/commands/state/plan.js.map +1 -1
  123. package/dist/cli/commands/state/planTasks.js +58 -80
  124. package/dist/cli/commands/state/planTasks.js.map +1 -1
  125. package/dist/cli/commands/state/plans.js +27 -38
  126. package/dist/cli/commands/state/plans.js.map +1 -1
  127. package/dist/cli/commands/state/progress.js +11 -44
  128. package/dist/cli/commands/state/progress.js.map +1 -1
  129. package/dist/cli/commands/state/shared.js.map +1 -1
  130. package/dist/cli/commands/state/todo.js +27 -106
  131. package/dist/cli/commands/state/todo.js.map +1 -1
  132. package/dist/cli/commands/state/write.js +57 -4
  133. package/dist/cli/commands/state/write.js.map +1 -1
  134. package/dist/cli/commands/upgrade.js +99 -35
  135. package/dist/cli/commands/upgrade.js.map +1 -1
  136. package/dist/cli/commands/upgradeVerify.js +52 -14
  137. package/dist/cli/commands/upgradeVerify.js.map +1 -1
  138. package/dist/cli/commands/validate.js +66 -9
  139. package/dist/cli/commands/validate.js.map +1 -1
  140. package/dist/cli/dispatch/check.js +13 -13
  141. package/dist/cli/dispatch/check.js.map +1 -1
  142. package/dist/cli/dispatch/index.js +35 -28
  143. package/dist/cli/dispatch/index.js.map +1 -1
  144. package/dist/cli/dispatch/lifecycle.js +23 -43
  145. package/dist/cli/dispatch/lifecycle.js.map +1 -1
  146. package/dist/cli/dispatch/prime.js +15 -0
  147. package/dist/cli/dispatch/prime.js.map +1 -1
  148. package/dist/cli/dispatch/state.js +7 -1
  149. package/dist/cli/dispatch/state.js.map +1 -1
  150. package/dist/cli/help.js +90 -112
  151. package/dist/cli/help.js.map +1 -1
  152. package/dist/cli/migrationRequired.js +87 -0
  153. package/dist/cli/migrationRequired.js.map +1 -0
  154. package/dist/cli/orientation/attention.js +3 -99
  155. package/dist/cli/orientation/attention.js.map +1 -1
  156. package/dist/cli/orientation.js +10 -7
  157. package/dist/cli/orientation.js.map +1 -1
  158. package/dist/cli/planArtifacts.js +192 -42
  159. package/dist/cli/planArtifacts.js.map +1 -1
  160. package/dist/cli/prime-blob.js +5 -5
  161. package/dist/cli/prime-blob.js.map +1 -1
  162. package/dist/cli/stateQuery.js +6 -7
  163. package/dist/cli/stateQuery.js.map +1 -1
  164. package/dist/core/skillIdentity.js +24 -0
  165. package/dist/core/skillIdentity.js.map +1 -0
  166. package/dist/hooks/common.js +1 -1
  167. package/dist/hooks/common.js.map +1 -1
  168. package/dist/hooks/compaction/status.js +27 -2
  169. package/dist/hooks/compaction/status.js.map +1 -1
  170. package/dist/hooks/cursorSessionStart.js +4 -19
  171. package/dist/hooks/cursorSessionStart.js.map +1 -1
  172. package/dist/hooks/projectHookInput.js +26 -0
  173. package/dist/hooks/projectHookInput.js.map +1 -0
  174. package/dist/hooks/sessionStart.js +58 -212
  175. package/dist/hooks/sessionStart.js.map +1 -1
  176. package/dist/hooks/sessionStop.js +4 -12
  177. package/dist/hooks/sessionStop.js.map +1 -1
  178. package/dist/hooks/validateArtifact/traversal.js +1 -1
  179. package/dist/hooks/validateArtifact/traversal.js.map +1 -1
  180. package/dist/registries/artifactRegistry.js +24 -14
  181. package/dist/registries/artifactRegistry.js.map +1 -1
  182. package/dist/registries/packageRegistry.js +58 -232
  183. package/dist/registries/packageRegistry.js.map +1 -1
  184. package/dist/release/releaseMetadata.js +1 -13
  185. package/dist/release/releaseMetadata.js.map +1 -1
  186. package/dist/runtime/lifecycleAdapterContract.js +4 -4
  187. package/dist/runtime/lifecycleAdapterContract.js.map +1 -1
  188. package/dist/runtime/lifecycleAdapters.js +14 -2
  189. package/dist/runtime/lifecycleAdapters.js.map +1 -1
  190. package/dist/runtime/lifecycleAuthority.js +5 -43
  191. package/dist/runtime/lifecycleAuthority.js.map +1 -1
  192. package/dist/runtime/lifecycleOperationContract.js +3 -2
  193. package/dist/runtime/lifecycleOperationContract.js.map +1 -1
  194. package/dist/runtime/lifecyclePublication.js +21 -0
  195. package/dist/runtime/lifecyclePublication.js.map +1 -1
  196. package/dist/setup/opencode.js +20 -12
  197. package/dist/setup/opencode.js.map +1 -1
  198. package/dist/setup/sharedSkill.js +46 -0
  199. package/dist/setup/sharedSkill.js.map +1 -0
  200. package/dist/setup/smokeChecks.js +16 -43
  201. package/dist/setup/smokeChecks.js.map +1 -1
  202. package/dist/state/archivePublication.js +3 -2
  203. package/dist/state/archivePublication.js.map +1 -1
  204. package/dist/state/decisionEntities.js +282 -0
  205. package/dist/state/decisionEntities.js.map +1 -0
  206. package/dist/state/decisionOverlay.js +2 -2
  207. package/dist/state/decisionOverlay.js.map +1 -1
  208. package/dist/state/decisionRevision.js +44 -34
  209. package/dist/state/decisionRevision.js.map +1 -1
  210. package/dist/state/directRetrieval.js.map +1 -1
  211. package/dist/state/durability.js +81 -226
  212. package/dist/state/durability.js.map +1 -1
  213. package/dist/state/entityCutover.js +294 -0
  214. package/dist/state/entityCutover.js.map +1 -0
  215. package/dist/state/entityCutoverGit.js +128 -0
  216. package/dist/state/entityCutoverGit.js.map +1 -0
  217. package/dist/state/entityMigrationPreview.js +803 -0
  218. package/dist/state/entityMigrationPreview.js.map +1 -0
  219. package/dist/state/entityPublicationContext.js +612 -0
  220. package/dist/state/entityPublicationContext.js.map +1 -0
  221. package/dist/state/entityStorage.js +826 -0
  222. package/dist/state/entityStorage.js.map +1 -0
  223. package/dist/state/experimentIdentity.js +23 -11
  224. package/dist/state/experimentIdentity.js.map +1 -1
  225. package/dist/state/healthEntities.js +230 -0
  226. package/dist/state/healthEntities.js.map +1 -0
  227. package/dist/state/healthEntityValidation.js +6 -0
  228. package/dist/state/healthEntityValidation.js.map +1 -0
  229. package/dist/state/legacyEntityCutoverEvidence.js +66 -0
  230. package/dist/state/legacyEntityCutoverEvidence.js.map +1 -0
  231. package/dist/state/listRetrieval.js.map +1 -1
  232. package/dist/state/objectiveExperimentEntities.js +323 -0
  233. package/dist/state/objectiveExperimentEntities.js.map +1 -0
  234. package/dist/state/planEntities.js +362 -0
  235. package/dist/state/planEntities.js.map +1 -0
  236. package/dist/state/progressEntities.js +384 -0
  237. package/dist/state/progressEntities.js.map +1 -0
  238. package/dist/state/projectRoot.js +63 -0
  239. package/dist/state/projectRoot.js.map +1 -0
  240. package/dist/state/retrievalAuthority.js +13 -6
  241. package/dist/state/retrievalAuthority.js.map +1 -1
  242. package/dist/state/safeProjectFile.js +153 -0
  243. package/dist/state/safeProjectFile.js.map +1 -0
  244. package/dist/state/startupProjection.js +3 -2
  245. package/dist/state/startupProjection.js.map +1 -1
  246. package/dist/state/stateMode.js +83 -0
  247. package/dist/state/stateMode.js.map +1 -0
  248. package/dist/state/todoDocsEntities.js +225 -0
  249. package/dist/state/todoDocsEntities.js.map +1 -0
  250. package/dist/state/todoDocsEntityValidation.js +26 -0
  251. package/dist/state/todoDocsEntityValidation.js.map +1 -0
  252. package/dist/state/write/candidateMutation.js +5 -55
  253. package/dist/state/write/candidateMutation.js.map +1 -1
  254. package/dist/state/write/explain.js +119 -28
  255. package/dist/state/write/explain.js.map +1 -1
  256. package/dist/state/write/lock.js +794 -87
  257. package/dist/state/write/lock.js.map +1 -1
  258. package/dist/state/write/mutation.js +5 -2
  259. package/dist/state/write/mutation.js.map +1 -1
  260. package/dist/state/write/operations.js +52 -7
  261. package/dist/state/write/operations.js.map +1 -1
  262. package/dist/state/write/planEvaluation.js +107 -0
  263. package/dist/state/write/planEvaluation.js.map +1 -0
  264. package/dist/state/write/planPublication.js +11 -55
  265. package/dist/state/write/planPublication.js.map +1 -1
  266. package/dist/state/write/transaction.js +32 -455
  267. package/dist/state/write/transaction.js.map +1 -1
  268. package/dist/upgrade/appContentRefresh.js +43 -28
  269. package/dist/upgrade/appContentRefresh.js.map +1 -1
  270. package/dist/upgrade/compatibility.js +4 -4
  271. package/dist/upgrade/compatibility.js.map +1 -1
  272. package/dist/upgrade/doctor.js +5 -1
  273. package/dist/upgrade/doctor.js.map +1 -1
  274. package/dist/upgrade/installedHooksRetirement.js +46 -39
  275. package/dist/upgrade/installedHooksRetirement.js.map +1 -1
  276. package/dist/upgrade/legacyAgentCleanup.js +132 -32
  277. package/dist/upgrade/legacyAgentCleanup.js.map +1 -1
  278. package/dist/upgrade/lifecycleUpgrade.js +65 -378
  279. package/dist/upgrade/lifecycleUpgrade.js.map +1 -1
  280. package/dist/upgrade/migrateArtifactsV2ToV3.js +164 -66
  281. package/dist/upgrade/migrateArtifactsV2ToV3.js.map +1 -1
  282. package/dist/upgrade/migrationPublication.js +123 -0
  283. package/dist/upgrade/migrationPublication.js.map +1 -0
  284. package/dist/upgrade/projectIntegration.js +46 -113
  285. package/dist/upgrade/projectIntegration.js.map +1 -1
  286. package/dist/upgrade/projectIntegrationDecision.js +6 -96
  287. package/dist/upgrade/projectIntegrationDecision.js.map +1 -1
  288. package/dist/upgrade/runtimeMigration.js +222 -402
  289. package/dist/upgrade/runtimeMigration.js.map +1 -1
  290. package/dist/upgrade/upgradeCommands.js +5 -4
  291. package/dist/upgrade/upgradeCommands.js.map +1 -1
  292. package/dist/upgrade/upgradeLock.js +73 -41
  293. package/dist/upgrade/upgradeLock.js.map +1 -1
  294. package/dist/upgrade/upgradeOrchestrator.js +181 -145
  295. package/dist/upgrade/upgradeOrchestrator.js.map +1 -1
  296. package/dist/upgrade/v3CapabilitySurface.js +2 -2
  297. package/dist/validate/appHomeContract.js +0 -4
  298. package/dist/validate/appHomeContract.js.map +1 -1
  299. package/dist/validate/crossCapability.js +2 -2
  300. package/dist/validate/crossCapability.js.map +1 -1
  301. package/package.json +8 -6
  302. package/bundle/.codex-plugin/plugin.json +0 -92
  303. package/bundle/.cursor/agents/agentera.md +0 -7
  304. package/bundle/.cursor/hooks.json +0 -32
  305. package/bundle/.cursor-plugin/plugin.json +0 -34
  306. package/bundle/.github/hooks/postToolUse.json +0 -9
  307. package/bundle/.github/hooks/preToolUse.json +0 -8
  308. package/bundle/.github/hooks/sessionEnd.json +0 -8
  309. package/bundle/.github/hooks/sessionStart.json +0 -8
  310. package/bundle/.github/plugin/plugin.json +0 -7
  311. package/bundle/.opencode/agents/agentera.md +0 -12
  312. package/bundle/.opencode/commands/agentera.md +0 -5
  313. package/bundle/.opencode/package.json +0 -31
  314. package/bundle/.opencode/plugins/agentera.js +0 -820
  315. package/bundle/agents/openai.yaml +0 -87
  316. package/bundle/hooks/codex-hooks.json +0 -31
  317. package/bundle/hooks/codex-plugin-hooks.json +0 -31
  318. package/bundle/plugin.json +0 -13
  319. package/bundle/references/adapters/cursor.md +0 -224
  320. package/bundle/references/adapters/opencode.md +0 -529
  321. package/bundle/references/adapters/runtime-adapter-characterization.md +0 -44
  322. package/bundle/references/adapters/runtime-adapter-interface-model.yaml +0 -200
  323. package/bundle/references/adapters/runtime-adapter-registry.yaml +0 -387
  324. package/bundle/references/adapters/runtime-feature-parity.md +0 -129
  325. package/dist/cli/commands/backfill.js +0 -194
  326. package/dist/cli/commands/backfill.js.map +0 -1
  327. package/dist/cli/commands/migrate.js +0 -677
  328. package/dist/cli/commands/migrate.js.map +0 -1
  329. package/dist/registries/runtimeAdapterRegistry.js +0 -324
  330. package/dist/registries/runtimeAdapterRegistry.js.map +0 -1
  331. package/dist/setup/copilot.js +0 -227
  332. package/dist/setup/copilot.js.map +0 -1
  333. package/dist/setup/cursor.js +0 -129
  334. package/dist/setup/cursor.js.map +0 -1
  335. package/dist/setup/cursorSurfaces.js +0 -67
  336. package/dist/setup/cursorSurfaces.js.map +0 -1
  337. package/dist/setup/doctor/core.js +0 -296
  338. package/dist/setup/doctor/core.js.map +0 -1
  339. package/dist/setup/doctor/diagnostics.js +0 -201
  340. package/dist/setup/doctor/diagnostics.js.map +0 -1
  341. package/dist/setup/doctor/opencode.js +0 -301
  342. package/dist/setup/doctor/opencode.js.map +0 -1
  343. package/dist/setup/doctor/report.js +0 -425
  344. package/dist/setup/doctor/report.js.map +0 -1
  345. package/dist/setup/doctor.js +0 -10
  346. package/dist/setup/doctor.js.map +0 -1
  347. package/dist/state/gitBackfill.js +0 -561
  348. package/dist/state/gitBackfill.js.map +0 -1
  349. package/dist/state/gitBackfillAuthority.js +0 -141
  350. package/dist/state/gitBackfillAuthority.js.map +0 -1
  351. package/dist/state/gitBackfillHistory.js +0 -278
  352. package/dist/state/gitBackfillHistory.js.map +0 -1
  353. package/dist/state/gitBackfillOutput.js +0 -209
  354. package/dist/state/gitBackfillOutput.js.map +0 -1
  355. package/dist/state/legacyMigration.js +0 -516
  356. package/dist/state/legacyMigration.js.map +0 -1
  357. package/dist/state/legacyMigrationParser.js +0 -489
  358. package/dist/state/legacyMigrationParser.js.map +0 -1
  359. package/dist/state/migrationAuthority.js +0 -413
  360. package/dist/state/migrationAuthority.js.map +0 -1
  361. package/dist/state/migrationEnrichment.js +0 -35
  362. package/dist/state/migrationEnrichment.js.map +0 -1
  363. package/dist/upgrade/migrateArtifactsV1ToV2.js +0 -568
  364. package/dist/upgrade/migrateArtifactsV1ToV2.js.map +0 -1
  365. package/dist/upgrade/upgradeSnapshot.js +0 -93
  366. package/dist/upgrade/upgradeSnapshot.js.map +0 -1
  367. package/dist/validate/lifecycleAdapters/legacyPythonParity.js +0 -93
  368. package/dist/validate/lifecycleAdapters/legacyPythonParity.js.map +0 -1
  369. package/dist/validate/lifecycleAdapters/lifecycleMain.js +0 -154
  370. package/dist/validate/lifecycleAdapters/lifecycleMain.js.map +0 -1
  371. package/dist/validate/lifecycleAdapters/nodeFormChecks.js +0 -481
  372. package/dist/validate/lifecycleAdapters/nodeFormChecks.js.map +0 -1
  373. package/dist/validate/lifecycleAdapters/shared.js +0 -198
  374. package/dist/validate/lifecycleAdapters/shared.js.map +0 -1
  375. package/dist/validate/lifecycleAdapters.js +0 -10
  376. package/dist/validate/lifecycleAdapters.js.map +0 -1
@@ -1,131 +1,838 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { assertRealpathBoundary } from "../../registries/artifactRegistry.js";
5
+ import { assertValidatedProjectRoot } from "../projectRoot.js";
4
6
  import { reject } from "./errors.js";
5
7
  const sleepArray = new Int32Array(new SharedArrayBuffer(4));
6
8
  const INITIALIZATION_GRACE_MS = 250;
7
- function malformedLockIsStale(lockPath) {
9
+ const OWNER_NAME = "owner.json";
10
+ const OWNER_PREPARATION_NAME = ".owner.json.tmp";
11
+ const CLAIM_NAME = ".reclaim.json";
12
+ const MAX_OWNER_BYTES = 8 * 1024;
13
+ const MAX_RECOVERY_ENTRIES = 128;
14
+ const DIRECTORY_FLAGS = fs.constants.O_RDONLY
15
+ | (fs.constants.O_DIRECTORY ?? 0)
16
+ | (fs.constants.O_NOFOLLOW ?? 0);
17
+ const FILE_FLAGS = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0);
18
+ const PRIVATE_NAME = /^\.writer\.(?:(\d+)\.)?([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\.tmp$/;
19
+ const LEGACY_CLAIM_NAME = /^\.writer\.[0-9a-f-]{36}\.claim$/;
20
+ function lockRecord(value) {
21
+ if (value === null
22
+ || typeof value !== "object"
23
+ || !Number.isSafeInteger(value.pid)
24
+ || value.pid <= 0
25
+ || typeof value.token !== "string"
26
+ || value.token.length === 0
27
+ || typeof value.created_at !== "string")
28
+ return null;
29
+ return value;
30
+ }
31
+ function identity(fd) {
32
+ const stat = fs.fstatSync(fd, { bigint: true });
33
+ return { dev: stat.dev, ino: stat.ino };
34
+ }
35
+ function sameIdentity(left, right) {
36
+ return left.dev === right.dev && left.ino === right.ino;
37
+ }
38
+ function sameRecord(left, right) {
39
+ return left.pid === right.pid
40
+ && left.token === right.token
41
+ && left.created_at === right.created_at;
42
+ }
43
+ function fdPath(fd, name) {
44
+ return name ? `/proc/self/fd/${fd}/${name}` : `/proc/self/fd/${fd}`;
45
+ }
46
+ function pathMatches(pathname, expectedFd, flags) {
47
+ let observedFd;
8
48
  try {
9
- return Date.now() - fs.statSync(lockPath).mtimeMs >= INITIALIZATION_GRACE_MS;
49
+ observedFd = fs.openSync(pathname, flags);
50
+ return sameIdentity(identity(observedFd), identity(expectedFd));
10
51
  }
11
52
  catch {
12
- return true;
53
+ return false;
54
+ }
55
+ finally {
56
+ if (observedFd !== undefined)
57
+ fs.closeSync(observedFd);
13
58
  }
14
59
  }
15
- function ownerIsDead(lockPath) {
60
+ function entryAbsent(directoryFd, name) {
16
61
  try {
17
- const parsed = JSON.parse(fs.readFileSync(lockPath, "utf8"));
18
- if (!Number.isInteger(parsed.pid))
19
- return malformedLockIsStale(lockPath);
20
- if (parsed.pid === process.pid)
21
- return false;
62
+ fs.lstatSync(fdPath(directoryFd, name));
63
+ return false;
64
+ }
65
+ catch (error) {
66
+ return error.code === "ENOENT";
67
+ }
68
+ }
69
+ function readRecord(fd) {
70
+ try {
71
+ const stat = fs.fstatSync(fd, { bigint: true });
72
+ if (!stat.isFile() || stat.size > BigInt(MAX_OWNER_BYTES))
73
+ return null;
74
+ const size = Number(stat.size);
75
+ const bytes = Buffer.alloc(size);
76
+ let offset = 0;
77
+ while (offset < size) {
78
+ const read = fs.readSync(fd, bytes, offset, size - offset, offset);
79
+ if (read === 0)
80
+ break;
81
+ offset += read;
82
+ }
83
+ if (offset !== size)
84
+ return null;
85
+ return lockRecord(JSON.parse(bytes.toString("utf8")));
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ function inspectRecord(directoryFd, name) {
92
+ let fd;
93
+ try {
94
+ fd = fs.openSync(fdPath(directoryFd, name), FILE_FLAGS);
95
+ const stat = fs.fstatSync(fd, { bigint: true });
96
+ const file = stat.isFile();
97
+ const record = file ? readRecord(fd) : null;
98
+ return record
99
+ ? { state: "valid", stale: false, fd, file, record }
100
+ : {
101
+ state: "malformed",
102
+ stale: Date.now() - Number(stat.mtimeMs) >= INITIALIZATION_GRACE_MS,
103
+ fd,
104
+ file,
105
+ };
106
+ }
107
+ catch (error) {
108
+ if (fd !== undefined)
109
+ fs.closeSync(fd);
110
+ if (error.code !== "ENOENT") {
111
+ return { state: "malformed", stale: false, file: false };
112
+ }
113
+ const stat = fs.fstatSync(directoryFd, { bigint: true });
114
+ return {
115
+ state: "malformed",
116
+ stale: Date.now() - Number(stat.mtimeMs) >= INITIALIZATION_GRACE_MS,
117
+ file: false,
118
+ };
119
+ }
120
+ }
121
+ function inspectLock(lockPath) {
122
+ for (let attempt = 0; attempt < 3; attempt += 1) {
22
123
  try {
23
- process.kill(parsed.pid, 0);
24
- return false;
124
+ const fd = fs.openSync(lockPath, DIRECTORY_FLAGS);
125
+ return { state: "directory", fd, owner: inspectRecord(fd, OWNER_NAME) };
25
126
  }
26
127
  catch (error) {
27
- return error.code === "ESRCH";
128
+ const code = error.code;
129
+ if (code === "ENOENT")
130
+ return { state: "absent" };
131
+ if (code !== "ENOTDIR" && code !== "ELOOP")
132
+ throw error;
133
+ }
134
+ let stat;
135
+ try {
136
+ stat = fs.lstatSync(lockPath);
137
+ }
138
+ catch (error) {
139
+ if (error.code === "ENOENT")
140
+ return { state: "absent" };
141
+ throw error;
142
+ }
143
+ if (stat.isDirectory() && !stat.isSymbolicLink())
144
+ continue;
145
+ if (!stat.isFile() || stat.isSymbolicLink()) {
146
+ return { state: "unsupported", kind: stat.isSymbolicLink() ? "symbolic link" : "non-file entry" };
147
+ }
148
+ try {
149
+ const fd = fs.openSync(lockPath, FILE_FLAGS);
150
+ const opened = fs.fstatSync(fd);
151
+ if (opened.isFile())
152
+ return { state: "legacy_file", fd };
153
+ fs.closeSync(fd);
154
+ }
155
+ catch (error) {
156
+ const code = error.code;
157
+ if (code === "ENOENT" || code === "EISDIR" || code === "ENOTDIR")
158
+ continue;
159
+ throw error;
28
160
  }
29
161
  }
30
- catch {
31
- return malformedLockIsStale(lockPath);
162
+ return { state: "unsupported", kind: "entry changing during inspection" };
163
+ }
164
+ function closeRecord(inspection) {
165
+ if (inspection.fd !== undefined)
166
+ fs.closeSync(inspection.fd);
167
+ }
168
+ function closeInspection(inspection) {
169
+ if (inspection.state === "directory") {
170
+ closeRecord(inspection.owner);
171
+ fs.closeSync(inspection.fd);
172
+ }
173
+ else if (inspection.state === "legacy_file") {
174
+ fs.closeSync(inspection.fd);
175
+ }
176
+ }
177
+ function pidState(pid) {
178
+ if (pid === process.pid)
179
+ return "live";
180
+ try {
181
+ process.kill(pid, 0);
182
+ return "live";
183
+ }
184
+ catch (error) {
185
+ return error.code === "ESRCH" ? "dead" : "indeterminate";
32
186
  }
33
187
  }
188
+ function ownerIsDead(record) {
189
+ return pidState(record.pid) === "dead";
190
+ }
34
191
  function syncDirectory(directory) {
35
- const fd = fs.openSync(directory, "r");
192
+ const fd = typeof directory === "number" ? directory : fs.openSync(directory, DIRECTORY_FLAGS);
36
193
  try {
37
194
  fs.fsyncSync(fd);
38
195
  }
39
196
  finally {
40
- fs.closeSync(fd);
197
+ if (typeof directory !== "number")
198
+ fs.closeSync(fd);
41
199
  }
42
200
  }
43
- export function acquireWriterLock(projectRoot, timeoutMs = 2000) {
44
- const dir = path.join(projectRoot, ".agentera");
45
- assertRealpathBoundary(projectRoot, dir, "writer lock");
46
- const createdDir = !fs.existsSync(dir);
47
- fs.mkdirSync(dir, { recursive: true });
48
- const lockPath = path.join(dir, ".writer.lock");
49
- const deadline = Date.now() + timeoutMs;
50
- while (true) {
51
- let fd;
52
- let createdLock = false;
53
- try {
54
- fd = fs.openSync(lockPath, "wx");
55
- createdLock = true;
56
- fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, created_at: new Date().toISOString() }) + "\n");
57
- fs.fsyncSync(fd);
58
- fs.closeSync(fd);
59
- fd = undefined;
60
- syncDirectory(dir);
61
- let released = false;
62
- return {
63
- path: lockPath,
64
- release: () => {
65
- if (released)
66
- return;
67
- released = true;
68
- try {
69
- fs.unlinkSync(lockPath);
70
- }
71
- catch {
72
- /* already released */
73
- }
74
- try {
75
- syncDirectory(dir);
76
- }
77
- catch {
78
- /* lock removal is already visible to this process */
79
- }
80
- if (createdDir) {
81
- try {
82
- fs.rmdirSync(dir);
83
- }
84
- catch {
85
- /* a published artifact or peer now owns the directory */
86
- }
87
- }
88
- },
89
- };
90
- }
91
- catch (error) {
92
- if (fd !== undefined) {
201
+ function unlinkPinnedFile(directoryFd, name, expectedFd) {
202
+ if (!fs.fstatSync(expectedFd).isFile())
203
+ return false;
204
+ const target = fdPath(directoryFd, name);
205
+ if (!pathMatches(target, expectedFd, FILE_FLAGS))
206
+ return false;
207
+ try {
208
+ fs.unlinkSync(target);
209
+ syncDirectory(directoryFd);
210
+ return true;
211
+ }
212
+ catch (error) {
213
+ if (error.code === "ENOENT")
214
+ return false;
215
+ throw error;
216
+ }
217
+ }
218
+ function removePinnedDirectory(parentFd, name, directoryFd) {
219
+ const target = fdPath(parentFd, name);
220
+ if (!pathMatches(target, directoryFd, DIRECTORY_FLAGS))
221
+ return false;
222
+ try {
223
+ fs.rmdirSync(target);
224
+ syncDirectory(parentFd);
225
+ return true;
226
+ }
227
+ catch (error) {
228
+ if (["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error.code ?? ""))
229
+ return false;
230
+ throw error;
231
+ }
232
+ }
233
+ function recordCanBeRecovered(inspection, token) {
234
+ if (!inspection.file || inspection.fd === undefined)
235
+ return false;
236
+ if (inspection.state === "malformed")
237
+ return inspection.stale;
238
+ return (token === undefined || inspection.record.token === token) && ownerIsDead(inspection.record);
239
+ }
240
+ function recoverPrivateDirectory(parentFd, name, token, pid, transitionedTokens) {
241
+ // Preparation may pause indefinitely; only positive owner-death evidence permits mutation.
242
+ const privatePath = fdPath(parentFd, name);
243
+ let directoryFd;
244
+ try {
245
+ directoryFd = fs.openSync(privatePath, DIRECTORY_FLAGS);
246
+ const transitioned = transitionedTokens.has(token);
247
+ const namedPidState = pid === undefined ? undefined : pidState(pid);
248
+ for (let attempt = 0; attempt < 3; attempt += 1) {
249
+ const names = fs.readdirSync(fdPath(directoryFd));
250
+ if (!pathMatches(privatePath, directoryFd, DIRECTORY_FLAGS)) {
251
+ return entryAbsent(parentFd, name) ? "recovered" : "unknown";
252
+ }
253
+ if (names.length === 0) {
254
+ if (namedPidState === "live")
255
+ return "live";
256
+ if (!transitioned && namedPidState !== "dead")
257
+ return "unknown";
258
+ return removePinnedDirectory(parentFd, name, directoryFd) || entryAbsent(parentFd, name)
259
+ ? "recovered"
260
+ : "unknown";
261
+ }
262
+ if (names.includes(OWNER_PREPARATION_NAME)) {
263
+ if (names.length !== 1)
264
+ return "unknown";
265
+ const preparation = inspectRecord(directoryFd, OWNER_PREPARATION_NAME);
93
266
  try {
94
- fs.closeSync(fd);
267
+ if (!preparation.file || preparation.fd === undefined) {
268
+ if (entryAbsent(directoryFd, OWNER_PREPARATION_NAME))
269
+ continue;
270
+ return "unknown";
271
+ }
272
+ if (preparation.state === "valid"
273
+ && (preparation.record.token !== token || preparation.record.pid !== pid))
274
+ return "unknown";
275
+ if (namedPidState === "live")
276
+ return "live";
277
+ if (namedPidState !== "dead")
278
+ return "unknown";
279
+ unlinkPinnedFile(directoryFd, OWNER_PREPARATION_NAME, preparation.fd);
280
+ return removePinnedDirectory(parentFd, name, directoryFd) || entryAbsent(parentFd, name)
281
+ ? "recovered"
282
+ : "unknown";
95
283
  }
96
- catch {
97
- /* acquisition already failed */
284
+ finally {
285
+ closeRecord(preparation);
286
+ }
287
+ }
288
+ if (names.length > MAX_RECOVERY_ENTRIES
289
+ || names.some((entry) => entry !== OWNER_NAME && !LEGACY_CLAIM_NAME.test(entry)))
290
+ return "unknown";
291
+ const records = names.map((entry) => ({ entry, inspection: inspectRecord(directoryFd, entry) }));
292
+ try {
293
+ const missing = records.filter(({ inspection }) => !inspection.file || inspection.fd === undefined);
294
+ if (missing.length > 0) {
295
+ if (missing.every(({ entry }) => entryAbsent(directoryFd, entry)))
296
+ continue;
297
+ return "unknown";
298
+ }
299
+ if (records.some(({ inspection }) => inspection.state === "valid" && inspection.record.token !== token)) {
300
+ return "unknown";
301
+ }
302
+ if (pid !== undefined
303
+ && records.some(({ inspection }) => inspection.state === "valid" && inspection.record.pid !== pid))
304
+ return "unknown";
305
+ const validRecords = records.filter(({ inspection }) => inspection.state === "valid");
306
+ const recordPidStates = validRecords.map(({ inspection }) => pidState(inspection.record.pid));
307
+ if (namedPidState === "indeterminate" || recordPidStates.includes("indeterminate"))
308
+ return "unknown";
309
+ const fullyInitializedLivePreparation = pid !== undefined
310
+ && namedPidState === "live"
311
+ && records.every(({ inspection }) => inspection.state === "valid")
312
+ && records.some(({ entry }) => entry === OWNER_NAME)
313
+ && recordPidStates.every((state) => state === "live");
314
+ if (fullyInitializedLivePreparation)
315
+ return "live";
316
+ if (namedPidState === "live" || recordPidStates.includes("live"))
317
+ return "unknown";
318
+ if (!transitioned && (pid === undefined || namedPidState !== "dead"))
319
+ return "unknown";
320
+ for (const { entry, inspection } of records) {
321
+ unlinkPinnedFile(directoryFd, entry, inspection.fd);
322
+ }
323
+ return removePinnedDirectory(parentFd, name, directoryFd) || entryAbsent(parentFd, name)
324
+ ? "recovered"
325
+ : "unknown";
326
+ }
327
+ finally {
328
+ for (const { inspection } of records)
329
+ closeRecord(inspection);
330
+ }
331
+ }
332
+ return "unknown";
333
+ }
334
+ catch (error) {
335
+ const code = error.code;
336
+ if (code === "ENOENT")
337
+ return "recovered";
338
+ if (code === "ENOTDIR" || code === "ELOOP")
339
+ return "unknown";
340
+ throw error;
341
+ }
342
+ finally {
343
+ if (directoryFd !== undefined)
344
+ fs.closeSync(directoryFd);
345
+ }
346
+ }
347
+ function staleTransitionTokens(parentFd) {
348
+ const tokens = new Set();
349
+ let lockFd;
350
+ try {
351
+ lockFd = fs.openSync(fdPath(parentFd, ".writer.lock"), DIRECTORY_FLAGS);
352
+ for (const name of [OWNER_NAME, CLAIM_NAME]) {
353
+ const record = inspectRecord(lockFd, name);
354
+ try {
355
+ if (record.state === "valid" && ownerIsDead(record.record))
356
+ tokens.add(record.record.token);
357
+ }
358
+ finally {
359
+ closeRecord(record);
360
+ }
361
+ }
362
+ }
363
+ catch (error) {
364
+ if (!["ENOENT", "ENOTDIR", "ELOOP"].includes(error.code ?? ""))
365
+ throw error;
366
+ }
367
+ finally {
368
+ if (lockFd !== undefined)
369
+ fs.closeSync(lockFd);
370
+ }
371
+ return tokens;
372
+ }
373
+ function recoverPreparedLocks(parentFd, lockPath) {
374
+ const candidates = fs.readdirSync(fdPath(parentFd), { withFileTypes: true })
375
+ .map((entry) => ({ entry, match: PRIVATE_NAME.exec(entry.name) }))
376
+ .filter(({ match }) => match !== null);
377
+ if (candidates.length > MAX_RECOVERY_ENTRIES) {
378
+ privateResidue(lockPath, candidates.map(({ entry }) => entry.name));
379
+ }
380
+ const transitionedTokens = staleTransitionTokens(parentFd);
381
+ const live = [];
382
+ const unresolved = [];
383
+ for (const { entry, match } of candidates) {
384
+ if (!entry.isDirectory() || entry.isSymbolicLink()) {
385
+ unresolved.push(entry.name);
386
+ continue;
387
+ }
388
+ const recovery = recoverPrivateDirectory(parentFd, entry.name, match[2], match[1] === undefined ? undefined : Number(match[1]), transitionedTokens);
389
+ if (recovery === "live")
390
+ live.push(entry.name);
391
+ if (recovery === "unknown")
392
+ unresolved.push(entry.name);
393
+ }
394
+ if (unresolved.length > 0)
395
+ privateResidue(lockPath, unresolved);
396
+ return live.length > 0;
397
+ }
398
+ function prepareLock(parentFd) {
399
+ const record = {
400
+ pid: process.pid,
401
+ token: randomUUID(),
402
+ created_at: new Date().toISOString(),
403
+ };
404
+ const name = `.writer.${record.pid}.${record.token}.tmp`;
405
+ const preparedPath = fdPath(parentFd, name);
406
+ let dirFd;
407
+ let ownerFd;
408
+ let ownerName = OWNER_PREPARATION_NAME;
409
+ try {
410
+ fs.mkdirSync(preparedPath, { mode: 0o700 });
411
+ dirFd = fs.openSync(preparedPath, DIRECTORY_FLAGS);
412
+ ownerFd = fs.openSync(fdPath(dirFd, ownerName), fs.constants.O_RDWR | fs.constants.O_CREAT | fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW ?? 0), 0o600);
413
+ fs.writeFileSync(ownerFd, `${JSON.stringify(record)}\n`);
414
+ fs.fsyncSync(ownerFd);
415
+ const validated = readRecord(ownerFd);
416
+ if (validated === null || !sameRecord(validated, record)) {
417
+ throw new Error("writer owner metadata validation failed before publication");
418
+ }
419
+ fs.renameSync(fdPath(dirFd, ownerName), fdPath(dirFd, OWNER_NAME));
420
+ ownerName = OWNER_NAME;
421
+ syncDirectory(dirFd);
422
+ return { path: preparedPath, dirFd, ownerFd, record };
423
+ }
424
+ catch (error) {
425
+ if (dirFd !== undefined && ownerFd !== undefined) {
426
+ try {
427
+ unlinkPinnedFile(dirFd, ownerName, ownerFd);
428
+ }
429
+ catch {
430
+ // Preserve the acquisition error.
431
+ }
432
+ }
433
+ if (ownerFd !== undefined)
434
+ fs.closeSync(ownerFd);
435
+ if (dirFd !== undefined)
436
+ fs.closeSync(dirFd);
437
+ try {
438
+ fs.rmdirSync(preparedPath);
439
+ syncDirectory(parentFd);
440
+ }
441
+ catch {
442
+ // Preserve the acquisition error.
443
+ }
444
+ throw error;
445
+ }
446
+ }
447
+ function disposePrepared(prepared, parentFd) {
448
+ if (prepared.ownerFd >= 0 && prepared.dirFd >= 0) {
449
+ try {
450
+ unlinkPinnedFile(prepared.dirFd, OWNER_NAME, prepared.ownerFd);
451
+ }
452
+ catch {
453
+ // The owner was moved or the private directory changed identity.
454
+ }
455
+ }
456
+ if (prepared.ownerFd >= 0) {
457
+ fs.closeSync(prepared.ownerFd);
458
+ prepared.ownerFd = -1;
459
+ }
460
+ const exactDirectory = prepared.dirFd >= 0
461
+ && pathMatches(prepared.path, prepared.dirFd, DIRECTORY_FLAGS);
462
+ if (prepared.dirFd >= 0) {
463
+ fs.closeSync(prepared.dirFd);
464
+ prepared.dirFd = -1;
465
+ }
466
+ if (exactDirectory) {
467
+ try {
468
+ fs.rmdirSync(prepared.path);
469
+ syncDirectory(parentFd);
470
+ }
471
+ catch {
472
+ // A non-empty replacement is not this attempt's directory.
473
+ }
474
+ }
475
+ }
476
+ function verifyCanonicalLock(projectDirectory, parentFd, lockPath, lockFd, ownerFd, expected) {
477
+ if (!pathMatches(projectDirectory, parentFd, DIRECTORY_FLAGS))
478
+ return false;
479
+ let canonicalFd;
480
+ let canonicalOwnerFd;
481
+ try {
482
+ canonicalFd = fs.openSync(lockPath, DIRECTORY_FLAGS);
483
+ if (!sameIdentity(identity(canonicalFd), identity(lockFd)))
484
+ return false;
485
+ canonicalOwnerFd = fs.openSync(fdPath(canonicalFd, OWNER_NAME), FILE_FLAGS);
486
+ if (!sameIdentity(identity(canonicalOwnerFd), identity(ownerFd)))
487
+ return false;
488
+ const observed = readRecord(canonicalOwnerFd);
489
+ return observed !== null && sameRecord(observed, expected);
490
+ }
491
+ catch {
492
+ return false;
493
+ }
494
+ finally {
495
+ if (canonicalOwnerFd !== undefined)
496
+ fs.closeSync(canonicalOwnerFd);
497
+ if (canonicalFd !== undefined)
498
+ fs.closeSync(canonicalFd);
499
+ }
500
+ }
501
+ function removeOwnedDirectory(parentFd, lockName, lockFd, ownerFd) {
502
+ if (!unlinkPinnedFile(lockFd, OWNER_NAME, ownerFd))
503
+ return false;
504
+ return removePinnedDirectory(parentFd, lockName, lockFd);
505
+ }
506
+ function writerLock(projectDirectory, lockPath, lockName, parentFd, lockFd, ownerFd, record, createdDirectory) {
507
+ let released = false;
508
+ return {
509
+ path: lockPath,
510
+ release: () => {
511
+ if (released)
512
+ return;
513
+ released = true;
514
+ try {
515
+ if (verifyCanonicalLock(projectDirectory, parentFd, lockPath, lockFd, ownerFd, record)) {
516
+ removeOwnedDirectory(parentFd, lockName, lockFd, ownerFd);
98
517
  }
99
518
  }
100
- if (createdLock) {
519
+ catch {
520
+ // A missing or replaced lock is not this owner's to remove.
521
+ }
522
+ fs.closeSync(ownerFd);
523
+ const exactDirectory = pathMatches(projectDirectory, parentFd, DIRECTORY_FLAGS);
524
+ fs.closeSync(lockFd);
525
+ fs.closeSync(parentFd);
526
+ if (createdDirectory && exactDirectory) {
101
527
  try {
102
- fs.unlinkSync(lockPath);
528
+ fs.rmdirSync(projectDirectory);
103
529
  }
104
530
  catch {
105
- /* acquisition already failed */
531
+ // A published artifact or peer now owns the directory.
106
532
  }
107
533
  }
108
- if (error.code !== "EEXIST")
109
- throw error;
110
- if (ownerIsDead(lockPath)) {
534
+ },
535
+ };
536
+ }
537
+ function publishPrepared(prepared, projectDirectory, parentFd, lockPath, lockName, createdDirectory) {
538
+ try {
539
+ fs.renameSync(prepared.path, fdPath(parentFd, lockName));
540
+ }
541
+ catch (error) {
542
+ if (renameConflict(error))
543
+ return null;
544
+ throw error;
545
+ }
546
+ syncDirectory(parentFd);
547
+ if (verifyCanonicalLock(projectDirectory, parentFd, lockPath, prepared.dirFd, prepared.ownerFd, prepared.record)) {
548
+ return writerLock(projectDirectory, lockPath, lockName, parentFd, prepared.dirFd, prepared.ownerFd, prepared.record, createdDirectory);
549
+ }
550
+ try {
551
+ removeOwnedDirectory(parentFd, lockName, prepared.dirFd, prepared.ownerFd);
552
+ }
553
+ catch {
554
+ // Only the exact prepared instance may be cleaned.
555
+ }
556
+ fs.closeSync(prepared.ownerFd);
557
+ prepared.ownerFd = -1;
558
+ fs.closeSync(prepared.dirFd);
559
+ prepared.dirFd = -1;
560
+ return null;
561
+ }
562
+ function inspectedOwnerMatches(inspection) {
563
+ return inspection.owner.fd === undefined
564
+ ? entryAbsent(inspection.fd, OWNER_NAME)
565
+ : pathMatches(fdPath(inspection.fd, OWNER_NAME), inspection.owner.fd, FILE_FLAGS);
566
+ }
567
+ function recoverLegacyClaims(inspection) {
568
+ const names = fs.readdirSync(fdPath(inspection.fd));
569
+ if (names.length > MAX_RECOVERY_ENTRIES)
570
+ return false;
571
+ for (const name of names.filter((entry) => LEGACY_CLAIM_NAME.test(entry))) {
572
+ const claim = inspectRecord(inspection.fd, name);
573
+ try {
574
+ if (!recordCanBeRecovered(claim))
575
+ return false;
576
+ unlinkPinnedFile(inspection.fd, name, claim.fd);
577
+ }
578
+ finally {
579
+ closeRecord(claim);
580
+ }
581
+ }
582
+ return fs.readdirSync(fdPath(inspection.fd)).every((name) => name === OWNER_NAME || name === CLAIM_NAME);
583
+ }
584
+ function removeClaim(inspection, claim) {
585
+ const removed = unlinkPinnedFile(inspection.fd, CLAIM_NAME, claim.fd);
586
+ fs.closeSync(claim.fd);
587
+ claim.fd = -1;
588
+ return removed;
589
+ }
590
+ function claimInspectedDirectory(inspection, prepared, parentFd, lockName) {
591
+ const canonicalPath = fdPath(parentFd, lockName);
592
+ if (!pathMatches(canonicalPath, inspection.fd, DIRECTORY_FLAGS)
593
+ || !inspectedOwnerMatches(inspection)
594
+ || !recoverLegacyClaims(inspection))
595
+ return null;
596
+ const claimPath = fdPath(inspection.fd, CLAIM_NAME);
597
+ try {
598
+ // One fixed O_EXCL entry elects one reclaimer inside the exact directory inspected above.
599
+ fs.linkSync(fdPath(prepared.dirFd, OWNER_NAME), claimPath);
600
+ }
601
+ catch (error) {
602
+ if (error.code !== "EEXIST")
603
+ throw error;
604
+ const existing = inspectRecord(inspection.fd, CLAIM_NAME);
605
+ try {
606
+ if (recordCanBeRecovered(existing))
607
+ unlinkPinnedFile(inspection.fd, CLAIM_NAME, existing.fd);
608
+ }
609
+ finally {
610
+ closeRecord(existing);
611
+ }
612
+ return null;
613
+ }
614
+ let claimFd;
615
+ try {
616
+ claimFd = fs.openSync(claimPath, FILE_FLAGS);
617
+ if (!sameIdentity(identity(claimFd), identity(prepared.ownerFd))
618
+ || fs.fstatSync(claimFd, { bigint: true }).nlink !== 2n) {
619
+ fs.closeSync(claimFd);
620
+ unlinkPinnedFile(inspection.fd, CLAIM_NAME, prepared.ownerFd);
621
+ return null;
622
+ }
623
+ const claim = { fd: claimFd };
624
+ syncDirectory(inspection.fd);
625
+ if (pathMatches(canonicalPath, inspection.fd, DIRECTORY_FLAGS)
626
+ && pathMatches(claimPath, claimFd, FILE_FLAGS)
627
+ && inspectedOwnerMatches(inspection))
628
+ return claim;
629
+ removeClaim(inspection, claim);
630
+ return null;
631
+ }
632
+ catch (error) {
633
+ if (claimFd !== undefined)
634
+ fs.closeSync(claimFd);
635
+ try {
636
+ unlinkPinnedFile(inspection.fd, CLAIM_NAME, prepared.ownerFd);
637
+ }
638
+ catch {
639
+ // Preserve the claim-publication error.
640
+ }
641
+ throw error;
642
+ }
643
+ }
644
+ function adoptInspectedDirectory(inspection, prepared, projectDirectory, parentFd, lockPath, lockName, createdDirectory) {
645
+ const claim = claimInspectedDirectory(inspection, prepared, parentFd, lockName);
646
+ if (!claim)
647
+ return null;
648
+ let transitioned = false;
649
+ try {
650
+ if (!inspectedOwnerMatches(inspection)) {
651
+ removeClaim(inspection, claim);
652
+ return null;
653
+ }
654
+ fs.renameSync(fdPath(prepared.dirFd, OWNER_NAME), fdPath(inspection.fd, OWNER_NAME));
655
+ transitioned = true;
656
+ syncDirectory(inspection.fd);
657
+ syncDirectory(parentFd);
658
+ if (!verifyCanonicalLock(projectDirectory, parentFd, lockPath, inspection.fd, prepared.ownerFd, prepared.record)) {
659
+ unlinkPinnedFile(inspection.fd, OWNER_NAME, prepared.ownerFd);
660
+ removeClaim(inspection, claim);
661
+ return null;
662
+ }
663
+ closeRecord(inspection.owner);
664
+ inspection.owner.fd = undefined;
665
+ const exactPrivateDirectory = pathMatches(prepared.path, prepared.dirFd, DIRECTORY_FLAGS);
666
+ if (!exactPrivateDirectory || !removePinnedDirectory(parentFd, path.basename(prepared.path), prepared.dirFd)) {
667
+ unlinkPinnedFile(inspection.fd, OWNER_NAME, prepared.ownerFd);
668
+ removeClaim(inspection, claim);
669
+ return null;
670
+ }
671
+ fs.closeSync(prepared.dirFd);
672
+ prepared.dirFd = -1;
673
+ if (!removeClaim(inspection, claim)) {
674
+ unlinkPinnedFile(inspection.fd, OWNER_NAME, prepared.ownerFd);
675
+ return null;
676
+ }
677
+ if (!verifyCanonicalLock(projectDirectory, parentFd, lockPath, inspection.fd, prepared.ownerFd, prepared.record)) {
678
+ unlinkPinnedFile(inspection.fd, OWNER_NAME, prepared.ownerFd);
679
+ return null;
680
+ }
681
+ return writerLock(projectDirectory, lockPath, lockName, parentFd, inspection.fd, prepared.ownerFd, prepared.record, createdDirectory);
682
+ }
683
+ catch (error) {
684
+ if (transitioned) {
685
+ try {
686
+ unlinkPinnedFile(inspection.fd, OWNER_NAME, prepared.ownerFd);
687
+ }
688
+ catch {
689
+ // The exact transitioned owner is already absent.
690
+ }
691
+ }
692
+ if (claim.fd >= 0) {
693
+ try {
694
+ removeClaim(inspection, claim);
695
+ }
696
+ catch {
697
+ if (claim.fd >= 0)
698
+ fs.closeSync(claim.fd);
699
+ }
700
+ }
701
+ throw error;
702
+ }
703
+ }
704
+ function renameConflict(error) {
705
+ return ["EEXIST", "ENOTEMPTY", "ENOTDIR", "EISDIR"].includes(error.code ?? "");
706
+ }
707
+ function timeout(lockPath) {
708
+ reject({
709
+ class: "conflict",
710
+ message: `writer lock timeout at '${lockPath}'; retry the command after the active writer finishes`,
711
+ syntax: "agentera state <artifact-id> <verb> ... --format json",
712
+ example: "retry the same command after the active writer finishes",
713
+ });
714
+ }
715
+ function waitForContention(lockPath, deadline) {
716
+ const remaining = deadline - Date.now();
717
+ if (remaining <= 0)
718
+ timeout(lockPath);
719
+ Atomics.wait(sleepArray, 0, 0, Math.min(25, remaining));
720
+ }
721
+ function privateResidue(lockPath, names) {
722
+ const directory = path.dirname(lockPath);
723
+ const listed = names.slice(0, 3).map((name) => `'${path.join(directory, name)}'`).join(", ");
724
+ const omitted = names.length > 3 ? `, and ${names.length - 3} more` : "";
725
+ reject({
726
+ class: "conflict",
727
+ message: `private writer preparation residue at ${listed}${omitted} has unknown or unverifiable ownership and cannot be reclaimed safely; verify no Agentera writer is running, remove the listed residue, and retry`,
728
+ syntax: "agentera state <artifact-id> <verb> ... --format json",
729
+ example: `after verifying no Agentera writer is running, remove '${path.join(directory, names[0])}' and retry the same command`,
730
+ });
731
+ }
732
+ function unsupportedLock(lockPath, kind) {
733
+ const legacy = kind === "legacy file" ? "legacy writer lock file" : `writer lock ${kind}`;
734
+ reject({
735
+ class: "conflict",
736
+ message: `${legacy} at '${lockPath}' cannot be reclaimed safely; verify no Agentera writer is running, remove the entry, and retry`,
737
+ syntax: "agentera state <artifact-id> <verb> ... --format json",
738
+ example: `after verifying no Agentera writer is running, remove '${lockPath}' and retry the same command`,
739
+ });
740
+ }
741
+ function cleanupCreatedDirectory(projectDirectory, createdDirectory) {
742
+ if (!createdDirectory)
743
+ return;
744
+ try {
745
+ fs.rmdirSync(projectDirectory);
746
+ }
747
+ catch {
748
+ // A peer or project artifact now owns the directory.
749
+ }
750
+ }
751
+ function openProjectDirectory(projectRoot, expectedRoot) {
752
+ if (expectedRoot)
753
+ assertValidatedProjectRoot(expectedRoot);
754
+ const projectDirectory = path.join(projectRoot, ".agentera");
755
+ assertRealpathBoundary(projectRoot, projectDirectory, "writer lock");
756
+ const createdDirectory = !fs.existsSync(projectDirectory);
757
+ let parentFd;
758
+ try {
759
+ fs.mkdirSync(projectDirectory, { recursive: true });
760
+ if (expectedRoot)
761
+ assertValidatedProjectRoot(expectedRoot);
762
+ parentFd = fs.openSync(projectDirectory, DIRECTORY_FLAGS);
763
+ if (expectedRoot)
764
+ assertValidatedProjectRoot(expectedRoot);
765
+ return { projectDirectory, createdDirectory, parentFd };
766
+ }
767
+ catch (error) {
768
+ if (parentFd !== undefined)
769
+ fs.closeSync(parentFd);
770
+ cleanupCreatedDirectory(projectDirectory, createdDirectory);
771
+ throw error;
772
+ }
773
+ }
774
+ export function acquireWriterLock(projectRoot, timeoutMs = 2000, expectedRoot) {
775
+ const { projectDirectory, createdDirectory, parentFd } = openProjectDirectory(projectRoot, expectedRoot);
776
+ const lockName = ".writer.lock";
777
+ const lockPath = path.join(projectDirectory, lockName);
778
+ const anchoredLockPath = fdPath(parentFd, lockName);
779
+ const deadline = Date.now() + timeoutMs;
780
+ let prepared;
781
+ try {
782
+ let waitedForPreparation = false;
783
+ while (recoverPreparedLocks(parentFd, lockPath)) {
784
+ waitedForPreparation = true;
785
+ waitForContention(lockPath, deadline);
786
+ }
787
+ if (waitedForPreparation && Date.now() >= deadline)
788
+ timeout(lockPath);
789
+ prepared = prepareLock(parentFd);
790
+ while (true) {
791
+ if (expectedRoot)
792
+ assertValidatedProjectRoot(expectedRoot);
793
+ const published = publishPrepared(prepared, projectDirectory, parentFd, lockPath, lockName, createdDirectory);
794
+ if (published) {
111
795
  try {
112
- fs.unlinkSync(lockPath);
796
+ if (expectedRoot)
797
+ assertValidatedProjectRoot(expectedRoot);
113
798
  }
114
- catch {
115
- /* raced with another writer */
799
+ catch (error) {
800
+ published.release();
801
+ prepared = undefined;
802
+ throw error;
116
803
  }
117
- continue;
804
+ return published;
805
+ }
806
+ if (prepared.dirFd < 0 || prepared.ownerFd < 0) {
807
+ prepared = prepareLock(parentFd);
118
808
  }
119
- if (Date.now() >= deadline) {
120
- reject({
121
- class: "conflict",
122
- message: `writer lock timeout at '${lockPath}'; retry the command after the active writer finishes`,
123
- syntax: "agentera state <artifact-id> <verb> ... --format json",
124
- example: "retry the same command after the active writer finishes",
125
- });
809
+ const existing = inspectLock(anchoredLockPath);
810
+ if (existing.state === "legacy_file") {
811
+ closeInspection(existing);
812
+ unsupportedLock(lockPath, "legacy file");
126
813
  }
127
- Atomics.wait(sleepArray, 0, 0, 25);
814
+ if (existing.state === "unsupported")
815
+ unsupportedLock(lockPath, existing.kind);
816
+ if (existing.state === "directory") {
817
+ const reclaimable = existing.owner.state === "valid"
818
+ ? ownerIsDead(existing.owner.record)
819
+ : existing.owner.stale;
820
+ if (reclaimable) {
821
+ const lock = adoptInspectedDirectory(existing, prepared, projectDirectory, parentFd, lockPath, lockName, createdDirectory);
822
+ if (lock)
823
+ return lock;
824
+ }
825
+ }
826
+ closeInspection(existing);
827
+ waitForContention(lockPath, deadline);
128
828
  }
129
829
  }
830
+ catch (error) {
831
+ if (prepared !== undefined)
832
+ disposePrepared(prepared, parentFd);
833
+ fs.closeSync(parentFd);
834
+ cleanupCreatedDirectory(projectDirectory, createdDirectory);
835
+ throw error;
836
+ }
130
837
  }
131
838
  //# sourceMappingURL=lock.js.map