@latysh-development/monolith 0.0.0-stage → 0.1.0-alpha.1

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 (345) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +179 -2
  3. package/dist/brief/cli.d.ts +30 -0
  4. package/dist/brief/cli.d.ts.map +1 -0
  5. package/dist/brief/cli.js +85 -0
  6. package/dist/brief/collect.d.ts +17 -0
  7. package/dist/brief/collect.d.ts.map +1 -0
  8. package/dist/brief/collect.js +342 -0
  9. package/dist/brief/config.d.ts +40 -0
  10. package/dist/brief/config.d.ts.map +1 -0
  11. package/dist/brief/config.js +85 -0
  12. package/dist/brief/constants.d.ts +12 -0
  13. package/dist/brief/constants.d.ts.map +1 -0
  14. package/dist/brief/constants.js +11 -0
  15. package/dist/brief/docs.d.ts +53 -0
  16. package/dist/brief/docs.d.ts.map +1 -0
  17. package/dist/brief/docs.js +229 -0
  18. package/dist/brief/freshness.d.ts +104 -0
  19. package/dist/brief/freshness.d.ts.map +1 -0
  20. package/dist/brief/freshness.js +263 -0
  21. package/dist/brief/git-signals.d.ts +17 -0
  22. package/dist/brief/git-signals.d.ts.map +1 -0
  23. package/dist/brief/git-signals.js +101 -0
  24. package/dist/brief/index.d.ts +11 -0
  25. package/dist/brief/index.d.ts.map +1 -0
  26. package/dist/brief/index.js +5 -0
  27. package/dist/brief/reconcile.d.ts +75 -0
  28. package/dist/brief/reconcile.d.ts.map +1 -0
  29. package/dist/brief/reconcile.js +259 -0
  30. package/dist/brief/render.d.ts +67 -0
  31. package/dist/brief/render.d.ts.map +1 -0
  32. package/dist/brief/render.js +458 -0
  33. package/dist/brief/sanitize.d.ts +38 -0
  34. package/dist/brief/sanitize.d.ts.map +1 -0
  35. package/dist/brief/sanitize.js +48 -0
  36. package/dist/brief/selectors.d.ts +36 -0
  37. package/dist/brief/selectors.d.ts.map +1 -0
  38. package/dist/brief/selectors.js +75 -0
  39. package/dist/brief/story.d.ts +18 -0
  40. package/dist/brief/story.d.ts.map +1 -0
  41. package/dist/brief/story.js +59 -0
  42. package/dist/brief/types.d.ts +126 -0
  43. package/dist/brief/types.d.ts.map +1 -0
  44. package/dist/brief/types.js +11 -0
  45. package/dist/cli/index.d.ts +3 -0
  46. package/dist/cli/index.d.ts.map +1 -0
  47. package/dist/cli/index.js +199 -0
  48. package/dist/cli/init-args.d.ts +16 -0
  49. package/dist/cli/init-args.d.ts.map +1 -0
  50. package/dist/cli/init-args.js +24 -0
  51. package/dist/cli/ui-args.d.ts +23 -0
  52. package/dist/cli/ui-args.d.ts.map +1 -0
  53. package/dist/cli/ui-args.js +66 -0
  54. package/dist/cli/ui.d.ts +40 -0
  55. package/dist/cli/ui.d.ts.map +1 -0
  56. package/dist/cli/ui.js +180 -0
  57. package/dist/coordinatorium/checkpoint.d.ts +44 -0
  58. package/dist/coordinatorium/checkpoint.d.ts.map +1 -0
  59. package/dist/coordinatorium/checkpoint.js +124 -0
  60. package/dist/coordinatorium/cli.d.ts +39 -0
  61. package/dist/coordinatorium/cli.d.ts.map +1 -0
  62. package/dist/coordinatorium/cli.js +203 -0
  63. package/dist/coordinatorium/errors.d.ts +112 -0
  64. package/dist/coordinatorium/errors.d.ts.map +1 -0
  65. package/dist/coordinatorium/errors.js +145 -0
  66. package/dist/coordinatorium/index.d.ts +11 -0
  67. package/dist/coordinatorium/index.d.ts.map +1 -0
  68. package/dist/coordinatorium/index.js +5 -0
  69. package/dist/coordinatorium/lock.d.ts +187 -0
  70. package/dist/coordinatorium/lock.d.ts.map +1 -0
  71. package/dist/coordinatorium/lock.js +659 -0
  72. package/dist/coordinatorium/mission.d.ts +32 -0
  73. package/dist/coordinatorium/mission.d.ts.map +1 -0
  74. package/dist/coordinatorium/mission.js +87 -0
  75. package/dist/coordinatorium/state-keeper.d.ts +115 -0
  76. package/dist/coordinatorium/state-keeper.d.ts.map +1 -0
  77. package/dist/coordinatorium/state-keeper.js +396 -0
  78. package/dist/coordinatorium/types.d.ts +69 -0
  79. package/dist/coordinatorium/types.d.ts.map +1 -0
  80. package/dist/coordinatorium/types.js +27 -0
  81. package/dist/discovery/compose.d.ts +28 -0
  82. package/dist/discovery/compose.d.ts.map +1 -0
  83. package/dist/discovery/compose.js +159 -0
  84. package/dist/discovery/contracts.d.ts +211 -0
  85. package/dist/discovery/contracts.d.ts.map +1 -0
  86. package/dist/discovery/contracts.js +555 -0
  87. package/dist/discovery/docs.d.ts +278 -0
  88. package/dist/discovery/docs.d.ts.map +1 -0
  89. package/dist/discovery/docs.js +1182 -0
  90. package/dist/discovery/facts.d.ts +49 -0
  91. package/dist/discovery/facts.d.ts.map +1 -0
  92. package/dist/discovery/facts.js +541 -0
  93. package/dist/discovery/index.d.ts +8 -0
  94. package/dist/discovery/index.d.ts.map +1 -0
  95. package/dist/discovery/index.js +9 -0
  96. package/dist/discovery/orchestrate.d.ts +35 -0
  97. package/dist/discovery/orchestrate.d.ts.map +1 -0
  98. package/dist/discovery/orchestrate.js +32 -0
  99. package/dist/discovery/render.d.ts +36 -0
  100. package/dist/discovery/render.d.ts.map +1 -0
  101. package/dist/discovery/render.js +278 -0
  102. package/dist/discovery/traverse.d.ts +197 -0
  103. package/dist/discovery/traverse.d.ts.map +1 -0
  104. package/dist/discovery/traverse.js +510 -0
  105. package/dist/durable/assess.d.ts +105 -0
  106. package/dist/durable/assess.d.ts.map +1 -0
  107. package/dist/durable/assess.js +239 -0
  108. package/dist/durable/canonical.d.ts +25 -0
  109. package/dist/durable/canonical.d.ts.map +1 -0
  110. package/dist/durable/canonical.js +78 -0
  111. package/dist/durable/cli.d.ts +37 -0
  112. package/dist/durable/cli.d.ts.map +1 -0
  113. package/dist/durable/cli.js +141 -0
  114. package/dist/durable/errors.d.ts +192 -0
  115. package/dist/durable/errors.d.ts.map +1 -0
  116. package/dist/durable/errors.js +323 -0
  117. package/dist/durable/fs-store.d.ts +254 -0
  118. package/dist/durable/fs-store.d.ts.map +1 -0
  119. package/dist/durable/fs-store.js +1493 -0
  120. package/dist/durable/index.d.ts +20 -0
  121. package/dist/durable/index.d.ts.map +1 -0
  122. package/dist/durable/index.js +13 -0
  123. package/dist/durable/log.d.ts +33 -0
  124. package/dist/durable/log.d.ts.map +1 -0
  125. package/dist/durable/log.js +157 -0
  126. package/dist/durable/memory-store.d.ts +51 -0
  127. package/dist/durable/memory-store.d.ts.map +1 -0
  128. package/dist/durable/memory-store.js +302 -0
  129. package/dist/durable/projection.d.ts +35 -0
  130. package/dist/durable/projection.d.ts.map +1 -0
  131. package/dist/durable/projection.js +328 -0
  132. package/dist/durable/prune-check.d.ts +34 -0
  133. package/dist/durable/prune-check.d.ts.map +1 -0
  134. package/dist/durable/prune-check.js +72 -0
  135. package/dist/durable/register.d.ts +100 -0
  136. package/dist/durable/register.d.ts.map +1 -0
  137. package/dist/durable/register.js +532 -0
  138. package/dist/durable/retention.d.ts +49 -0
  139. package/dist/durable/retention.d.ts.map +1 -0
  140. package/dist/durable/retention.js +59 -0
  141. package/dist/durable/runtime.d.ts +499 -0
  142. package/dist/durable/runtime.d.ts.map +1 -0
  143. package/dist/durable/runtime.js +1288 -0
  144. package/dist/durable/seams.d.ts +44 -0
  145. package/dist/durable/seams.d.ts.map +1 -0
  146. package/dist/durable/seams.js +21 -0
  147. package/dist/durable/testing.d.ts +11 -0
  148. package/dist/durable/testing.d.ts.map +1 -0
  149. package/dist/durable/testing.js +25 -0
  150. package/dist/durable/types.d.ts +442 -0
  151. package/dist/durable/types.d.ts.map +1 -0
  152. package/dist/durable/types.js +75 -0
  153. package/dist/graph/builder.d.ts +11 -0
  154. package/dist/graph/builder.d.ts.map +1 -0
  155. package/dist/graph/builder.js +153 -0
  156. package/dist/graph/identity.d.ts +37 -0
  157. package/dist/graph/identity.d.ts.map +1 -0
  158. package/dist/graph/identity.js +46 -0
  159. package/dist/graph/index.d.ts +7 -0
  160. package/dist/graph/index.d.ts.map +1 -0
  161. package/dist/graph/index.js +3 -0
  162. package/dist/graph/resolver.d.ts +41 -0
  163. package/dist/graph/resolver.d.ts.map +1 -0
  164. package/dist/graph/resolver.js +36 -0
  165. package/dist/graph/types.d.ts +42 -0
  166. package/dist/graph/types.d.ts.map +1 -0
  167. package/dist/graph/types.js +1 -0
  168. package/dist/http/index.d.ts +5 -0
  169. package/dist/http/index.d.ts.map +1 -0
  170. package/dist/http/index.js +2 -0
  171. package/dist/http/server.d.ts +76 -0
  172. package/dist/http/server.d.ts.map +1 -0
  173. package/dist/http/server.js +195 -0
  174. package/dist/http/ui-assets.d.ts +22 -0
  175. package/dist/http/ui-assets.d.ts.map +1 -0
  176. package/dist/http/ui-assets.js +100 -0
  177. package/dist/ideas/cli.d.ts +33 -0
  178. package/dist/ideas/cli.d.ts.map +1 -0
  179. package/dist/ideas/cli.js +158 -0
  180. package/dist/ideas/format.d.ts +38 -0
  181. package/dist/ideas/format.d.ts.map +1 -0
  182. package/dist/ideas/format.js +194 -0
  183. package/dist/ideas/index.d.ts +6 -0
  184. package/dist/ideas/index.d.ts.map +1 -0
  185. package/dist/ideas/index.js +3 -0
  186. package/dist/ideas/store.d.ts +55 -0
  187. package/dist/ideas/store.d.ts.map +1 -0
  188. package/dist/ideas/store.js +262 -0
  189. package/dist/ideas/types.d.ts +54 -0
  190. package/dist/ideas/types.d.ts.map +1 -0
  191. package/dist/ideas/types.js +26 -0
  192. package/dist/index.d.ts +32 -0
  193. package/dist/index.d.ts.map +1 -0
  194. package/dist/index.js +28 -0
  195. package/dist/init/index.d.ts +12 -0
  196. package/dist/init/index.d.ts.map +1 -0
  197. package/dist/init/index.js +90 -0
  198. package/dist/init/prompts.d.ts +3 -0
  199. package/dist/init/prompts.d.ts.map +1 -0
  200. package/dist/init/prompts.js +47 -0
  201. package/dist/init/scaffold.d.ts +29 -0
  202. package/dist/init/scaffold.d.ts.map +1 -0
  203. package/dist/init/scaffold.js +208 -0
  204. package/dist/init/types.d.ts +14 -0
  205. package/dist/init/types.d.ts.map +1 -0
  206. package/dist/init/types.js +1 -0
  207. package/dist/managed/index.d.ts +73 -0
  208. package/dist/managed/index.d.ts.map +1 -0
  209. package/dist/managed/index.js +222 -0
  210. package/dist/readmodel/dashboard.d.ts +40 -0
  211. package/dist/readmodel/dashboard.d.ts.map +1 -0
  212. package/dist/readmodel/dashboard.js +236 -0
  213. package/dist/readmodel/index.d.ts +3 -0
  214. package/dist/readmodel/index.d.ts.map +1 -0
  215. package/dist/readmodel/index.js +1 -0
  216. package/dist/readmodel/types.d.ts +104 -0
  217. package/dist/readmodel/types.d.ts.map +1 -0
  218. package/dist/readmodel/types.js +15 -0
  219. package/dist/root/index.d.ts +186 -0
  220. package/dist/root/index.d.ts.map +1 -0
  221. package/dist/root/index.js +430 -0
  222. package/dist/root/safe-write.d.ts +268 -0
  223. package/dist/root/safe-write.d.ts.map +1 -0
  224. package/dist/root/safe-write.js +966 -0
  225. package/dist/root/write-artifacts.d.ts +20 -0
  226. package/dist/root/write-artifacts.d.ts.map +1 -0
  227. package/dist/root/write-artifacts.js +42 -0
  228. package/dist/scriptorium/compile.d.ts +4 -0
  229. package/dist/scriptorium/compile.d.ts.map +1 -0
  230. package/dist/scriptorium/compile.js +185 -0
  231. package/dist/scriptorium/frontmatter.d.ts +13 -0
  232. package/dist/scriptorium/frontmatter.d.ts.map +1 -0
  233. package/dist/scriptorium/frontmatter.js +90 -0
  234. package/dist/scriptorium/index.d.ts +3 -0
  235. package/dist/scriptorium/index.d.ts.map +1 -0
  236. package/dist/scriptorium/index.js +1 -0
  237. package/dist/scriptorium/markdown.d.ts +31 -0
  238. package/dist/scriptorium/markdown.d.ts.map +1 -0
  239. package/dist/scriptorium/markdown.js +88 -0
  240. package/dist/scriptorium/types.d.ts +78 -0
  241. package/dist/scriptorium/types.d.ts.map +1 -0
  242. package/dist/scriptorium/types.js +1 -0
  243. package/dist/types/enforcement.d.ts +11 -0
  244. package/dist/types/enforcement.d.ts.map +1 -0
  245. package/dist/types/enforcement.js +1 -0
  246. package/dist/types/integrity.d.ts +33 -0
  247. package/dist/types/integrity.d.ts.map +1 -0
  248. package/dist/types/integrity.js +1 -0
  249. package/dist/types/project-definition.d.ts +30 -0
  250. package/dist/types/project-definition.d.ts.map +1 -0
  251. package/dist/types/project-definition.js +1 -0
  252. package/dist/ui/assets/cosmic-glass-bg-Dwx47zPu.webp +0 -0
  253. package/dist/ui/assets/index-C4NhMaq7.js +28 -0
  254. package/dist/ui/assets/index-DMZ7ZvZY.css +1 -0
  255. package/dist/ui/index.html +13 -0
  256. package/dist/update/cli.d.ts +8 -0
  257. package/dist/update/cli.d.ts.map +1 -0
  258. package/dist/update/cli.js +115 -0
  259. package/dist/update/index.d.ts +78 -0
  260. package/dist/update/index.d.ts.map +1 -0
  261. package/dist/update/index.js +285 -0
  262. package/dist/validate/index.d.ts +6 -0
  263. package/dist/validate/index.d.ts.map +1 -0
  264. package/dist/validate/index.js +214 -0
  265. package/dist/version.d.ts +2 -0
  266. package/dist/version.d.ts.map +1 -0
  267. package/dist/version.js +1 -0
  268. package/package.json +67 -4
  269. package/templates/.gitkeep +0 -0
  270. package/templates/default-instance/instruction/.cursorrules +59 -0
  271. package/templates/default-instance/instruction/.windsurfrules +59 -0
  272. package/templates/default-instance/instruction/CLAUDE.md +57 -0
  273. package/templates/default-instance/managed/agent.md +20 -0
  274. package/templates/default-instance/managed/bootstrap.md +7 -0
  275. package/templates/default-instance/managed/index-idea.md +5 -0
  276. package/templates/default-instance/managed/routing-idea.md +14 -0
  277. package/templates/default-instance/monolith/AGENT.md +1 -0
  278. package/templates/default-instance/monolith/context/boot/HANDOFF.md +49 -0
  279. package/templates/default-instance/monolith/context/boot/WORK_SESSION.md +30 -0
  280. package/templates/default-instance/monolith/context/execution/current-state.md +30 -0
  281. package/templates/default-instance/monolith/context/maps/dependency-map.md +38 -0
  282. package/templates/default-instance/monolith/context/maps/project-map.md +85 -0
  283. package/templates/default-instance/monolith/context/maps/roadmap-index.md +41 -0
  284. package/templates/default-instance/monolith/context/maps/routing-map.md +151 -0
  285. package/templates/default-instance/monolith/context/maps/semantic-map.md +75 -0
  286. package/templates/default-instance/monolith/context/models/model-registry.md +37 -0
  287. package/templates/default-instance/monolith/context/models/privacy-classification.md +35 -0
  288. package/templates/default-instance/monolith/context/templates/plan-template.md +56 -0
  289. package/templates/default-instance/monolith/governance/README.md +29 -0
  290. package/templates/default-instance/monolith/governance/RULE_SCHEMA.md +58 -0
  291. package/templates/default-instance/monolith/learning/INDEX.md +48 -0
  292. package/templates/default-instance/monolith/learning/injection-map.md +49 -0
  293. package/templates/default-instance/monolith/learning/taxonomy.md +30 -0
  294. package/templates/default-instance/monolith/memory/errors/README.md +26 -0
  295. package/templates/default-instance/monolith/memory/errors/SCHEMA.md +47 -0
  296. package/templates/default-instance/monolith/memory/errors/TAXONOMY.md +27 -0
  297. package/templates/default-instance/monolith/memory/long-term/architecture-principles.md +31 -0
  298. package/templates/default-instance/monolith/memory/long-term/decisions.md +36 -0
  299. package/templates/default-instance/monolith/memory/long-term/execution-history.md +20 -0
  300. package/templates/default-instance/monolith/memory/long-term/tech-debt.md +25 -0
  301. package/templates/default-instance/monolith/memory/working/active.md +41 -0
  302. package/templates/default-instance/monolith/memory/working/governance-result.md +0 -0
  303. package/templates/default-instance/monolith/memory/working/integrity-result.md +0 -0
  304. package/templates/default-instance/monolith/memory/working/story-index.md +28 -0
  305. package/templates/default-instance/monolith/protocols/boot/boot-sequence.md +59 -0
  306. package/templates/default-instance/monolith/protocols/cognition/cognitive-modes.md +90 -0
  307. package/templates/default-instance/monolith/protocols/cognition/dry-run.md +56 -0
  308. package/templates/default-instance/monolith/protocols/cognition/execution-trace-format.md +52 -0
  309. package/templates/default-instance/monolith/protocols/cognition/semantic-planning.md +51 -0
  310. package/templates/default-instance/monolith/protocols/execution/hybrid-execution.md +37 -0
  311. package/templates/default-instance/monolith/protocols/execution/local-llm-governance.md +42 -0
  312. package/templates/default-instance/monolith/protocols/governance/command-policy.md +51 -0
  313. package/templates/default-instance/monolith/protocols/governance/core-invariants.md +40 -0
  314. package/templates/default-instance/monolith/protocols/governance/memory-governance.md +54 -0
  315. package/templates/default-instance/monolith/protocols/governance/metadata-schema.md +62 -0
  316. package/templates/default-instance/monolith/protocols/governance/skills.md +53 -0
  317. package/templates/default-instance/monolith/protocols/governance/work-session.md +46 -0
  318. package/templates/default-instance/monolith/skills/INDEX.md +32 -0
  319. package/templates/default-instance/monolith/skills/current-status/SKILL.md +47 -0
  320. package/templates/default-instance/monolith/skills/finalize-story/SKILL.md +86 -0
  321. package/templates/default-instance/monolith/skills/global-status/SKILL.md +56 -0
  322. package/templates/default-instance/monolith/skills/idea-capture/SKILL.md +157 -0
  323. package/templates/default-instance/monolith/skills/self-improvement/SKILL.md +77 -0
  324. package/templates/default-instance/monolith/state.json +11 -0
  325. package/templates/default-instance/monolith/tests/INDEX.md +57 -0
  326. package/templates/default-instance/monolith/tests/scenarios/architecture-review.md +32 -0
  327. package/templates/default-instance/monolith/tests/scenarios/debug-failure.md +32 -0
  328. package/templates/default-instance/monolith/tests/scenarios/dry-run-activation.md +33 -0
  329. package/templates/default-instance/monolith/tests/scenarios/execution-trace-verification.md +31 -0
  330. package/templates/default-instance/monolith/tests/scenarios/finalize-story.md +35 -0
  331. package/templates/default-instance/monolith/tests/scenarios/implement-feature.md +36 -0
  332. package/templates/default-instance/monolith/tests/scenarios/local-llm-delegation.md +31 -0
  333. package/templates/default-instance/monolith/tests/scenarios/planning-request.md +32 -0
  334. package/templates/default-instance/monolith/tests/scenarios/sensitive-context-task.md +30 -0
  335. package/templates/default-instance/monolith/tests/scenarios/validator-verification.md +33 -0
  336. package/templates/default-instance/monolith/tests/validation/expected-behavior.md +70 -0
  337. package/templates/default-instance/monolith/tests/validators/INDEX.md +17 -0
  338. package/templates/default-instance/monolith/tests/validators/delegation-semantics.md +22 -0
  339. package/templates/default-instance/monolith/tests/validators/mode-consistency.md +25 -0
  340. package/templates/default-instance/monolith/tests/validators/privacy-routing.md +23 -0
  341. package/templates/default-instance/monolith/tests/validators/protocol-references.md +27 -0
  342. package/templates/default-instance/monolith/tests/validators/routing-integrity.md +30 -0
  343. package/templates/default-instance/projectroot/AGENTS.md +111 -0
  344. package/templates/default-instance/projectroot/monolith.config.json +32 -0
  345. package/templates/default-instance/projectroot/project.definition.mjs.template +41 -0
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Resume assessment — "may I simply carry on?"
3
+ *
4
+ * A pure, deterministic function of a task's state and log (no clock except
5
+ * the `now` the caller passes, no I/O, no LLM). It answers the question every
6
+ * resumed agentic task has to answer, and classifies the answer into the
7
+ * three continuation scenarios:
8
+ *
9
+ * - `direct` : results are in, nothing the plan relied on changed, the next
10
+ * step is known — continue without further reasoning.
11
+ * - `adaptive` : results are in, the world moved, but nothing *blocking* was
12
+ * invalidated — continue, taking the listed changes into account.
13
+ * - `recovery` : a blocking assumption broke, an effect is in doubt, an awaited
14
+ * result never came, or the next step is unknown — the plan can't
15
+ * be trusted; hand the resume packet to a reasoning component.
16
+ *
17
+ * plus `waiting` (nothing runnable yet; more results are expected) and
18
+ * `closed` (terminal). The classification is conservative: a doubt is never
19
+ * resolved in favour of `direct`.
20
+ *
21
+ * The assessment compares *recorded* assumptions with the *current* values of
22
+ * the facts they were about. Current values come from `fact.observed` events
23
+ * (other agents/systems reporting) and from `observations` the caller supplies
24
+ * live; the live value wins. Durable State does not know how to look at the
25
+ * world — the application does — but it knows what the plan relied on.
26
+ */
27
+ import { canonicalize, fingerprintOf, toJson } from './canonical.js';
28
+ const WORLD_TYPES = new Set(['fact.observed', 'plan.declared', 'assumption.recorded', 'assumption.retracted', 'decision.recorded']);
29
+ const PROGRESS_TYPES = new Set(['plan.step_done', 'plan.step_skipped', 'decision.recorded', 'effect.completed']);
30
+ function keyOf(e) {
31
+ switch (e.type) {
32
+ case 'fact.observed':
33
+ case 'assumption.recorded':
34
+ case 'assumption.retracted':
35
+ return e.data.key;
36
+ case 'decision.recorded':
37
+ return e.data.id;
38
+ default:
39
+ return null;
40
+ }
41
+ }
42
+ export function leaseIsLive(state, now) {
43
+ const l = state.lease;
44
+ return l !== null && !l.released && Date.parse(l.leaseUntil) > now.getTime();
45
+ }
46
+ export function assessTask(state, events, opts) {
47
+ const reasons = [];
48
+ const base = {
49
+ taskId: state.taskId,
50
+ status: state.status,
51
+ headSeq: state.headSeq,
52
+ };
53
+ const live = leaseIsLive(state, opts.now);
54
+ if (state.status !== 'active') {
55
+ return {
56
+ ...base, mode: 'closed', reasons, nextSteps: [], planComplete: state.plan?.every((p) => p.status !== 'pending') ?? false,
57
+ results: [], pendingAwaits: [], assumptionChanges: [], unresolvedEffects: [], sinceSend: [], claimable: false,
58
+ };
59
+ }
60
+ const awaits = Object.values(state.awaits).sort((a, b) => a.registeredAtSeq - b.registeredAtSeq);
61
+ const plan = state.plan ?? [];
62
+ const stepById = new Map(plan.map((p) => [p.id, p]));
63
+ const closed = (id) => stepById.get(id)?.status !== 'pending';
64
+ // --- plan readiness -------------------------------------------------------
65
+ const nextSteps = plan.filter((p) => {
66
+ if (p.status !== 'pending')
67
+ return false;
68
+ if (!p.dependsOn.every(closed))
69
+ return false;
70
+ return awaits.filter((a) => a.stepId === p.id).every((a) => a.status === 'resolved');
71
+ });
72
+ const pendingSteps = plan.filter((p) => p.status === 'pending');
73
+ const planComplete = state.plan !== null && pendingSteps.length === 0;
74
+ const pendingAwaits = awaits
75
+ .filter((a) => a.status === 'pending')
76
+ .map((a) => ({ awaitId: a.awaitId, purpose: a.purpose, stepId: a.stepId, deadlineAt: a.deadlineAt, registeredAtSeq: a.registeredAtSeq }));
77
+ // --- results that arrived but were not acted on ---------------------------
78
+ const lastProgress = events.reduce((m, e) => (PROGRESS_TYPES.has(e.type) ? Math.max(m, e.seq) : m), 0);
79
+ const results = awaits
80
+ .filter((a) => a.status === 'resolved')
81
+ .filter((a) => (a.stepId !== null && stepById.has(a.stepId) ? stepById.get(a.stepId)?.status === 'pending' : a.resolvedAtSeq > lastProgress))
82
+ .map((a) => ({
83
+ awaitId: a.awaitId, stepId: a.stepId, purpose: a.purpose, payload: a.payload, late: a.late, basisSeq: a.basisSeq,
84
+ resolvedAtSeq: a.resolvedAtSeq,
85
+ }));
86
+ // --- assumptions vs. the present -----------------------------------------
87
+ const liveObs = new Map();
88
+ const liveVal = new Map();
89
+ for (const [k, v] of Object.entries(opts.observations ?? {})) {
90
+ const j = toJson(v, `observation ${k}`);
91
+ liveObs.set(k, fingerprintOf(j));
92
+ liveVal.set(k, j);
93
+ }
94
+ const assumptionChanges = [];
95
+ const unverified = opts.unverifiedBlocking ?? 'adaptive';
96
+ for (const a of Object.values(state.assumptions).filter((x) => x.status === 'active').sort((x, y) => (x.key < y.key ? -1 : x.key > y.key ? 1 : 0))) {
97
+ const liveFp = liveObs.get(a.key);
98
+ const rec = state.observations[a.key];
99
+ const currentFp = liveFp ?? rec?.fingerprint;
100
+ if (currentFp === undefined) {
101
+ if (a.criticality === 'blocking' && unverified !== 'ignore') {
102
+ reasons.push({
103
+ code: 'BLOCKING_ASSUMPTION_UNVERIFIED',
104
+ severity: unverified === 'recovery' ? 'recovery' : 'adaptive',
105
+ detail: `no current value is known for blocking assumption "${a.key}": ${a.statement}`,
106
+ refs: [a.key],
107
+ });
108
+ }
109
+ continue;
110
+ }
111
+ // A recorded observation older than the assumption says nothing about what changed since.
112
+ if (liveFp === undefined && rec && rec.seq < a.recordedAtSeq)
113
+ continue;
114
+ if (currentFp !== a.fingerprint) {
115
+ assumptionChanges.push({
116
+ key: a.key, criticality: a.criticality, statement: a.statement, was: a.value,
117
+ now: liveFp !== undefined ? liveVal.get(a.key) : (rec?.value ?? null), source: liveFp !== undefined ? 'live' : 'recorded',
118
+ });
119
+ reasons.push({
120
+ code: a.criticality === 'blocking' ? 'BLOCKING_ASSUMPTION_CHANGED' : 'ADVISORY_ASSUMPTION_CHANGED',
121
+ severity: a.criticality === 'blocking' ? 'recovery' : 'adaptive',
122
+ detail: `assumption "${a.key}" no longer holds: ${a.statement}`,
123
+ refs: [a.key],
124
+ });
125
+ }
126
+ }
127
+ // --- effects whose outcome is unknown --------------------------------------
128
+ const unresolvedEffects = Object.values(state.effects)
129
+ .filter((x) => x.status === 'intended' || (x.status === 'failed' && x.uncertain))
130
+ .sort((a, b) => a.intendedAtSeq - b.intendedAtSeq)
131
+ .map((x) => ({
132
+ key: x.key, description: x.description, intendedAtSeq: x.intendedAtSeq,
133
+ // In flight (not in doubt) only while its own epoch still holds a live lease. A call that already
134
+ // failed with an unknown outcome is not in flight: nobody is going to record a result for it.
135
+ inDoubt: x.status === 'failed' || !(live && state.lease !== null && x.intendedEpoch === state.lease.epoch),
136
+ uncertainFailure: x.status === 'failed',
137
+ }));
138
+ for (const x of unresolvedEffects.filter((u) => u.inDoubt)) {
139
+ reasons.push({
140
+ code: 'EFFECT_IN_DOUBT', severity: 'recovery',
141
+ detail: x.uncertainFailure
142
+ ? `effect "${x.key}" (${x.description}) failed with an unknown outcome: the external operation may have taken effect`
143
+ : `effect "${x.key}" (${x.description}) was started but its outcome was never recorded`,
144
+ refs: [x.key],
145
+ });
146
+ }
147
+ // --- awaits that did not deliver -------------------------------------------
148
+ for (const a of awaits) {
149
+ if (a.status === 'expired') {
150
+ reasons.push({ code: 'AWAIT_EXPIRED', severity: 'recovery', detail: `no result for "${a.purpose}" arrived before its deadline`, refs: [a.awaitId] });
151
+ }
152
+ else if (a.status === 'resolved' && a.late) {
153
+ reasons.push({ code: 'LATE_RESULT', severity: 'adaptive', detail: `the result for "${a.purpose}" arrived after it was declared expired`, refs: [a.awaitId] });
154
+ }
155
+ }
156
+ // --- what happened elsewhere while we waited -------------------------------
157
+ const sinceSend = [];
158
+ if (results.length > 0) {
159
+ const oldest = results.reduce((m, r) => Math.min(m, r.basisSeq), Number.MAX_SAFE_INTEGER);
160
+ const senderOf = (awaitId) => {
161
+ const reg = events.find((e) => e.type === 'await.registered' && e.data.awaitId === awaitId);
162
+ return reg ? reg.actor : null;
163
+ };
164
+ const ownActors = new Set(results.map((r) => senderOf(r.awaitId)));
165
+ for (const e of events) {
166
+ if (e.seq <= oldest || !WORLD_TYPES.has(e.type))
167
+ continue;
168
+ if (e.actor !== null && ownActors.has(e.actor))
169
+ continue;
170
+ sinceSend.push({ seq: e.seq, type: e.type, at: e.at, actor: e.actor, key: keyOf(e) });
171
+ }
172
+ if (sinceSend.some((e) => e.type === 'plan.declared')) {
173
+ reasons.push({ code: 'REPLANNED_SINCE_SEND', severity: 'adaptive', detail: 'the plan was changed by someone else after the request was sent', refs: [] });
174
+ }
175
+ const facts = sinceSend.filter((e) => e.type !== 'plan.declared');
176
+ if (facts.length > 0) {
177
+ reasons.push({
178
+ code: 'WORLD_CHANGED_SINCE_SEND', severity: 'adaptive',
179
+ detail: `${facts.length} change(s) by other actors were recorded after the request was sent`, refs: facts.map((f) => String(f.seq)),
180
+ });
181
+ }
182
+ }
183
+ // --- plan shape --------------------------------------------------------------
184
+ const anyProgress = events.some((e) => PROGRESS_TYPES.has(e.type) || e.type.startsWith('await.') || e.type.startsWith('effect.'));
185
+ if (state.plan === null) {
186
+ reasons.push(anyProgress
187
+ ? { code: 'NO_PLAN_BUT_PROGRESS', severity: 'recovery', detail: 'work was recorded but no plan exists, so the next step is unknown', refs: [] }
188
+ : { code: 'NO_PLAN_YET', severity: 'info', detail: 'the task has no plan yet', refs: [] });
189
+ }
190
+ else if (pendingSteps.length > 0 && nextSteps.length === 0 && pendingAwaits.length === 0 && !reasons.some((r) => r.code === 'AWAIT_EXPIRED')) {
191
+ reasons.push({ code: 'PLAN_STUCK', severity: 'recovery', detail: 'steps remain but none can run and nothing is awaited', refs: pendingSteps.map((p) => p.id) });
192
+ }
193
+ if (live && state.lease) {
194
+ reasons.push({ code: 'LEASE_HELD', severity: 'info', detail: `held by "${state.lease.owner}" until ${state.lease.leaseUntil}`, refs: [state.lease.owner] });
195
+ }
196
+ // --- classification --------------------------------------------------------------
197
+ const has = (sev) => reasons.some((r) => r.severity === sev);
198
+ let mode;
199
+ if (has('recovery'))
200
+ mode = 'recovery';
201
+ else if (nextSteps.length === 0 && !planComplete && pendingAwaits.length > 0)
202
+ mode = 'waiting';
203
+ else if (has('adaptive'))
204
+ mode = 'adaptive';
205
+ else
206
+ mode = 'direct';
207
+ return {
208
+ ...base, mode, reasons, nextSteps, planComplete, results, pendingAwaits, assumptionChanges, unresolvedEffects, sinceSend,
209
+ claimable: !live,
210
+ };
211
+ }
212
+ function sameJson(a, b) {
213
+ return canonicalize(toJson(a)) === canonicalize(toJson(b));
214
+ }
215
+ /** What differs between `before` (e.g. the state when a request was sent) and `after` (now). */
216
+ export function diffStates(before, after) {
217
+ const out = [];
218
+ const cmp = (section, a, b) => {
219
+ for (const k of Object.keys(b).sort()) {
220
+ if (!Object.hasOwn(a, k))
221
+ out.push({ section, key: k, change: 'added' });
222
+ else if (!sameJson(a[k], b[k]))
223
+ out.push({ section, key: k, change: 'changed' });
224
+ }
225
+ for (const k of Object.keys(a).sort())
226
+ if (!Object.hasOwn(b, k))
227
+ out.push({ section, key: k, change: 'removed' });
228
+ };
229
+ const byId = (xs) => Object.fromEntries((xs ?? []).map((x) => [x.id, x]));
230
+ cmp('plan', byId(before.plan), byId(after.plan));
231
+ cmp('assumptions', before.assumptions, after.assumptions);
232
+ cmp('observations', before.observations, after.observations);
233
+ cmp('effects', before.effects, after.effects);
234
+ cmp('awaits', before.awaits, after.awaits);
235
+ cmp('decisions', byId(before.decisions), byId(after.decisions));
236
+ if (before.status !== after.status)
237
+ out.push({ section: 'status', key: 'status', change: 'changed' });
238
+ return out;
239
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Canonical JSON and hashing.
3
+ *
4
+ * Event hashes must be identical on every machine and every run, so objects
5
+ * are serialized with sorted keys. Application data is validated to be plain
6
+ * JSON up front: a value that would not survive a round trip (undefined in an
7
+ * array, NaN, a Date, a class instance, a cycle) is rejected at the boundary
8
+ * instead of being silently altered by `JSON.stringify` — a durable record
9
+ * must read back exactly as it was written.
10
+ */
11
+ export type Json = null | boolean | number | string | Json[] | {
12
+ [key: string]: Json;
13
+ };
14
+ /**
15
+ * Deep-copies `value` into plain JSON. Properties whose value is `undefined`
16
+ * are dropped (the usual optional-field idiom); everything that is not
17
+ * representable is a `ValidationError` naming the path, never the value.
18
+ */
19
+ export declare function toJson(value: unknown, label?: string): Json;
20
+ /** Deterministic serialization: object keys sorted by UTF-16 code unit order. */
21
+ export declare function canonicalize(v: Json): string;
22
+ export declare function sha256Hex(data: string | Buffer): string;
23
+ /** Fingerprint of an observed value; equal values (any key order) give equal fingerprints. */
24
+ export declare function fingerprintOf(value: unknown): string;
25
+ //# sourceMappingURL=canonical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../../src/durable/canonical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAIvF;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAU,GAAG,IAAI,CAE5D;AAoCD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAK5C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEvD;AAED,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Canonical JSON and hashing.
3
+ *
4
+ * Event hashes must be identical on every machine and every run, so objects
5
+ * are serialized with sorted keys. Application data is validated to be plain
6
+ * JSON up front: a value that would not survive a round trip (undefined in an
7
+ * array, NaN, a Date, a class instance, a cycle) is rejected at the boundary
8
+ * instead of being silently altered by `JSON.stringify` — a durable record
9
+ * must read back exactly as it was written.
10
+ */
11
+ import { createHash } from 'crypto';
12
+ import { ValidationError } from './errors.js';
13
+ const MAX_DEPTH = 64;
14
+ /**
15
+ * Deep-copies `value` into plain JSON. Properties whose value is `undefined`
16
+ * are dropped (the usual optional-field idiom); everything that is not
17
+ * representable is a `ValidationError` naming the path, never the value.
18
+ */
19
+ export function toJson(value, label = 'value') {
20
+ return walk(value, label, 0);
21
+ }
22
+ function walk(v, path, depth) {
23
+ if (depth > MAX_DEPTH)
24
+ throw new ValidationError(`${path}: nesting deeper than ${MAX_DEPTH}.`);
25
+ if (v === null)
26
+ return null;
27
+ switch (typeof v) {
28
+ case 'boolean':
29
+ case 'string':
30
+ return v;
31
+ case 'number':
32
+ if (!Number.isFinite(v))
33
+ throw new ValidationError(`${path}: non-finite number.`);
34
+ if (Object.is(v, -0))
35
+ return 0;
36
+ return v;
37
+ case 'object':
38
+ break;
39
+ default:
40
+ throw new ValidationError(`${path}: ${typeof v} is not JSON.`);
41
+ }
42
+ if (Array.isArray(v)) {
43
+ return v.map((item, i) => {
44
+ if (item === undefined)
45
+ throw new ValidationError(`${path}[${i}]: undefined in an array.`);
46
+ return walk(item, `${path}[${i}]`, depth + 1);
47
+ });
48
+ }
49
+ const proto = Object.getPrototypeOf(v);
50
+ if (proto !== Object.prototype && proto !== null)
51
+ throw new ValidationError(`${path}: only plain objects are allowed.`);
52
+ const out = {};
53
+ for (const key of Object.keys(v)) {
54
+ const item = v[key];
55
+ if (item === undefined)
56
+ continue;
57
+ if (key === '__proto__')
58
+ throw new ValidationError(`${path}: key "__proto__" is not allowed.`);
59
+ out[key] = walk(item, `${path}.${key}`, depth + 1);
60
+ }
61
+ return out;
62
+ }
63
+ /** Deterministic serialization: object keys sorted by UTF-16 code unit order. */
64
+ export function canonicalize(v) {
65
+ if (v === null || typeof v !== 'object')
66
+ return JSON.stringify(v);
67
+ if (Array.isArray(v))
68
+ return `[${v.map(canonicalize).join(',')}]`;
69
+ const keys = Object.keys(v).sort();
70
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalize(v[k])}`).join(',')}}`;
71
+ }
72
+ export function sha256Hex(data) {
73
+ return createHash('sha256').update(data).digest('hex');
74
+ }
75
+ /** Fingerprint of an observed value; equal values (any key order) give equal fingerprints. */
76
+ export function fingerprintOf(value) {
77
+ return sha256Hex(canonicalize(toJson(value, 'observation')));
78
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `monolith durable ...` — read-mostly operator commands over the durable store
3
+ * of the current MONOLITH instance (`<monolithRoot>/durable`).
4
+ *
5
+ * monolith durable list [--status active|completed|failed|cancelled]
6
+ * monolith durable show <taskId> [--reveal-tokens]
7
+ * monolith durable assess <taskId>
8
+ * monolith durable packet <taskId> [--reveal-tokens]
9
+ * monolith durable verify [taskId]
10
+ * monolith durable retention --retain-days N [--compact-after N] [--apply]
11
+ *
12
+ * Output is JSON so it can be piped. Nothing here creates, claims or edits a
13
+ * task: tasks are driven by application code through `DurableTasks`. The only
14
+ * mutating command is `retention --apply`; without `--apply` it only reports.
15
+ */
16
+ import { DurableTasks } from './runtime.js';
17
+ export declare const DURABLE_DIR_NAME = "durable";
18
+ export interface CliIo {
19
+ cwd: string;
20
+ out: (s: string) => void;
21
+ err: (s: string) => void;
22
+ }
23
+ /**
24
+ * Opens the durable store of the instance. A store without `durable-protocol.json` is open for diagnostics only: reads
25
+ * work, every write is refused (`STORE_PROTOCOL`). Opening never creates the marker.
26
+ */
27
+ export declare function openDurable(cwd: string): DurableTasks;
28
+ /**
29
+ * The explicit operator action that makes an EMPTY store writable (Design A section 46.5, ADR-018 D3). Not reachable from
30
+ * `runDurable` on purpose: it must be called by a person or script that knows no runtime of an older version is running.
31
+ * Refuses a store that already holds tasks.
32
+ */
33
+ export declare function initializeDurable(cwd: string): 'initialized' | 'already-initialized';
34
+ /** Returns the process exit code: 0 ok, 1 usage/error, 2 verification found a problem. */
35
+ export declare function runDurable(args: string[], io: CliIo): Promise<number>;
36
+ export declare function runDurableCommand(args: string[]): Promise<void>;
37
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/durable/cli.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1B;AAiCD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAErD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,qBAAqB,CAEpF;AAED,0FAA0F;AAC1F,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAwD3E;AAOD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrE"}
@@ -0,0 +1,141 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * `monolith durable ...` — read-mostly operator commands over the durable store
4
+ * of the current MONOLITH instance (`<monolithRoot>/durable`).
5
+ *
6
+ * monolith durable list [--status active|completed|failed|cancelled]
7
+ * monolith durable show <taskId> [--reveal-tokens]
8
+ * monolith durable assess <taskId>
9
+ * monolith durable packet <taskId> [--reveal-tokens]
10
+ * monolith durable verify [taskId]
11
+ * monolith durable retention --retain-days N [--compact-after N] [--apply]
12
+ *
13
+ * Output is JSON so it can be piped. Nothing here creates, claims or edits a
14
+ * task: tasks are driven by application code through `DurableTasks`. The only
15
+ * mutating command is `retention --apply`; without `--apply` it only reports.
16
+ */
17
+ import { join } from 'path';
18
+ import { assertContainedFile, resolveMonolithRoot, reportWriteWarning } from '../root/index.js';
19
+ import { DurableError } from './errors.js';
20
+ import { FsEventStore } from './fs-store.js';
21
+ import { DurableTasks } from './runtime.js';
22
+ export const DURABLE_DIR_NAME = 'durable';
23
+ const STATUSES = ['active', 'completed', 'failed', 'cancelled'];
24
+ /**
25
+ * Await tokens are capabilities: whoever holds one can deliver a result to the task. The CLI output ends up in
26
+ * terminals, logs and tickets, so the secret part (the nonce) is replaced there; `--reveal-tokens` prints it.
27
+ * The programmatic API (`getState`, `resumePacket`) is unchanged: a worker needs the real token to send it out.
28
+ */
29
+ function redactTokens(v) {
30
+ if (Array.isArray(v))
31
+ return v.map(redactTokens);
32
+ if (v !== null && typeof v === 'object') {
33
+ const out = {};
34
+ for (const [k, x] of Object.entries(v)) {
35
+ out[k] = k === 'token' && typeof x === 'string' ? x.replace(/\.[0-9a-f]{32}$/, '.<redacted>') : redactTokens(x);
36
+ }
37
+ return out;
38
+ }
39
+ return v;
40
+ }
41
+ function flag(args, name) {
42
+ const i = args.indexOf(name);
43
+ return i >= 0 ? args[i + 1] : undefined;
44
+ }
45
+ function openStore(cwd) {
46
+ const root = resolveMonolithRoot(cwd);
47
+ assertContainedFile(root, `${DURABLE_DIR_NAME}/x`);
48
+ const dir = join(root.rootAbs, DURABLE_DIR_NAME);
49
+ return new FsEventStore({ target: { scope: 'project', projectRoot: root.projectRoot, dir }, onWarning: (w) => reportWriteWarning(w) });
50
+ }
51
+ /**
52
+ * Opens the durable store of the instance. A store without `durable-protocol.json` is open for diagnostics only: reads
53
+ * work, every write is refused (`STORE_PROTOCOL`). Opening never creates the marker.
54
+ */
55
+ export function openDurable(cwd) {
56
+ return new DurableTasks({ store: openStore(cwd) });
57
+ }
58
+ /**
59
+ * The explicit operator action that makes an EMPTY store writable (Design A section 46.5, ADR-018 D3). Not reachable from
60
+ * `runDurable` on purpose: it must be called by a person or script that knows no runtime of an older version is running.
61
+ * Refuses a store that already holds tasks.
62
+ */
63
+ export function initializeDurable(cwd) {
64
+ return openStore(cwd).initializeProtocolSync();
65
+ }
66
+ /** Returns the process exit code: 0 ok, 1 usage/error, 2 verification found a problem. */
67
+ export async function runDurable(args, io) {
68
+ const [sub, ...rest] = args;
69
+ const reveal = rest.includes('--reveal-tokens');
70
+ const json = (v) => io.out(JSON.stringify(reveal ? v : redactTokens(v), null, 2));
71
+ try {
72
+ const rt = openDurable(io.cwd);
73
+ switch (sub) {
74
+ case 'list': {
75
+ const status = flag(rest, '--status');
76
+ if (status !== undefined && !STATUSES.includes(status)) {
77
+ io.err(`--status must be one of ${STATUSES.join(', ')}.`);
78
+ return 1;
79
+ }
80
+ json(await rt.listTasks(status ? { status: status } : {}));
81
+ return 0;
82
+ }
83
+ case 'show': {
84
+ if (!rest[0])
85
+ return usage(io, 'show <taskId>');
86
+ json(await rt.getState(rest[0]));
87
+ return 0;
88
+ }
89
+ case 'assess': {
90
+ if (!rest[0])
91
+ return usage(io, 'assess <taskId>');
92
+ json(await rt.assess(rest[0]));
93
+ return 0;
94
+ }
95
+ case 'packet': {
96
+ if (!rest[0])
97
+ return usage(io, 'packet <taskId>');
98
+ json(await rt.resumePacket(rest[0]));
99
+ return 0;
100
+ }
101
+ case 'verify': {
102
+ const reports = await rt.verify(rest[0]);
103
+ json(reports);
104
+ return reports.every((r) => r.ok) ? 0 : 2;
105
+ }
106
+ case 'retention': {
107
+ const days = Number(flag(rest, '--retain-days'));
108
+ if (!Number.isFinite(days) || days < 0)
109
+ return usage(io, 'retention --retain-days N [--compact-after N] [--apply]');
110
+ const compact = flag(rest, '--compact-after');
111
+ const policy = {
112
+ terminalRetentionMs: days * 86_400_000,
113
+ ...(compact !== undefined ? { compactAfterEvents: Number(compact) } : {}),
114
+ };
115
+ if (rest.includes('--apply'))
116
+ json(await rt.applyRetention(policy));
117
+ else
118
+ json({ dryRun: true, decisions: await rt.planRetention(policy) });
119
+ return 0;
120
+ }
121
+ default:
122
+ return usage(io, 'list | show <id> | assess <id> | packet <id> | verify [id] | retention --retain-days N [--apply]');
123
+ }
124
+ }
125
+ catch (err) {
126
+ if (err instanceof DurableError)
127
+ io.err(`${err.code}: ${err.message}`);
128
+ else
129
+ io.err(err instanceof Error ? err.message : String(err));
130
+ return 1;
131
+ }
132
+ }
133
+ function usage(io, text) {
134
+ io.err(`Usage: monolith durable ${text}`);
135
+ return 1;
136
+ }
137
+ export async function runDurableCommand(args) {
138
+ const code = await runDurable(args, { cwd: process.cwd(), out: (s) => console.log(s), err: (s) => console.error(s) });
139
+ if (code !== 0)
140
+ process.exit(code);
141
+ }