@ashlr/hub 2.2.0

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 (477) hide show
  1. package/CHANGELOG.md +1158 -0
  2. package/LICENSE +21 -0
  3. package/README.md +833 -0
  4. package/bin/ashlr +8 -0
  5. package/dist/api/core.d.ts +28 -0
  6. package/dist/api/core.js +42 -0
  7. package/dist/api/core.js.map +1 -0
  8. package/dist/api/index.d.ts +9 -0
  9. package/dist/api/index.js +9 -0
  10. package/dist/api/index.js.map +1 -0
  11. package/dist/api/plugin.d.ts +12 -0
  12. package/dist/api/plugin.js +13 -0
  13. package/dist/api/plugin.js.map +1 -0
  14. package/dist/api/types.d.ts +9 -0
  15. package/dist/api/types.js +9 -0
  16. package/dist/api/types.js.map +1 -0
  17. package/dist/cli/args.d.ts +20 -0
  18. package/dist/cli/args.js +23 -0
  19. package/dist/cli/args.js.map +1 -0
  20. package/dist/cli/ask.d.ts +22 -0
  21. package/dist/cli/ask.js +240 -0
  22. package/dist/cli/ask.js.map +1 -0
  23. package/dist/cli/audit.d.ts +26 -0
  24. package/dist/cli/audit.js +199 -0
  25. package/dist/cli/audit.js.map +1 -0
  26. package/dist/cli/backlog.d.ts +26 -0
  27. package/dist/cli/backlog.js +322 -0
  28. package/dist/cli/backlog.js.map +1 -0
  29. package/dist/cli/completions.d.ts +18 -0
  30. package/dist/cli/completions.js +156 -0
  31. package/dist/cli/completions.js.map +1 -0
  32. package/dist/cli/daemon.d.ts +31 -0
  33. package/dist/cli/daemon.js +334 -0
  34. package/dist/cli/daemon.js.map +1 -0
  35. package/dist/cli/demo-sandbox.d.ts +78 -0
  36. package/dist/cli/demo-sandbox.js +227 -0
  37. package/dist/cli/demo-sandbox.js.map +1 -0
  38. package/dist/cli/demo.d.ts +76 -0
  39. package/dist/cli/demo.js +285 -0
  40. package/dist/cli/demo.js.map +1 -0
  41. package/dist/cli/digest.d.ts +46 -0
  42. package/dist/cli/digest.js +352 -0
  43. package/dist/cli/digest.js.map +1 -0
  44. package/dist/cli/doctor-init.d.ts +28 -0
  45. package/dist/cli/doctor-init.js +335 -0
  46. package/dist/cli/doctor-init.js.map +1 -0
  47. package/dist/cli/genome.d.ts +62 -0
  48. package/dist/cli/genome.js +1061 -0
  49. package/dist/cli/genome.js.map +1 -0
  50. package/dist/cli/gh.d.ts +27 -0
  51. package/dist/cli/gh.js +322 -0
  52. package/dist/cli/gh.js.map +1 -0
  53. package/dist/cli/goals.d.ts +55 -0
  54. package/dist/cli/goals.js +667 -0
  55. package/dist/cli/goals.js.map +1 -0
  56. package/dist/cli/health.d.ts +56 -0
  57. package/dist/cli/health.js +407 -0
  58. package/dist/cli/health.js.map +1 -0
  59. package/dist/cli/help.d.ts +51 -0
  60. package/dist/cli/help.js +436 -0
  61. package/dist/cli/help.js.map +1 -0
  62. package/dist/cli/inbox.d.ts +28 -0
  63. package/dist/cli/inbox.js +589 -0
  64. package/dist/cli/inbox.js.map +1 -0
  65. package/dist/cli/index.d.ts +41 -0
  66. package/dist/cli/index.js +1208 -0
  67. package/dist/cli/index.js.map +1 -0
  68. package/dist/cli/knowledge.d.ts +33 -0
  69. package/dist/cli/knowledge.js +575 -0
  70. package/dist/cli/knowledge.js.map +1 -0
  71. package/dist/cli/mcp.d.ts +26 -0
  72. package/dist/cli/mcp.js +487 -0
  73. package/dist/cli/mcp.js.map +1 -0
  74. package/dist/cli/models.d.ts +26 -0
  75. package/dist/cli/models.js +435 -0
  76. package/dist/cli/models.js.map +1 -0
  77. package/dist/cli/new.d.ts +32 -0
  78. package/dist/cli/new.js +565 -0
  79. package/dist/cli/new.js.map +1 -0
  80. package/dist/cli/notify.d.ts +20 -0
  81. package/dist/cli/notify.js +188 -0
  82. package/dist/cli/notify.js.map +1 -0
  83. package/dist/cli/onboard.d.ts +85 -0
  84. package/dist/cli/onboard.js +305 -0
  85. package/dist/cli/onboard.js.map +1 -0
  86. package/dist/cli/open.d.ts +38 -0
  87. package/dist/cli/open.js +86 -0
  88. package/dist/cli/open.js.map +1 -0
  89. package/dist/cli/orient.d.ts +14 -0
  90. package/dist/cli/orient.js +110 -0
  91. package/dist/cli/orient.js.map +1 -0
  92. package/dist/cli/picker.d.ts +21 -0
  93. package/dist/cli/picker.js +177 -0
  94. package/dist/cli/picker.js.map +1 -0
  95. package/dist/cli/plugins.d.ts +21 -0
  96. package/dist/cli/plugins.js +394 -0
  97. package/dist/cli/plugins.js.map +1 -0
  98. package/dist/cli/preflight.d.ts +29 -0
  99. package/dist/cli/preflight.js +131 -0
  100. package/dist/cli/preflight.js.map +1 -0
  101. package/dist/cli/pulse.d.ts +26 -0
  102. package/dist/cli/pulse.js +482 -0
  103. package/dist/cli/pulse.js.map +1 -0
  104. package/dist/cli/reflect.d.ts +42 -0
  105. package/dist/cli/reflect.js +381 -0
  106. package/dist/cli/reflect.js.map +1 -0
  107. package/dist/cli/run.d.ts +29 -0
  108. package/dist/cli/run.js +687 -0
  109. package/dist/cli/run.js.map +1 -0
  110. package/dist/cli/sandbox.d.ts +20 -0
  111. package/dist/cli/sandbox.js +345 -0
  112. package/dist/cli/sandbox.js.map +1 -0
  113. package/dist/cli/seams.d.ts +41 -0
  114. package/dist/cli/seams.js +167 -0
  115. package/dist/cli/seams.js.map +1 -0
  116. package/dist/cli/serve.d.ts +25 -0
  117. package/dist/cli/serve.js +249 -0
  118. package/dist/cli/serve.js.map +1 -0
  119. package/dist/cli/ship.d.ts +33 -0
  120. package/dist/cli/ship.js +433 -0
  121. package/dist/cli/ship.js.map +1 -0
  122. package/dist/cli/spec.d.ts +19 -0
  123. package/dist/cli/spec.js +478 -0
  124. package/dist/cli/spec.js.map +1 -0
  125. package/dist/cli/swarm.d.ts +42 -0
  126. package/dist/cli/swarm.js +1365 -0
  127. package/dist/cli/swarm.js.map +1 -0
  128. package/dist/cli/telemetry.d.ts +26 -0
  129. package/dist/cli/telemetry.js +364 -0
  130. package/dist/cli/telemetry.js.map +1 -0
  131. package/dist/cli/tui.d.ts +17 -0
  132. package/dist/cli/tui.js +52 -0
  133. package/dist/cli/tui.js.map +1 -0
  134. package/dist/cli/ui.d.ts +53 -0
  135. package/dist/cli/ui.js +66 -0
  136. package/dist/cli/ui.js.map +1 -0
  137. package/dist/cli/update.d.ts +38 -0
  138. package/dist/cli/update.js +682 -0
  139. package/dist/cli/update.js.map +1 -0
  140. package/dist/cli/vercel.d.ts +20 -0
  141. package/dist/cli/vercel.js +193 -0
  142. package/dist/cli/vercel.js.map +1 -0
  143. package/dist/cli/verify-safety.d.ts +96 -0
  144. package/dist/cli/verify-safety.js +509 -0
  145. package/dist/cli/verify-safety.js.map +1 -0
  146. package/dist/cli/wire.d.ts +22 -0
  147. package/dist/cli/wire.js +205 -0
  148. package/dist/cli/wire.js.map +1 -0
  149. package/dist/core/classify.d.ts +51 -0
  150. package/dist/core/classify.js +450 -0
  151. package/dist/core/classify.js.map +1 -0
  152. package/dist/core/config.d.ts +63 -0
  153. package/dist/core/config.js +474 -0
  154. package/dist/core/config.js.map +1 -0
  155. package/dist/core/daemon/loop.d.ts +74 -0
  156. package/dist/core/daemon/loop.js +618 -0
  157. package/dist/core/daemon/loop.js.map +1 -0
  158. package/dist/core/daemon/state.d.ts +66 -0
  159. package/dist/core/daemon/state.js +197 -0
  160. package/dist/core/daemon/state.js.map +1 -0
  161. package/dist/core/dashboard.d.ts +40 -0
  162. package/dist/core/dashboard.js +463 -0
  163. package/dist/core/dashboard.js.map +1 -0
  164. package/dist/core/digest/build.d.ts +51 -0
  165. package/dist/core/digest/build.js +230 -0
  166. package/dist/core/digest/build.js.map +1 -0
  167. package/dist/core/digest/deliver.d.ts +47 -0
  168. package/dist/core/digest/deliver.js +230 -0
  169. package/dist/core/digest/deliver.js.map +1 -0
  170. package/dist/core/digest/store.d.ts +57 -0
  171. package/dist/core/digest/store.js +223 -0
  172. package/dist/core/digest/store.js.map +1 -0
  173. package/dist/core/doctor-fix.d.ts +21 -0
  174. package/dist/core/doctor-fix.js +440 -0
  175. package/dist/core/doctor-fix.js.map +1 -0
  176. package/dist/core/doctor.d.ts +18 -0
  177. package/dist/core/doctor.js +806 -0
  178. package/dist/core/doctor.js.map +1 -0
  179. package/dist/core/env-bridge.d.ts +64 -0
  180. package/dist/core/env-bridge.js +103 -0
  181. package/dist/core/env-bridge.js.map +1 -0
  182. package/dist/core/genome/capture.d.ts +49 -0
  183. package/dist/core/genome/capture.js +352 -0
  184. package/dist/core/genome/capture.js.map +1 -0
  185. package/dist/core/genome/consolidate.d.ts +38 -0
  186. package/dist/core/genome/consolidate.js +426 -0
  187. package/dist/core/genome/consolidate.js.map +1 -0
  188. package/dist/core/genome/export.d.ts +29 -0
  189. package/dist/core/genome/export.js +102 -0
  190. package/dist/core/genome/export.js.map +1 -0
  191. package/dist/core/genome/playbook.d.ts +33 -0
  192. package/dist/core/genome/playbook.js +320 -0
  193. package/dist/core/genome/playbook.js.map +1 -0
  194. package/dist/core/genome/recall.d.ts +45 -0
  195. package/dist/core/genome/recall.js +293 -0
  196. package/dist/core/genome/recall.js.map +1 -0
  197. package/dist/core/genome/store.d.ts +62 -0
  198. package/dist/core/genome/store.js +711 -0
  199. package/dist/core/genome/store.js.map +1 -0
  200. package/dist/core/git.d.ts +34 -0
  201. package/dist/core/git.js +115 -0
  202. package/dist/core/git.js.map +1 -0
  203. package/dist/core/goals/advance.d.ts +82 -0
  204. package/dist/core/goals/advance.js +263 -0
  205. package/dist/core/goals/advance.js.map +1 -0
  206. package/dist/core/goals/planner.d.ts +58 -0
  207. package/dist/core/goals/planner.js +233 -0
  208. package/dist/core/goals/planner.js.map +1 -0
  209. package/dist/core/goals/store.d.ts +128 -0
  210. package/dist/core/goals/store.js +442 -0
  211. package/dist/core/goals/store.js.map +1 -0
  212. package/dist/core/inbox/apply.d.ts +36 -0
  213. package/dist/core/inbox/apply.js +407 -0
  214. package/dist/core/inbox/apply.js.map +1 -0
  215. package/dist/core/inbox/notify-proposal.d.ts +13 -0
  216. package/dist/core/inbox/notify-proposal.js +21 -0
  217. package/dist/core/inbox/notify-proposal.js.map +1 -0
  218. package/dist/core/inbox/store.d.ts +67 -0
  219. package/dist/core/inbox/store.js +258 -0
  220. package/dist/core/inbox/store.js.map +1 -0
  221. package/dist/core/index-engine.d.ts +62 -0
  222. package/dist/core/index-engine.js +486 -0
  223. package/dist/core/index-engine.js.map +1 -0
  224. package/dist/core/integrations/desktop-notify.d.ts +18 -0
  225. package/dist/core/integrations/desktop-notify.js +42 -0
  226. package/dist/core/integrations/desktop-notify.js.map +1 -0
  227. package/dist/core/integrations/editors.d.ts +50 -0
  228. package/dist/core/integrations/editors.js +216 -0
  229. package/dist/core/integrations/editors.js.map +1 -0
  230. package/dist/core/integrations/github.d.ts +66 -0
  231. package/dist/core/integrations/github.js +332 -0
  232. package/dist/core/integrations/github.js.map +1 -0
  233. package/dist/core/integrations/identity.d.ts +29 -0
  234. package/dist/core/integrations/identity.js +359 -0
  235. package/dist/core/integrations/identity.js.map +1 -0
  236. package/dist/core/integrations/notify.d.ts +23 -0
  237. package/dist/core/integrations/notify.js +96 -0
  238. package/dist/core/integrations/notify.js.map +1 -0
  239. package/dist/core/integrations/vercel.d.ts +37 -0
  240. package/dist/core/integrations/vercel.js +192 -0
  241. package/dist/core/integrations/vercel.js.map +1 -0
  242. package/dist/core/knowledge/ask.d.ts +34 -0
  243. package/dist/core/knowledge/ask.js +325 -0
  244. package/dist/core/knowledge/ask.js.map +1 -0
  245. package/dist/core/knowledge/graph.d.ts +51 -0
  246. package/dist/core/knowledge/graph.js +564 -0
  247. package/dist/core/knowledge/graph.js.map +1 -0
  248. package/dist/core/knowledge/index.d.ts +74 -0
  249. package/dist/core/knowledge/index.js +586 -0
  250. package/dist/core/knowledge/index.js.map +1 -0
  251. package/dist/core/learn/playbooks.d.ts +84 -0
  252. package/dist/core/learn/playbooks.js +241 -0
  253. package/dist/core/learn/playbooks.js.map +1 -0
  254. package/dist/core/learn/reflect.d.ts +87 -0
  255. package/dist/core/learn/reflect.js +435 -0
  256. package/dist/core/learn/reflect.js.map +1 -0
  257. package/dist/core/learn/store.d.ts +51 -0
  258. package/dist/core/learn/store.js +165 -0
  259. package/dist/core/learn/store.js.map +1 -0
  260. package/dist/core/learn/tuning.d.ts +48 -0
  261. package/dist/core/learn/tuning.js +201 -0
  262. package/dist/core/learn/tuning.js.map +1 -0
  263. package/dist/core/lifecycle/scaffold.d.ts +43 -0
  264. package/dist/core/lifecycle/scaffold.js +260 -0
  265. package/dist/core/lifecycle/scaffold.js.map +1 -0
  266. package/dist/core/lifecycle/ship.d.ts +48 -0
  267. package/dist/core/lifecycle/ship.js +513 -0
  268. package/dist/core/lifecycle/ship.js.map +1 -0
  269. package/dist/core/lifecycle/templates.d.ts +20 -0
  270. package/dist/core/lifecycle/templates.js +605 -0
  271. package/dist/core/lifecycle/templates.js.map +1 -0
  272. package/dist/core/mcp-gateway.d.ts +59 -0
  273. package/dist/core/mcp-gateway.js +385 -0
  274. package/dist/core/mcp-gateway.js.map +1 -0
  275. package/dist/core/mcp-native.d.ts +53 -0
  276. package/dist/core/mcp-native.js +507 -0
  277. package/dist/core/mcp-native.js.map +1 -0
  278. package/dist/core/mcp-registry.d.ts +36 -0
  279. package/dist/core/mcp-registry.js +180 -0
  280. package/dist/core/mcp-registry.js.map +1 -0
  281. package/dist/core/observability/budget-alert.d.ts +18 -0
  282. package/dist/core/observability/budget-alert.js +90 -0
  283. package/dist/core/observability/budget-alert.js.map +1 -0
  284. package/dist/core/observability/estimate.d.ts +27 -0
  285. package/dist/core/observability/estimate.js +188 -0
  286. package/dist/core/observability/estimate.js.map +1 -0
  287. package/dist/core/observability/forecast.d.ts +19 -0
  288. package/dist/core/observability/forecast.js +101 -0
  289. package/dist/core/observability/forecast.js.map +1 -0
  290. package/dist/core/observability/governance.d.ts +28 -0
  291. package/dist/core/observability/governance.js +101 -0
  292. package/dist/core/observability/governance.js.map +1 -0
  293. package/dist/core/observability/otlp.d.ts +88 -0
  294. package/dist/core/observability/otlp.js +217 -0
  295. package/dist/core/observability/otlp.js.map +1 -0
  296. package/dist/core/observability/rollup.d.ts +35 -0
  297. package/dist/core/observability/rollup.js +311 -0
  298. package/dist/core/observability/rollup.js.map +1 -0
  299. package/dist/core/observability/telemetry-sink.d.ts +63 -0
  300. package/dist/core/observability/telemetry-sink.js +350 -0
  301. package/dist/core/observability/telemetry-sink.js.map +1 -0
  302. package/dist/core/observability/usage-source.d.ts +60 -0
  303. package/dist/core/observability/usage-source.js +347 -0
  304. package/dist/core/observability/usage-source.js.map +1 -0
  305. package/dist/core/onboard.d.ts +27 -0
  306. package/dist/core/onboard.js +288 -0
  307. package/dist/core/onboard.js.map +1 -0
  308. package/dist/core/orient.d.ts +23 -0
  309. package/dist/core/orient.js +135 -0
  310. package/dist/core/orient.js.map +1 -0
  311. package/dist/core/phantom.d.ts +23 -0
  312. package/dist/core/phantom.js +279 -0
  313. package/dist/core/phantom.js.map +1 -0
  314. package/dist/core/plugins/host-api.d.ts +29 -0
  315. package/dist/core/plugins/host-api.js +113 -0
  316. package/dist/core/plugins/host-api.js.map +1 -0
  317. package/dist/core/plugins/integrity.d.ts +36 -0
  318. package/dist/core/plugins/integrity.js +73 -0
  319. package/dist/core/plugins/integrity.js.map +1 -0
  320. package/dist/core/plugins/manifest.d.ts +31 -0
  321. package/dist/core/plugins/manifest.js +316 -0
  322. package/dist/core/plugins/manifest.js.map +1 -0
  323. package/dist/core/plugins/registry.d.ts +87 -0
  324. package/dist/core/plugins/registry.js +415 -0
  325. package/dist/core/plugins/registry.js.map +1 -0
  326. package/dist/core/plugins/types.d.ts +182 -0
  327. package/dist/core/plugins/types.js +40 -0
  328. package/dist/core/plugins/types.js.map +1 -0
  329. package/dist/core/plugins/wrappers.d.ts +40 -0
  330. package/dist/core/plugins/wrappers.js +229 -0
  331. package/dist/core/plugins/wrappers.js.map +1 -0
  332. package/dist/core/portfolio/backlog.d.ts +40 -0
  333. package/dist/core/portfolio/backlog.js +177 -0
  334. package/dist/core/portfolio/backlog.js.map +1 -0
  335. package/dist/core/portfolio/scanners.d.ts +21 -0
  336. package/dist/core/portfolio/scanners.js +600 -0
  337. package/dist/core/portfolio/scanners.js.map +1 -0
  338. package/dist/core/providers.d.ts +34 -0
  339. package/dist/core/providers.js +250 -0
  340. package/dist/core/providers.js.map +1 -0
  341. package/dist/core/quality/conventions.d.ts +35 -0
  342. package/dist/core/quality/conventions.js +267 -0
  343. package/dist/core/quality/conventions.js.map +1 -0
  344. package/dist/core/quality/fixes.d.ts +56 -0
  345. package/dist/core/quality/fixes.js +209 -0
  346. package/dist/core/quality/fixes.js.map +1 -0
  347. package/dist/core/quality/health.d.ts +69 -0
  348. package/dist/core/quality/health.js +350 -0
  349. package/dist/core/quality/health.js.map +1 -0
  350. package/dist/core/quality/store.d.ts +56 -0
  351. package/dist/core/quality/store.js +195 -0
  352. package/dist/core/quality/store.js.map +1 -0
  353. package/dist/core/readiness.d.ts +112 -0
  354. package/dist/core/readiness.js +431 -0
  355. package/dist/core/readiness.js.map +1 -0
  356. package/dist/core/run/agent-loop.d.ts +40 -0
  357. package/dist/core/run/agent-loop.js +291 -0
  358. package/dist/core/run/agent-loop.js.map +1 -0
  359. package/dist/core/run/budget.d.ts +47 -0
  360. package/dist/core/run/budget.js +113 -0
  361. package/dist/core/run/budget.js.map +1 -0
  362. package/dist/core/run/engines.d.ts +75 -0
  363. package/dist/core/run/engines.js +199 -0
  364. package/dist/core/run/engines.js.map +1 -0
  365. package/dist/core/run/model-manager.d.ts +64 -0
  366. package/dist/core/run/model-manager.js +339 -0
  367. package/dist/core/run/model-manager.js.map +1 -0
  368. package/dist/core/run/orchestrator.d.ts +100 -0
  369. package/dist/core/run/orchestrator.js +1515 -0
  370. package/dist/core/run/orchestrator.js.map +1 -0
  371. package/dist/core/run/provider-client.d.ts +46 -0
  372. package/dist/core/run/provider-client.js +796 -0
  373. package/dist/core/run/provider-client.js.map +1 -0
  374. package/dist/core/run/retry.d.ts +19 -0
  375. package/dist/core/run/retry.js +68 -0
  376. package/dist/core/run/retry.js.map +1 -0
  377. package/dist/core/run/router.d.ts +50 -0
  378. package/dist/core/run/router.js +257 -0
  379. package/dist/core/run/router.js.map +1 -0
  380. package/dist/core/run/self-heal.d.ts +52 -0
  381. package/dist/core/run/self-heal.js +181 -0
  382. package/dist/core/run/self-heal.js.map +1 -0
  383. package/dist/core/run/streaming.d.ts +31 -0
  384. package/dist/core/run/streaming.js +122 -0
  385. package/dist/core/run/streaming.js.map +1 -0
  386. package/dist/core/run/verify.d.ts +30 -0
  387. package/dist/core/run/verify.js +204 -0
  388. package/dist/core/run/verify.js.map +1 -0
  389. package/dist/core/sandbox/audit.d.ts +31 -0
  390. package/dist/core/sandbox/audit.js +169 -0
  391. package/dist/core/sandbox/audit.js.map +1 -0
  392. package/dist/core/sandbox/policy.d.ts +61 -0
  393. package/dist/core/sandbox/policy.js +211 -0
  394. package/dist/core/sandbox/policy.js.map +1 -0
  395. package/dist/core/sandbox/worktree.d.ts +175 -0
  396. package/dist/core/sandbox/worktree.js +673 -0
  397. package/dist/core/sandbox/worktree.js.map +1 -0
  398. package/dist/core/seams/backlog.d.ts +47 -0
  399. package/dist/core/seams/backlog.js +47 -0
  400. package/dist/core/seams/backlog.js.map +1 -0
  401. package/dist/core/seams/daemon-coordinator.d.ts +72 -0
  402. package/dist/core/seams/daemon-coordinator.js +76 -0
  403. package/dist/core/seams/daemon-coordinator.js.map +1 -0
  404. package/dist/core/seams/genome.d.ts +45 -0
  405. package/dist/core/seams/genome.js +53 -0
  406. package/dist/core/seams/genome.js.map +1 -0
  407. package/dist/core/seams/identity.d.ts +40 -0
  408. package/dist/core/seams/identity.js +44 -0
  409. package/dist/core/seams/identity.js.map +1 -0
  410. package/dist/core/seams/inbox.d.ts +60 -0
  411. package/dist/core/seams/inbox.js +66 -0
  412. package/dist/core/seams/inbox.js.map +1 -0
  413. package/dist/core/seams/index.d.ts +20 -0
  414. package/dist/core/seams/index.js +21 -0
  415. package/dist/core/seams/index.js.map +1 -0
  416. package/dist/core/seams/portfolio.d.ts +50 -0
  417. package/dist/core/seams/portfolio.js +61 -0
  418. package/dist/core/seams/portfolio.js.map +1 -0
  419. package/dist/core/seams/registry.d.ts +42 -0
  420. package/dist/core/seams/registry.js +128 -0
  421. package/dist/core/seams/registry.js.map +1 -0
  422. package/dist/core/seams/run-swarm.d.ts +66 -0
  423. package/dist/core/seams/run-swarm.js +74 -0
  424. package/dist/core/seams/run-swarm.js.map +1 -0
  425. package/dist/core/seams/types.d.ts +123 -0
  426. package/dist/core/seams/types.js +35 -0
  427. package/dist/core/seams/types.js.map +1 -0
  428. package/dist/core/spec/spec-store.d.ts +62 -0
  429. package/dist/core/spec/spec-store.js +359 -0
  430. package/dist/core/spec/spec-store.js.map +1 -0
  431. package/dist/core/swarm/gate.d.ts +45 -0
  432. package/dist/core/swarm/gate.js +114 -0
  433. package/dist/core/swarm/gate.js.map +1 -0
  434. package/dist/core/swarm/planner.d.ts +32 -0
  435. package/dist/core/swarm/planner.js +293 -0
  436. package/dist/core/swarm/planner.js.map +1 -0
  437. package/dist/core/swarm/rollback.d.ts +56 -0
  438. package/dist/core/swarm/rollback.js +266 -0
  439. package/dist/core/swarm/rollback.js.map +1 -0
  440. package/dist/core/swarm/runner.d.ts +62 -0
  441. package/dist/core/swarm/runner.js +1263 -0
  442. package/dist/core/swarm/runner.js.map +1 -0
  443. package/dist/core/swarm/sign.d.ts +71 -0
  444. package/dist/core/swarm/sign.js +362 -0
  445. package/dist/core/swarm/sign.js.map +1 -0
  446. package/dist/core/swarm/store.d.ts +52 -0
  447. package/dist/core/swarm/store.js +195 -0
  448. package/dist/core/swarm/store.js.map +1 -0
  449. package/dist/core/tidy.d.ts +32 -0
  450. package/dist/core/tidy.js +354 -0
  451. package/dist/core/tidy.js.map +1 -0
  452. package/dist/core/tools-registry.d.ts +16 -0
  453. package/dist/core/tools-registry.js +308 -0
  454. package/dist/core/tools-registry.js.map +1 -0
  455. package/dist/core/types.d.ts +2545 -0
  456. package/dist/core/types.js +9 -0
  457. package/dist/core/types.js.map +1 -0
  458. package/dist/core/web/api.d.ts +53 -0
  459. package/dist/core/web/api.js +698 -0
  460. package/dist/core/web/api.js.map +1 -0
  461. package/dist/core/web/public/app.js +1906 -0
  462. package/dist/core/web/public/index.html +721 -0
  463. package/dist/core/web/public/styles.css +2007 -0
  464. package/dist/core/web/server.d.ts +18 -0
  465. package/dist/core/web/server.js +122 -0
  466. package/dist/core/web/server.js.map +1 -0
  467. package/dist/core/web/static.d.ts +18 -0
  468. package/dist/core/web/static.js +122 -0
  469. package/dist/core/web/static.js.map +1 -0
  470. package/dist/tui/app.d.ts +33 -0
  471. package/dist/tui/app.js +350 -0
  472. package/dist/tui/app.js.map +1 -0
  473. package/dist/tui/render.d.ts +20 -0
  474. package/dist/tui/render.js +558 -0
  475. package/dist/tui/render.js.map +1 -0
  476. package/package.json +80 -0
  477. package/schema/config.schema.json +223 -0
@@ -0,0 +1,169 @@
1
+ /**
2
+ * M21: Append-only audit trail for autonomous/sandbox actions.
3
+ *
4
+ * Writes to ~/.ashlr/audit/<YYYY-MM-DD>.jsonl — one JSON object per line.
5
+ * Rules:
6
+ * - Append-only: never truncate, never rewrite, never delete a prior line.
7
+ * - Never write secrets: summary is metadata only; secret-shaped tokens are
8
+ * stripped defensively before persisting.
9
+ * - audit() never throws; readAudit() skips malformed lines, never throws.
10
+ */
11
+ import { homedir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ import { existsSync, mkdirSync, readdirSync, appendFileSync, readFileSync, } from 'node:fs';
14
+ // ---------------------------------------------------------------------------
15
+ // Public: auditDir()
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Returns the absolute path to the audit directory: ~/.ashlr/audit.
19
+ * Created lazily by audit() — this function itself does NOT create it.
20
+ */
21
+ export function auditDir() {
22
+ return join(homedir(), '.ashlr', 'audit');
23
+ }
24
+ // ---------------------------------------------------------------------------
25
+ // Internal helpers
26
+ // ---------------------------------------------------------------------------
27
+ /** Current date as YYYY-MM-DD (UTC) for the daily file name. */
28
+ function todayDateString() {
29
+ return new Date().toISOString().slice(0, 10);
30
+ }
31
+ /**
32
+ * Defensively strip secret-shaped tokens from a summary string before
33
+ * persisting. Targets common patterns: long hex strings, bearer tokens,
34
+ * base64-looking blobs, and key=value pairs that look like credentials.
35
+ *
36
+ * This is a best-effort guardrail — the contract says "summary is metadata
37
+ * only, never secrets"; callers are the primary enforcement point.
38
+ */
39
+ function stripSecrets(summary) {
40
+ return (summary
41
+ // bearer / auth headers
42
+ .replace(/\b(Bearer|Token|Authorization)\s+[A-Za-z0-9\-._~+/]+=*/gi, '$1 [REDACTED]')
43
+ // key=value credential patterns (api_key=xxx, secret=xxx, token=xxx, password=xxx)
44
+ .replace(/\b(api[_-]?key|secret|token|password|passwd|auth|credential)[=:\s]+[^\s,;'"]{8,}/gi, '$1=[REDACTED]')
45
+ // Known-prefix provider tokens (anchored on the prefix so we don't redact
46
+ // ordinary identifiers). Covers OpenAI/Anthropic-style sk-, GitHub PAT/OAuth
47
+ // ghp_/gho_/ghu_/ghs_/ghr_, Slack xox[baprs]-, and AWS access key ids.
48
+ .replace(/\bsk-[A-Za-z0-9_-]{16,}/g, '[REDACTED]')
49
+ .replace(/\bgh[poursa]_[A-Za-z0-9]{16,}/g, '[REDACTED]')
50
+ .replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}/gi, '[REDACTED]')
51
+ .replace(/\bAKIA[0-9A-Z]{16}\b/g, '[REDACTED]')
52
+ // JWTs: three base64url segments separated by dots, header starts with eyJ.
53
+ .replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '[REDACTED]')
54
+ // Long hex strings ≥ 40 hex chars (private keys, raw API keys). Threshold
55
+ // raised above 32 so git SHA-1 (40) is borderline — but git short SHAs and
56
+ // path components are <40, and we WANT full forensic SHAs preserved, so we
57
+ // anchor on a length that real secrets exceed while commit/file metadata
58
+ // generally does not. Conservative: over-redaction would harm forensics.
59
+ .replace(/\b[0-9a-fA-F]{64,}\b/g, '[REDACTED]'));
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Public: audit()
63
+ // ---------------------------------------------------------------------------
64
+ /**
65
+ * Append one audit entry to today's JSONL file under ~/.ashlr/audit/.
66
+ * Sets `ts` to the current ISO timestamp; the caller supplies everything else.
67
+ *
68
+ * Append-only: never truncates, never rewrites existing lines.
69
+ * Never throws — errors are swallowed silently to keep callers unblocked.
70
+ */
71
+ export function audit(entry) {
72
+ try {
73
+ const dir = auditDir();
74
+ // Ensure audit directory exists (lazy mkdir).
75
+ if (!existsSync(dir)) {
76
+ mkdirSync(dir, { recursive: true });
77
+ }
78
+ const record = {
79
+ ts: new Date().toISOString(),
80
+ action: entry.action,
81
+ repo: entry.repo,
82
+ sandboxId: entry.sandboxId,
83
+ // Strip secret-shaped tokens defensively; summary is metadata only.
84
+ summary: stripSecrets(entry.summary),
85
+ result: entry.result,
86
+ };
87
+ const line = JSON.stringify(record) + '\n';
88
+ const filePath = join(dir, `${todayDateString()}.jsonl`);
89
+ // Append-only — appendFileSync creates the file if missing, never truncates.
90
+ appendFileSync(filePath, line, 'utf8');
91
+ }
92
+ catch {
93
+ // Intentionally swallowed: audit must never disrupt the caller's flow.
94
+ }
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // Public: readAudit()
98
+ // ---------------------------------------------------------------------------
99
+ /**
100
+ * Read audit entries across all date files, newest-first.
101
+ * `limit` caps the number returned (undefined or 0 => return all).
102
+ *
103
+ * Malformed JSONL lines are silently skipped; never throws.
104
+ */
105
+ export function readAudit(limit) {
106
+ try {
107
+ const dir = auditDir();
108
+ if (!existsSync(dir)) {
109
+ return [];
110
+ }
111
+ // List all .jsonl files, sort descending (newest date first).
112
+ let files;
113
+ try {
114
+ files = readdirSync(dir)
115
+ .filter((f) => f.endsWith('.jsonl'))
116
+ .sort()
117
+ .reverse();
118
+ }
119
+ catch {
120
+ return [];
121
+ }
122
+ const entries = [];
123
+ const cap = limit !== undefined && limit > 0 ? limit : Infinity;
124
+ for (const file of files) {
125
+ if (entries.length >= cap)
126
+ break;
127
+ const filePath = join(dir, file);
128
+ let raw;
129
+ try {
130
+ raw = readFileSync(filePath, 'utf8');
131
+ }
132
+ catch {
133
+ continue; // unreadable file — skip
134
+ }
135
+ // Collect lines in reverse order so newest entries within the file come first.
136
+ const lines = raw.split('\n').filter((l) => l.trim() !== '').reverse();
137
+ for (const line of lines) {
138
+ if (entries.length >= cap)
139
+ break;
140
+ try {
141
+ const parsed = JSON.parse(line);
142
+ if (parsed !== null &&
143
+ typeof parsed === 'object' &&
144
+ !Array.isArray(parsed)) {
145
+ // Light structural validation — skip clearly malformed entries.
146
+ const obj = parsed;
147
+ if (typeof obj['ts'] === 'string' &&
148
+ typeof obj['action'] === 'string' &&
149
+ typeof obj['summary'] === 'string' &&
150
+ (obj['result'] === 'ok' ||
151
+ obj['result'] === 'refused' ||
152
+ obj['result'] === 'error')) {
153
+ entries.push(obj);
154
+ }
155
+ }
156
+ }
157
+ catch {
158
+ // Malformed JSON line — skip silently.
159
+ }
160
+ }
161
+ }
162
+ return entries;
163
+ }
164
+ catch {
165
+ // Top-level guard: readAudit must never throw.
166
+ return [];
167
+ }
168
+ }
169
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/core/sandbox/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * policy.ts — Enrollment registry + kill switch (the gate).
3
+ *
4
+ * SAFETY RULES:
5
+ * - Enrollment registry persisted at ~/.ashlr/enrollment.json, DEFAULT EMPTY.
6
+ * - Kill switch backed by the presence of ~/.ashlr/KILL file.
7
+ * - assertMayMutate ALWAYS throws when the kill switch is on, regardless of
8
+ * enrollment or allowAnyRepo. allowAnyRepo ONLY bypasses the enrollment
9
+ * check (for tests operating on tmp repos) — it NEVER bypasses the kill switch.
10
+ * - enroll/unenroll normalize repo paths to absolute via path.resolve().
11
+ * - All functions are idempotent; never throw except the intentional assert.
12
+ * - No new runtime deps; node builtins only.
13
+ */
14
+ /** Path to the enrollment registry file. */
15
+ export declare function enrollmentPath(): string;
16
+ /** Path to the kill-switch sentinel file. */
17
+ export declare function killSwitchPath(): string;
18
+ /**
19
+ * Returns true when the global kill switch is active.
20
+ * Backed by the presence of ~/.ashlr/KILL.
21
+ */
22
+ export declare function killSwitchOn(): boolean;
23
+ /**
24
+ * Turn the kill switch on (creates ~/.ashlr/KILL) or off (removes it).
25
+ * Idempotent in both directions.
26
+ */
27
+ export declare function setKill(on: boolean): void;
28
+ /**
29
+ * Returns true when `repo` (normalized to absolute path) is enrolled
30
+ * for autonomous work.
31
+ */
32
+ export declare function isEnrolled(repo: string): boolean;
33
+ /**
34
+ * Enroll `repo` for autonomous/sandbox mutation. Idempotent — enrolling
35
+ * an already-enrolled repo is a no-op. Normalizes to absolute path.
36
+ */
37
+ export declare function enroll(repo: string): void;
38
+ /**
39
+ * Remove `repo` from the enrollment registry. Idempotent — unenrolling
40
+ * an absent repo is a no-op. Normalizes to absolute path.
41
+ */
42
+ export declare function unenroll(repo: string): void;
43
+ /**
44
+ * Return all enrolled repos (absolute paths). Returns [] when nothing is
45
+ * enrolled (the default state — DEFAULT EMPTY).
46
+ */
47
+ export declare function listEnrolled(): string[];
48
+ /**
49
+ * Assert that autonomous/sandbox mutation of `repo` is permitted.
50
+ *
51
+ * Throws when:
52
+ * 1. The kill switch is on (ALWAYS, regardless of enrollment or opts).
53
+ * 2. `repo` is not enrolled AND `opts.allowAnyRepo` is not true.
54
+ *
55
+ * `opts.allowAnyRepo` is a TEST SEAM only — it bypasses enrollment so tests
56
+ * can operate on tmp repos without enrolling them. It NEVER bypasses the kill
57
+ * switch.
58
+ */
59
+ export declare function assertMayMutate(repo: string, opts?: {
60
+ allowAnyRepo?: boolean;
61
+ }): void;
@@ -0,0 +1,211 @@
1
+ /**
2
+ * policy.ts — Enrollment registry + kill switch (the gate).
3
+ *
4
+ * SAFETY RULES:
5
+ * - Enrollment registry persisted at ~/.ashlr/enrollment.json, DEFAULT EMPTY.
6
+ * - Kill switch backed by the presence of ~/.ashlr/KILL file.
7
+ * - assertMayMutate ALWAYS throws when the kill switch is on, regardless of
8
+ * enrollment or allowAnyRepo. allowAnyRepo ONLY bypasses the enrollment
9
+ * check (for tests operating on tmp repos) — it NEVER bypasses the kill switch.
10
+ * - enroll/unenroll normalize repo paths to absolute via path.resolve().
11
+ * - All functions are idempotent; never throw except the intentional assert.
12
+ * - No new runtime deps; node builtins only.
13
+ */
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
15
+ import { homedir } from 'node:os';
16
+ import { join, resolve } from 'node:path';
17
+ // H6 (PART A — audit completeness): emit an audit() record inside
18
+ // enroll/unenroll/setKill so EVERY path (CLI cmdEnroll OR any programmatic
19
+ // caller — fixture, daemon, onboard) is captured — see
20
+ // docs/contracts/CONTRACT-H6.md §A.2. audit.ts imports only node builtins + a
21
+ // `type` from ../types.js and does NOT import policy.ts, so this static import
22
+ // creates NO import cycle (re-verified by an H6 [STATIC] test). Metadata only:
23
+ // action verb + repo abs path (a path is NOT a secret) + 'ok'.
24
+ import { audit } from './audit.js';
25
+ // ---------------------------------------------------------------------------
26
+ // Path helpers (re-resolved at call time so tests can relocate HOME)
27
+ // ---------------------------------------------------------------------------
28
+ function ashlrDir() {
29
+ return join(homedir(), '.ashlr');
30
+ }
31
+ /** Path to the enrollment registry file. */
32
+ export function enrollmentPath() {
33
+ return join(ashlrDir(), 'enrollment.json');
34
+ }
35
+ /** Path to the kill-switch sentinel file. */
36
+ export function killSwitchPath() {
37
+ return join(ashlrDir(), 'KILL');
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Internal I/O helpers
41
+ // ---------------------------------------------------------------------------
42
+ /** Read the enrollment registry. Returns { repos: [] } when absent/malformed. */
43
+ function readRegistry() {
44
+ const p = enrollmentPath();
45
+ if (!existsSync(p))
46
+ return { repos: [] };
47
+ try {
48
+ const raw = readFileSync(p, 'utf8');
49
+ const parsed = JSON.parse(raw);
50
+ if (typeof parsed === 'object' &&
51
+ parsed !== null &&
52
+ !Array.isArray(parsed) &&
53
+ Array.isArray(parsed['repos'])) {
54
+ const repos = parsed['repos']
55
+ .filter((r) => typeof r === 'string');
56
+ return { repos };
57
+ }
58
+ }
59
+ catch {
60
+ // malformed — treat as empty
61
+ }
62
+ return { repos: [] };
63
+ }
64
+ /** Persist the enrollment registry, creating ~/.ashlr if needed. */
65
+ function writeRegistry(reg) {
66
+ const dir = ashlrDir();
67
+ if (!existsSync(dir)) {
68
+ mkdirSync(dir, { recursive: true });
69
+ }
70
+ writeFileSync(enrollmentPath(), JSON.stringify(reg, null, 2) + '\n', 'utf8');
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // Kill switch
74
+ // ---------------------------------------------------------------------------
75
+ /**
76
+ * Returns true when the global kill switch is active.
77
+ * Backed by the presence of ~/.ashlr/KILL.
78
+ */
79
+ export function killSwitchOn() {
80
+ return existsSync(killSwitchPath());
81
+ }
82
+ /**
83
+ * Turn the kill switch on (creates ~/.ashlr/KILL) or off (removes it).
84
+ * Idempotent in both directions.
85
+ */
86
+ export function setKill(on) {
87
+ const kp = killSwitchPath();
88
+ const dir = ashlrDir();
89
+ if (on) {
90
+ if (!existsSync(dir)) {
91
+ mkdirSync(dir, { recursive: true });
92
+ }
93
+ if (!existsSync(kp)) {
94
+ writeFileSync(kp, 'kill switch active\n', 'utf8');
95
+ }
96
+ }
97
+ else {
98
+ if (existsSync(kp)) {
99
+ unlinkSync(kp);
100
+ }
101
+ }
102
+ // H6 (§A.2): audit the kill-switch toggle on EVERY call (idempotent on disk;
103
+ // we audit the requested intent). repo is null (not repo-scoped); summary is
104
+ // metadata only. audit() swallows its own errors, so the "never throws"
105
+ // contract of setKill is preserved.
106
+ audit({
107
+ action: on ? 'kill:on' : 'kill:off',
108
+ repo: null,
109
+ sandboxId: null,
110
+ summary: `kill switch ${on ? 'on' : 'off'}`,
111
+ result: 'ok',
112
+ });
113
+ }
114
+ // ---------------------------------------------------------------------------
115
+ // Enrollment registry
116
+ // ---------------------------------------------------------------------------
117
+ /**
118
+ * Returns true when `repo` (normalized to absolute path) is enrolled
119
+ * for autonomous work.
120
+ */
121
+ export function isEnrolled(repo) {
122
+ const abs = resolve(repo);
123
+ const reg = readRegistry();
124
+ return reg.repos.includes(abs);
125
+ }
126
+ /**
127
+ * Enroll `repo` for autonomous/sandbox mutation. Idempotent — enrolling
128
+ * an already-enrolled repo is a no-op. Normalizes to absolute path.
129
+ */
130
+ export function enroll(repo) {
131
+ const abs = resolve(repo);
132
+ const reg = readRegistry();
133
+ if (!reg.repos.includes(abs)) {
134
+ reg.repos.push(abs);
135
+ writeRegistry(reg);
136
+ }
137
+ // H6 (§A.2): audit AFTER the (idempotent) write so a no-op re-enroll STILL
138
+ // records the requested intent. The repo abs path is metadata, never a secret;
139
+ // audit() swallows its own errors so enroll's "never throws" contract holds.
140
+ audit({
141
+ action: 'enroll:add',
142
+ repo: abs,
143
+ sandboxId: null,
144
+ summary: `enrolled ${abs}`,
145
+ result: 'ok',
146
+ });
147
+ }
148
+ /**
149
+ * Remove `repo` from the enrollment registry. Idempotent — unenrolling
150
+ * an absent repo is a no-op. Normalizes to absolute path.
151
+ */
152
+ export function unenroll(repo) {
153
+ const abs = resolve(repo);
154
+ const reg = readRegistry();
155
+ const filtered = reg.repos.filter(r => r !== abs);
156
+ if (filtered.length !== reg.repos.length) {
157
+ writeRegistry({ repos: filtered });
158
+ }
159
+ // H6 (§A.2): audit AFTER the (idempotent) write so a no-op unenroll STILL
160
+ // records the requested intent. Metadata only (abs path is not a secret);
161
+ // audit() swallows its own errors so unenroll's "never throws" contract holds.
162
+ audit({
163
+ action: 'enroll:remove',
164
+ repo: abs,
165
+ sandboxId: null,
166
+ summary: `unenrolled ${abs}`,
167
+ result: 'ok',
168
+ });
169
+ }
170
+ /**
171
+ * Return all enrolled repos (absolute paths). Returns [] when nothing is
172
+ * enrolled (the default state — DEFAULT EMPTY).
173
+ */
174
+ export function listEnrolled() {
175
+ return readRegistry().repos;
176
+ }
177
+ // ---------------------------------------------------------------------------
178
+ // assertMayMutate — the gate every sandbox-mutating op calls first
179
+ // ---------------------------------------------------------------------------
180
+ /**
181
+ * Assert that autonomous/sandbox mutation of `repo` is permitted.
182
+ *
183
+ * Throws when:
184
+ * 1. The kill switch is on (ALWAYS, regardless of enrollment or opts).
185
+ * 2. `repo` is not enrolled AND `opts.allowAnyRepo` is not true.
186
+ *
187
+ * `opts.allowAnyRepo` is a TEST SEAM only — it bypasses enrollment so tests
188
+ * can operate on tmp repos without enrolling them. It NEVER bypasses the kill
189
+ * switch.
190
+ */
191
+ export function assertMayMutate(repo, opts) {
192
+ // Kill switch check — always enforced, no exceptions.
193
+ if (killSwitchOn()) {
194
+ throw new Error('autonomy kill switch is ON');
195
+ }
196
+ // Enrollment check — bypassed only by the explicit test hatch.
197
+ //
198
+ // H5 CHANGE 3 (env-gate allowAnyRepo): the `allowAnyRepo` hatch is effective
199
+ // ONLY when the process ALSO sets ASHLR_TEST_ALLOW_ANY_REPO=1, so a stray
200
+ // `allowAnyRepo:true` in any PRODUCTION path can NEVER bypass enrollment
201
+ // (mirrors advance.ts:155-157 EXACTLY). The kill-switch check above STAYS
202
+ // first and unconditional — it always wins (verify-safety CHECK 2 still
203
+ // passes). createSandbox passes opts straight through, so it inherits this
204
+ // gate transitively — single source of truth.
205
+ const allowAnyRepo = opts?.allowAnyRepo === true &&
206
+ process.env.ASHLR_TEST_ALLOW_ANY_REPO === '1';
207
+ if (!allowAnyRepo && !isEnrolled(repo)) {
208
+ throw new Error(`repo not enrolled for autonomous work: ${resolve(repo)}`);
209
+ }
210
+ }
211
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/core/sandbox/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * worktree.ts — isolated git-worktree sandbox (M21 SAFETY FOUNDATION).
3
+ *
4
+ * Creates an ISOLATED git worktree of a source repo on a NEW scratch branch
5
+ * under ~/.ashlr/sandboxes/<id>/, so autonomous edits NEVER touch the user's
6
+ * working tree, index, HEAD, or their checked-out branch. The worktree is
7
+ * created, used, then discarded (git worktree remove + scratch-branch delete).
8
+ *
9
+ * ISOLATION + DESTRUCTIVE-SAFETY (the whole point):
10
+ * - Sandbox worktrees live ONLY under ~/.ashlr/sandboxes/.
11
+ * - Creating/removing them MUST NOT modify the source repo's working tree,
12
+ * index, HEAD, or the user's branches.
13
+ * - `git worktree add -b <branch> <path> <baseHead>` off the current HEAD;
14
+ * `git worktree remove --force` + `git branch -D` on cleanup.
15
+ * - NEVER `git reset --hard` / `git checkout` in the source repo. NEVER push.
16
+ * NEVER delete user branches. NEVER touch a repo that is not ENROLLED
17
+ * (except via the explicit allowAnyRepo test hatch on a tmp repo).
18
+ * - Every mutating op (and every refused/errored attempt) is audited.
19
+ * - All git invoked via node:child_process arg ARRAYS (execFile, no shell).
20
+ * - No new runtime deps; node builtins only.
21
+ */
22
+ import type { Sandbox, SandboxDiff } from '../types.js';
23
+ /**
24
+ * H5 CHANGE 1 — orphan-sweep staleness threshold (ms). This is the FALLBACK
25
+ * liveness proxy used ONLY for a sandbox that carries no usable `ownerPid`
26
+ * marker (older metadata, or a crash fixture that models a GONE owner). The
27
+ * PRIMARY liveness signal is the positive `ownerPid` marker (see Sandbox.ownerPid
28
+ * + ownerAlive() below): a sandbox whose owner pid is still alive is skipped by
29
+ * the sweep / disk-cap pre-sweep REGARDLESS of age, so a live in-flight worktree
30
+ * is never reclaimed out from under a running swarm — even a cross-process one.
31
+ *
32
+ * Because there is NO hard wall-clock cap on a swarm (the runner bounds a swarm
33
+ * by step count <= 200 and the token budget only — there is no elapsed-time
34
+ * deadline), createdAt-age is NOT by itself a sound liveness proxy. So this
35
+ * threshold is set FAR above any plausible 200-step run (6 hours) as a
36
+ * belt-and-suspenders bound for the only residual gap: the rare pid-reuse case
37
+ * where a crashed swarm's recorded pid was recycled by an unrelated live process
38
+ * (ownerAlive() would then falsely read 'alive', so age must also have elapsed
39
+ * before we'd ever reclaim). The cost of a too-LARGE value (a stale orphan
40
+ * lingering a few hours longer) is far cheaper than the cost of a too-SMALL one
41
+ * (force-removing a live worktree), so we err large. Shared by the daemon-start
42
+ * sweep wiring (loop.ts) and the disk-cap pre-sweep below.
43
+ *
44
+ * CROSS-PROCESS LIVENESS LIMITATION (documented): the only way a live worktree
45
+ * could still be reclaimed is if BOTH (a) its owner process crashed AND (b) the
46
+ * OS recycled its exact pid for a new live process AND (c) >6h elapsed — a
47
+ * vanishingly small window that, even if hit, only reclaims a worktree whose
48
+ * original owner is provably dead.
49
+ */
50
+ export declare const ORPHAN_STALE_MS: number;
51
+ /** Absolute path to the sandboxes root: ~/.ashlr/sandboxes. Created lazily. */
52
+ export declare function sandboxesDir(): string;
53
+ /**
54
+ * Create an isolated git-worktree sandbox of `sourceRepo` on a NEW scratch
55
+ * branch under ~/.ashlr/sandboxes/<id>/.
56
+ *
57
+ * FIRST calls assertMayMutate(sourceRepo, opts) — refuses (throws + audits
58
+ * result:'refused') if the kill switch is on OR the repo is not enrolled and
59
+ * opts.allowAnyRepo is not set. Verifies sourceRepo is a git repo. Reads the
60
+ * source HEAD WITHOUT mutating it. Adds the worktree via
61
+ * `git worktree add -b <branch> <path> <baseHead>` run in sourceRepo — this
62
+ * MUST NOT modify the source working tree, index, HEAD, or any user branch.
63
+ */
64
+ export declare function createSandbox(sourceRepo: string, opts?: {
65
+ allowAnyRepo?: boolean;
66
+ }): Sandbox;
67
+ /**
68
+ * Capture the git diff of the sandbox worktree vs its base HEAD. Read-only —
69
+ * never mutates the worktree or the source repo. Counts come from --numstat;
70
+ * the unified patch from a plain `git diff <baseHead>` inside the worktree.
71
+ */
72
+ export declare function sandboxDiff(sb: Sandbox): SandboxDiff;
73
+ /**
74
+ * Remove a sandbox: `git worktree remove --force` then delete the scratch
75
+ * branch (`git branch -D`), both run against the source repo; then clean up
76
+ * persisted metadata. MUST NOT touch the source working tree/index/HEAD/user
77
+ * branches. Idempotent — tolerates an already-removed worktree, never throws
78
+ * on a missing dir.
79
+ */
80
+ export declare function removeSandbox(sb: Sandbox): void;
81
+ /**
82
+ * Enumerate persisted sandbox metadata under sandboxesDir(). Returns [] when
83
+ * none. Never throws on a malformed/partial entry (skips it).
84
+ */
85
+ export declare function listSandboxes(): Sandbox[];
86
+ /**
87
+ * Sweep ORPHANED sandboxes on restart (H2 crash-recovery). A swarm creates a
88
+ * worktree, uses it, then `removeSandbox`es it — all within a single in-memory
89
+ * `runSwarm` call. The sandbox lifetime is NOT persisted on the SwarmRun (there
90
+ * is no `sandboxId` field on a SwarmRun; the only `sandboxId` references are
91
+ * audit-log entries), so a process killed AFTER `git worktree add` but BEFORE
92
+ * `removeSandbox` leaves a worktree on disk under ~/.ashlr/sandboxes/<id>/ with
93
+ * no record linking it back to any swarm. Such a leftover would otherwise
94
+ * accumulate forever.
95
+ *
96
+ * LIVENESS (important — read before wiring a caller): a persisted sandbox is NOT
97
+ * necessarily an orphan. A `runSwarm` keeps its sandbox metadata on disk for the
98
+ * WHOLE run (createSandbox writes it at start, removeSandbox deletes it at the
99
+ * end), and the daemon runs several sandboxed swarms CONCURRENTLY, each with a
100
+ * live on-disk sandbox; a separately-launched `ashlr swarm` in another process is
101
+ * also possible (the re-entrancy guards are per-process). To NEVER force-remove a
102
+ * live worktree, the sweep uses TWO guards, in order:
103
+ *
104
+ * 1. POSITIVE liveness (primary, age-independent): a sandbox whose recorded
105
+ * `ownerPid` is still alive (ownerAlive()) is SKIPPED regardless of age — so
106
+ * a long-running swarm (same- OR cross-process) older than any staleMs is
107
+ * never reclaimed out from under its owner. This closes the central gap that
108
+ * createdAt-age alone could not (there is no wall-clock cap on a swarm).
109
+ * 2. AGE fallback (secondary): for a sandbox with NO usable ownerPid (older
110
+ * metadata, or a crash fixture that models a GONE owner), the OPTIONAL
111
+ * `staleMs` guard applies — a sandbox whose `createdAt` is younger than
112
+ * `staleMs` is SKIPPED (conservatively assumed possibly-live). With NO
113
+ * `staleMs` (the default, used by recovery PROOF tests where every sandbox is
114
+ * a deliberately-dropped orphan with a dead/absent owner) it sweeps every
115
+ * listed sandbox not protected by guard 1.
116
+ *
117
+ * This sweep composes the two EXISTING safe primitives — `listSandboxes()` to
118
+ * enumerate persisted sandbox metadata, then `removeSandbox()` for each — so it
119
+ * inherits all of removeSandbox's containment guards verbatim: it only ever runs
120
+ * `git worktree remove` / `git branch -D` against a path RE-DERIVED to live under
121
+ * `sandboxesDir()` and a branch RE-DERIVED into the `ashlr/sandbox/<id>` namespace
122
+ * (a metadata mismatch falls through to LOCAL dir cleanup only — never a git op on
123
+ * an arbitrary branch/path). It therefore can NEVER touch a user's working tree,
124
+ * index, HEAD, or any user branch.
125
+ *
126
+ * LOCAL-ONLY by construction: it adds NO outward capability (cleanup is purely
127
+ * inward — it removes only ashlr/sandbox/* worktrees + scratch refs, pushes
128
+ * nothing, opens no PR, applies no proposal), weakens NO guard (every removal
129
+ * goes through removeSandbox's full guard set), and changes NO happy-path
130
+ * behavior (a clean run already removes its own sandbox, so a healthy install
131
+ * has nothing to sweep). H5 wires this into daemon start (loop.ts, staleMs=
132
+ * ORPHAN_STALE_MS), the disk-cap pre-sweep above, and `ashlr sandbox gc` — every
133
+ * caller passes a staleMs and is additionally protected by the age-independent
134
+ * ownerAlive() guard, so no LIVE worktree is ever force-removed. Idempotent
135
+ * and never throws — a removal failure on one id is swallowed so the sweep always
136
+ * makes maximal progress. Returns the ids it swept so a caller / test can assert
137
+ * what was reclaimed.
138
+ *
139
+ * @param opts.staleMs Optional age guard (ms). Skip any sandbox whose `createdAt`
140
+ * is younger than this — a conservative liveness proxy so a concurrently-live
141
+ * sandbox is never force-removed. Omit to sweep all listed sandboxes.
142
+ */
143
+ export declare function sweepOrphanSandboxes(opts?: {
144
+ staleMs?: number;
145
+ }): string[];
146
+ /**
147
+ * Reclaim the on-disk sandboxes belonging to ONE specific source repo — the
148
+ * scoped cleanup behind `ashlr onboard --rollback <repo>` (H7 BUILD ITEM 4).
149
+ *
150
+ * WHY a distinct sweep (not sweepOrphanSandboxes): the generic orphan sweep is a
151
+ * BACKGROUND restart heuristic — it protects a possibly-live owner whose pid is
152
+ * unreadable by ALSO requiring `createdAt` to be older than `staleMs` (6h). But a
153
+ * crash-leftover worktree from the VERY activation the user is now explicitly
154
+ * undoing is, by definition, fresh (< 6h old), so the generic sweep would SKIP it
155
+ * and the rollback would leave it on disk — contradicting "one-command undo of a
156
+ * first activation". For an explicit, user-requested cleanup of a NAMED repo that
157
+ * the user just unenrolled, the age heuristic is unwarranted.
158
+ *
159
+ * SAFETY (the real boundary is preserved): this drops ONLY the age guard, never
160
+ * the LIVE-OWNER guard. It still SKIPS any sandbox whose recorded `ownerPid` is
161
+ * still alive (ownerAlive()), so a running in-flight swarm's worktree is NEVER
162
+ * force-removed — even a fresh one. It is SCOPED: only sandboxes whose
163
+ * `sourceRepo` resolves to `resolve(repo)` are considered, so it never touches a
164
+ * different repo's sandboxes. Each removal goes through removeSandbox(), so it
165
+ * inherits the full branch-prefix / path-containment guards verbatim and can
166
+ * NEVER touch a user's working tree, index, HEAD, or any user branch.
167
+ *
168
+ * LOCAL-ONLY by construction: adds NO outward capability (inward cleanup only —
169
+ * removes only ashlr/sandbox/* worktrees + scratch refs), weakens NO guard.
170
+ * Idempotent; never throws — a failure on one id is swallowed so the sweep makes
171
+ * maximal progress. Returns the ids it reclaimed.
172
+ *
173
+ * @param repo The source repo whose sandboxes to reclaim (resolved internally).
174
+ */
175
+ export declare function sweepRepoSandboxes(repo: string): string[];