agentera 3.0.0-dev.5 → 3.0.0-dev.51

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 (598) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +119 -22
  3. package/bundle/CHANGELOG.md +1351 -0
  4. package/bundle/DESIGN.md +229 -0
  5. package/bundle/LICENSE +201 -0
  6. package/bundle/README.md +143 -0
  7. package/bundle/UPGRADE.md +236 -0
  8. package/bundle/extract-corpus-parity.json +21 -0
  9. package/bundle/fixtures/routing/hybrid-corpus.yaml +154 -0
  10. package/bundle/references/adapters/package-publication.json +437 -0
  11. package/bundle/references/adapters/package-registry.yaml +435 -189
  12. package/bundle/references/adapters/package-surface-characterization.md +66 -35
  13. package/bundle/references/adapters/runtime-lifecycle-adapters.yaml +74 -0
  14. package/bundle/references/adapters/runtime-lifecycle-authority.yaml +62 -0
  15. package/bundle/references/adapters/runtime-lifecycle-operation-contract.yaml +84 -0
  16. package/bundle/references/adapters/runtime-retired-resources.yaml +333 -0
  17. package/bundle/references/analysis/evidence-tier-authority.yaml +518 -0
  18. package/bundle/references/analysis/verification-policy.yaml +191 -0
  19. package/bundle/references/artifacts/artifact-registry-interface-model.yaml +20 -10
  20. package/bundle/references/artifacts/glossary-entry-contract.yaml +1254 -0
  21. package/bundle/references/artifacts/state-storage-authority.yaml +3022 -0
  22. package/bundle/references/artifacts/verbosity-budget-authority.yaml +42 -0
  23. package/bundle/references/cli/app-lifecycle-vocabulary.yaml +7 -8
  24. package/bundle/references/cli/capability-instruction-contract.yaml +11 -14
  25. package/bundle/references/cli/hybrid-route-contract.yaml +438 -0
  26. package/bundle/references/cli/routing-model.md +155 -0
  27. package/bundle/references/cli/trigger-schema-enrichment.md +147 -0
  28. package/bundle/references/cli/update-channels.yaml +19 -1
  29. package/bundle/references/cli/vocabulary-index.yaml +20 -17
  30. package/bundle/references/cli/vocabulary.md +326 -361
  31. package/bundle/references/meta/retained-reference-authority.yaml +151 -0
  32. package/bundle/registry.json +13 -13
  33. package/bundle/skills/agentera/SKILL.md +224 -422
  34. package/bundle/skills/agentera/capabilities/audit/schemas/artifacts.yaml +84 -0
  35. package/bundle/skills/agentera/capabilities/{inspektera → audit}/schemas/exit.yaml +2 -2
  36. package/bundle/skills/agentera/capabilities/audit/schemas/triggers.yaml +25 -0
  37. package/bundle/skills/agentera/capabilities/{inspektera → audit}/schemas/validation.yaml +37 -18
  38. package/bundle/skills/agentera/capabilities/build/schemas/artifacts.yaml +102 -0
  39. package/bundle/skills/agentera/capabilities/{realisera → build}/schemas/exit.yaml +6 -5
  40. package/bundle/skills/agentera/capabilities/build/schemas/triggers.yaml +27 -0
  41. package/bundle/skills/agentera/capabilities/build/schemas/validation.yaml +176 -0
  42. package/bundle/skills/agentera/capabilities/{visualisera → design}/schemas/artifacts.yaml +12 -12
  43. package/bundle/skills/agentera/capabilities/{visualisera → design}/schemas/exit.yaml +5 -5
  44. package/bundle/skills/agentera/capabilities/design/schemas/triggers.yaml +16 -0
  45. package/bundle/skills/agentera/capabilities/{visualisera → design}/schemas/validation.yaml +7 -7
  46. package/bundle/skills/agentera/capabilities/discuss/schemas/artifacts.yaml +51 -0
  47. package/bundle/skills/agentera/capabilities/{resonera → discuss}/schemas/exit.yaml +2 -2
  48. package/bundle/skills/agentera/capabilities/discuss/schemas/triggers.yaml +20 -0
  49. package/bundle/skills/agentera/capabilities/discuss/schemas/validation.yaml +144 -0
  50. package/bundle/skills/agentera/capabilities/document/schemas/artifacts.yaml +80 -0
  51. package/bundle/skills/agentera/capabilities/document/schemas/triggers.yaml +16 -0
  52. package/bundle/skills/agentera/capabilities/{dokumentera → document}/schemas/validation.yaml +18 -18
  53. package/bundle/skills/agentera/capabilities/{optimera → optimize}/schemas/artifacts.yaml +18 -18
  54. package/bundle/skills/agentera/capabilities/{optimera → optimize}/schemas/exit.yaml +2 -2
  55. package/bundle/skills/agentera/capabilities/optimize/schemas/triggers.yaml +16 -0
  56. package/bundle/skills/agentera/capabilities/{optimera → optimize}/schemas/validation.yaml +9 -9
  57. package/bundle/skills/agentera/capabilities/orchestrate/schemas/artifacts.yaml +76 -0
  58. package/bundle/skills/agentera/capabilities/{orkestrera → orchestrate}/schemas/exit.yaml +9 -7
  59. package/bundle/skills/agentera/capabilities/orchestrate/schemas/triggers.yaml +23 -0
  60. package/bundle/skills/agentera/capabilities/orchestrate/schemas/validation.yaml +121 -0
  61. package/bundle/skills/agentera/capabilities/plan/schemas/artifacts.yaml +66 -0
  62. package/bundle/skills/agentera/capabilities/{planera → plan}/schemas/exit.yaml +2 -2
  63. package/bundle/skills/agentera/capabilities/plan/schemas/triggers.yaml +23 -0
  64. package/bundle/skills/agentera/capabilities/plan/schemas/validation.yaml +121 -0
  65. package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +23 -0
  66. package/bundle/skills/agentera/capabilities/{profilera → profile}/schemas/exit.yaml +6 -5
  67. package/bundle/skills/agentera/capabilities/profile/schemas/triggers.yaml +20 -0
  68. package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +115 -0
  69. package/bundle/skills/agentera/capabilities/research/schemas/artifacts.yaml +24 -0
  70. package/bundle/skills/agentera/capabilities/research/schemas/triggers.yaml +16 -0
  71. package/bundle/skills/agentera/capabilities/{inspirera → research}/schemas/validation.yaml +2 -2
  72. package/bundle/skills/agentera/capabilities/status/schemas/artifacts.yaml +73 -0
  73. package/bundle/skills/agentera/capabilities/status/schemas/triggers.yaml +22 -0
  74. package/bundle/skills/agentera/capabilities/{hej → status}/schemas/validation.yaml +5 -5
  75. package/bundle/skills/agentera/capabilities/{visionera → vision}/schemas/artifacts.yaml +16 -16
  76. package/bundle/skills/agentera/capabilities/{visionera → vision}/schemas/exit.yaml +7 -6
  77. package/bundle/skills/agentera/capabilities/vision/schemas/triggers.yaml +23 -0
  78. package/bundle/skills/agentera/capabilities/{visionera → vision}/schemas/validation.yaml +18 -20
  79. package/bundle/skills/agentera/capability_schema_contract.yaml +178 -34
  80. package/bundle/skills/agentera/protocol.yaml +31 -31
  81. package/bundle/skills/agentera/route-phrases.yaml +81 -0
  82. package/bundle/skills/agentera/schemas/artifacts/changelog.yaml +5 -4
  83. package/bundle/skills/agentera/schemas/artifacts/decisions.yaml +39 -21
  84. package/bundle/skills/agentera/schemas/artifacts/design.yaml +5 -4
  85. package/bundle/skills/agentera/schemas/artifacts/docs.yaml +15 -19
  86. package/bundle/skills/agentera/schemas/artifacts/experiments.yaml +145 -14
  87. package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +82 -0
  88. package/bundle/skills/agentera/schemas/artifacts/health.yaml +58 -21
  89. package/bundle/skills/agentera/schemas/artifacts/objective.yaml +22 -8
  90. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +458 -36
  91. package/bundle/skills/agentera/schemas/artifacts/progress.yaml +128 -17
  92. package/bundle/skills/agentera/schemas/artifacts/todo.yaml +342 -42
  93. package/bundle/skills/agentera/schemas/artifacts/vision.yaml +3 -3
  94. package/dist/analytics/extractCorpus/cli.js +190 -0
  95. package/dist/analytics/extractCorpus/copilotSessions.js +275 -0
  96. package/dist/analytics/extractCorpus/core.js +392 -0
  97. package/dist/analytics/extractCorpus/corpus.js +167 -0
  98. package/dist/analytics/extractCorpus/coverageAudit.js +260 -0
  99. package/dist/analytics/extractCorpus/cursorSessions.js +425 -0
  100. package/dist/analytics/extractCorpus/evidenceTiers.js +603 -0
  101. package/dist/analytics/extractCorpus/extractCorpusParity.js +105 -0
  102. package/dist/analytics/extractCorpus/filesystemSources.js +118 -0
  103. package/dist/analytics/extractCorpus/index.js +14 -0
  104. package/dist/analytics/extractCorpus/jsonlSessions.js +204 -0
  105. package/dist/analytics/extractCorpus/sqliteCaps.js +43 -0
  106. package/dist/analytics/extractCorpus/sqliteSessions.js +367 -0
  107. package/dist/analytics/extractCorpus/tierReader.js +108 -0
  108. package/dist/analytics/extractCorpus.js +2 -1791
  109. package/dist/analytics/glossaryAdviceResolution.js +166 -0
  110. package/dist/analytics/glossaryInputAcquisition.js +319 -0
  111. package/dist/analytics/personalGlossaryAdmission.js +133 -0
  112. package/dist/analytics/personalGlossaryProfile.js +219 -0
  113. package/dist/analytics/profileSignals.js +211 -0
  114. package/dist/analytics/usageStats.js +169 -42
  115. package/dist/audit/terminologyDrift.js +281 -0
  116. package/dist/bin/agentera.js +3 -2
  117. package/dist/capabilities/audit/instructions.js +16 -0
  118. package/dist/capabilities/build/instructions.js +56 -0
  119. package/dist/capabilities/design/instructions.js +4 -0
  120. package/dist/capabilities/discuss/instructions.js +29 -0
  121. package/dist/capabilities/document/instructions.js +11 -0
  122. package/dist/capabilities/index.js +29 -27
  123. package/dist/capabilities/optimize/instructions.js +14 -0
  124. package/dist/capabilities/orchestrate/instructions.js +42 -0
  125. package/dist/capabilities/plan/instructions.js +33 -0
  126. package/dist/capabilities/profile/instructions.js +70 -0
  127. package/dist/capabilities/research/instructions.js +5 -0
  128. package/dist/capabilities/status/instructions.js +15 -0
  129. package/dist/capabilities/status/startupInstructions.js +34 -0
  130. package/dist/capabilities/vision/instructions.js +4 -0
  131. package/dist/cli/appContext.js +30 -24
  132. package/dist/cli/argvalidate.js +0 -1
  133. package/dist/cli/capabilityContext/benchmark.js +565 -0
  134. package/dist/cli/capabilityContext/bespoke.js +44 -0
  135. package/dist/cli/capabilityContext/build.js +242 -0
  136. package/dist/cli/capabilityContext/closeout.js +233 -0
  137. package/dist/cli/capabilityContext/contract.js +179 -0
  138. package/dist/cli/capabilityContext/evidence.js +463 -0
  139. package/dist/cli/capabilityContext/index.js +3 -0
  140. package/dist/cli/capabilityContext/orchestration.js +131 -0
  141. package/dist/cli/capabilityContext/planState.js +260 -0
  142. package/dist/cli/capabilityContext/progress.js +142 -0
  143. package/dist/cli/capabilityContext/projectVerification.js +409 -0
  144. package/dist/cli/capabilityContext/shared.js +83 -0
  145. package/dist/cli/capabilityContext/slim.js +152 -0
  146. package/dist/cli/capabilityContext/startup.js +150 -0
  147. package/dist/cli/capabilityContext/startupAggregation.js +68 -0
  148. package/dist/cli/capabilityContext/types.js +56 -0
  149. package/dist/cli/capabilityContext.js +1 -2487
  150. package/dist/cli/commands/appHome.js +22 -0
  151. package/dist/cli/commands/capability.js +4 -4
  152. package/dist/cli/commands/compact.js +164 -14
  153. package/dist/cli/commands/doctor.js +122 -97
  154. package/dist/cli/commands/durability.js +81 -0
  155. package/dist/cli/commands/glossaryAdvice.js +138 -0
  156. package/dist/cli/commands/lint.js +26 -44
  157. package/dist/cli/commands/personalGlossary.js +176 -0
  158. package/dist/cli/commands/prime/briefOrientation.js +747 -0
  159. package/dist/cli/commands/prime/buildExecutionRequest.js +165 -0
  160. package/dist/cli/commands/prime/bundleStatus.js +190 -0
  161. package/dist/cli/commands/prime/collectEntityOrientation.js +326 -0
  162. package/dist/cli/commands/prime/collectOrientationState.js +212 -0
  163. package/dist/cli/commands/prime/orientationOutput.js +399 -0
  164. package/dist/cli/commands/prime/types.js +1 -0
  165. package/dist/cli/commands/prime/v1Migration.js +38 -0
  166. package/dist/cli/commands/prime.js +80 -558
  167. package/dist/cli/commands/profileGrounding.js +55 -0
  168. package/dist/cli/commands/query.js +78 -53
  169. package/dist/cli/commands/report.js +96 -25
  170. package/dist/cli/commands/route.js +142 -0
  171. package/dist/cli/commands/schema.js +266 -143
  172. package/dist/cli/commands/state/decisions.js +387 -0
  173. package/dist/cli/commands/state/docs.js +70 -0
  174. package/dist/cli/commands/state/experimentRecords.js +151 -0
  175. package/dist/cli/commands/state/experiments.js +14 -0
  176. package/dist/cli/commands/state/get.js +129 -0
  177. package/dist/cli/commands/state/health.js +14 -0
  178. package/dist/cli/commands/state/index.js +16 -0
  179. package/dist/cli/commands/state/list.js +194 -0
  180. package/dist/cli/commands/state/objective.js +25 -0
  181. package/dist/cli/commands/state/plan.js +14 -0
  182. package/dist/cli/commands/state/planTasks.js +154 -0
  183. package/dist/cli/commands/state/plans.js +154 -0
  184. package/dist/cli/commands/state/progress.js +13 -0
  185. package/dist/cli/commands/state/shared.js +14 -0
  186. package/dist/cli/commands/state/todo.js +42 -0
  187. package/dist/cli/commands/state/write.js +430 -0
  188. package/dist/cli/commands/upgrade.js +146 -5
  189. package/dist/cli/commands/upgradeVerify.js +118 -0
  190. package/dist/cli/commands/validate.js +272 -223
  191. package/dist/cli/commands/verify.js +24 -9
  192. package/dist/cli/contracts/bundleStatus.js +1 -0
  193. package/dist/cli/contracts/orientationState.js +1 -0
  194. package/dist/cli/dispatch/argvParser.js +32 -0
  195. package/dist/cli/dispatch/check.js +372 -0
  196. package/dist/cli/dispatch/commands.js +27 -0
  197. package/dist/cli/dispatch/index.js +207 -0
  198. package/dist/cli/dispatch/lifecycle.js +652 -0
  199. package/dist/cli/dispatch/prime.js +139 -0
  200. package/dist/cli/dispatch/projections.js +17 -0
  201. package/dist/cli/dispatch/shared.js +63 -0
  202. package/dist/cli/dispatch/state.js +140 -0
  203. package/dist/cli/dispatch.js +1 -1294
  204. package/dist/cli/errors.js +16 -17
  205. package/dist/cli/help.js +394 -43
  206. package/dist/cli/migrationRequired.js +87 -0
  207. package/dist/cli/orientation/attention.js +61 -0
  208. package/dist/cli/orientation/corpusCoverage.js +102 -0
  209. package/dist/cli/orientation.js +438 -170
  210. package/dist/cli/planArtifacts.js +350 -0
  211. package/dist/cli/planEvidence.js +157 -0
  212. package/dist/cli/planLifecycleState.js +36 -0
  213. package/dist/cli/planTaskIndex.js +11 -0
  214. package/dist/cli/preCutoverCommand.js +32 -0
  215. package/dist/cli/prime-blob.js +71 -3
  216. package/dist/cli/profileAcquisition.js +167 -0
  217. package/dist/cli/startupCompletenessContract.js +59 -0
  218. package/dist/cli/stateQuery.js +56 -14
  219. package/dist/cli/structured.js +0 -1
  220. package/dist/cli/todoMarkdown.js +25 -12
  221. package/dist/cli/todoReadinessSelection.js +192 -0
  222. package/dist/cli/todoSeverity.js +18 -0
  223. package/dist/core/atomicWriter.js +20 -0
  224. package/dist/core/developmentChannel.js +1 -0
  225. package/dist/core/developmentInvocation.js +374 -0
  226. package/dist/core/difflib.js +0 -1
  227. package/dist/core/envPaths.js +20 -0
  228. package/dist/core/jsonValue.js +5 -0
  229. package/dist/core/paths.js +0 -1
  230. package/dist/core/pyjson.js +67 -3
  231. package/dist/core/shell.js +4 -0
  232. package/dist/core/skillIdentity.js +23 -0
  233. package/dist/core/sourceRoot.js +9 -12
  234. package/dist/core/text.js +10 -0
  235. package/dist/core/toml.js +0 -1
  236. package/dist/core/yaml.js +8 -1
  237. package/dist/eval/evalSkills.js +30 -26
  238. package/dist/eval/hybridRouteEvaluation.js +155 -0
  239. package/dist/eval/semanticEval.js +43 -20
  240. package/dist/eval/semanticFixtures.js +16 -5
  241. package/dist/hooks/common.js +11 -79
  242. package/dist/hooks/compaction/apply.js +446 -0
  243. package/dist/hooks/compaction/dryRun.js +184 -0
  244. package/dist/hooks/compaction/index.js +22 -0
  245. package/dist/hooks/compaction/parse.js +325 -0
  246. package/dist/hooks/compaction/retention.js +202 -0
  247. package/dist/hooks/compaction/status.js +360 -0
  248. package/dist/hooks/compaction/types.js +5 -0
  249. package/dist/hooks/validateArtifact/agentFacing.js +9 -0
  250. package/dist/hooks/validateArtifact/index.js +92 -0
  251. package/dist/hooks/validateArtifact/markdown.js +145 -0
  252. package/dist/hooks/validateArtifact/schema.js +546 -0
  253. package/dist/hooks/validateArtifact/traversal.js +107 -0
  254. package/dist/hooks/validateArtifact/violations.js +107 -0
  255. package/dist/migrate/v2HandoffManifest.js +452 -0
  256. package/dist/registries/activationContract.js +2 -0
  257. package/dist/registries/activationTuples.js +107 -0
  258. package/dist/registries/artifactProtocolIds.js +78 -0
  259. package/dist/registries/artifactRegistry.js +224 -63
  260. package/dist/registries/artifactSchemaProjection.js +45 -0
  261. package/dist/registries/capabilityContract.js +129 -2
  262. package/dist/registries/evaluatorHandoffContract.js +2 -3
  263. package/dist/registries/evidenceTierContract.js +371 -0
  264. package/dist/registries/glossaryAdviceContract.js +79 -0
  265. package/dist/registries/glossaryCaveatContract.js +67 -0
  266. package/dist/registries/glossaryConsumerContractValidation.js +787 -0
  267. package/dist/registries/glossaryEntryContract.js +757 -0
  268. package/dist/registries/glossaryTermIdentity.js +17 -0
  269. package/dist/registries/glossaryTermOccurrence.js +20 -0
  270. package/dist/registries/hybridRoute.js +274 -0
  271. package/dist/registries/hybridRouteReceipt.js +221 -0
  272. package/dist/registries/packagePublication.js +229 -0
  273. package/dist/registries/packageRegistry.js +187 -211
  274. package/dist/registries/todoReadinessContract.js +448 -0
  275. package/dist/registries/triggerLoader.js +174 -0
  276. package/dist/registries/verbosityBudgetContract.js +205 -0
  277. package/dist/release/releaseMetadata.js +385 -0
  278. package/dist/runtime/lifecycleAdapterContract.js +457 -0
  279. package/dist/runtime/lifecycleAdapters.js +777 -0
  280. package/dist/runtime/lifecycleAuthority.js +453 -0
  281. package/dist/runtime/lifecycleOperationContract.js +123 -0
  282. package/dist/runtime/lifecycleOperations.js +634 -0
  283. package/dist/runtime/lifecycleOwnershipJournal.js +622 -0
  284. package/dist/runtime/lifecyclePublication.js +415 -0
  285. package/dist/runtime/lifecycleSnapshot.js +438 -0
  286. package/dist/runtime/nativeResourceCleanup.js +502 -0
  287. package/dist/setup/opencode.js +20 -0
  288. package/dist/setup/opencodeConstants.js +1 -0
  289. package/dist/setup/sharedSkill.js +45 -0
  290. package/dist/setup/smokeChecks.js +21 -82
  291. package/dist/state/archiveDiscovery.js +580 -0
  292. package/dist/state/archivePublication.js +225 -0
  293. package/dist/state/archiveRecovery.js +237 -0
  294. package/dist/state/archiveReplay.js +53 -0
  295. package/dist/state/canonicalMigrationRecord.js +36 -0
  296. package/dist/state/changelog.js +221 -0
  297. package/dist/state/decisionEntities.js +367 -0
  298. package/dist/state/decisionLegacyValidation.js +215 -0
  299. package/dist/state/decisionMigrationProvenance.js +199 -0
  300. package/dist/state/decisionOverlay.js +254 -0
  301. package/dist/state/decisionRevision.js +256 -0
  302. package/dist/state/decisionRevisionMigration.js +89 -0
  303. package/dist/state/directRetrieval.js +323 -0
  304. package/dist/state/durability.js +324 -0
  305. package/dist/state/entityCutover.js +459 -0
  306. package/dist/state/entityCutoverGit.js +127 -0
  307. package/dist/state/entityGlossaryAuthority.js +13 -0
  308. package/dist/state/entityListProjection.js +187 -0
  309. package/dist/state/entityListRuntimeRegistry.js +85 -0
  310. package/dist/state/entityMigrationCausality.js +97 -0
  311. package/dist/state/entityMigrationIdentity.js +5 -0
  312. package/dist/state/entityMigrationPreview.js +846 -0
  313. package/dist/state/entityMigrationTodo.js +133 -0
  314. package/dist/state/entityPublicationContext.js +806 -0
  315. package/dist/state/entityRetrievalHelp.js +506 -0
  316. package/dist/state/entityStorage.js +899 -0
  317. package/dist/state/exactReplacementRecovery.js +18 -0
  318. package/dist/state/experimentArchive.js +63 -0
  319. package/dist/state/experimentIdentity.js +202 -0
  320. package/dist/state/healthEntities.js +262 -0
  321. package/dist/state/healthEntityValidation.js +18 -0
  322. package/dist/state/healthRepair.js +69 -0
  323. package/dist/state/installRoot.js +64 -25
  324. package/dist/state/legacyEntityCutoverEvidence.js +65 -0
  325. package/dist/state/legacyIdentity.js +47 -0
  326. package/dist/state/legacySummaryRecord.js +6 -0
  327. package/dist/state/listAccounting.js +54 -0
  328. package/dist/state/listClassification.js +17 -0
  329. package/dist/state/listCursor.js +42 -0
  330. package/dist/state/listRetrieval.js +781 -0
  331. package/dist/state/migrationSourceBinding.js +30 -0
  332. package/dist/state/objectiveExperimentEntities.js +322 -0
  333. package/dist/state/planEntities.js +982 -0
  334. package/dist/state/planIdentity.js +47 -0
  335. package/dist/state/planLineageValidation.js +52 -0
  336. package/dist/state/planReplacementTransaction.js +475 -0
  337. package/dist/state/planRetrieval.js +313 -0
  338. package/dist/state/planTaskRetrieval.js +294 -0
  339. package/dist/state/progressEntities.js +512 -0
  340. package/dist/state/progressGlossaryCaveat.js +168 -0
  341. package/dist/state/progressPublicationOrder.js +19 -0
  342. package/dist/state/projectRoot.js +62 -0
  343. package/dist/state/projectionPolicy.js +190 -0
  344. package/dist/state/retrievalAuthority.js +156 -0
  345. package/dist/state/safeProjectFile.js +155 -0
  346. package/dist/state/startupAnalysis/benchmark.js +432 -0
  347. package/dist/state/startupAnalysis/contract.js +160 -0
  348. package/dist/state/startupAnalysis/helpers.js +286 -0
  349. package/dist/state/startupAnalysis/index.js +6 -0
  350. package/dist/state/startupAnalysis/metrics.js +340 -0
  351. package/dist/state/startupAnalysis/records.js +198 -0
  352. package/dist/state/startupAnalysis/report.js +111 -0
  353. package/dist/state/startupAnalysis/threshold.js +512 -0
  354. package/dist/state/startupAnalysis.js +2 -1953
  355. package/dist/state/startupProjection.js +499 -0
  356. package/dist/state/stateMode.js +85 -0
  357. package/dist/state/stateStorageAuthority.js +47 -0
  358. package/dist/state/summaryEntityRead.js +25 -0
  359. package/dist/state/summaryMigrationProvenance.js +66 -0
  360. package/dist/state/summarySourceRowAuthority.js +36 -0
  361. package/dist/state/todoActivationSafety.js +58 -0
  362. package/dist/state/todoDocsEntities.js +1069 -0
  363. package/dist/state/todoDocsEntityValidation.js +158 -0
  364. package/dist/state/todoMarkdownProjection.js +51 -0
  365. package/dist/state/todoReconciliationActivation.js +140 -0
  366. package/dist/state/todoReconciliationInspection.js +169 -0
  367. package/dist/state/todoReconciliationRepair.js +170 -0
  368. package/dist/state/todoReconciliationTransaction.js +582 -0
  369. package/dist/state/write/assign.js +20 -0
  370. package/dist/state/write/candidateMutation.js +269 -0
  371. package/dist/state/write/errors.js +11 -0
  372. package/dist/state/write/experimentPublication.js +192 -0
  373. package/dist/state/write/explain.js +363 -0
  374. package/dist/state/write/fields.js +61 -0
  375. package/dist/state/write/glossaryPublication.js +263 -0
  376. package/dist/state/write/grammar.js +285 -0
  377. package/dist/state/write/helpers.js +29 -0
  378. package/dist/state/write/index.js +12 -0
  379. package/dist/state/write/input.js +367 -0
  380. package/dist/state/write/lock.js +837 -0
  381. package/dist/state/write/mutation.js +234 -0
  382. package/dist/state/write/operations.js +170 -0
  383. package/dist/state/write/planEvaluation.js +128 -0
  384. package/dist/state/write/planPublication.js +179 -0
  385. package/dist/state/write/runtimeOperations.js +144 -0
  386. package/dist/state/write/serialize.js +1 -0
  387. package/dist/state/write/transaction.js +52 -0
  388. package/dist/state/write/validate.js +7 -0
  389. package/dist/upgrade/appContentRefresh.js +383 -0
  390. package/dist/upgrade/appModel.js +8 -7
  391. package/dist/upgrade/atomicWriter.js +1 -0
  392. package/dist/upgrade/bundleEvidence.js +33 -0
  393. package/dist/upgrade/channels.js +12 -8
  394. package/dist/upgrade/compatibility.js +56 -21
  395. package/dist/upgrade/doctor.js +197 -192
  396. package/dist/upgrade/doctorClassifier.js +187 -0
  397. package/dist/upgrade/installedHooksRetirement.js +196 -0
  398. package/dist/upgrade/legacyAgentCleanup.js +282 -0
  399. package/dist/upgrade/lifecycleUpgrade.js +113 -0
  400. package/dist/upgrade/migrateArtifactsV2ToV3.js +367 -149
  401. package/dist/upgrade/migrationPublication.js +122 -0
  402. package/dist/upgrade/nextMajorDoctor.js +42 -8
  403. package/dist/upgrade/npxPlatformStatus.js +22 -0
  404. package/dist/upgrade/projectIntegration.js +145 -187
  405. package/dist/upgrade/projectIntegrationDecision.js +50 -0
  406. package/dist/upgrade/retiredResourceDiagnostics.js +117 -0
  407. package/dist/upgrade/runtimeMigration.js +273 -363
  408. package/dist/upgrade/upgradeCommands.js +14 -1
  409. package/dist/upgrade/upgradeLock.js +85 -0
  410. package/dist/upgrade/upgradeOrchestrator.js +263 -59
  411. package/dist/upgrade/v3CapabilitySurface.js +14 -0
  412. package/dist/upgrade/versionResolution.js +8 -1
  413. package/dist/validate/activationArtifactEvidence.js +844 -0
  414. package/dist/validate/activationConjunction.js +816 -0
  415. package/dist/validate/activationEvidenceManifest.js +515 -0
  416. package/dist/validate/activationPackageSemantics.js +29 -0
  417. package/dist/validate/appHomeContract.js +5 -11
  418. package/dist/validate/bootstrapAuthority.js +44 -0
  419. package/dist/validate/capability.js +133 -12
  420. package/dist/validate/crossCapability.js +15 -10
  421. package/dist/validate/retainedReferenceAuthority.js +879 -0
  422. package/dist/validate/selfAudit.js +73 -37
  423. package/dist/validate/v1LegacyCruft.js +131 -0
  424. package/dist/validate/vocabularyAuthority.js +303 -0
  425. package/package.json +53 -27
  426. package/bundle/references/adapters/cursor.md +0 -213
  427. package/bundle/references/adapters/opencode.md +0 -530
  428. package/bundle/references/adapters/package-manifest-interface-model.yaml +0 -337
  429. package/bundle/references/adapters/runtime-adapter-characterization.md +0 -79
  430. package/bundle/references/adapters/runtime-adapter-interface-model.yaml +0 -200
  431. package/bundle/references/adapters/runtime-adapter-registry.yaml +0 -548
  432. package/bundle/references/adapters/runtime-feature-parity.md +0 -189
  433. package/bundle/references/analysis/benchmark.md +0 -267
  434. package/bundle/references/analysis/startup-measurement-contract.yaml +0 -424
  435. package/bundle/references/cli/agent-ready-state-contract.yaml +0 -953
  436. package/bundle/references/cli/audience-namespace-cli-migration.yaml +0 -355
  437. package/bundle/references/cli/bundle-skill-vocabulary.yaml +0 -278
  438. package/bundle/references/cli/capability-tool-classification.yaml +0 -53
  439. package/bundle/references/cli/routing-execution-vocabulary.yaml +0 -281
  440. package/bundle/references/meta/documentation-inventory.md +0 -43
  441. package/bundle/references/v1-section-mapping.md +0 -47
  442. package/bundle/skills/agentera/.claude-plugin/plugin.json +0 -27
  443. package/bundle/skills/agentera/agents/dokumentera.toml +0 -6
  444. package/bundle/skills/agentera/agents/hej.toml +0 -6
  445. package/bundle/skills/agentera/agents/inspektera.toml +0 -6
  446. package/bundle/skills/agentera/agents/inspirera.toml +0 -6
  447. package/bundle/skills/agentera/agents/optimera.toml +0 -6
  448. package/bundle/skills/agentera/agents/orkestrera.toml +0 -6
  449. package/bundle/skills/agentera/agents/planera.toml +0 -6
  450. package/bundle/skills/agentera/agents/profilera.toml +0 -6
  451. package/bundle/skills/agentera/agents/realisera.toml +0 -6
  452. package/bundle/skills/agentera/agents/resonera.toml +0 -6
  453. package/bundle/skills/agentera/agents/visionera.toml +0 -6
  454. package/bundle/skills/agentera/agents/visualisera.toml +0 -6
  455. package/bundle/skills/agentera/capabilities/dokumentera/schemas/artifacts.yaml +0 -73
  456. package/bundle/skills/agentera/capabilities/dokumentera/schemas/triggers.yaml +0 -35
  457. package/bundle/skills/agentera/capabilities/hej/schemas/artifacts.yaml +0 -69
  458. package/bundle/skills/agentera/capabilities/hej/schemas/triggers.yaml +0 -58
  459. package/bundle/skills/agentera/capabilities/inspektera/schemas/artifacts.yaml +0 -76
  460. package/bundle/skills/agentera/capabilities/inspektera/schemas/triggers.yaml +0 -38
  461. package/bundle/skills/agentera/capabilities/inspirera/schemas/artifacts.yaml +0 -24
  462. package/bundle/skills/agentera/capabilities/inspirera/schemas/triggers.yaml +0 -34
  463. package/bundle/skills/agentera/capabilities/optimera/schemas/triggers.yaml +0 -39
  464. package/bundle/skills/agentera/capabilities/orkestrera/schemas/artifacts.yaml +0 -64
  465. package/bundle/skills/agentera/capabilities/orkestrera/schemas/triggers.yaml +0 -42
  466. package/bundle/skills/agentera/capabilities/orkestrera/schemas/validation.yaml +0 -120
  467. package/bundle/skills/agentera/capabilities/planera/schemas/artifacts.yaml +0 -62
  468. package/bundle/skills/agentera/capabilities/planera/schemas/triggers.yaml +0 -34
  469. package/bundle/skills/agentera/capabilities/planera/schemas/validation.yaml +0 -61
  470. package/bundle/skills/agentera/capabilities/profilera/schemas/artifacts.yaml +0 -18
  471. package/bundle/skills/agentera/capabilities/profilera/schemas/triggers.yaml +0 -45
  472. package/bundle/skills/agentera/capabilities/profilera/schemas/validation.yaml +0 -57
  473. package/bundle/skills/agentera/capabilities/realisera/schemas/artifacts.yaml +0 -80
  474. package/bundle/skills/agentera/capabilities/realisera/schemas/triggers.yaml +0 -39
  475. package/bundle/skills/agentera/capabilities/realisera/schemas/validation.yaml +0 -110
  476. package/bundle/skills/agentera/capabilities/resonera/schemas/artifacts.yaml +0 -47
  477. package/bundle/skills/agentera/capabilities/resonera/schemas/triggers.yaml +0 -46
  478. package/bundle/skills/agentera/capabilities/resonera/schemas/validation.yaml +0 -77
  479. package/bundle/skills/agentera/capabilities/visionera/schemas/triggers.yaml +0 -41
  480. package/bundle/skills/agentera/capabilities/visualisera/schemas/triggers.yaml +0 -33
  481. package/bundle/skills/agentera/references/contract.md +0 -1039
  482. package/bundle/skills/hej/.claude-plugin/plugin.json +0 -6
  483. package/bundle/skills/hej/SKILL.md +0 -69
  484. package/bundle/skills/hej/agents/hej.toml +0 -11
  485. package/bundle/skills/hej/agents/openai.yaml +0 -8
  486. package/dist/analytics/extractCorpus.js.map +0 -1
  487. package/dist/analytics/usageStats.js.map +0 -1
  488. package/dist/bin/agentera.js.map +0 -1
  489. package/dist/capabilities/dokumentera/instructions.js +0 -6
  490. package/dist/capabilities/dokumentera/instructions.js.map +0 -1
  491. package/dist/capabilities/hej/instructions.js +0 -6
  492. package/dist/capabilities/hej/instructions.js.map +0 -1
  493. package/dist/capabilities/index.js.map +0 -1
  494. package/dist/capabilities/inspektera/instructions.js +0 -6
  495. package/dist/capabilities/inspektera/instructions.js.map +0 -1
  496. package/dist/capabilities/inspirera/instructions.js +0 -6
  497. package/dist/capabilities/inspirera/instructions.js.map +0 -1
  498. package/dist/capabilities/optimera/instructions.js +0 -6
  499. package/dist/capabilities/optimera/instructions.js.map +0 -1
  500. package/dist/capabilities/orkestrera/instructions.js +0 -6
  501. package/dist/capabilities/orkestrera/instructions.js.map +0 -1
  502. package/dist/capabilities/planera/instructions.js +0 -6
  503. package/dist/capabilities/planera/instructions.js.map +0 -1
  504. package/dist/capabilities/profilera/instructions.js +0 -6
  505. package/dist/capabilities/profilera/instructions.js.map +0 -1
  506. package/dist/capabilities/realisera/instructions.js +0 -6
  507. package/dist/capabilities/realisera/instructions.js.map +0 -1
  508. package/dist/capabilities/resonera/instructions.js +0 -6
  509. package/dist/capabilities/resonera/instructions.js.map +0 -1
  510. package/dist/capabilities/visionera/instructions.js +0 -6
  511. package/dist/capabilities/visionera/instructions.js.map +0 -1
  512. package/dist/capabilities/visualisera/instructions.js +0 -6
  513. package/dist/capabilities/visualisera/instructions.js.map +0 -1
  514. package/dist/cli/appContext.js.map +0 -1
  515. package/dist/cli/argvalidate.js.map +0 -1
  516. package/dist/cli/capabilityContext.js.map +0 -1
  517. package/dist/cli/commands/capability.js.map +0 -1
  518. package/dist/cli/commands/compact.js.map +0 -1
  519. package/dist/cli/commands/doctor.js.map +0 -1
  520. package/dist/cli/commands/lint.js.map +0 -1
  521. package/dist/cli/commands/prime.js.map +0 -1
  522. package/dist/cli/commands/query.js.map +0 -1
  523. package/dist/cli/commands/report.js.map +0 -1
  524. package/dist/cli/commands/schema.js.map +0 -1
  525. package/dist/cli/commands/state.js +0 -1023
  526. package/dist/cli/commands/state.js.map +0 -1
  527. package/dist/cli/commands/upgrade.js.map +0 -1
  528. package/dist/cli/commands/validate.js.map +0 -1
  529. package/dist/cli/commands/verify.js.map +0 -1
  530. package/dist/cli/dispatch.js.map +0 -1
  531. package/dist/cli/errors.js.map +0 -1
  532. package/dist/cli/help.js.map +0 -1
  533. package/dist/cli/orientation.js.map +0 -1
  534. package/dist/cli/prime-blob.js.map +0 -1
  535. package/dist/cli/stateQuery.js.map +0 -1
  536. package/dist/cli/structured.js.map +0 -1
  537. package/dist/cli/todoMarkdown.js.map +0 -1
  538. package/dist/core/difflib.js.map +0 -1
  539. package/dist/core/paths.js.map +0 -1
  540. package/dist/core/pyjson.js.map +0 -1
  541. package/dist/core/sourceRoot.js.map +0 -1
  542. package/dist/core/toml.js.map +0 -1
  543. package/dist/core/yaml.js.map +0 -1
  544. package/dist/eval/evalSkills.js.map +0 -1
  545. package/dist/eval/semanticEval.js.map +0 -1
  546. package/dist/eval/semanticFixtures.js.map +0 -1
  547. package/dist/hooks/common.js.map +0 -1
  548. package/dist/hooks/compaction.js +0 -935
  549. package/dist/hooks/compaction.js.map +0 -1
  550. package/dist/hooks/cursorPreToolUse.js +0 -19
  551. package/dist/hooks/cursorPreToolUse.js.map +0 -1
  552. package/dist/hooks/cursorSessionStart.js +0 -71
  553. package/dist/hooks/cursorSessionStart.js.map +0 -1
  554. package/dist/hooks/sessionStart.js +0 -209
  555. package/dist/hooks/sessionStart.js.map +0 -1
  556. package/dist/hooks/sessionStop.js +0 -212
  557. package/dist/hooks/sessionStop.js.map +0 -1
  558. package/dist/hooks/validateArtifact.js +0 -935
  559. package/dist/hooks/validateArtifact.js.map +0 -1
  560. package/dist/registries/artifactRegistry.js.map +0 -1
  561. package/dist/registries/capabilityContract.js.map +0 -1
  562. package/dist/registries/evaluatorHandoffContract.js.map +0 -1
  563. package/dist/registries/packageRegistry.js.map +0 -1
  564. package/dist/registries/runtimeAdapterRegistry.js +0 -315
  565. package/dist/registries/runtimeAdapterRegistry.js.map +0 -1
  566. package/dist/setup/codex.js +0 -1197
  567. package/dist/setup/codex.js.map +0 -1
  568. package/dist/setup/copilot.js +0 -227
  569. package/dist/setup/copilot.js.map +0 -1
  570. package/dist/setup/cursor.js +0 -127
  571. package/dist/setup/cursor.js.map +0 -1
  572. package/dist/setup/doctor.js +0 -1297
  573. package/dist/setup/doctor.js.map +0 -1
  574. package/dist/setup/smokeChecks.js.map +0 -1
  575. package/dist/state/installRoot.js.map +0 -1
  576. package/dist/state/startupAnalysis.js.map +0 -1
  577. package/dist/upgrade/appModel.js.map +0 -1
  578. package/dist/upgrade/channels.js.map +0 -1
  579. package/dist/upgrade/compatibility.js.map +0 -1
  580. package/dist/upgrade/doctor.js.map +0 -1
  581. package/dist/upgrade/migrateArtifactsV1ToV2.js +0 -570
  582. package/dist/upgrade/migrateArtifactsV1ToV2.js.map +0 -1
  583. package/dist/upgrade/migrateArtifactsV2ToV3.js.map +0 -1
  584. package/dist/upgrade/nextMajorDoctor.js.map +0 -1
  585. package/dist/upgrade/projectIntegration.js.map +0 -1
  586. package/dist/upgrade/runtimeMigration.js.map +0 -1
  587. package/dist/upgrade/upgradeCommands.js.map +0 -1
  588. package/dist/upgrade/upgradeOrchestrator.js.map +0 -1
  589. package/dist/upgrade/versionResolution.js.map +0 -1
  590. package/dist/validate/appHomeContract.js.map +0 -1
  591. package/dist/validate/capability.js.map +0 -1
  592. package/dist/validate/crossCapability.js.map +0 -1
  593. package/dist/validate/lifecycleAdapters.js +0 -772
  594. package/dist/validate/lifecycleAdapters.js.map +0 -1
  595. package/dist/validate/selfAudit.js.map +0 -1
  596. /package/bundle/skills/agentera/capabilities/{dokumentera → document}/schemas/exit.yaml +0 -0
  597. /package/bundle/skills/agentera/capabilities/{inspirera → research}/schemas/exit.yaml +0 -0
  598. /package/bundle/skills/agentera/capabilities/{hej → status}/schemas/exit.yaml +0 -0
@@ -0,0 +1,806 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { ExactReplacementConflictError, FileReplacementError } from "./exactReplacementRecovery.js";
5
+ import { assertValidatedProjectRoot } from "./projectRoot.js";
6
+ import { reject, StateWriteInputError } from "./write/errors.js";
7
+ const RECOVERY_IGNORE_BYTES = "*\n!.gitignore\n";
8
+ export const FILE_REPLACEMENT_RECOVERY_VERSION = "agentera.fileReplacementRecovery.v1";
9
+ export const FILE_REPLACEMENT_METADATA_NAME = "replacement.json";
10
+ function identity(stat) {
11
+ return {
12
+ dev: stat.dev,
13
+ ino: stat.ino,
14
+ type: stat.mode & BigInt(fs.constants.S_IFMT),
15
+ size: stat.size,
16
+ mtimeNs: stat.mtimeNs,
17
+ ctimeNs: stat.ctimeNs,
18
+ nlink: stat.nlink,
19
+ mode: stat.mode,
20
+ };
21
+ }
22
+ function sameIdentity(left, right) {
23
+ return left.dev === right.dev
24
+ && left.ino === right.ino
25
+ && left.type === right.type
26
+ && left.size === right.size
27
+ && left.mtimeNs === right.mtimeNs
28
+ && left.ctimeNs === right.ctimeNs
29
+ && left.nlink === right.nlink;
30
+ }
31
+ function sameDirectory(left, right) {
32
+ return right.isDirectory()
33
+ && !right.isSymbolicLink()
34
+ && left.dev === right.dev
35
+ && left.ino === right.ino;
36
+ }
37
+ function sha256(bytes) {
38
+ return createHash("sha256").update(bytes).digest("hex");
39
+ }
40
+ function publishedIdentity(file) {
41
+ return {
42
+ dev: file.identity.dev,
43
+ ino: file.identity.ino,
44
+ type: file.identity.type,
45
+ size: file.identity.size,
46
+ sha256: sha256(file.bytes),
47
+ };
48
+ }
49
+ function matchesPublished(file, expected) {
50
+ const observed = publishedIdentity(file);
51
+ return observed.dev === expected.dev
52
+ && observed.ino === expected.ino
53
+ && observed.type === expected.type
54
+ && observed.size === expected.size
55
+ && observed.sha256 === expected.sha256;
56
+ }
57
+ function matchesExpected(file, expected) {
58
+ return Buffer.isBuffer(expected) ? file.bytes.equals(expected) : matchesPublished(file, expected);
59
+ }
60
+ function sameStableFile(left, right) {
61
+ return sameIdentity(left.identity, right.identity) && left.bytes.equals(right.bytes);
62
+ }
63
+ function readStableFile(absolute, label) {
64
+ let before;
65
+ try {
66
+ before = fs.lstatSync(absolute, { bigint: true });
67
+ }
68
+ catch (error) {
69
+ throw new Error(`${label} is unavailable: ${error.message}`, { cause: error });
70
+ }
71
+ if (!before.isFile() || before.isSymbolicLink()) {
72
+ throw new Error(`${label} is not a safe regular file`);
73
+ }
74
+ let descriptor;
75
+ try {
76
+ descriptor = fs.openSync(absolute, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
77
+ const opened = fs.fstatSync(descriptor, { bigint: true });
78
+ if (!opened.isFile() || opened.isSymbolicLink() || opened.dev !== before.dev || opened.ino !== before.ino) {
79
+ throw new Error(`${label} changed while it was opened`);
80
+ }
81
+ const bytes = fs.readFileSync(descriptor);
82
+ const after = fs.fstatSync(descriptor, { bigint: true });
83
+ const pathname = fs.lstatSync(absolute, { bigint: true });
84
+ const openedIdentity = identity(opened);
85
+ if (!sameIdentity(openedIdentity, identity(after))
86
+ || !sameIdentity(openedIdentity, identity(pathname))
87
+ || BigInt(bytes.length) !== opened.size)
88
+ throw new Error(`${label} changed while its exact bytes were read`);
89
+ return { absolute, identity: identity(after), bytes };
90
+ }
91
+ finally {
92
+ if (descriptor !== undefined)
93
+ fs.closeSync(descriptor);
94
+ }
95
+ }
96
+ function readFileIfPresent(absolute, label) {
97
+ try {
98
+ fs.lstatSync(absolute);
99
+ }
100
+ catch (error) {
101
+ if (error.code === "ENOENT")
102
+ return null;
103
+ throw error;
104
+ }
105
+ return readStableFile(absolute, label);
106
+ }
107
+ function readStableDirectory(absolute, created = false) {
108
+ const stat = fs.lstatSync(absolute, { bigint: true });
109
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
110
+ throw new Error(`publication directory '${absolute}' is not a safe real directory`);
111
+ }
112
+ return { absolute, dev: stat.dev, ino: stat.ino, created };
113
+ }
114
+ function projectLocalPath(root, absolute, allowRoot = false) {
115
+ const resolved = path.resolve(absolute);
116
+ const relative = path.relative(root.path, resolved);
117
+ return resolved === absolute
118
+ && (allowRoot || relative.length > 0)
119
+ && relative !== ".."
120
+ && !relative.startsWith(`..${path.sep}`)
121
+ && !path.isAbsolute(relative);
122
+ }
123
+ /** Validate a recovery directory before listing or mutating anything through it. */
124
+ export function validateEntityRecoveryDirectory(root, absolute, targetDirectory, label, expected) {
125
+ assertValidatedProjectRoot(root);
126
+ if (!projectLocalPath(root, absolute) || !projectLocalPath(root, targetDirectory, true)) {
127
+ throw new FileReplacementError(`${label} must be a real project-local directory under the validated project root`);
128
+ }
129
+ let directory;
130
+ let target;
131
+ try {
132
+ directory = readStableDirectory(absolute);
133
+ target = readStableDirectory(targetDirectory);
134
+ if (fs.realpathSync(absolute) !== absolute || fs.realpathSync(targetDirectory) !== targetDirectory) {
135
+ throw new Error("a directory path traverses a symbolic link");
136
+ }
137
+ }
138
+ catch (error) {
139
+ throw new FileReplacementError(`${label} must be a real project-local non-symlink directory: ${error.message}`, error);
140
+ }
141
+ if (typeof process.getuid === "function") {
142
+ const stat = fs.lstatSync(directory.absolute, { bigint: true });
143
+ if ((stat.mode & 18n) !== 0n || stat.uid !== BigInt(process.getuid())) {
144
+ throw new FileReplacementError(`${label} must be owned by the current user and not group/world-writable`);
145
+ }
146
+ }
147
+ if (directory.dev !== target.dev) {
148
+ throw new FileReplacementError(`${label} must share the replacement target filesystem`);
149
+ }
150
+ if (expected && (directory.absolute !== expected.absolute
151
+ || directory.dev !== expected.dev
152
+ || directory.ino !== expected.ino)) {
153
+ throw new FileReplacementError(`${label} changed after its recovery boundary was validated`);
154
+ }
155
+ return { absolute: directory.absolute, dev: directory.dev, ino: directory.ino };
156
+ }
157
+ function syncDirectory(absolute) {
158
+ if (process.platform === "win32")
159
+ return;
160
+ let descriptor;
161
+ try {
162
+ descriptor = fs.openSync(absolute, fs.constants.O_RDONLY | (fs.constants.O_DIRECTORY ?? 0));
163
+ fs.fsyncSync(descriptor);
164
+ }
165
+ catch (error) {
166
+ if (!["EINVAL", "ENOTSUP", "ENOSYS"].includes(error.code ?? ""))
167
+ throw error;
168
+ }
169
+ finally {
170
+ if (descriptor !== undefined)
171
+ fs.closeSync(descriptor);
172
+ }
173
+ }
174
+ function syncDirectoryBestEffort(absolute) {
175
+ try {
176
+ syncDirectory(absolute);
177
+ }
178
+ catch { /* The canonical rename already linearized. */ }
179
+ }
180
+ function safeSegments(relativeTarget, label) {
181
+ const segments = relativeTarget.split(/[\\/]/).filter(Boolean);
182
+ if (segments.length < 1
183
+ || path.posix.isAbsolute(relativeTarget)
184
+ || path.win32.isAbsolute(relativeTarget)
185
+ || relativeTarget.includes("\0")
186
+ || segments.includes("..")
187
+ || segments.includes("."))
188
+ reject({
189
+ class: "unsupported_target",
190
+ message: `${label} '${relativeTarget}' is outside the validated project path grammar`,
191
+ recovery: "Use one project-relative path without symbolic, dot, parent, drive, UNC, or absolute segments; no state was changed.",
192
+ });
193
+ return segments;
194
+ }
195
+ function relativeDisplay(root, absolute) {
196
+ return path.relative(root, absolute).split(path.sep).join("/");
197
+ }
198
+ function removeExactFile(file) {
199
+ const current = readFileIfPresent(file.absolute, `attempt-owned file '${file.absolute}'`);
200
+ if (!current || !sameStableFile(file, current))
201
+ return false;
202
+ fs.unlinkSync(file.absolute);
203
+ return true;
204
+ }
205
+ function createDurableFile(absolute, bytes, mode = 0o600) {
206
+ let descriptor;
207
+ let opened;
208
+ try {
209
+ descriptor = fs.openSync(absolute, fs.constants.O_RDWR | fs.constants.O_CREAT | fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW ?? 0), mode);
210
+ opened = identity(fs.fstatSync(descriptor, { bigint: true }));
211
+ fs.writeFileSync(descriptor, bytes);
212
+ if (typeof process.getuid === "function")
213
+ fs.fchmodSync(descriptor, mode);
214
+ fs.fsyncSync(descriptor);
215
+ }
216
+ catch (error) {
217
+ if (descriptor !== undefined) {
218
+ try {
219
+ fs.closeSync(descriptor);
220
+ }
221
+ catch { /* Preserve the primary failure. */ }
222
+ descriptor = undefined;
223
+ }
224
+ if (opened) {
225
+ try {
226
+ const current = readFileIfPresent(absolute, `failed stage '${absolute}'`);
227
+ if (current && sameIdentity(opened, current.identity))
228
+ fs.unlinkSync(absolute);
229
+ }
230
+ catch { /* Preserve changed or partially inspectable attempt state. */ }
231
+ }
232
+ throw error;
233
+ }
234
+ finally {
235
+ if (descriptor !== undefined)
236
+ fs.closeSync(descriptor);
237
+ }
238
+ return readStableFile(absolute, `durable stage '${absolute}'`);
239
+ }
240
+ function publicationConflict(markerPath) {
241
+ reject({
242
+ class: "conflict",
243
+ message: `state mode marker '${markerPath}' changed after entity mode detection; this is a conflict and cannot fall back to legacy state`,
244
+ syntax: "agentera state <artifact-id> <verb> ... --format json",
245
+ example: `restore the exact '${markerPath}' marker selected at command start and retry`,
246
+ });
247
+ }
248
+ /**
249
+ * Binds mutations to a validated project and exact entity-mode marker. Every
250
+ * pathname and byte identity is checked at declared boundaries. Publication
251
+ * linearizes through standard complete-file link or rename operations; a
252
+ * non-cooperating writer that races after the final successful check is outside
253
+ * the contract and is not represented as a cross-process compare-and-swap.
254
+ */
255
+ export class EntityPublicationContext {
256
+ projectRoot;
257
+ validatedRoot;
258
+ markerPath;
259
+ marker;
260
+ createdDirectories = new Map();
261
+ closed = false;
262
+ constructor(root, markerPath, expectedBytes) {
263
+ this.projectRoot = root.path;
264
+ this.validatedRoot = root;
265
+ this.markerPath = markerPath;
266
+ const markerAbsolute = path.join(root.path, ...safeSegments(markerPath, "state mode marker"));
267
+ this.marker = readStableFile(markerAbsolute, `state mode marker '${markerPath}'`);
268
+ if (!this.marker.bytes.equals(expectedBytes))
269
+ publicationConflict(markerPath);
270
+ this.assertValid();
271
+ }
272
+ static open(root, markerPath, expectedBytes) {
273
+ return new EntityPublicationContext(root, markerPath, expectedBytes);
274
+ }
275
+ pinnedPath(relativePath = "") {
276
+ if (this.closed)
277
+ throw new Error("entity publication context is closed");
278
+ if (!relativePath)
279
+ return this.projectRoot;
280
+ return path.join(this.projectRoot, ...safeSegments(relativePath, "entity publication path"));
281
+ }
282
+ assertValid() {
283
+ if (this.closed)
284
+ throw new Error("entity publication context is closed");
285
+ assertValidatedProjectRoot(this.validatedRoot);
286
+ let current;
287
+ try {
288
+ current = readStableFile(this.marker.absolute, `state mode marker '${this.markerPath}'`);
289
+ }
290
+ catch {
291
+ publicationConflict(this.markerPath);
292
+ }
293
+ if (!sameStableFile(this.marker, current))
294
+ publicationConflict(this.markerPath);
295
+ }
296
+ publishImmutable(relativeTarget, bytes) {
297
+ const segments = safeSegments(relativeTarget, "immutable entity target");
298
+ if (segments.length < 2)
299
+ throw new Error(`unsafe immutable entity target '${relativeTarget}'`);
300
+ const directories = [];
301
+ const target = path.join(this.projectRoot, ...segments);
302
+ let stage;
303
+ let published;
304
+ let publicationOwned = false;
305
+ try {
306
+ directories.push(...this.openDirectories(segments.slice(0, -1), true));
307
+ const directory = directories.at(-1)?.absolute ?? this.projectRoot;
308
+ const existing = readFileIfPresent(target, `immutable entity target '${relativeTarget}'`);
309
+ if (existing) {
310
+ this.removeCreatedDirectories(directories);
311
+ return null;
312
+ }
313
+ const stagePath = path.join(directory, `.${segments.at(-1)}.${process.pid}.${randomUUID()}.tmp`);
314
+ stage = createDurableFile(stagePath, bytes);
315
+ this.assertBoundary(directories, undefined, [stage]);
316
+ if (readFileIfPresent(target, `immutable entity target '${relativeTarget}'`)) {
317
+ removeExactFile(stage);
318
+ stage = undefined;
319
+ this.removeCreatedDirectories(directories);
320
+ return null;
321
+ }
322
+ try {
323
+ fs.linkSync(stage.absolute, target);
324
+ }
325
+ catch (error) {
326
+ if (error.code === "EEXIST") {
327
+ removeExactFile(stage);
328
+ stage = undefined;
329
+ this.removeCreatedDirectories(directories);
330
+ return null;
331
+ }
332
+ throw new FileReplacementError(`immutable publication for '${relativeTarget}' failed before effects`, error);
333
+ }
334
+ syncDirectory(directory);
335
+ stage = readStableFile(stage.absolute, `immutable publication stage '${relativeTarget}'`);
336
+ published = readStableFile(target, `published entity '${relativeTarget}'`);
337
+ if (!published.bytes.equals(Buffer.from(bytes)) || published.identity.ino !== stage.identity.ino || published.identity.dev !== stage.identity.dev) {
338
+ throw new ExactReplacementConflictError(`immutable entity target '${relativeTarget}' changed at its publication boundary`);
339
+ }
340
+ publicationOwned = true;
341
+ this.assertBoundary(directories, published, [stage]);
342
+ removeExactFile(stage);
343
+ stage = undefined;
344
+ syncDirectory(directory);
345
+ published = readStableFile(target, `published entity '${relativeTarget}'`);
346
+ this.assertBoundary(directories, published);
347
+ for (const entry of directories.filter(({ created }) => created)) {
348
+ this.createdDirectories.set(relativeDisplay(this.projectRoot, entry.absolute), entry);
349
+ }
350
+ return publishedIdentity(published);
351
+ }
352
+ catch (error) {
353
+ if (publicationOwned && published) {
354
+ if (stage) {
355
+ try {
356
+ if (removeExactFile(stage))
357
+ stage = undefined;
358
+ }
359
+ catch { /* Preserve changed attempt bytes. */ }
360
+ }
361
+ if (!stage) {
362
+ try {
363
+ const current = readFileIfPresent(target, `failed immutable publication '${relativeTarget}'`);
364
+ if (current && matchesPublished(current, publishedIdentity(published)))
365
+ removeExactFile(current);
366
+ }
367
+ catch { /* Preserve a changed successor. */ }
368
+ }
369
+ }
370
+ if (stage) {
371
+ try {
372
+ removeExactFile(stage);
373
+ }
374
+ catch { /* Preserve changed attempt bytes. */ }
375
+ }
376
+ this.removeCreatedDirectories(directories);
377
+ throw error;
378
+ }
379
+ }
380
+ publishPreowned(relativeTarget, relativeReceipt, expected) {
381
+ const targetSegments = safeSegments(relativeTarget, "preowned target");
382
+ const receiptSegments = safeSegments(relativeReceipt, "preowned receipt");
383
+ if (targetSegments.length < 2 || receiptSegments.length < 2)
384
+ throw new Error("unsafe preowned publication path");
385
+ this.assertValid();
386
+ const receipt = readStableFile(path.join(this.projectRoot, ...receiptSegments), `preowned receipt '${relativeReceipt}'`);
387
+ if (!matchesPublished(receipt, expected))
388
+ throw new Error(`preowned receipt '${relativeReceipt}' changed before publication`);
389
+ const directories = this.openDirectories(targetSegments.slice(0, -1), true);
390
+ const directory = directories.at(-1)?.absolute ?? this.projectRoot;
391
+ const target = path.join(directory, targetSegments.at(-1));
392
+ try {
393
+ this.assertBoundary(directories, undefined, [receipt]);
394
+ if (!readFileIfPresent(target, `preowned target '${relativeTarget}'`)) {
395
+ try {
396
+ fs.linkSync(receipt.absolute, target);
397
+ syncDirectory(directory);
398
+ }
399
+ catch (error) {
400
+ if (error.code !== "EEXIST")
401
+ throw error;
402
+ }
403
+ }
404
+ const published = readStableFile(target, `preowned target '${relativeTarget}'`);
405
+ if (!matchesPublished(published, expected) || published.identity.dev !== receipt.identity.dev || published.identity.ino !== receipt.identity.ino) {
406
+ throw new Error(`canonical target '${relativeTarget}' collides with migration ownership receipt; byte-identical replacement inodes are never adopted`);
407
+ }
408
+ this.assertBoundary(directories, published, [receipt]);
409
+ for (const entry of directories.filter(({ created }) => created)) {
410
+ this.createdDirectories.set(relativeDisplay(this.projectRoot, entry.absolute), entry);
411
+ }
412
+ return publishedIdentity(published);
413
+ }
414
+ catch (error) {
415
+ this.removeCreatedDirectories(directories);
416
+ throw error;
417
+ }
418
+ }
419
+ removeExact(relativeTarget, expected, verifyContext = true) {
420
+ const segments = safeSegments(relativeTarget, "exact removal target");
421
+ const directories = this.openDirectories(segments.slice(0, -1), false, true, verifyContext);
422
+ if (!directories)
423
+ return "absent";
424
+ const directory = directories.at(-1)?.absolute ?? this.projectRoot;
425
+ const target = path.join(directory, segments.at(-1));
426
+ const current = readFileIfPresent(target, `exact removal target '${relativeTarget}'`);
427
+ if (!current)
428
+ return "absent";
429
+ if (!matchesPublished(current, expected))
430
+ return "identity_mismatch";
431
+ this.assertBoundary(directories, current, [], verifyContext);
432
+ const confirmed = readStableFile(target, `exact removal target '${relativeTarget}'`);
433
+ if (!sameStableFile(current, confirmed))
434
+ return "identity_mismatch";
435
+ fs.unlinkSync(target);
436
+ syncDirectory(directory);
437
+ this.removeAttemptDirectories();
438
+ return "removed";
439
+ }
440
+ replaceExisting(relativeTarget, expectedBytes, bytes, maxEntityBytes) {
441
+ return this.replaceOwned(relativeTarget, expectedBytes, bytes, maxEntityBytes);
442
+ }
443
+ replaceVisible(relativeTarget, expectedBytes, bytes, maxEntityBytes) {
444
+ return this.replaceOwned(relativeTarget, expectedBytes, bytes, maxEntityBytes);
445
+ }
446
+ restoreExact(relativeTarget, expected, bytes, maxEntityBytes) {
447
+ return this.replaceOwned(relativeTarget, expected, bytes, maxEntityBytes, false);
448
+ }
449
+ restoreVisible(relativeTarget, expected, bytes, maxEntityBytes) {
450
+ return this.replaceOwned(relativeTarget, expected, bytes, maxEntityBytes, false);
451
+ }
452
+ close() {
453
+ this.closed = true;
454
+ }
455
+ replaceOwned(relativeTarget, expected, bytes, maxEntityBytes, verifyContext = true) {
456
+ const segments = safeSegments(relativeTarget, "recoverable replacement target");
457
+ const directories = this.openDirectories(segments.slice(0, -1), false, false, verifyContext);
458
+ if (!directories)
459
+ throw new FileReplacementError(`replacement target '${relativeTarget}' is absent before publication`);
460
+ const directory = directories.at(-1)?.absolute ?? this.projectRoot;
461
+ const target = path.join(directory, segments.at(-1));
462
+ const baseline = readStableFile(target, `replacement target '${relativeTarget}'`);
463
+ if (!matchesExpected(baseline, expected)) {
464
+ throw new ExactReplacementConflictError(`target '${relativeTarget}' ownership or bytes changed before replacement; the detected change was preserved`);
465
+ }
466
+ if (!Number.isSafeInteger(maxEntityBytes) || maxEntityBytes < 1 || baseline.bytes.length > maxEntityBytes) {
467
+ throw new FileReplacementError(`target '${relativeTarget}' exact ${baseline.bytes.length}-byte baseline exceeds the authority-owned ${maxEntityBytes}-byte recovery limit`);
468
+ }
469
+ const after = Buffer.from(bytes);
470
+ let recovery;
471
+ let backup;
472
+ let stage;
473
+ let metadata;
474
+ let committed;
475
+ let renameCompleted = false;
476
+ try {
477
+ recovery = this.createRecoveryAttempt(directory);
478
+ backup = createDurableFile(path.join(recovery.attempt.absolute, "original.previous"), baseline.bytes);
479
+ stage = createDurableFile(path.join(recovery.attempt.absolute, "replacement.tmp"), after, Number(baseline.identity.mode & 4095n));
480
+ metadata = createDurableFile(path.join(recovery.attempt.absolute, FILE_REPLACEMENT_METADATA_NAME), `${JSON.stringify({
481
+ schema_version: FILE_REPLACEMENT_RECOVERY_VERSION,
482
+ target_path: segments.join("/"),
483
+ before_sha256: sha256(baseline.bytes),
484
+ after_sha256: sha256(after),
485
+ })}\n`);
486
+ syncDirectory(recovery.attempt.absolute);
487
+ this.assertBoundary(directories, baseline, [backup, stage, metadata], verifyContext);
488
+ const boundaryTarget = readStableFile(target, `replacement target '${relativeTarget}'`);
489
+ if (!sameStableFile(boundaryTarget, baseline)) {
490
+ throw new ExactReplacementConflictError(`target '${relativeTarget}' changed at the final validation boundary; the detected change was preserved`);
491
+ }
492
+ try {
493
+ fs.renameSync(stage.absolute, target);
494
+ renameCompleted = true;
495
+ }
496
+ catch (error) {
497
+ throw new FileReplacementError(`complete-file replacement for '${relativeTarget}' failed before publication${error.code ? ` (${error.code})` : ""}`, error);
498
+ }
499
+ syncDirectory(directory);
500
+ syncDirectory(recovery.attempt.absolute);
501
+ const published = readStableFile(target, `published replacement '${relativeTarget}'`);
502
+ if (!published.bytes.equals(after)) {
503
+ throw new ExactReplacementConflictError(`target '${relativeTarget}' changed through the publication boundary`);
504
+ }
505
+ this.assertBoundary(directories, published, [backup, metadata], verifyContext);
506
+ committed = { previousBytes: baseline.bytes.toString("utf8"), publishedIdentity: publishedIdentity(published) };
507
+ this.cleanupCommittedReplacement(recovery.attempt.absolute, [backup, metadata]);
508
+ backup = undefined;
509
+ metadata = undefined;
510
+ stage = undefined;
511
+ return committed;
512
+ }
513
+ catch (error) {
514
+ if (committed)
515
+ return committed;
516
+ let current = null;
517
+ try {
518
+ current = readFileIfPresent(target, `replacement target '${relativeTarget}'`);
519
+ }
520
+ catch { /* Preserve all recovery evidence. */ }
521
+ if (!renameCompleted && error instanceof ExactReplacementConflictError) {
522
+ const retained = this.cleanupAttemptFiles([stage, backup, metadata]);
523
+ stage = undefined;
524
+ backup = undefined;
525
+ metadata = undefined;
526
+ if (retained.length) {
527
+ throw new ExactReplacementConflictError(`${error.message}; cleanup retained changed recovery roles`, retained);
528
+ }
529
+ throw error;
530
+ }
531
+ if (renameCompleted && current?.bytes.equals(after) && backup) {
532
+ try {
533
+ this.assertBoundary(directories, current, [backup], false);
534
+ fs.renameSync(backup.absolute, target);
535
+ backup = undefined;
536
+ syncDirectory(directory);
537
+ const restored = readStableFile(target, `restored replacement target '${relativeTarget}'`);
538
+ if (!restored.bytes.equals(baseline.bytes))
539
+ throw new Error("restored bytes do not match the exact baseline");
540
+ if (stage) {
541
+ try {
542
+ removeExactFile(stage);
543
+ }
544
+ catch { /* Preserve changed stage. */ }
545
+ stage = undefined;
546
+ }
547
+ if (metadata) {
548
+ try {
549
+ removeExactFile(metadata);
550
+ }
551
+ catch { /* Preserve changed metadata. */ }
552
+ metadata = undefined;
553
+ }
554
+ syncDirectoryBestEffort(recovery?.attempt.absolute ?? directory);
555
+ throw new FileReplacementError(`${error.message}; restored exact prior bytes at '${relativeTarget}'`, error);
556
+ }
557
+ catch (restoreError) {
558
+ if (restoreError instanceof FileReplacementError && restoreError.cause === error)
559
+ throw restoreError;
560
+ }
561
+ }
562
+ if (!renameCompleted && current && sameStableFile(current, baseline)) {
563
+ const retained = this.cleanupAttemptFiles([stage, backup, metadata]);
564
+ stage = undefined;
565
+ backup = undefined;
566
+ metadata = undefined;
567
+ if (retained.length) {
568
+ throw new ExactReplacementConflictError(`${error.message}; cleanup retained changed recovery roles`, retained);
569
+ }
570
+ if (error instanceof ExactReplacementConflictError || error instanceof FileReplacementError || error instanceof StateWriteInputError)
571
+ throw error;
572
+ throw new FileReplacementError(`${error.message}; target bytes remained unchanged`, error);
573
+ }
574
+ const retained = [backup, stage, metadata]
575
+ .filter((file) => Boolean(file && fs.existsSync(file.absolute)))
576
+ .map((file) => relativeDisplay(this.projectRoot, file.absolute));
577
+ throw new ExactReplacementConflictError(`${error.message}; recoverable publication did not report success and preserved the canonical target plus ${retained.length} bounded recovery role${retained.length === 1 ? "" : "s"}`, retained);
578
+ }
579
+ finally {
580
+ if (recovery)
581
+ this.closeRecoveryAttempt(recovery);
582
+ }
583
+ }
584
+ openDirectories(segments, create, missingIsAbsent = false, verifyContext = true) {
585
+ const directories = [];
586
+ let parent = this.projectRoot;
587
+ for (const segment of segments) {
588
+ if (verifyContext)
589
+ this.assertValid();
590
+ else
591
+ assertValidatedProjectRoot(this.validatedRoot);
592
+ const absolute = path.join(parent, segment);
593
+ let entry;
594
+ try {
595
+ entry = readStableDirectory(absolute);
596
+ }
597
+ catch (error) {
598
+ if (error.code === "ENOENT" || error.message.includes("ENOENT")) {
599
+ if (missingIsAbsent)
600
+ return null;
601
+ if (!create)
602
+ throw error;
603
+ this.assertBoundary(directories, undefined, [], verifyContext);
604
+ fs.mkdirSync(absolute, 0o700);
605
+ syncDirectory(parent);
606
+ entry = readStableDirectory(absolute, true);
607
+ }
608
+ else
609
+ throw error;
610
+ }
611
+ directories.push(entry);
612
+ parent = absolute;
613
+ }
614
+ return directories;
615
+ }
616
+ assertBoundary(directories, target, roles = [], verifyContext = true) {
617
+ if (verifyContext)
618
+ this.assertValid();
619
+ else
620
+ assertValidatedProjectRoot(this.validatedRoot);
621
+ for (const directory of directories) {
622
+ let current;
623
+ try {
624
+ current = fs.lstatSync(directory.absolute, { bigint: true });
625
+ }
626
+ catch {
627
+ throw new ExactReplacementConflictError(`publication directory '${relativeDisplay(this.projectRoot, directory.absolute)}' changed at a validation boundary`);
628
+ }
629
+ if (!sameDirectory(directory, current)) {
630
+ throw new ExactReplacementConflictError(`publication directory '${relativeDisplay(this.projectRoot, directory.absolute)}' changed at a validation boundary`);
631
+ }
632
+ }
633
+ if (target) {
634
+ const current = readFileIfPresent(target.absolute, `publication target '${relativeDisplay(this.projectRoot, target.absolute)}'`);
635
+ if (!current || !sameStableFile(target, current)) {
636
+ throw new ExactReplacementConflictError(`publication target '${relativeDisplay(this.projectRoot, target.absolute)}' changed at a validation boundary`);
637
+ }
638
+ }
639
+ for (const role of roles) {
640
+ const current = readFileIfPresent(role.absolute, `publication role '${relativeDisplay(this.projectRoot, role.absolute)}'`);
641
+ if (!current || !sameStableFile(role, current)) {
642
+ throw new ExactReplacementConflictError(`publication recovery role '${relativeDisplay(this.projectRoot, role.absolute)}' changed at a validation boundary`);
643
+ }
644
+ }
645
+ }
646
+ createRecoveryAttempt(targetDirectory) {
647
+ const agentera = readStableDirectory(path.join(this.projectRoot, ".agentera"));
648
+ const rootPath = path.join(agentera.absolute, ".entity-recovery");
649
+ let rootCreated = false;
650
+ let ignoreCreated = false;
651
+ let root;
652
+ let ignore;
653
+ let attempt;
654
+ try {
655
+ try {
656
+ root = readStableDirectory(rootPath);
657
+ }
658
+ catch (error) {
659
+ if (!error.message.includes("ENOENT")) {
660
+ throw new FileReplacementError(`private entity recovery root '.agentera/.entity-recovery' cannot be opened safely: ${error.message}`, error);
661
+ }
662
+ fs.mkdirSync(rootPath, 0o700);
663
+ rootCreated = true;
664
+ syncDirectory(agentera.absolute);
665
+ root = readStableDirectory(rootPath, true);
666
+ }
667
+ const trustedRoot = validateEntityRecoveryDirectory(this.validatedRoot, root.absolute, targetDirectory, "private entity recovery root '.agentera/.entity-recovery'", root);
668
+ root = { ...trustedRoot, created: rootCreated };
669
+ const ignorePath = path.join(root.absolute, ".gitignore");
670
+ const existingIgnore = readFileIfPresent(ignorePath, "private entity recovery ignore marker");
671
+ if (existingIgnore)
672
+ ignore = existingIgnore;
673
+ else {
674
+ ignore = createDurableFile(ignorePath, RECOVERY_IGNORE_BYTES);
675
+ ignoreCreated = true;
676
+ }
677
+ if (!ignore.bytes.equals(Buffer.from(RECOVERY_IGNORE_BYTES))) {
678
+ throw new FileReplacementError("private entity recovery ignore marker must contain the authoritative rules");
679
+ }
680
+ const ignoreStat = fs.lstatSync(ignore.absolute, { bigint: true });
681
+ if (typeof process.getuid === "function" && ((ignoreStat.mode & 18n) !== 0n || ignoreStat.uid !== BigInt(process.getuid()))) {
682
+ throw new FileReplacementError("private entity recovery ignore marker must be owner-controlled and not group/world-writable");
683
+ }
684
+ syncDirectory(root.absolute);
685
+ for (let index = 0; index < 8; index += 1) {
686
+ const attemptPath = path.join(root.absolute, `entity-${process.pid}-${randomUUID()}`);
687
+ try {
688
+ fs.mkdirSync(attemptPath, 0o700);
689
+ const opened = readStableDirectory(attemptPath, true);
690
+ attempt = opened;
691
+ const trusted = validateEntityRecoveryDirectory(this.validatedRoot, attemptPath, targetDirectory, `private entity recovery attempt '${relativeDisplay(this.projectRoot, attemptPath)}'`, opened);
692
+ attempt = { ...trusted, created: true };
693
+ break;
694
+ }
695
+ catch (error) {
696
+ if (error.code !== "EEXIST")
697
+ throw error;
698
+ }
699
+ }
700
+ if (!attempt)
701
+ throw new FileReplacementError("could not allocate a unique private entity recovery attempt after 8 tries");
702
+ syncDirectory(root.absolute);
703
+ return { root, attempt, ignore, relativePath: relativeDisplay(this.projectRoot, attempt.absolute) };
704
+ }
705
+ catch (error) {
706
+ if (attempt) {
707
+ try {
708
+ fs.rmdirSync(attempt.absolute);
709
+ }
710
+ catch { /* Preserve unexpected contents. */ }
711
+ }
712
+ if (ignore && ignoreCreated) {
713
+ try {
714
+ removeExactFile(ignore);
715
+ }
716
+ catch { /* Preserve changed marker. */ }
717
+ }
718
+ if (root && rootCreated) {
719
+ try {
720
+ fs.rmdirSync(root.absolute);
721
+ }
722
+ catch { /* Preserve non-empty root. */ }
723
+ }
724
+ throw error;
725
+ }
726
+ }
727
+ cleanupAttemptFiles(files) {
728
+ const retained = [];
729
+ for (const file of files) {
730
+ if (!file)
731
+ continue;
732
+ try {
733
+ if (!removeExactFile(file) && fs.existsSync(file.absolute))
734
+ retained.push(relativeDisplay(this.projectRoot, file.absolute));
735
+ }
736
+ catch {
737
+ retained.push(relativeDisplay(this.projectRoot, file.absolute));
738
+ }
739
+ }
740
+ return retained;
741
+ }
742
+ cleanupCommittedReplacement(directory, files) {
743
+ this.cleanupAttemptFiles(files);
744
+ syncDirectoryBestEffort(directory);
745
+ }
746
+ closeRecoveryAttempt(recovery) {
747
+ try {
748
+ fs.rmdirSync(recovery.attempt.absolute);
749
+ syncDirectoryBestEffort(recovery.root.absolute);
750
+ }
751
+ catch {
752
+ return;
753
+ }
754
+ try {
755
+ const names = fs.readdirSync(recovery.root.absolute);
756
+ if (names.length === 1 && names[0] === ".gitignore") {
757
+ removeExactFile(recovery.ignore);
758
+ syncDirectoryBestEffort(recovery.root.absolute);
759
+ }
760
+ if (fs.readdirSync(recovery.root.absolute).length === 0) {
761
+ fs.rmdirSync(recovery.root.absolute);
762
+ syncDirectoryBestEffort(path.dirname(recovery.root.absolute));
763
+ }
764
+ }
765
+ catch { /* Preserve non-empty or changed recovery authority. */ }
766
+ }
767
+ removeCreatedDirectories(directories) {
768
+ for (const directory of [...directories].reverse()) {
769
+ if (!directory.created)
770
+ continue;
771
+ try {
772
+ const current = fs.lstatSync(directory.absolute, { bigint: true });
773
+ if (!sameDirectory(directory, current))
774
+ continue;
775
+ fs.rmdirSync(directory.absolute);
776
+ syncDirectoryBestEffort(path.dirname(directory.absolute));
777
+ }
778
+ catch (error) {
779
+ if (!["ENOTEMPTY", "EEXIST", "ENOENT"].includes(error.code ?? ""))
780
+ throw error;
781
+ }
782
+ }
783
+ }
784
+ removeAttemptDirectories() {
785
+ const paths = [...this.createdDirectories].sort((left, right) => right[0].split("/").length - left[0].split("/").length);
786
+ for (const [relativePath, expected] of paths) {
787
+ try {
788
+ const current = fs.lstatSync(expected.absolute, { bigint: true });
789
+ if (!sameDirectory(expected, current)) {
790
+ this.createdDirectories.delete(relativePath);
791
+ continue;
792
+ }
793
+ fs.rmdirSync(expected.absolute);
794
+ syncDirectoryBestEffort(path.dirname(expected.absolute));
795
+ this.createdDirectories.delete(relativePath);
796
+ }
797
+ catch (error) {
798
+ const code = error.code ?? "";
799
+ if (code === "ENOENT")
800
+ this.createdDirectories.delete(relativePath);
801
+ else if (!["ENOTEMPTY", "EEXIST"].includes(code))
802
+ throw error;
803
+ }
804
+ }
805
+ }
806
+ }