contextos-agents 1.7.0 → 2.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/.agents/AGENTS.md +1 -1
  2. package/.agents/adapters/aider/export.js +117 -99
  3. package/.agents/adapters/claude/export.js +68 -26
  4. package/.agents/adapters/copilot/export.js +90 -53
  5. package/.agents/adapters/cursor/export.js +80 -101
  6. package/.agents/adapters/drift-detector.js +196 -0
  7. package/.agents/adapters/gemini/export.js +76 -45
  8. package/.agents/adapters/pure-compiler.js +443 -0
  9. package/.agents/adapters/zed/export.js +104 -96
  10. package/.agents/compiled/registry.v2.json +504 -0
  11. package/.agents/compiled/registry.v2.sha256 +1 -0
  12. package/.agents/compiler/manifest-compiler.js +963 -0
  13. package/.agents/compiler/vendor/yaml.LICENSE.txt +13 -0
  14. package/.agents/compiler/vendor/yaml.SBOM.json +6 -0
  15. package/.agents/compiler/vendor/yaml.js +139 -0
  16. package/.agents/core/profiles/init.yaml +25 -0
  17. package/.agents/core/skills/context-manager/skill.yaml +3 -5
  18. package/.agents/core/skills/context-os/SKILL.md +3 -6
  19. package/.agents/core/skills/context-os/skill.yaml +3 -8
  20. package/.agents/core/skills/engineering-workflow/skill.yaml +1 -7
  21. package/.agents/core/skills/gemini-precision/skill.yaml +1 -6
  22. package/.agents/core/skills/gstack-roles/skill.yaml +3 -6
  23. package/.agents/core/skills/ponytail-mindset/skill.yaml +1 -7
  24. package/.agents/core/skills/security/skill.yaml +15 -3
  25. package/.agents/ctx.js +574 -111
  26. package/.agents/customization-dx.js +282 -0
  27. package/.agents/doctor.js +719 -64
  28. package/.agents/filesystem/index.js +71 -0
  29. package/.agents/filesystem/journaled-transaction.js +451 -0
  30. package/.agents/filesystem/lockfile-v2.js +275 -0
  31. package/.agents/filesystem/platform-hardening.js +222 -0
  32. package/.agents/filesystem/project-lock.js +218 -0
  33. package/.agents/filesystem/safe-path.js +256 -0
  34. package/.agents/generated/claude/skills/context-os/SKILL.md +1 -1
  35. package/.agents/generated/gemini/skills/context-os/SKILL.md +2 -2
  36. package/.agents/plugins/contextos/plugin.json +1 -1
  37. package/.agents/plugins.js +259 -60
  38. package/.agents/profiles.js +486 -51
  39. package/.agents/resolver.js +50 -534
  40. package/.agents/schemas/attestation.review.v1.json +111 -0
  41. package/.agents/schemas/attestation.verification.v1.json +85 -0
  42. package/.agents/schemas/lockfile.v2.schema.json +134 -0
  43. package/.agents/schemas/profile.v2.schema.json +114 -0
  44. package/.agents/schemas/runtime.thread.v1.json +192 -0
  45. package/.agents/schemas/skill.manifest.v2.json +177 -0
  46. package/.agents/schemas/verification.spec.v1.json +39 -0
  47. package/.agents/schemas/workspace.graph.schema.json +106 -0
  48. package/.agents/transaction-core/event-store.js +288 -0
  49. package/.agents/transaction-core/idempotency.js +129 -0
  50. package/.agents/transaction-core/ipc-lock.js +311 -0
  51. package/.agents/transaction-core/plugin-supply-chain-bundle.js +436 -0
  52. package/.agents/validate.js +44 -1
  53. package/.agents/watch.js +354 -102
  54. package/.agents/workspace/workspace-graph.js +778 -0
  55. package/README.md +59 -387
  56. package/benchmarks/v2/analysis/statistics.js +140 -0
  57. package/benchmarks/v2/analysis/stats.js +69 -0
  58. package/benchmarks/v2/arms/arm-definitions.js +79 -0
  59. package/benchmarks/v2/dataset.schema.json +34 -0
  60. package/benchmarks/v2/evaluators/index.js +25 -0
  61. package/benchmarks/v2/evaluators/verified-success.js +116 -0
  62. package/benchmarks/v2/harness/runner.js +88 -0
  63. package/benchmarks/v2/pilot-tasks.json +392 -0
  64. package/bin/commands/recover.js +88 -0
  65. package/bin/commands/update.js +80 -17
  66. package/bin/commands.js +62 -25
  67. package/bin/index.js +138 -81
  68. package/bin/lib/lockfile.js +5 -3
  69. package/bin/lib/safe-writer.js +34 -3
  70. package/package.json +85 -72
  71. package/registry.json +2 -2
  72. package/registry.v2.schema.json +86 -0
  73. package/.agents/core/profiles/backend.yaml +0 -47
  74. package/.agents/core/profiles/enterprise.yaml +0 -46
  75. package/.agents/core/profiles/frontend.yaml +0 -46
  76. package/.agents/core/profiles/hackathon.yaml +0 -45
  77. package/.agents/core/profiles/mvp.yaml +0 -44
  78. package/.agents/core/profiles/startup.yaml +0 -48
  79. package/.agents/core/skills/adapters/EXAMPLES.md +0 -19
  80. package/.agents/core/skills/adapters/SKILL.md +0 -105
  81. package/.agents/core/skills/adapters/TROUBLESHOOTING.md +0 -7
  82. package/.agents/core/skills/adapters/VALIDATION.json +0 -12
  83. package/.agents/core/skills/adapters/skill.yaml +0 -16
  84. package/.agents/core/skills/architecture-diagrams/SKILL.md +0 -108
  85. package/.agents/core/skills/architecture-diagrams/VALIDATION.json +0 -12
  86. package/.agents/core/skills/architecture-diagrams/skill.yaml +0 -12
  87. package/.agents/core/skills/brutalist-design/SKILL.md +0 -150
  88. package/.agents/core/skills/brutalist-design/VALIDATION.json +0 -12
  89. package/.agents/core/skills/brutalist-design/skill.yaml +0 -12
  90. package/.agents/core/skills/database/EXAMPLES.md +0 -74
  91. package/.agents/core/skills/database/SKILL.md +0 -101
  92. package/.agents/core/skills/database/TROUBLESHOOTING.md +0 -18
  93. package/.agents/core/skills/database/VALIDATION.json +0 -11
  94. package/.agents/core/skills/database/skill.yaml +0 -31
  95. package/.agents/core/skills/ddd/EXAMPLES.md +0 -42
  96. package/.agents/core/skills/ddd/SKILL.md +0 -247
  97. package/.agents/core/skills/ddd/TROUBLESHOOTING.md +0 -19
  98. package/.agents/core/skills/ddd/VALIDATION.json +0 -12
  99. package/.agents/core/skills/ddd/ddd.md +0 -178
  100. package/.agents/core/skills/ddd/skill.yaml +0 -17
  101. package/.agents/core/skills/decisions/EXAMPLES.md +0 -35
  102. package/.agents/core/skills/decisions/SKILL.md +0 -90
  103. package/.agents/core/skills/decisions/TROUBLESHOOTING.md +0 -13
  104. package/.agents/core/skills/decisions/VALIDATION.json +0 -12
  105. package/.agents/core/skills/decisions/skill.yaml +0 -16
  106. package/.agents/core/skills/docker/EXAMPLES.md +0 -56
  107. package/.agents/core/skills/docker/SKILL.md +0 -63
  108. package/.agents/core/skills/docker/TROUBLESHOOTING.md +0 -18
  109. package/.agents/core/skills/docker/VALIDATION.json +0 -11
  110. package/.agents/core/skills/docker/skill.yaml +0 -29
  111. package/.agents/core/skills/fastapi/EXAMPLES.md +0 -36
  112. package/.agents/core/skills/fastapi/SKILL.md +0 -148
  113. package/.agents/core/skills/fastapi/TROUBLESHOOTING.md +0 -19
  114. package/.agents/core/skills/fastapi/VALIDATION.json +0 -12
  115. package/.agents/core/skills/fastapi/fastapi.md +0 -112
  116. package/.agents/core/skills/fastapi/skill.yaml +0 -17
  117. package/.agents/core/skills/generators/EXAMPLES.md +0 -19
  118. package/.agents/core/skills/generators/SKILL.md +0 -112
  119. package/.agents/core/skills/generators/TROUBLESHOOTING.md +0 -7
  120. package/.agents/core/skills/generators/VALIDATION.json +0 -12
  121. package/.agents/core/skills/generators/skill.yaml +0 -25
  122. package/.agents/core/skills/generators/templates/API.md +0 -77
  123. package/.agents/core/skills/generators/templates/ARCHITECTURE.md +0 -70
  124. package/.agents/core/skills/generators/templates/DATABASE.md +0 -42
  125. package/.agents/core/skills/generators/templates/DECISION.md +0 -46
  126. package/.agents/core/skills/generators/templates/PRD.md +0 -67
  127. package/.agents/core/skills/generators/templates/PROJECT_GRAPH.md +0 -56
  128. package/.agents/core/skills/generators/templates/ROADMAP.md +0 -51
  129. package/.agents/core/skills/generators/templates/TASKS.md +0 -43
  130. package/.agents/core/skills/generators/templates/UI.md +0 -73
  131. package/.agents/core/skills/graphify/EXAMPLES.md +0 -73
  132. package/.agents/core/skills/graphify/SKILL.md +0 -130
  133. package/.agents/core/skills/graphify/VALIDATION.json +0 -12
  134. package/.agents/core/skills/graphify/skill.yaml +0 -18
  135. package/.agents/core/skills/impeccable-design/EXAMPLES.md +0 -26
  136. package/.agents/core/skills/impeccable-design/SKILL.md +0 -201
  137. package/.agents/core/skills/impeccable-design/TROUBLESHOOTING.md +0 -19
  138. package/.agents/core/skills/impeccable-design/VALIDATION.json +0 -12
  139. package/.agents/core/skills/impeccable-design/skill.yaml +0 -20
  140. package/.agents/core/skills/interview-me/SKILL.md +0 -97
  141. package/.agents/core/skills/interview-me/VALIDATION.json +0 -12
  142. package/.agents/core/skills/interview-me/skill.yaml +0 -12
  143. package/.agents/core/skills/microservices/EXAMPLES.md +0 -38
  144. package/.agents/core/skills/microservices/SKILL.md +0 -164
  145. package/.agents/core/skills/microservices/TROUBLESHOOTING.md +0 -19
  146. package/.agents/core/skills/microservices/VALIDATION.json +0 -12
  147. package/.agents/core/skills/microservices/microservices.md +0 -119
  148. package/.agents/core/skills/microservices/skill.yaml +0 -17
  149. package/.agents/core/skills/minimalist-design/SKILL.md +0 -113
  150. package/.agents/core/skills/minimalist-design/VALIDATION.json +0 -12
  151. package/.agents/core/skills/minimalist-design/skill.yaml +0 -12
  152. package/.agents/core/skills/nestjs/EXAMPLES.md +0 -40
  153. package/.agents/core/skills/nestjs/SKILL.md +0 -139
  154. package/.agents/core/skills/nestjs/TROUBLESHOOTING.md +0 -19
  155. package/.agents/core/skills/nestjs/VALIDATION.json +0 -12
  156. package/.agents/core/skills/nestjs/nestjs.md +0 -103
  157. package/.agents/core/skills/nestjs/skill.yaml +0 -17
  158. package/.agents/core/skills/nextjs/EXAMPLES.md +0 -40
  159. package/.agents/core/skills/nextjs/SKILL.md +0 -163
  160. package/.agents/core/skills/nextjs/TROUBLESHOOTING.md +0 -19
  161. package/.agents/core/skills/nextjs/VALIDATION.json +0 -12
  162. package/.agents/core/skills/nextjs/nextjs.md +0 -67
  163. package/.agents/core/skills/nextjs/skill.yaml +0 -17
  164. package/.agents/core/skills/node/EXAMPLES.md +0 -80
  165. package/.agents/core/skills/node/SKILL.md +0 -128
  166. package/.agents/core/skills/node/TROUBLESHOOTING.md +0 -19
  167. package/.agents/core/skills/node/VALIDATION.json +0 -12
  168. package/.agents/core/skills/node/node.md +0 -87
  169. package/.agents/core/skills/node/skill.yaml +0 -17
  170. package/.agents/core/skills/performance/EXAMPLES.md +0 -30
  171. package/.agents/core/skills/performance/SKILL.md +0 -75
  172. package/.agents/core/skills/performance/TROUBLESHOOTING.md +0 -19
  173. package/.agents/core/skills/performance/VALIDATION.json +0 -12
  174. package/.agents/core/skills/performance/performance.md +0 -52
  175. package/.agents/core/skills/performance/skill.yaml +0 -17
  176. package/.agents/core/skills/react/EXAMPLES.md +0 -79
  177. package/.agents/core/skills/react/SKILL.md +0 -132
  178. package/.agents/core/skills/react/TROUBLESHOOTING.md +0 -19
  179. package/.agents/core/skills/react/VALIDATION.json +0 -12
  180. package/.agents/core/skills/react/react.md +0 -93
  181. package/.agents/core/skills/react/skill.yaml +0 -17
  182. package/.agents/core/skills/react-best-practices/SKILL.md +0 -155
  183. package/.agents/core/skills/react-best-practices/VALIDATION.json +0 -12
  184. package/.agents/core/skills/react-best-practices/skill.yaml +0 -14
  185. package/.agents/core/skills/redesign-audit/SKILL.md +0 -117
  186. package/.agents/core/skills/redesign-audit/VALIDATION.json +0 -12
  187. package/.agents/core/skills/redesign-audit/skill.yaml +0 -12
  188. package/.agents/core/skills/soft-design/SKILL.md +0 -108
  189. package/.agents/core/skills/soft-design/VALIDATION.json +0 -12
  190. package/.agents/core/skills/soft-design/skill.yaml +0 -12
  191. package/.agents/core/skills/state-management/EXAMPLES.md +0 -56
  192. package/.agents/core/skills/state-management/SKILL.md +0 -48
  193. package/.agents/core/skills/state-management/TROUBLESHOOTING.md +0 -18
  194. package/.agents/core/skills/state-management/VALIDATION.json +0 -11
  195. package/.agents/core/skills/state-management/skill.yaml +0 -28
  196. package/.agents/core/skills/subagent-orchestrator/SKILL.md +0 -117
  197. package/.agents/core/skills/subagent-orchestrator/VALIDATION.json +0 -12
  198. package/.agents/core/skills/subagent-orchestrator/skill.yaml +0 -12
  199. package/.agents/core/skills/system-design/EXAMPLES.md +0 -75
  200. package/.agents/core/skills/system-design/SKILL.md +0 -419
  201. package/.agents/core/skills/system-design/TROUBLESHOOTING.md +0 -19
  202. package/.agents/core/skills/system-design/VALIDATION.json +0 -12
  203. package/.agents/core/skills/system-design/skill.yaml +0 -20
  204. package/.agents/core/skills/system-design/system-design.md +0 -112
  205. package/.agents/core/skills/testing/EXAMPLES.md +0 -71
  206. package/.agents/core/skills/testing/SKILL.md +0 -70
  207. package/.agents/core/skills/testing/TROUBLESHOOTING.md +0 -18
  208. package/.agents/core/skills/testing/VALIDATION.json +0 -11
  209. package/.agents/core/skills/testing/skill.yaml +0 -32
  210. package/.agents/core/skills/typescript/EXAMPLES.md +0 -64
  211. package/.agents/core/skills/typescript/SKILL.md +0 -112
  212. package/.agents/core/skills/typescript/TROUBLESHOOTING.md +0 -19
  213. package/.agents/core/skills/typescript/VALIDATION.json +0 -12
  214. package/.agents/core/skills/typescript/skill.yaml +0 -17
  215. package/.agents/core/skills/typescript/typescript.md +0 -71
  216. package/.agents/core/skills/ui-design/EXAMPLES.md +0 -21
  217. package/.agents/core/skills/ui-design/SKILL.md +0 -124
  218. package/.agents/core/skills/ui-design/TROUBLESHOOTING.md +0 -19
  219. package/.agents/core/skills/ui-design/VALIDATION.json +0 -12
  220. package/.agents/core/skills/ui-design/skill.yaml +0 -17
  221. package/.agents/core/skills/ui-design/ui.md +0 -88
  222. package/.agents/core/skills/ui-ux-pro/EXAMPLES.md +0 -62
  223. package/.agents/core/skills/ui-ux-pro/SKILL.md +0 -375
  224. package/.agents/core/skills/ui-ux-pro/TROUBLESHOOTING.md +0 -19
  225. package/.agents/core/skills/ui-ux-pro/VALIDATION.json +0 -12
  226. package/.agents/core/skills/ui-ux-pro/skill.yaml +0 -19
  227. package/.agents/core/skills/ux-design/EXAMPLES.md +0 -36
  228. package/.agents/core/skills/ux-design/SKILL.md +0 -116
  229. package/.agents/core/skills/ux-design/TROUBLESHOOTING.md +0 -19
  230. package/.agents/core/skills/ux-design/VALIDATION.json +0 -12
  231. package/.agents/core/skills/ux-design/skill.yaml +0 -17
  232. package/.agents/core/skills/ux-design/ux.md +0 -80
  233. package/.agents/core/skills/vercel-optimize/SKILL.md +0 -83
  234. package/.agents/core/skills/vercel-optimize/VALIDATION.json +0 -12
  235. package/.agents/core/skills/vercel-optimize/scripts/collect-signals.mjs +0 -131
  236. package/.agents/core/skills/vercel-optimize/scripts/gate-investigations.mjs +0 -142
  237. package/.agents/core/skills/vercel-optimize/scripts/merge-signals.mjs +0 -143
  238. package/.agents/core/skills/vercel-optimize/scripts/scan-codebase.mjs +0 -174
  239. package/.agents/core/skills/vercel-optimize/skill.yaml +0 -18
  240. package/.agents/core/skills/web-accessibility/EXAMPLES.md +0 -39
  241. package/.agents/core/skills/web-accessibility/SKILL.md +0 -170
  242. package/.agents/core/skills/web-accessibility/TROUBLESHOOTING.md +0 -19
  243. package/.agents/core/skills/web-accessibility/VALIDATION.json +0 -12
  244. package/.agents/core/skills/web-accessibility/accessibility.md +0 -63
  245. package/.agents/core/skills/web-accessibility/skill.yaml +0 -17
  246. package/.agents/generated/claude/skills/adapters/SKILL.md +0 -126
  247. package/.agents/generated/claude/skills/architecture-diagrams/SKILL.md +0 -101
  248. package/.agents/generated/claude/skills/brutalist-design/SKILL.md +0 -145
  249. package/.agents/generated/claude/skills/database/SKILL.md +0 -191
  250. package/.agents/generated/claude/skills/ddd/SKILL.md +0 -305
  251. package/.agents/generated/claude/skills/decisions/SKILL.md +0 -134
  252. package/.agents/generated/claude/skills/docker/SKILL.md +0 -135
  253. package/.agents/generated/claude/skills/fastapi/SKILL.md +0 -200
  254. package/.agents/generated/claude/skills/generators/SKILL.md +0 -133
  255. package/.agents/generated/claude/skills/graphify/SKILL.md +0 -198
  256. package/.agents/generated/claude/skills/impeccable-design/SKILL.md +0 -241
  257. package/.agents/generated/claude/skills/interview-me/SKILL.md +0 -90
  258. package/.agents/generated/claude/skills/microservices/SKILL.md +0 -218
  259. package/.agents/generated/claude/skills/minimalist-design/SKILL.md +0 -108
  260. package/.agents/generated/claude/skills/nestjs/SKILL.md +0 -195
  261. package/.agents/generated/claude/skills/nextjs/SKILL.md +0 -219
  262. package/.agents/generated/claude/skills/node/SKILL.md +0 -224
  263. package/.agents/generated/claude/skills/performance/SKILL.md +0 -121
  264. package/.agents/generated/claude/skills/react/SKILL.md +0 -227
  265. package/.agents/generated/claude/skills/react-best-practices/SKILL.md +0 -146
  266. package/.agents/generated/claude/skills/redesign-audit/SKILL.md +0 -112
  267. package/.agents/generated/claude/skills/soft-design/SKILL.md +0 -103
  268. package/.agents/generated/claude/skills/state-management/SKILL.md +0 -120
  269. package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +0 -110
  270. package/.agents/generated/claude/skills/system-design/SKILL.md +0 -507
  271. package/.agents/generated/claude/skills/testing/SKILL.md +0 -157
  272. package/.agents/generated/claude/skills/typescript/SKILL.md +0 -192
  273. package/.agents/generated/claude/skills/ui-design/SKILL.md +0 -161
  274. package/.agents/generated/claude/skills/ui-ux-pro/SKILL.md +0 -451
  275. package/.agents/generated/claude/skills/ux-design/SKILL.md +0 -168
  276. package/.agents/generated/claude/skills/vercel-optimize/SKILL.md +0 -76
  277. package/.agents/generated/claude/skills/web-accessibility/SKILL.md +0 -225
  278. package/.agents/generated/gemini/skills/adapters/SKILL.md +0 -135
  279. package/.agents/generated/gemini/skills/architecture-diagrams/SKILL.md +0 -107
  280. package/.agents/generated/gemini/skills/brutalist-design/SKILL.md +0 -151
  281. package/.agents/generated/gemini/skills/database/SKILL.md +0 -200
  282. package/.agents/generated/gemini/skills/ddd/SKILL.md +0 -314
  283. package/.agents/generated/gemini/skills/decisions/SKILL.md +0 -143
  284. package/.agents/generated/gemini/skills/docker/SKILL.md +0 -144
  285. package/.agents/generated/gemini/skills/fastapi/SKILL.md +0 -209
  286. package/.agents/generated/gemini/skills/generators/SKILL.md +0 -142
  287. package/.agents/generated/gemini/skills/graphify/SKILL.md +0 -205
  288. package/.agents/generated/gemini/skills/impeccable-design/SKILL.md +0 -250
  289. package/.agents/generated/gemini/skills/interview-me/SKILL.md +0 -96
  290. package/.agents/generated/gemini/skills/microservices/SKILL.md +0 -227
  291. package/.agents/generated/gemini/skills/minimalist-design/SKILL.md +0 -114
  292. package/.agents/generated/gemini/skills/nestjs/SKILL.md +0 -204
  293. package/.agents/generated/gemini/skills/nextjs/SKILL.md +0 -298
  294. package/.agents/generated/gemini/skills/node/SKILL.md +0 -323
  295. package/.agents/generated/gemini/skills/performance/SKILL.md +0 -185
  296. package/.agents/generated/gemini/skills/react/SKILL.md +0 -332
  297. package/.agents/generated/gemini/skills/react-best-practices/SKILL.md +0 -152
  298. package/.agents/generated/gemini/skills/redesign-audit/SKILL.md +0 -118
  299. package/.agents/generated/gemini/skills/soft-design/SKILL.md +0 -109
  300. package/.agents/generated/gemini/skills/state-management/SKILL.md +0 -129
  301. package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +0 -116
  302. package/.agents/generated/gemini/skills/system-design/SKILL.md +0 -631
  303. package/.agents/generated/gemini/skills/testing/SKILL.md +0 -166
  304. package/.agents/generated/gemini/skills/typescript/SKILL.md +0 -275
  305. package/.agents/generated/gemini/skills/ui-design/SKILL.md +0 -170
  306. package/.agents/generated/gemini/skills/ui-ux-pro/SKILL.md +0 -460
  307. package/.agents/generated/gemini/skills/ux-design/SKILL.md +0 -177
  308. package/.agents/generated/gemini/skills/vercel-optimize/SKILL.md +0 -82
  309. package/.agents/generated/gemini/skills/web-accessibility/SKILL.md +0 -300
@@ -0,0 +1,71 @@
1
+ /**
2
+ * .agents/filesystem/index.js
3
+ * ContextOS Filesystem Safety & Transaction Barrel
4
+ */
5
+
6
+ const {
7
+ resolveManagedPath,
8
+ toPosix,
9
+ isReservedDeviceName,
10
+ SafePathError,
11
+ ERROR_CODES: SAFE_PATH_ERRORS,
12
+ WINDOWS_RESERVED_NAMES,
13
+ } = require('./safe-path.js');
14
+
15
+ const {
16
+ ProjectMutationLock,
17
+ ProjectLockError,
18
+ ERROR_CODES: LOCK_ERRORS,
19
+ isPidAlive,
20
+ } = require('./project-lock.js');
21
+
22
+ const {
23
+ LockfileV2Manager,
24
+ computeExactHash,
25
+ computeSemanticHash,
26
+ LockfileV2Error,
27
+ ERROR_CODES: LOCKFILE_ERRORS,
28
+ DEFAULT_LOCKFILE_SUBPATH,
29
+ } = require('./lockfile-v2.js');
30
+
31
+ const {
32
+ JournaledTransaction,
33
+ TX_STATES,
34
+ ERROR_CODES: TX_ERRORS,
35
+ TransactionError,
36
+ safeRenameSync,
37
+ } = require('./journaled-transaction.js');
38
+
39
+ module.exports = {
40
+ // Safe Paths
41
+ resolveManagedPath,
42
+ toPosix,
43
+ isReservedDeviceName,
44
+ SafePathError,
45
+ SAFE_PATH_ERRORS,
46
+ WINDOWS_RESERVED_NAMES,
47
+
48
+ // Project Locks
49
+ ProjectMutationLock,
50
+ ProjectLockError,
51
+ LOCK_ERRORS,
52
+ isPidAlive,
53
+
54
+ // Lockfile v2
55
+ LockfileV2Manager,
56
+ computeExactHash,
57
+ computeSemanticHash,
58
+ LockfileV2Error,
59
+ LOCKFILE_ERRORS,
60
+ DEFAULT_LOCKFILE_SUBPATH,
61
+
62
+ // Transactions
63
+ JournaledTransaction,
64
+ TX_STATES,
65
+ TX_ERRORS,
66
+ TransactionError,
67
+ safeRenameSync,
68
+
69
+ // Platform Hardening
70
+ ...require('./platform-hardening.js'),
71
+ };
@@ -0,0 +1,451 @@
1
+ /**
2
+ * .agents/filesystem/journaled-transaction.js
3
+ * ContextOS Journaled Recoverable File Transactions
4
+ *
5
+ * Implements crash-resilient multi-file mutation protocol:
6
+ * - State machine: PLANNED -> PREPARED -> APPLYING -> COMMITTED
7
+ * - Failure handling: APPLYING -> ROLLING_BACK -> ROLLED_BACK
8
+ * - Crash recovery: incomplete journals trigger RECOVERY_REQUIRED
9
+ * - Precondition checks (expectedBeforeHash) prevent clobbering concurrent user edits
10
+ * - Transient Windows locking backoff retry (EACCES, EPERM, EBUSY)
11
+ * - Zero direct-write fallback: atomicity or complete rollback
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const crypto = require('crypto');
17
+ const { resolveManagedPath, toPosix } = require('./safe-path.js');
18
+ const { computeExactHash } = require('./lockfile-v2.js');
19
+ const { isNetworkOrUNCPath } = require('./platform-hardening.js');
20
+
21
+ const TX_BASE_SUBPATH = path.join('.agents', '.contextos', 'transactions');
22
+
23
+ const TX_STATES = {
24
+ PLANNED: 'PLANNED',
25
+ PREPARED: 'PREPARED',
26
+ APPLYING: 'APPLYING',
27
+ COMMITTED: 'COMMITTED',
28
+ ROLLING_BACK: 'ROLLING_BACK',
29
+ ROLLED_BACK: 'ROLLED_BACK',
30
+ RECOVERY_REQUIRED: 'RECOVERY_REQUIRED',
31
+ };
32
+
33
+ const ERROR_CODES = {
34
+ PRECONDITION_FAILED: 'CTX_TX_PRECONDITION_FAILED',
35
+ APPLY_FAILED: 'CTX_TX_APPLY_FAILED',
36
+ ROLLBACK_FAILED: 'CTX_TX_ROLLBACK_FAILED',
37
+ RECOVERY_REQUIRED: 'CTX_TX_RECOVERY_REQUIRED',
38
+ INVALID_STATE: 'CTX_TX_INVALID_STATE',
39
+ UNSUPPORTED: 'UNSUPPORTED',
40
+ };
41
+
42
+ class TransactionError extends Error {
43
+ constructor(code, message, details = {}) {
44
+ super(message);
45
+ this.name = 'TransactionError';
46
+ this.code = code;
47
+ this.details = details;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Windows-aware atomic rename with bounded retry for transient locks.
53
+ */
54
+ function safeRenameSync(sourcePath, targetPath, maxRetries = 3, delays = [10, 50, 150]) {
55
+ const targetDir = path.dirname(targetPath);
56
+ if (!fs.existsSync(targetDir)) {
57
+ fs.mkdirSync(targetDir, { recursive: true });
58
+ }
59
+
60
+ let lastError = null;
61
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
62
+ try {
63
+ fs.renameSync(sourcePath, targetPath);
64
+ return;
65
+ } catch (err) {
66
+ lastError = err;
67
+ const isTransient = err.code === 'EPERM' || err.code === 'EBUSY' || err.code === 'EACCES';
68
+ if (isTransient && attempt < maxRetries) {
69
+ const waitMs = delays[attempt] || 50;
70
+ try {
71
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, waitMs);
72
+ } catch {
73
+ const start = Date.now();
74
+ while (Date.now() - start < waitMs) {}
75
+ }
76
+ } else {
77
+ break;
78
+ }
79
+ }
80
+ }
81
+ throw lastError;
82
+ }
83
+
84
+ class JournaledTransaction {
85
+ constructor(projectRoot, txId = null) {
86
+ this.projectRoot = path.resolve(projectRoot);
87
+ this.txId = txId || `tx-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
88
+ this.txDir = path.resolve(this.projectRoot, TX_BASE_SUBPATH, this.txId);
89
+ this.journalPath = path.join(this.txDir, 'journal.json');
90
+ this.stagingDir = path.join(this.txDir, 'staging');
91
+ this.backupDir = path.join(this.txDir, 'backup');
92
+
93
+ this.state = TX_STATES.PLANNED;
94
+ this.operations = [];
95
+ this.appliedOperations = [];
96
+ this.createdAt = new Date().toISOString();
97
+ }
98
+
99
+ /**
100
+ * Plans a write or replace operation.
101
+ */
102
+ stageWrite(relativePath, content, options = {}) {
103
+ if (this.state !== TX_STATES.PLANNED) {
104
+ throw new TransactionError(
105
+ ERROR_CODES.INVALID_STATE,
106
+ `Cannot stage operations in state '${this.state}' (must be PLANNED)`
107
+ );
108
+ }
109
+
110
+ const { resolvedPath, relativePosixPath } = resolveManagedPath(this.projectRoot, relativePath);
111
+ const buf = Buffer.isBuffer(content) ? content : Buffer.from(String(content), 'utf8');
112
+ const afterHash = computeExactHash(buf);
113
+
114
+ this.operations.push({
115
+ type: 'write',
116
+ relativePath: relativePosixPath,
117
+ resolvedPath,
118
+ content: buf,
119
+ afterHash,
120
+ expectedBeforeHash: options.expectedBeforeHash || null,
121
+ mode: options.mode || 420,
122
+ });
123
+ }
124
+
125
+ /**
126
+ * Plans a delete operation.
127
+ */
128
+ stageDelete(relativePath, options = {}) {
129
+ if (this.state !== TX_STATES.PLANNED) {
130
+ throw new TransactionError(
131
+ ERROR_CODES.INVALID_STATE,
132
+ `Cannot stage operations in state '${this.state}' (must be PLANNED)`
133
+ );
134
+ }
135
+
136
+ const { resolvedPath, relativePosixPath } = resolveManagedPath(this.projectRoot, relativePath);
137
+
138
+ this.operations.push({
139
+ type: 'delete',
140
+ relativePath: relativePosixPath,
141
+ resolvedPath,
142
+ expectedBeforeHash: options.expectedBeforeHash || null,
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Plans an empty directory deletion operation.
148
+ */
149
+ stageDeleteDir(relativePath, options = {}) {
150
+ if (this.state !== TX_STATES.PLANNED) {
151
+ throw new TransactionError(
152
+ ERROR_CODES.INVALID_STATE,
153
+ `Cannot stage operations in state '${this.state}' (must be PLANNED)`
154
+ );
155
+ }
156
+
157
+ const { resolvedPath, relativePosixPath } = resolveManagedPath(this.projectRoot, relativePath);
158
+
159
+ this.operations.push({
160
+ type: 'deleteDir',
161
+ relativePath: relativePosixPath,
162
+ resolvedPath,
163
+ });
164
+ }
165
+
166
+ /**
167
+ * Writes the journal and staged files to disk.
168
+ */
169
+ prepare() {
170
+ if (this.state !== TX_STATES.PLANNED) {
171
+ throw new TransactionError(
172
+ ERROR_CODES.INVALID_STATE,
173
+ `Cannot prepare transaction from state '${this.state}'`
174
+ );
175
+ }
176
+
177
+ fs.mkdirSync(this.stagingDir, { recursive: true });
178
+ fs.mkdirSync(this.backupDir, { recursive: true });
179
+
180
+ // Write staged files
181
+ for (let i = 0; i < this.operations.length; i++) {
182
+ const op = this.operations[i];
183
+ if (op.type === 'write') {
184
+ const stagedFilePath = path.join(this.stagingDir, `${i}-${path.basename(op.relativePath)}`);
185
+ fs.writeFileSync(stagedFilePath, op.content);
186
+ op.stagedFilePath = stagedFilePath;
187
+ }
188
+ }
189
+
190
+ this.state = TX_STATES.PREPARED;
191
+ this.saveJournal();
192
+ return this;
193
+ }
194
+
195
+ /**
196
+ * Executes the transaction, modifying disk files.
197
+ */
198
+ commit() {
199
+ if (this.state === TX_STATES.PLANNED) {
200
+ this.prepare();
201
+ }
202
+
203
+ if (this.state !== TX_STATES.PREPARED) {
204
+ throw new TransactionError(
205
+ ERROR_CODES.INVALID_STATE,
206
+ `Cannot commit transaction in state '${this.state}'`
207
+ );
208
+ }
209
+
210
+ this.state = TX_STATES.APPLYING;
211
+ this.saveJournal();
212
+
213
+ // 1. Verify all preconditions before touching any file
214
+ for (const op of this.operations) {
215
+ if (op.expectedBeforeHash) {
216
+ if (fs.existsSync(op.resolvedPath)) {
217
+ const currentBuf = fs.readFileSync(op.resolvedPath);
218
+ const currentHash = computeExactHash(currentBuf);
219
+ if (currentHash !== op.expectedBeforeHash) {
220
+ this.rollback();
221
+ throw new TransactionError(
222
+ ERROR_CODES.PRECONDITION_FAILED,
223
+ `Precondition hash mismatch for '${op.relativePath}': expected ${op.expectedBeforeHash}, found ${currentHash}`,
224
+ { relativePath: op.relativePath, expected: op.expectedBeforeHash, actual: currentHash }
225
+ );
226
+ }
227
+ } else if (op.expectedBeforeHash !== 'NONE') {
228
+ this.rollback();
229
+ throw new TransactionError(
230
+ ERROR_CODES.PRECONDITION_FAILED,
231
+ `Precondition failed: target file '${op.relativePath}' does not exist on disk`,
232
+ { relativePath: op.relativePath }
233
+ );
234
+ }
235
+ }
236
+ }
237
+
238
+ // 2. Apply operations sequentially with backups
239
+ try {
240
+ for (let i = 0; i < this.operations.length; i++) {
241
+ const op = this.operations[i];
242
+ const backupFilePath = path.join(this.backupDir, `${i}-${path.basename(op.relativePath)}`);
243
+
244
+ if (fs.existsSync(op.resolvedPath)) {
245
+ if (op.type !== 'deleteDir') {
246
+ fs.copyFileSync(op.resolvedPath, backupFilePath);
247
+ op.backupFilePath = backupFilePath;
248
+ }
249
+ op.hadExisting = true;
250
+ } else {
251
+ op.hadExisting = false;
252
+ }
253
+
254
+ if (op.type === 'write') {
255
+ safeRenameSync(op.stagedFilePath, op.resolvedPath);
256
+ } else if (op.type === 'delete') {
257
+ if (fs.existsSync(op.resolvedPath)) {
258
+ fs.unlinkSync(op.resolvedPath);
259
+ }
260
+ } else if (op.type === 'deleteDir') {
261
+ if (fs.existsSync(op.resolvedPath)) {
262
+ try {
263
+ fs.rmdirSync(op.resolvedPath);
264
+ } catch (err) {
265
+ // Gracefully ignore ENOTEMPTY or ENOENT as per W3.3 safety requirements
266
+ if (err.code !== 'ENOTEMPTY' && err.code !== 'ENOENT' && err.code !== 'EEXIST') {
267
+ throw err;
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ this.appliedOperations.push(op);
274
+ }
275
+ } catch (applyErr) {
276
+ // Automatic reverse-order rollback on failure
277
+ this.rollback();
278
+ throw new TransactionError(
279
+ ERROR_CODES.APPLY_FAILED,
280
+ `Transaction apply failed: ${applyErr.message}. All modifications rolled back successfully.`,
281
+ { originalError: applyErr.message }
282
+ );
283
+ }
284
+
285
+ this.state = TX_STATES.COMMITTED;
286
+ this.completedAt = new Date().toISOString();
287
+ this.saveJournal();
288
+
289
+ // Clean up temporary transaction staging/backups upon clean commit
290
+ this.cleanup();
291
+
292
+ return {
293
+ txId: this.txId,
294
+ status: 'COMMITTED',
295
+ appliedCount: this.appliedOperations.length,
296
+ };
297
+ }
298
+
299
+ /**
300
+ * Rolls back applied modifications in reverse order.
301
+ */
302
+ rollback() {
303
+ this.state = TX_STATES.ROLLING_BACK;
304
+ this.saveJournal();
305
+
306
+ let rollbackError = null;
307
+ const toRevert = [...this.appliedOperations].reverse();
308
+
309
+ for (const op of toRevert) {
310
+ try {
311
+ if (op.hadExisting && op.backupFilePath && fs.existsSync(op.backupFilePath)) {
312
+ safeRenameSync(op.backupFilePath, op.resolvedPath);
313
+ } else if (op.hadExisting && op.type === 'deleteDir') {
314
+ if (!fs.existsSync(op.resolvedPath)) {
315
+ fs.mkdirSync(op.resolvedPath, { recursive: true });
316
+ }
317
+ } else if (!op.hadExisting && fs.existsSync(op.resolvedPath)) {
318
+ // File or dir was newly created by this tx, remove it
319
+ if (op.type === 'write' || op.type === 'delete') {
320
+ fs.unlinkSync(op.resolvedPath);
321
+ } else if (op.type === 'deleteDir') {
322
+ // If we didn't have existing deleteDir, but we somehow created it? That's not a thing, but just in case
323
+ fs.rmdirSync(op.resolvedPath);
324
+ }
325
+ }
326
+ } catch (err) {
327
+ rollbackError = err;
328
+ }
329
+ }
330
+
331
+ if (rollbackError) {
332
+ this.state = TX_STATES.RECOVERY_REQUIRED;
333
+ this.saveJournal();
334
+ throw new TransactionError(
335
+ ERROR_CODES.ROLLBACK_FAILED,
336
+ `Rollback failed: ${rollbackError.message}. Manual recovery required for tx '${this.txId}'`,
337
+ { txId: this.txId, originalError: rollbackError.message }
338
+ );
339
+ }
340
+
341
+ this.state = TX_STATES.ROLLED_BACK;
342
+ this.saveJournal();
343
+ this.cleanup();
344
+
345
+ return { txId: this.txId, status: 'ROLLED_BACK' };
346
+ }
347
+
348
+ saveJournal() {
349
+ if (!fs.existsSync(this.txDir)) {
350
+ fs.mkdirSync(this.txDir, { recursive: true });
351
+ }
352
+
353
+ const journalData = {
354
+ txId: this.txId,
355
+ state: this.state,
356
+ createdAt: this.createdAt,
357
+ updatedAt: new Date().toISOString(),
358
+ completedAt: this.completedAt || null,
359
+ operations: this.operations.map(o => ({
360
+ type: o.type,
361
+ relativePath: o.relativePath,
362
+ afterHash: o.afterHash,
363
+ expectedBeforeHash: o.expectedBeforeHash,
364
+ hadExisting: o.hadExisting,
365
+ stagedFilePath: o.stagedFilePath || null,
366
+ backupFilePath: o.backupFilePath || null,
367
+ })),
368
+ };
369
+
370
+ fs.writeFileSync(this.journalPath, JSON.stringify(journalData, null, 2), 'utf8');
371
+ }
372
+
373
+ cleanup() {
374
+ try {
375
+ if (fs.existsSync(this.stagingDir)) {
376
+ fs.rmSync(this.stagingDir, { recursive: true, force: true });
377
+ }
378
+ if (fs.existsSync(this.backupDir)) {
379
+ fs.rmSync(this.backupDir, { recursive: true, force: true });
380
+ }
381
+ } catch {
382
+ // Best effort cleanup of completed transactions
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Inspects pending or broken transactions across the project.
388
+ */
389
+ static listPending(projectRoot) {
390
+ const baseDir = path.resolve(projectRoot, TX_BASE_SUBPATH);
391
+ if (!fs.existsSync(baseDir)) return [];
392
+
393
+ const entries = fs.readdirSync(baseDir, { withFileTypes: true });
394
+ const pending = [];
395
+
396
+ for (const ent of entries) {
397
+ if (ent.isDirectory()) {
398
+ const jPath = path.join(baseDir, ent.name, 'journal.json');
399
+ if (fs.existsSync(jPath)) {
400
+ try {
401
+ const data = JSON.parse(fs.readFileSync(jPath, 'utf8'));
402
+ const st = data.state || data.status;
403
+ if (st === TX_STATES.APPLYING || st === TX_STATES.ROLLING_BACK || st === TX_STATES.RECOVERY_REQUIRED || st === TX_STATES.PREPARED) {
404
+ pending.push(data);
405
+ }
406
+ } catch {
407
+ pending.push({ txId: ent.name, state: TX_STATES.RECOVERY_REQUIRED, error: 'Unparseable journal' });
408
+ }
409
+ }
410
+ }
411
+ }
412
+
413
+ return pending;
414
+ }
415
+
416
+ /**
417
+ * Loads a transaction from an existing journal.json.
418
+ */
419
+ static load(projectRoot, txId) {
420
+ const tx = new JournaledTransaction(projectRoot, txId);
421
+ if (!fs.existsSync(tx.journalPath)) {
422
+ throw new Error(`Transaction ${txId} not found`);
423
+ }
424
+
425
+ const data = JSON.parse(fs.readFileSync(tx.journalPath, 'utf8'));
426
+ tx.state = data.state;
427
+ tx.createdAt = data.createdAt;
428
+ tx.completedAt = data.completedAt;
429
+
430
+ tx.operations = data.operations.map(o => ({
431
+ type: o.type,
432
+ relativePath: o.relativePath,
433
+ resolvedPath: resolveManagedPath(projectRoot, o.relativePath).resolvedPath,
434
+ afterHash: o.afterHash,
435
+ expectedBeforeHash: o.expectedBeforeHash,
436
+ hadExisting: o.hadExisting,
437
+ stagedFilePath: o.stagedFilePath,
438
+ backupFilePath: o.backupFilePath,
439
+ }));
440
+
441
+ return tx;
442
+ }
443
+ }
444
+
445
+ module.exports = {
446
+ JournaledTransaction,
447
+ TX_STATES,
448
+ ERROR_CODES,
449
+ TransactionError,
450
+ safeRenameSync,
451
+ };