forgeos 0.1.0-alpha.7 → 0.1.0-alpha.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (598) hide show
  1. package/.agents/skills/forge-agent-fabric/SKILL.md +178 -0
  2. package/.agents/skills/forge-agent-fabric/references/managed-workflows.md +95 -0
  3. package/.agents/skills/forge-agent-fabric/references/repository-analysis.md +102 -0
  4. package/.agents/skills/forge-agent-fabric/scripts/fabric.mjs +46 -0
  5. package/.agents/skills/forge-agent-fabric/scripts/repository.mjs +44 -0
  6. package/.npmignore +10 -0
  7. package/AGENTS.md +142 -90
  8. package/CHANGELOG.md +870 -9
  9. package/LICENSE +21 -0
  10. package/README.md +121 -6
  11. package/adapters/go/README.md +23 -0
  12. package/adapters/go/go.mod +3 -0
  13. package/adapters/go/http.go +149 -0
  14. package/adapters/go/registry.go +234 -0
  15. package/adapters/go/types.go +136 -0
  16. package/adapters/java/README.md +68 -0
  17. package/adapters/java/pom.xml +47 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  32. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  33. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  34. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  35. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  36. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  37. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  38. package/adapters/java-spring-boot-starter/README.md +32 -0
  39. package/adapters/java-spring-boot-starter/pom.xml +49 -0
  40. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  41. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  42. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  43. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  44. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  45. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  46. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  47. package/bin/forge.mjs +18 -0
  48. package/docs/agent-fabric-programs.md +151 -0
  49. package/docs/agent-fabric.md +582 -0
  50. package/docs/architecture/agent-fabric/PORTABLE_PROJECTS_PLAN.md +136 -0
  51. package/docs/architecture/agent-fabric/README.md +180 -0
  52. package/docs/architecture/agent-fabric/REPOSITORY_MANIFEST_PLAN.md +284 -0
  53. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_ADR.md +81 -0
  54. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_CORRECTIONS.md +39 -0
  55. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_CORRECTIONS_EVIDENCE.md +53 -0
  56. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_EVIDENCE.md +49 -0
  57. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_PLAN.md +590 -0
  58. package/docs/cair-protocol.md +103 -0
  59. package/docs/changelog.md +1070 -0
  60. package/docs/forge-protocol.md +189 -0
  61. package/docs/repository-agent-benchmark.md +179 -0
  62. package/docs/repository-analysis.md +139 -0
  63. package/docs/repository-context-quality.md +179 -0
  64. package/docs/repository-runtime-observation.md +82 -0
  65. package/examples/agent-fabric-v2/README.md +70 -0
  66. package/examples/agent-fabric-v2/item-child.workflow.ts +11 -0
  67. package/examples/agent-fabric-v2/make-request.mjs +4 -0
  68. package/examples/agent-fabric-v2/map-child.workflow.ts +10 -0
  69. package/examples/agent-fabric-v2/migrate.workflow.ts +35 -0
  70. package/examples/agent-fabric-v2/registry.example.json +256 -0
  71. package/examples/agent-fabric-v2/run-ui-fixture.ts +48 -0
  72. package/examples/agent-fabric-v2/ui-audit.workflow.ts +28 -0
  73. package/examples/agent-fabric-v2/ui-fixture-worker.cjs +36 -0
  74. package/examples/agent-fabric-v2/visual-population.json +13 -0
  75. package/examples/agent-fabric-v2/workers/check.mjs +2 -0
  76. package/examples/agent-fabric-v2/workers/common.mjs +7 -0
  77. package/examples/agent-fabric-v2/workers/discover.mjs +2 -0
  78. package/examples/agent-fabric-v2/workers/migrate.mjs +4 -0
  79. package/examples/agent-fabric-v2/workers/review.mjs +3 -0
  80. package/examples/go-billing/go.mod +7 -0
  81. package/examples/go-billing/main.go +120 -0
  82. package/examples/java-billing/pom.xml +60 -0
  83. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  84. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  85. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  86. package/package.json +60 -5
  87. package/schemas/agent-fabric/v0.1/control-event.schema.json +281 -0
  88. package/schemas/forge-manifest.schema.json +389 -0
  89. package/scripts/field-test-forgeos.mjs +1021 -0
  90. package/scripts/install-agent-fabric-skill.mjs +105 -0
  91. package/src/forge/_generated/releaseManifest.json +1 -2
  92. package/src/forge/_generated/releaseManifest.ts +3 -3
  93. package/src/forge/agent-adapters/index.ts +1592 -125
  94. package/src/forge/agent-adapters/types.ts +225 -1
  95. package/src/forge/agent-fabric/adapter.ts +131 -0
  96. package/src/forge/agent-fabric/attached-snapshot.ts +55 -0
  97. package/src/forge/agent-fabric/attached-task-contract.ts +105 -0
  98. package/src/forge/agent-fabric/attached-task-service.ts +127 -0
  99. package/src/forge/agent-fabric/attached-task-store.ts +109 -0
  100. package/src/forge/agent-fabric/authority.ts +298 -0
  101. package/src/forge/agent-fabric/canonical.ts +135 -0
  102. package/src/forge/agent-fabric/codex-adversarial-review.ts +229 -0
  103. package/src/forge/agent-fabric/codex-sdk-worker.ts +203 -0
  104. package/src/forge/agent-fabric/conductor.ts +754 -0
  105. package/src/forge/agent-fabric/dictionary.ts +12 -0
  106. package/src/forge/agent-fabric/errors.ts +32 -0
  107. package/src/forge/agent-fabric/hardened-conductor.ts +485 -0
  108. package/src/forge/agent-fabric/hardened-reducer.ts +211 -0
  109. package/src/forge/agent-fabric/index.ts +48 -0
  110. package/src/forge/agent-fabric/journal.ts +116 -0
  111. package/src/forge/agent-fabric/local-adaptive-digest-worker.mjs +26 -0
  112. package/src/forge/agent-fabric/local-adaptive-harness.ts +343 -0
  113. package/src/forge/agent-fabric/local-adaptive-service.ts +398 -0
  114. package/src/forge/agent-fabric/local-adaptive-worker.ts +146 -0
  115. package/src/forge/agent-fabric/local-approval-window.ts +211 -0
  116. package/src/forge/agent-fabric/local-change-review-service.ts +374 -0
  117. package/src/forge/agent-fabric/local-coding-worker.ts +285 -0
  118. package/src/forge/agent-fabric/local-control-store.ts +239 -0
  119. package/src/forge/agent-fabric/local-effect-broker.ts +348 -0
  120. package/src/forge/agent-fabric/local-evolution-approval.ts +81 -0
  121. package/src/forge/agent-fabric/local-evolution-profile.ts +87 -0
  122. package/src/forge/agent-fabric/local-evolution-registry.ts +427 -0
  123. package/src/forge/agent-fabric/local-evolution-service.ts +200 -0
  124. package/src/forge/agent-fabric/local-intelligence.ts +281 -0
  125. package/src/forge/agent-fabric/local-paths.ts +18 -0
  126. package/src/forge/agent-fabric/local-task-contract.ts +203 -0
  127. package/src/forge/agent-fabric/local-task-inbox.ts +543 -0
  128. package/src/forge/agent-fabric/local-task-server.ts +374 -0
  129. package/src/forge/agent-fabric/local-task-service.ts +982 -0
  130. package/src/forge/agent-fabric/local-verification.ts +551 -0
  131. package/src/forge/agent-fabric/managed-environment.ts +205 -0
  132. package/src/forge/agent-fabric/managed-run-contract.ts +103 -0
  133. package/src/forge/agent-fabric/managed-run-service.ts +401 -0
  134. package/src/forge/agent-fabric/managed-run-store.ts +165 -0
  135. package/src/forge/agent-fabric/managed-workspace.ts +253 -0
  136. package/src/forge/agent-fabric/p0a.ts +104 -0
  137. package/src/forge/agent-fabric/p0b-model-adapter.ts +434 -0
  138. package/src/forge/agent-fabric/planning.ts +155 -0
  139. package/src/forge/agent-fabric/program-api.ts +37 -0
  140. package/src/forge/agent-fabric/program-contract.ts +283 -0
  141. package/src/forge/agent-fabric/program-dsl.ts +126 -0
  142. package/src/forge/agent-fabric/program-evidence.ts +22 -0
  143. package/src/forge/agent-fabric/program-image.ts +15 -0
  144. package/src/forge/agent-fabric/program-lock.ts +50 -0
  145. package/src/forge/agent-fabric/program-owner.ts +16 -0
  146. package/src/forge/agent-fabric/program-scheduler.ts +38 -0
  147. package/src/forge/agent-fabric/program-service.ts +793 -0
  148. package/src/forge/agent-fabric/program-store.ts +142 -0
  149. package/src/forge/agent-fabric/program-structure.ts +65 -0
  150. package/src/forge/agent-fabric/program-types.ts +110 -0
  151. package/src/forge/agent-fabric/program-view.ts +12 -0
  152. package/src/forge/agent-fabric/program-worker.ts +108 -0
  153. package/src/forge/agent-fabric/project-profile.ts +50 -0
  154. package/src/forge/agent-fabric/project-registry.ts +120 -0
  155. package/src/forge/agent-fabric/project-runtime.ts +85 -0
  156. package/src/forge/agent-fabric/readiness.ts +26 -0
  157. package/src/forge/agent-fabric/reducer.ts +863 -0
  158. package/src/forge/agent-fabric/repository-context.ts +83 -0
  159. package/src/forge/agent-fabric/resource-ledger.ts +266 -0
  160. package/src/forge/agent-fabric/serialized-local-adapter.ts +60 -0
  161. package/src/forge/agent-fabric/types.ts +424 -0
  162. package/src/forge/agent-fabric/validation.ts +500 -0
  163. package/src/forge/agent-fabric/workflow-engine.ts +218 -0
  164. package/src/forge/agent-fabric/workflow-task-actions.ts +104 -0
  165. package/src/forge/agent-fabric/workflow-templates.ts +11 -0
  166. package/src/forge/agent-memory/bridge.ts +1728 -0
  167. package/src/forge/agent-memory/context-pack.ts +284 -0
  168. package/src/forge/agent-memory/hook-runner.ts +331 -0
  169. package/src/forge/agent-memory/mcp.ts +597 -0
  170. package/src/forge/agent-memory/normalize.ts +498 -0
  171. package/src/forge/agent-memory/redaction.ts +103 -0
  172. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  173. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +343 -0
  174. package/src/forge/agent-memory/sources/codex.ts +161 -0
  175. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  176. package/src/forge/agent-memory/types.ts +215 -0
  177. package/src/forge/bench.ts +248 -0
  178. package/src/forge/brownfield-import/index.ts +896 -0
  179. package/src/forge/brownfield-import/types.ts +127 -0
  180. package/src/forge/cair/action-journal.ts +61 -0
  181. package/src/forge/cair/action-parser.ts +314 -0
  182. package/src/forge/cair/action-validator.ts +40 -0
  183. package/src/forge/cair/actions.ts +1818 -0
  184. package/src/forge/cair/format.ts +77 -0
  185. package/src/forge/cair/index.ts +118 -0
  186. package/src/forge/cair/query.ts +478 -0
  187. package/src/forge/cair/snapshot.ts +315 -0
  188. package/src/forge/cair/types.ts +255 -0
  189. package/src/forge/cli/adaptive.ts +51 -0
  190. package/src/forge/cli/ai.ts +16 -1
  191. package/src/forge/cli/auth.ts +459 -15
  192. package/src/forge/cli/authmd.ts +441 -0
  193. package/src/forge/cli/baseline.ts +112 -0
  194. package/src/forge/cli/build.ts +20 -4
  195. package/src/forge/cli/changed.ts +418 -0
  196. package/src/forge/cli/codex-app-server.ts +877 -0
  197. package/src/forge/cli/commands.ts +1859 -20
  198. package/src/forge/cli/db.ts +209 -2
  199. package/src/forge/cli/deploy.ts +1339 -0
  200. package/src/forge/cli/deps.ts +150 -23
  201. package/src/forge/cli/dev.ts +1137 -53
  202. package/src/forge/cli/docs.ts +265 -0
  203. package/src/forge/cli/doctor.ts +297 -0
  204. package/src/forge/cli/env-placeholders.ts +62 -0
  205. package/src/forge/cli/evolution.ts +40 -0
  206. package/src/forge/cli/fabric.ts +253 -0
  207. package/src/forge/cli/field-test.ts +568 -0
  208. package/src/forge/cli/golden-path.ts +487 -0
  209. package/src/forge/cli/handoff.ts +336 -0
  210. package/src/forge/cli/index.ts +1 -0
  211. package/src/forge/cli/last-run.ts +84 -0
  212. package/src/forge/cli/main.ts +196 -3
  213. package/src/forge/cli/new.ts +169 -17
  214. package/src/forge/cli/next-actions.ts +23 -0
  215. package/src/forge/cli/output.ts +398 -6
  216. package/src/forge/cli/parse.ts +1377 -38
  217. package/src/forge/cli/progress.ts +51 -0
  218. package/src/forge/cli/query.ts +32 -0
  219. package/src/forge/cli/release.ts +35 -11
  220. package/src/forge/cli/repository.ts +151 -0
  221. package/src/forge/cli/run.ts +42 -0
  222. package/src/forge/cli/secrets.ts +137 -1
  223. package/src/forge/cli/seed.ts +581 -0
  224. package/src/forge/cli/self-host.ts +56 -14
  225. package/src/forge/cli/studio.ts +2282 -0
  226. package/src/forge/cli/verify.ts +1458 -32
  227. package/src/forge/cli/windows.ts +23 -0
  228. package/src/forge/cli/workos.ts +3509 -0
  229. package/src/forge/compiler/agent-contract/build.ts +728 -64
  230. package/src/forge/compiler/agent-contract/types.ts +45 -2
  231. package/src/forge/compiler/ai-registry/parse.ts +71 -2
  232. package/src/forge/compiler/api-surface/build.ts +47 -0
  233. package/src/forge/compiler/app-graph/build.ts +112 -9
  234. package/src/forge/compiler/app-graph/extract.ts +107 -0
  235. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  236. package/src/forge/compiler/app-graph/parser.ts +24 -24
  237. package/src/forge/compiler/app-graph/profile.ts +26 -0
  238. package/src/forge/compiler/app-graph/tsconfig-hash.ts +41 -4
  239. package/src/forge/compiler/app-graph/versions.ts +1 -1
  240. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  241. package/src/forge/compiler/classifier/classify.ts +32 -8
  242. package/src/forge/compiler/classifier/secrets.ts +3 -2
  243. package/src/forge/compiler/classifier/signals.ts +91 -1
  244. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  245. package/src/forge/compiler/client-sdk/render-client.ts +295 -13
  246. package/src/forge/compiler/data-graph/build.ts +29 -1
  247. package/src/forge/compiler/data-graph/parse.ts +20 -4
  248. package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
  249. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  250. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  251. package/src/forge/compiler/diagnostics/codes.ts +38 -0
  252. package/src/forge/compiler/diagnostics/create.ts +28 -2
  253. package/src/forge/compiler/diagnostics/index.ts +2 -0
  254. package/src/forge/compiler/emitter/barrel.ts +3 -0
  255. package/src/forge/compiler/emitter/render.ts +9 -0
  256. package/src/forge/compiler/external-manifest/registry.ts +217 -0
  257. package/src/forge/compiler/external-manifest/types.ts +91 -0
  258. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  259. package/src/forge/compiler/frontend-graph/build.ts +142 -14
  260. package/src/forge/compiler/integration/add.ts +991 -33
  261. package/src/forge/compiler/integration/plan.ts +43 -12
  262. package/src/forge/compiler/integration/render.ts +29 -0
  263. package/src/forge/compiler/integration/snapshot.ts +2 -0
  264. package/src/forge/compiler/integration/templates/convex.ts +70 -0
  265. package/src/forge/compiler/integration/templates/index.ts +1 -0
  266. package/src/forge/compiler/integration/templates/render.ts +38 -0
  267. package/src/forge/compiler/integration/templates/types.ts +3 -0
  268. package/src/forge/compiler/integration/templates/workos.ts +1439 -0
  269. package/src/forge/compiler/make-registry/build.ts +8 -9
  270. package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
  271. package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
  272. package/src/forge/compiler/orchestrator/manifest.ts +1 -1
  273. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  274. package/src/forge/compiler/orchestrator/plan.ts +249 -10
  275. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  276. package/src/forge/compiler/orchestrator/run.ts +101 -33
  277. package/src/forge/compiler/orchestrator/serialize.ts +33 -8
  278. package/src/forge/compiler/orchestrator/types.ts +1 -1
  279. package/src/forge/compiler/package-graph/compiler.ts +3 -1
  280. package/src/forge/compiler/package-graph/constants.ts +1 -1
  281. package/src/forge/compiler/package-graph/dts-extractor.ts +14 -1
  282. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  283. package/src/forge/compiler/package-manager/commands.ts +4 -0
  284. package/src/forge/compiler/package-manager/executor.ts +30 -1
  285. package/src/forge/compiler/package-manager/version.ts +36 -12
  286. package/src/forge/compiler/package-upgrades/planner.ts +155 -18
  287. package/src/forge/compiler/package-upgrades/types.ts +2 -0
  288. package/src/forge/compiler/policy-registry/build.ts +47 -2
  289. package/src/forge/compiler/policy-registry/parse.ts +32 -2
  290. package/src/forge/compiler/recipes/definitions.ts +63 -0
  291. package/src/forge/compiler/recipes/index.ts +2 -0
  292. package/src/forge/compiler/recipes/profiles.ts +81 -0
  293. package/src/forge/compiler/recipes/registry.ts +6 -0
  294. package/src/forge/compiler/test-graph/build.ts +11 -3
  295. package/src/forge/compiler/types/app-graph.ts +8 -2
  296. package/src/forge/compiler/types/cli.ts +76 -1
  297. package/src/forge/compiler/types/dev-manifest.ts +4 -0
  298. package/src/forge/compiler/types/emit.ts +2 -0
  299. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  300. package/src/forge/compiler/types/integration.ts +1 -0
  301. package/src/forge/compiler/types/package-graph.ts +6 -0
  302. package/src/forge/compiler/types/policy-registry.ts +3 -1
  303. package/src/forge/delta/broker-runner.mjs +5 -0
  304. package/src/forge/delta/broker.ts +429 -0
  305. package/src/forge/delta/classifier.ts +52 -0
  306. package/src/forge/delta/explain.ts +238 -0
  307. package/src/forge/delta/git-observer.ts +67 -0
  308. package/src/forge/delta/ids.ts +44 -0
  309. package/src/forge/delta/index.ts +25 -0
  310. package/src/forge/delta/process-identity.ts +37 -0
  311. package/src/forge/delta/queue-status.ts +190 -0
  312. package/src/forge/delta/recorder.ts +540 -0
  313. package/src/forge/delta/redaction.ts +50 -0
  314. package/src/forge/delta/schema.ts +240 -0
  315. package/src/forge/delta/session.ts +142 -0
  316. package/src/forge/delta/status.ts +1139 -0
  317. package/src/forge/delta/store.ts +3329 -0
  318. package/src/forge/delta/timeline.ts +178 -0
  319. package/src/forge/dev/server.ts +1061 -22
  320. package/src/forge/dev/types.ts +15 -1
  321. package/src/forge/dev/watch.ts +17 -7
  322. package/src/forge/dev-console/cycle.ts +285 -33
  323. package/src/forge/dev-console/types.ts +47 -1
  324. package/src/forge/impact/index.ts +222 -30
  325. package/src/forge/impact/types.ts +33 -1
  326. package/src/forge/intent/index.ts +35 -16
  327. package/src/forge/make/fields.ts +26 -0
  328. package/src/forge/make/index.ts +42 -6
  329. package/src/forge/make/templates.ts +211 -4
  330. package/src/forge/make/types.ts +2 -1
  331. package/src/forge/policy.ts +1 -1
  332. package/src/forge/react/index.ts +160 -2
  333. package/src/forge/refactor/index.ts +1 -0
  334. package/src/forge/repair/rules/index.ts +2 -2
  335. package/src/forge/repository-analysis/adapters/common.ts +72 -0
  336. package/src/forge/repository-analysis/adapters/docker.ts +185 -0
  337. package/src/forge/repository-analysis/adapters/java.ts +133 -0
  338. package/src/forge/repository-analysis/adapters/typescript.ts +350 -0
  339. package/src/forge/repository-analysis/adapters/vue.ts +71 -0
  340. package/src/forge/repository-analysis/analyze.ts +434 -0
  341. package/src/forge/repository-analysis/benchmark.ts +336 -0
  342. package/src/forge/repository-analysis/cache-gc.ts +154 -0
  343. package/src/forge/repository-analysis/check-selection.ts +123 -0
  344. package/src/forge/repository-analysis/context-node.ts +17 -0
  345. package/src/forge/repository-analysis/context.ts +456 -0
  346. package/src/forge/repository-analysis/http-resolution.ts +135 -0
  347. package/src/forge/repository-analysis/identity.ts +45 -0
  348. package/src/forge/repository-analysis/index.ts +18 -0
  349. package/src/forge/repository-analysis/nuxt-resolution.ts +188 -0
  350. package/src/forge/repository-analysis/quality.ts +60 -0
  351. package/src/forge/repository-analysis/retrieval.ts +309 -0
  352. package/src/forge/repository-analysis/runtime-artifacts.ts +195 -0
  353. package/src/forge/repository-analysis/runtime-observation.ts +325 -0
  354. package/src/forge/repository-analysis/scanner.ts +174 -0
  355. package/src/forge/repository-analysis/storage.ts +193 -0
  356. package/src/forge/repository-analysis/test-associations.ts +59 -0
  357. package/src/forge/repository-analysis/types.ts +99 -0
  358. package/src/forge/repository-manifest/index.ts +178 -0
  359. package/src/forge/repository-manifest/types.ts +63 -0
  360. package/src/forge/review/index.ts +162 -12
  361. package/src/forge/review/types.ts +15 -0
  362. package/src/forge/runtime/ai/providers.ts +19 -4
  363. package/src/forge/runtime/auth/config.ts +17 -0
  364. package/src/forge/runtime/auth/evaluate.ts +15 -2
  365. package/src/forge/runtime/auth/resolve.ts +61 -6
  366. package/src/forge/runtime/auth/types.ts +1 -0
  367. package/src/forge/runtime/db/factory.ts +1 -3
  368. package/src/forge/runtime/db/generated-client.ts +13 -2
  369. package/src/forge/runtime/db/memory-adapter.ts +139 -32
  370. package/src/forge/runtime/db/pglite-adapter.ts +285 -2
  371. package/src/forge/runtime/executor.ts +129 -16
  372. package/src/forge/runtime/external/bridge.ts +663 -0
  373. package/src/forge/runtime/runner/run-entry.ts +16 -7
  374. package/src/forge/runtime/telemetry/buffer.ts +4 -1
  375. package/src/forge/runtime/telemetry/scrubber.ts +35 -5
  376. package/src/forge/runtime/webhooks/security.ts +12 -7
  377. package/src/forge/server.ts +71 -7
  378. package/src/forge/ui/index.ts +831 -19
  379. package/src/forge/ui/types.ts +3 -1
  380. package/src/forge/version.ts +1 -1
  381. package/src/forge/vue/index.ts +422 -0
  382. package/src/forge/workspace/baseline.ts +112 -0
  383. package/src/forge/workspace/change-summary.ts +292 -0
  384. package/src/forge/workspace/forge-cli.ts +42 -0
  385. package/src/forge/workspace/git-summary.ts +393 -0
  386. package/templates/agent-workroom/AGENTS.md +29 -0
  387. package/templates/agent-workroom/README.md +34 -0
  388. package/templates/agent-workroom/forge.config.ts +3 -0
  389. package/templates/agent-workroom/package.json +34 -0
  390. package/templates/agent-workroom/pnpm-workspace.yaml +2 -0
  391. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  392. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  393. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  394. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  395. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  396. package/templates/agent-workroom/src/policies.ts +6 -0
  397. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  398. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  399. package/templates/agent-workroom/tsconfig.json +16 -0
  400. package/templates/agent-workroom/web/index.html +13 -0
  401. package/templates/agent-workroom/web/package.json +21 -0
  402. package/templates/agent-workroom/web/src/App.tsx +345 -0
  403. package/templates/agent-workroom/web/src/lib/forge.ts +20 -0
  404. package/templates/agent-workroom/web/src/main.tsx +13 -0
  405. package/templates/agent-workroom/web/src/styles.css +545 -0
  406. package/templates/agent-workroom/web/tsconfig.json +27 -0
  407. package/templates/agent-workroom/web/vite.config.ts +36 -0
  408. package/templates/b2b-support-web/package.json +2 -0
  409. package/templates/b2b-support-web/pnpm-workspace.yaml +2 -0
  410. package/templates/b2b-support-web/web/app/page.tsx +2 -2
  411. package/templates/b2b-support-web/web/lib/forge.ts +9 -2
  412. package/templates/b2b-support-web/web/next.config.ts +24 -0
  413. package/templates/b2b-support-web/web/package.json +1 -1
  414. package/templates/minimal-web/package.json +2 -1
  415. package/templates/minimal-web/pnpm-workspace.yaml +2 -0
  416. package/templates/minimal-web/web/index.html +1 -0
  417. package/templates/minimal-web/web/src/App.tsx +12 -6
  418. package/templates/minimal-web/web/src/lib/forge.ts +9 -2
  419. package/templates/minimal-web/web/src/styles.css +34 -0
  420. package/templates/minimal-web/web/vite.config.ts +36 -0
  421. package/templates/nuxt-web/.vscode/settings.json +14 -0
  422. package/templates/nuxt-web/.yarnrc.yml +1 -0
  423. package/templates/nuxt-web/README.md +30 -0
  424. package/templates/nuxt-web/forge.config.ts +3 -0
  425. package/templates/nuxt-web/package.json +37 -0
  426. package/templates/nuxt-web/pnpm-workspace.yaml +2 -0
  427. package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
  428. package/templates/nuxt-web/src/commands/createNote.ts +26 -0
  429. package/templates/nuxt-web/src/forge/schema.ts +12 -0
  430. package/templates/nuxt-web/src/policies.ts +6 -0
  431. package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
  432. package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
  433. package/templates/nuxt-web/tsconfig.json +17 -0
  434. package/templates/nuxt-web/web/app.vue +67 -0
  435. package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
  436. package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
  437. package/templates/nuxt-web/web/composables/forge.ts +13 -0
  438. package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
  439. package/templates/nuxt-web/web/nuxt.config.ts +11 -0
  440. package/templates/nuxt-web/web/package.json +22 -0
  441. package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
  442. package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
  443. package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
  444. package/templates/nuxt-web/web/tsconfig.json +6 -0
  445. package/templates/vendor-access/.vscode/settings.json +14 -0
  446. package/templates/vendor-access/README.md +30 -0
  447. package/templates/vendor-access/forge.config.ts +3 -0
  448. package/templates/vendor-access/package.json +35 -0
  449. package/templates/vendor-access/pnpm-workspace.yaml +2 -0
  450. package/templates/vendor-access/src/commands/addEvidence.ts +42 -0
  451. package/templates/vendor-access/src/commands/approveAccessRequest.ts +43 -0
  452. package/templates/vendor-access/src/commands/createAccessRequest.ts +49 -0
  453. package/templates/vendor-access/src/commands/seedVendorAccessDemo.ts +200 -0
  454. package/templates/vendor-access/src/forge/schema.ts +74 -0
  455. package/templates/vendor-access/src/policies.ts +11 -0
  456. package/templates/vendor-access/src/queries/listVendorAccessDashboard.ts +24 -0
  457. package/templates/vendor-access/src/queries/liveVendorAccessDashboard.ts +24 -0
  458. package/templates/vendor-access/tsconfig.json +17 -0
  459. package/templates/vendor-access/web/index.html +13 -0
  460. package/templates/vendor-access/web/package.json +21 -0
  461. package/templates/vendor-access/web/src/App.tsx +573 -0
  462. package/templates/vendor-access/web/src/lib/forge.ts +20 -0
  463. package/templates/vendor-access/web/src/main.tsx +205 -0
  464. package/templates/vendor-access/web/src/styles.css +682 -0
  465. package/templates/vendor-access/web/tsconfig.json +18 -0
  466. package/templates/vendor-access/web/vite.config.ts +36 -0
  467. package/src/forge/_generated/actionSubscriptions.json +0 -2
  468. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  469. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  470. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  471. package/src/forge/_generated/agentContract.json +0 -2
  472. package/src/forge/_generated/agentContract.ts +0 -7829
  473. package/src/forge/_generated/agentQuickstart.md +0 -34
  474. package/src/forge/_generated/agentTools.json +0 -2
  475. package/src/forge/_generated/agentTools.md +0 -16
  476. package/src/forge/_generated/agentTools.ts +0 -12
  477. package/src/forge/_generated/aiContext.ts +0 -125
  478. package/src/forge/_generated/aiModels.json +0 -2
  479. package/src/forge/_generated/aiModels.ts +0 -51
  480. package/src/forge/_generated/aiProviders.json +0 -2
  481. package/src/forge/_generated/aiProviders.ts +0 -23
  482. package/src/forge/_generated/aiRegistry.json +0 -2
  483. package/src/forge/_generated/aiRegistry.ts +0 -31
  484. package/src/forge/_generated/api.json +0 -2
  485. package/src/forge/_generated/api.ts +0 -8
  486. package/src/forge/_generated/appGraph.json +0 -2
  487. package/src/forge/_generated/appGraph.ts +0 -14937
  488. package/src/forge/_generated/appMap.md +0 -55
  489. package/src/forge/_generated/artifactManifest.json +0 -2
  490. package/src/forge/_generated/artifactManifest.ts +0 -7
  491. package/src/forge/_generated/authClaims.json +0 -2
  492. package/src/forge/_generated/authClaims.ts +0 -13
  493. package/src/forge/_generated/authConfig.json +0 -2
  494. package/src/forge/_generated/authConfig.ts +0 -17
  495. package/src/forge/_generated/authContext.ts +0 -23
  496. package/src/forge/_generated/authRegistry.json +0 -2
  497. package/src/forge/_generated/authRegistry.ts +0 -25
  498. package/src/forge/_generated/buildInfo.json +0 -2
  499. package/src/forge/_generated/buildInfo.ts +0 -9
  500. package/src/forge/_generated/capabilityMap.json +0 -2
  501. package/src/forge/_generated/capabilityMap.md +0 -15
  502. package/src/forge/_generated/capabilityMap.ts +0 -17
  503. package/src/forge/_generated/client.ts +0 -282
  504. package/src/forge/_generated/clientApi.ts +0 -9
  505. package/src/forge/_generated/clientManifest.json +0 -2
  506. package/src/forge/_generated/clientManifest.ts +0 -39
  507. package/src/forge/_generated/clientTypes.ts +0 -78
  508. package/src/forge/_generated/configRegistry.json +0 -2
  509. package/src/forge/_generated/configRegistry.ts +0 -4
  510. package/src/forge/_generated/dataGraph.json +0 -2
  511. package/src/forge/_generated/dataGraph.ts +0 -8
  512. package/src/forge/_generated/db.json +0 -2
  513. package/src/forge/_generated/db.ts +0 -2
  514. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  515. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  516. package/src/forge/_generated/dbSessionContext.json +0 -2
  517. package/src/forge/_generated/dbSessionContext.ts +0 -39
  518. package/src/forge/_generated/deployManifest.json +0 -2
  519. package/src/forge/_generated/deployManifest.ts +0 -14
  520. package/src/forge/_generated/devManifest.json +0 -2
  521. package/src/forge/_generated/devManifest.ts +0 -62
  522. package/src/forge/_generated/envSchema.json +0 -2
  523. package/src/forge/_generated/envSchema.ts +0 -59
  524. package/src/forge/_generated/frontendGraph.json +0 -2
  525. package/src/forge/_generated/frontendGraph.ts +0 -27
  526. package/src/forge/_generated/importGuards.json +0 -2
  527. package/src/forge/_generated/importGuards.ts +0 -686
  528. package/src/forge/_generated/index.ts +0 -68
  529. package/src/forge/_generated/liveProductionManifest.json +0 -2
  530. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  531. package/src/forge/_generated/liveProtocol.json +0 -2
  532. package/src/forge/_generated/liveProtocol.ts +0 -21
  533. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  534. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  535. package/src/forge/_generated/liveTransportConfig.json +0 -2
  536. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  537. package/src/forge/_generated/makeRegistry.json +0 -2
  538. package/src/forge/_generated/makeRegistry.ts +0 -177
  539. package/src/forge/_generated/makeTemplates.json +0 -2
  540. package/src/forge/_generated/makeTemplates.ts +0 -66
  541. package/src/forge/_generated/mockMap.json +0 -2
  542. package/src/forge/_generated/mockMap.ts +0 -7
  543. package/src/forge/_generated/operationPlaybooks.md +0 -167
  544. package/src/forge/_generated/packageGraph.json +0 -2
  545. package/src/forge/_generated/packageGraph.ts +0 -249334
  546. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  547. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  548. package/src/forge/_generated/permissionMatrix.json +0 -2
  549. package/src/forge/_generated/permissionMatrix.ts +0 -7
  550. package/src/forge/_generated/policyRegistry.json +0 -2
  551. package/src/forge/_generated/policyRegistry.ts +0 -11
  552. package/src/forge/_generated/queryRegistry.json +0 -2
  553. package/src/forge/_generated/queryRegistry.ts +0 -9
  554. package/src/forge/_generated/react.d.ts +0 -22
  555. package/src/forge/_generated/react.ts +0 -29
  556. package/src/forge/_generated/reactManifest.json +0 -2
  557. package/src/forge/_generated/reactManifest.ts +0 -19
  558. package/src/forge/_generated/rlsPolicies.json +0 -2
  559. package/src/forge/_generated/rlsPolicies.sql +0 -34
  560. package/src/forge/_generated/rlsPolicies.ts +0 -6
  561. package/src/forge/_generated/runtimeGraph.json +0 -2
  562. package/src/forge/_generated/runtimeGraph.ts +0 -8
  563. package/src/forge/_generated/runtimeMatrix.json +0 -2
  564. package/src/forge/_generated/runtimeMatrix.ts +0 -334130
  565. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  566. package/src/forge/_generated/runtimeRules.md +0 -91
  567. package/src/forge/_generated/secretRegistry.json +0 -2
  568. package/src/forge/_generated/secretRegistry.ts +0 -50
  569. package/src/forge/_generated/secretsContext.ts +0 -11
  570. package/src/forge/_generated/serverApi.ts +0 -10
  571. package/src/forge/_generated/sourceMapManifest.json +0 -2
  572. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  573. package/src/forge/_generated/sqlPlan.json +0 -2
  574. package/src/forge/_generated/sqlPlan.ts +0 -88
  575. package/src/forge/_generated/subscriptionManifest.json +0 -2
  576. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  577. package/src/forge/_generated/symbolicationManifest.json +0 -2
  578. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  579. package/src/forge/_generated/telemetryRegistry.json +0 -2
  580. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  581. package/src/forge/_generated/telemetrySinks.json +0 -2
  582. package/src/forge/_generated/telemetrySinks.ts +0 -11
  583. package/src/forge/_generated/tenantScope.json +0 -2
  584. package/src/forge/_generated/tenantScope.ts +0 -8
  585. package/src/forge/_generated/testGraph.json +0 -2
  586. package/src/forge/_generated/testGraph.ts +0 -3452
  587. package/src/forge/_generated/testPlanRegistry.json +0 -2
  588. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  589. package/src/forge/_generated/uiRoutes.json +0 -2
  590. package/src/forge/_generated/uiRoutes.ts +0 -16
  591. package/src/forge/_generated/uiScenarios.json +0 -2
  592. package/src/forge/_generated/uiScenarios.ts +0 -30
  593. package/src/forge/_generated/uiTestManifest.json +0 -2
  594. package/src/forge/_generated/uiTestManifest.ts +0 -27
  595. package/src/forge/_generated/workflowRegistry.json +0 -2
  596. package/src/forge/_generated/workflowRegistry.ts +0 -9
  597. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  598. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -0,0 +1,205 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rename, rm, symlink, unlink, writeFile } from "node:fs/promises";
4
+ import { execFile } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
7
+ import { tmpdir } from "node:os";
8
+ import { stableStringify } from "./canonical.ts";
9
+
10
+ type Manager = "npm" | "pnpm" | "yarn" | "bun" | "none";
11
+ export interface ManagedEnvironmentOptions { mode?: "auto" | "none"; ignoreScripts?: boolean; registry?: string; timeoutMs?: number; cacheDirectory?: string; signal?: AbortSignal }
12
+ export interface ManagedEnvironment { schemaVersion: 1; directory: string; manager: Manager; lockDigest: string; dependencyDigest: string; derivedPaths: string[]; cacheHit: boolean }
13
+ const execute = promisify(execFile);
14
+ const hash = (value: string | Uint8Array) => `sha256:${createHash("sha256").update(value).digest("hex")}`;
15
+ const absent = (error: unknown) => (error as NodeJS.ErrnoException).code === "ENOENT";
16
+ function assert(value: unknown, message: string): asserts value { if (!value) throw new Error(`managed environment: ${message}`); }
17
+ function inside(root: string, target: string) { const path = relative(root, target); return path === "" || (!isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`)); }
18
+ async function exists(path: string) { try { await access(path); return true; } catch (error) { if (absent(error)) return false; throw error; } }
19
+ function aborted(signal?: AbortSignal) { if (signal?.aborted) throw new Error("managed environment: preparation canceled"); }
20
+ async function cleanupOwned(path: string, parent: string, kind: "home" | "stage" | "cache"): Promise<void> {
21
+ const ownedName = path.slice(dirname(path).length + 1);
22
+ assert(resolve(dirname(path)) === resolve(parent) && (kind === "home" ? /^forge-managed-install-home-[a-zA-Z0-9]+$/.test(ownedName) : kind === "cache" ? /^[a-f0-9]{64}$/.test(ownedName) : /^[a-f0-9]{64}-[a-f0-9-]{36}\.stage$/.test(ownedName)), "refusing cleanup of unowned directory");
23
+ try { const stat = await lstat(path); assert(stat.isDirectory() && !stat.isSymbolicLink(), "refusing cleanup of substituted owned directory"); await rm(path, { recursive: true, force: true, maxRetries: 4, retryDelay: 20 }); }
24
+ catch (error) { if (!absent(error)) throw error; }
25
+ }
26
+ /** Only safe OS bootstrapping variables survive. Package-manager HOME/configs are private. */
27
+ export function managedInstallEnvironment(home: string): NodeJS.ProcessEnv {
28
+ const env: NodeJS.ProcessEnv = {};
29
+ for (const [key, value] of Object.entries(process.env)) if (["PATH", "SYSTEMROOT", "WINDIR", "COMSPEC", "PATHEXT", "TEMP", "TMP", "LANG", "LC_ALL"].includes(key.toUpperCase())) env[key] = value;
30
+ return { ...env, HOME: home, USERPROFILE: home, APPDATA: home, LOCALAPPDATA: home, CI: "1", NPM_CONFIG_USERCONFIG: join(home, "npmrc"), NPM_CONFIG_GLOBALCONFIG: join(home, "global-npmrc"), BUN_CONFIG_NO_CLEAR_TERMINAL: "1" };
31
+ }
32
+ async function managerCommand(manager: Exclude<Manager, "none">): Promise<{ executable: string; prefix: string[] }> {
33
+ const names = process.platform === "win32" ? [`${manager}.exe`, `${manager}.cmd`] : [manager];
34
+ const pathValue = Object.entries(process.env).find(([key]) => key.toUpperCase() === "PATH")?.[1] ?? "";
35
+ for (const directory of pathValue.split(process.platform === "win32" ? ";" : ":")) for (const name of names) {
36
+ const candidate = join(directory, name); if (!await exists(candidate)) continue;
37
+ if (!name.endsWith(".cmd")) return { executable: candidate, prefix: [] };
38
+ const scripts = manager === "npm" ? ["node_modules/npm/bin/npm-cli.js"] : manager === "pnpm" ? ["node_modules/pnpm/bin/pnpm.cjs", "node_modules/corepack/dist/pnpm.js"] : manager === "yarn" ? ["node_modules/yarn/bin/yarn.js", "node_modules/corepack/dist/yarn.js"] : [];
39
+ for (const script of scripts) if (await exists(join(directory, script))) return { executable: process.execPath, prefix: [join(directory, script)] };
40
+ }
41
+ throw new Error(`managed environment: ${manager} executable unavailable; install the project's declared package manager`);
42
+ }
43
+ async function sourceManifests(directory: string): Promise<string[]> {
44
+ const result: string[] = [];
45
+ const visit = async (root: string) => { for (const item of await readdir(root, { withFileTypes: true })) {
46
+ if ([".git", "node_modules", ".forge", ".yarn"].includes(item.name)) continue;
47
+ const target = join(root, item.name); assert(!item.isSymbolicLink(), "source symlink rejected during environment preparation");
48
+ if (item.isDirectory()) await visit(target); else if (item.name === "package.json") { assert(result.length < 1000, "too many Node manifests"); result.push(relative(directory, target).split(sep).join("/")); }
49
+ } };
50
+ await visit(directory); return result.sort();
51
+ }
52
+ async function derivePaths(directory: string): Promise<string[]> {
53
+ const result: string[] = [];
54
+ const visit = async (root: string) => { for (const item of await readdir(root, { withFileTypes: true })) {
55
+ if (item.name === ".git") continue;
56
+ const path = join(root, item.name);
57
+ if (item.name === "node_modules") { assert(item.isDirectory() && !item.isSymbolicLink(), "derived root cannot link to shared dependencies"); result.push(relative(directory, path).split(sep).join("/")); }
58
+ else if (item.isDirectory() && !item.isSymbolicLink()) await visit(path);
59
+ } };
60
+ await visit(directory); return result.sort();
61
+ }
62
+ async function portableLinks(directory: string, paths: string[]) {
63
+ const visit = async (absolute: string): Promise<void> => {
64
+ const stat = await lstat(absolute);
65
+ if (stat.isSymbolicLink()) {
66
+ const target = await readlink(absolute);
67
+ if (isAbsolute(target)) {
68
+ const actual = await realpath(absolute); assert(inside(directory, actual), "installed dependency links outside checkout");
69
+ const type = (await lstat(actual)).isDirectory() ? "dir" : "file";
70
+ // Windows junctions require no symlink privilege and already target this isolated checkout.
71
+ if (process.platform === "win32" && type === "dir") return;
72
+ await unlink(absolute); await symlink(relative(dirname(absolute), actual), absolute, type);
73
+ }
74
+ } else if (stat.isDirectory()) for (const name of await readdir(absolute)) await visit(join(absolute, name));
75
+ };
76
+ for (const path of paths) await visit(join(directory, path));
77
+ }
78
+ /** Copy dependency bytes; remap internal workspace links into the receiving isolated tree. */
79
+ async function copyDependencyPaths(sourceRoot: string, targetRoot: string, paths: string[], signal?: AbortSignal) {
80
+ let entries = 0, bytes = 0;
81
+ const visit = async (source: string, target: string): Promise<void> => {
82
+ aborted(signal); assert(++entries <= 200000 && inside(sourceRoot, source) && inside(targetRoot, target), "dependency copy bound or path escape");
83
+ const stat = await lstat(source);
84
+ if (stat.isSymbolicLink()) {
85
+ const link = await readlink(source), sourceTarget = resolve(dirname(source), link); assert(inside(sourceRoot, sourceTarget), "dependency copy link escapes source tree");
86
+ let directoryLink = process.platform === "win32";
87
+ try { const actual = await realpath(source); assert(inside(sourceRoot, actual), "dependency copy link resolves outside source tree"); directoryLink = (await lstat(actual)).isDirectory(); } catch (error) { if (!absent(error)) throw error; }
88
+ const receiver = resolve(targetRoot, relative(sourceRoot, sourceTarget)); assert(inside(targetRoot, receiver), "dependency copy link escapes receiving tree");
89
+ await mkdir(dirname(target), { recursive: true });
90
+ await symlink(process.platform === "win32" && directoryLink ? receiver : relative(dirname(target), receiver), target, directoryLink ? (process.platform === "win32" ? "junction" : "dir") : "file");
91
+ } else if (stat.isDirectory()) { await mkdir(target, { recursive: true }); for (const name of (await readdir(source)).sort()) await visit(join(source, name), join(target, name)); }
92
+ else { assert(stat.isFile() && stat.nlink === 1, "dependency copy cannot share hardlinked bytes"); bytes += stat.size; assert(bytes <= 4 * 1024 * 1024 * 1024, "dependency copy byte bound exceeded"); await mkdir(dirname(target), { recursive: true }); await cp(source, target, { force: false, errorOnExist: true }); }
93
+ };
94
+ for (const path of paths) await visit(join(sourceRoot, path), join(targetRoot, path));
95
+ }
96
+ async function rebaseCacheJunctions(directory: string, previous: string, paths: string[]) {
97
+ if (process.platform !== "win32") return;
98
+ const visit = async (absolute: string): Promise<void> => {
99
+ const stat = await lstat(absolute);
100
+ if (stat.isSymbolicLink()) {
101
+ const link = await readlink(absolute);
102
+ if (isAbsolute(link)) {
103
+ assert(inside(previous, resolve(link)), "cache junction relocation escapes owned tree");
104
+ const target = resolve(directory, relative(previous, resolve(link))); assert(inside(directory, target), "cache junction escapes published tree");
105
+ await unlink(absolute); await symlink(target, absolute, "junction");
106
+ }
107
+ } else if (stat.isDirectory()) for (const name of await readdir(absolute)) await visit(join(absolute, name));
108
+ };
109
+ for (const path of paths) await visit(join(directory, path));
110
+ }
111
+ /** Streaming dependency scan: 200k entries/4GiB, separately from bounded source snapshots. */
112
+ async function dependencyDigest(directory: string, paths: string[], signal?: AbortSignal): Promise<string> {
113
+ const entries: { path: string; digest: string; mode?: number }[] = []; let bytes = 0;
114
+ const visit = async (path: string) => {
115
+ aborted(signal); assert(entries.length < 200000, "dependency entry bound exceeded"); const absolute = resolve(directory, path); assert(inside(directory, absolute), "dependency path escape");
116
+ const stat = await lstat(absolute);
117
+ if (stat.isSymbolicLink()) {
118
+ const target = await readlink(absolute); assert(inside(directory, resolve(dirname(absolute), target)), "dependency symlink escapes isolated checkout");
119
+ try { assert(inside(directory, await realpath(absolute)), "dependency symlink resolves outside isolated checkout"); } catch (error) { if (!absent(error)) throw error; }
120
+ entries.push({ path, digest: hash(`link:${relative(dirname(absolute), resolve(dirname(absolute), target)).split(sep).join("/")}`) });
121
+ } else if (stat.isDirectory()) { entries.push({ path, digest: hash("directory") }); for (const name of (await readdir(absolute)).sort()) await visit(`${path}/${name}`); }
122
+ else {
123
+ assert(stat.isFile() && stat.nlink === 1, "dependency files cannot share writable hardlinks"); bytes += stat.size; assert(bytes <= 4 * 1024 * 1024 * 1024, "dependency byte bound exceeded");
124
+ const digest = createHash("sha256"); for await (const chunk of createReadStream(absolute)) { aborted(signal); digest.update(chunk as Buffer); }
125
+ entries.push({ path, digest: `sha256:${digest.digest("hex")}`, mode: stat.mode & 0o111 ? 0o755 : 0o644 });
126
+ }
127
+ };
128
+ assert(paths.length <= 1000 && new Set(paths).size === paths.length, "invalid derived paths");
129
+ for (const path of [...paths].sort()) { assert(!isAbsolute(path) && !path.includes("\\") && path.split("/").every(part => part && part !== "." && part !== "..") && (path.split("/").at(-1) === "node_modules" || [".yarn/install-state.gz", ".yarn/cache"].includes(path)), "invalid derived directory"); await visit(path); }
130
+ return hash(stableStringify(entries));
131
+ }
132
+ export async function verifyManagedEnvironment(environment: ManagedEnvironment): Promise<void> {
133
+ assert(environment?.schemaVersion === 1 && environment.directory === resolve(environment.directory) && ["npm", "pnpm", "yarn", "bun", "none"].includes(environment.manager), "invalid coordinator environment");
134
+ assert(await dependencyDigest(environment.directory, environment.derivedPaths) === environment.dependencyDigest, "prepared dependencies were modified; result is invalid");
135
+ }
136
+ export async function prepareManagedEnvironment(directory: string, options: ManagedEnvironmentOptions = {}): Promise<ManagedEnvironment> {
137
+ directory = resolve(directory); aborted(options.signal);
138
+ assert(!(await lstat(directory)).isSymbolicLink(), "workspace root cannot be a symlink");
139
+ const none = (): ManagedEnvironment => ({ schemaVersion: 1, directory, manager: "none", lockDigest: hash("none"), dependencyDigest: hash(stableStringify([])), derivedPaths: [], cacheHit: false });
140
+ if (options.mode === "none" || !await exists(join(directory, "package.json"))) return none();
141
+ const manifest = JSON.parse(await readFile(join(directory, "package.json"), "utf8"));
142
+ const candidates: { manager: Exclude<Manager, "none">; file: string }[] = [];
143
+ for (const [manager, file] of [["npm", "package-lock.json"], ["pnpm", "pnpm-lock.yaml"], ["yarn", "yarn.lock"], ["bun", "bun.lock"], ["bun", "bun.lockb"]] as const) if (await exists(join(directory, file))) candidates.push({ manager, file });
144
+ const declared = typeof manifest.packageManager === "string" ? manifest.packageManager.split("@")[0] : undefined;
145
+ const selected = declared ? candidates.filter(candidate => candidate.manager === declared) : candidates;
146
+ assert(selected.length === 1, "requires one deterministic frozen lockfile matching packageManager; missing or ambiguous lockfiles");
147
+ const { manager, file } = selected[0], command = await managerCommand(manager), home = await mkdtemp(join(tmpdir(), "forge-managed-install-home-"));
148
+ let stage: string | undefined, stageParent: string | undefined, uncommittedCache: string | undefined;
149
+ try {
150
+ await writeFile(join(home, "npmrc"), ""); await writeFile(join(home, "global-npmrc"), "");
151
+ const env = managedInstallEnvironment(home), registry = options.registry ?? "https://registry.npmjs.org/", url = new URL(registry);
152
+ assert(url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash, "registry must be a public HTTPS endpoint without credentials");
153
+ const timeout = options.timeoutMs ?? 180000; assert(Number.isSafeInteger(timeout) && timeout >= 100 && timeout <= 1800000, "invalid install timeout");
154
+ const version = (await execute(command.executable, [...command.prefix, "--version"], { cwd: directory, env, windowsHide: true, timeout: Math.min(timeout, 30000), signal: options.signal })).stdout.trim();
155
+ const manifests = await sourceManifests(directory), inputs = await Promise.all(manifests.map(async path => ({ path, digest: hash(await readFile(join(directory, path))) })));
156
+ const lockDigest = hash(stableStringify({ cacheSchema: 2, lock: hash(await readFile(join(directory, file))), manifests: inputs, manager, version, node: process.versions.node, platform: process.platform, arch: process.arch, registry, ignoreScripts: options.ignoreScripts !== false }));
157
+ const cacheRoot = resolve(options.cacheDirectory ?? join(tmpdir(), "forge-managed-dependency-cache"));
158
+ stageParent = cacheRoot;
159
+ assert(!inside(directory, cacheRoot), "dependency cache must be outside worker checkout"); await mkdir(cacheRoot, { recursive: true }); assert(!(await lstat(cacheRoot)).isSymbolicLink(), "cache root cannot be a symlink");
160
+ const cache = join(cacheRoot, lockDigest.slice(7)), marker = join(cache, "environment.json");
161
+ if (await exists(marker)) {
162
+ const cached = JSON.parse(await readFile(marker, "utf8")) as ManagedEnvironment;
163
+ assert(cached.lockDigest === lockDigest && cached.dependencyDigest === await dependencyDigest(join(cache, "tree"), cached.derivedPaths, options.signal), "dependency cache integrity failed");
164
+ await copyDependencyPaths(join(cache, "tree"), directory, cached.derivedPaths, options.signal);
165
+ const environment = { ...cached, directory, cacheHit: true }; await verifyManagedEnvironment(environment); return environment;
166
+ }
167
+ const configBackups: { path: string; bytes?: Buffer }[] = [];
168
+ try {
169
+ for (const parent of new Set(manifests.map(path => dirname(join(directory, path))))) for (const name of [".npmrc", ".yarnrc", ".yarnrc.yml", "bunfig.toml", ".env", ".env.local", ".env.development", ".env.production"]) {
170
+ const path = join(parent, name); let bytes: Buffer | undefined;
171
+ try { assert(!(await lstat(path)).isSymbolicLink(), "package config cannot be a symlink"); bytes = await readFile(path); } catch (error) { if (!absent(error)) throw error; }
172
+ if (bytes !== undefined) { configBackups.push({ path, bytes }); await writeFile(path, ""); }
173
+ }
174
+ const ignore = options.ignoreScripts !== false;
175
+ const args = manager === "npm" ? ["ci", "--no-audit", "--no-fund", `--registry=${registry}`, ...(ignore ? ["--ignore-scripts"] : [])]
176
+ : manager === "pnpm" ? ["install", "--frozen-lockfile", "--package-import-method=copy", `--registry=${registry}`, ...(ignore ? ["--ignore-scripts"] : [])]
177
+ : manager === "bun" ? ["install", "--frozen-lockfile", "--backend=copyfile", `--registry=${registry}`, ...(ignore ? ["--ignore-scripts"] : [])]
178
+ : Number.parseInt(version) >= 2 ? ["install", "--immutable", ...(ignore ? ["--mode=skip-builds"] : [])] : ["install", "--frozen-lockfile", "--non-interactive", `--registry=${registry}`, ...(ignore ? ["--ignore-scripts"] : [])];
179
+ env.YARN_NODE_LINKER = "node-modules"; env.YARN_NM_MODE = "classic"; env.YARN_ENABLE_GLOBAL_CACHE = "false"; env.YARN_NPM_REGISTRY_SERVER = registry; env.YARN_ENABLE_SCRIPTS = ignore ? "false" : "true";
180
+ env.NPM_CONFIG_CACHE = join(cacheRoot, "download-cache"); env.BUN_INSTALL_CACHE_DIR = join(cacheRoot, "bun-download-cache"); env.COREPACK_HOME = join(cacheRoot, "corepack-cache");
181
+ await execute(command.executable, [...command.prefix, ...args], { cwd: directory, env, windowsHide: true, timeout, signal: options.signal, maxBuffer: 1024 * 1024 });
182
+ } catch (error) { if (options.signal?.aborted) throw new Error("managed environment: preparation canceled"); throw new Error(`managed environment: frozen ${manager} install failed: ${(error as Error).message.slice(0, 2000)}`); }
183
+ finally { for (const config of configBackups) await writeFile(config.path, config.bytes!); }
184
+ aborted(options.signal); const derivedPaths = await derivePaths(directory);
185
+ if (manager === "yarn") for (const path of [".yarn/install-state.gz", ".yarn/cache"]) if (await exists(join(directory, path))) derivedPaths.push(path);
186
+ derivedPaths.sort(); await portableLinks(directory, derivedPaths); const dependency = await dependencyDigest(directory, derivedPaths, options.signal);
187
+ const environment: ManagedEnvironment = { schemaVersion: 1, directory, manager, lockDigest, dependencyDigest: dependency, derivedPaths, cacheHit: false };
188
+ stage = join(cacheRoot, `${lockDigest.slice(7)}-${randomUUID()}.stage`); await mkdir(join(stage, "tree"), { recursive: true });
189
+ await copyDependencyPaths(directory, join(stage, "tree"), derivedPaths, options.signal);
190
+ assert(await dependencyDigest(join(stage, "tree"), derivedPaths, options.signal) === dependency, "cache copy integrity failed");
191
+ try {
192
+ await rename(stage, cache); uncommittedCache = cache;
193
+ await rebaseCacheJunctions(join(cache, "tree"), join(stage, "tree"), derivedPaths);
194
+ assert(await dependencyDigest(join(cache, "tree"), derivedPaths, options.signal) === dependency, "published cache integrity failed");
195
+ const markerTemp = join(cache, "environment.json.tmp");
196
+ await writeFile(markerTemp, stableStringify(environment)); await rename(markerTemp, join(cache, "environment.json")); uncommittedCache = undefined;
197
+ } catch (error) { if (uncommittedCache || !["EEXIST", "ENOTEMPTY", "EPERM"].includes((error as NodeJS.ErrnoException).code ?? "")) throw error; }
198
+ return environment;
199
+ } finally {
200
+ // Cleanup never masks the original preparation error. Cache entries and worker trees are retained.
201
+ await cleanupOwned(home, tmpdir(), "home").catch(() => {});
202
+ if (stage && stageParent) await cleanupOwned(stage, stageParent, "stage").catch(() => {});
203
+ if (uncommittedCache && stageParent) await cleanupOwned(uncommittedCache, stageParent, "cache").catch(() => {});
204
+ }
205
+ }
@@ -0,0 +1,103 @@
1
+ import type { WorkflowLimits, WorkflowNode, WorkflowState } from "./workflow-engine.ts";
2
+ import { createWorkflow } from "./workflow-engine.ts";
3
+ import { createHash } from "node:crypto";
4
+
5
+ export const MANAGED_RUN_ACTIONS = ["run-start", "run-status", "run-wait", "run-steer", "run-pause", "run-resume", "run-cancel", "run-reconcile"] as const;
6
+ export type ManagedRunAction = typeof MANAGED_RUN_ACTIONS[number];
7
+ export type ManagedRole = "implementer" | "reviewer" | "investigator" | "decision";
8
+ export interface ManagedExecutorSpec {
9
+ nodeId: string; type: "codex" | "command"; role?: ManagedRole; prompt?: string;
10
+ writeScope?: string[]; model?: string; argv?: string[]; timeoutMs?: number;
11
+ }
12
+ export interface ManagedRunSpec {
13
+ requestId: string; goal: string; scope: string[];
14
+ workflow: { workflowId: string; nodes: WorkflowNode[]; limits?: Partial<WorkflowLimits> };
15
+ executors: ManagedExecutorSpec[]; publish?: boolean;
16
+ environment?: { mode?: "auto" | "none"; ignoreScripts?: boolean; registry?: string; timeoutMs?: number };
17
+ }
18
+ export interface ManagedEvent { cursor: number; at: string; type: string; nodeId?: string; attemptId?: string; summary: string; threadId?: string }
19
+ export interface ManagedStep {
20
+ nodeId: string; attemptId: string; status: "running" | "succeeded" | "failed" | "uncertain";
21
+ directory?: string; inputDigest?: string; threadId?: string; summary?: string;
22
+ report?: { summary: string; verdict?: "approved" | "changes_requested"; findings?: { description: string }[]; selectedNodeIds?: string[]; replanProposal?: string };
23
+ usage?: { input_tokens: number; cached_input_tokens: number; output_tokens: number };
24
+ artifact?: import("./managed-workspace.ts").ManagedArtifact;
25
+ environment?: import("./managed-environment.ts").ManagedEnvironment;
26
+ repositoryContext?: import("./repository-context.ts").FabricRepositoryContextMetadata;
27
+ }
28
+ export interface ManagedRunState {
29
+ schemaVersion: 1; runId: string; repositoryRoot: string; ownerPid: number; version: number;
30
+ spec: ManagedRunSpec; workflow: WorkflowState;
31
+ base?: import("./managed-workspace.ts").ManagedBase;
32
+ status: "preparing" | "running" | "paused" | "blocked" | "publishing" | "canceling" | "canceled" | "completed" | "failed";
33
+ steps: ManagedStep[]; events: ManagedEvent[]; cursor: number; instructions: string[];
34
+ createdAt: string; updatedAt: string; published?: { digest: string; changedFiles: string[] };
35
+ publicationIntent?: { digest: string; changedFiles: string[] }; error?: string;
36
+ }
37
+ export class ManagedRunError extends Error { constructor(readonly code: string, message: string) { super(message); this.name = "ManagedRunError"; } }
38
+ export function managedFail(code: string, message: string): never { throw new ManagedRunError(code, message); }
39
+ export const managedDigest = (value: string) => `sha256:${createHash("sha256").update(value).digest("hex")}`;
40
+ export function managedId(value: unknown, label: string): string {
41
+ if (typeof value !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/u.test(value)) managedFail("AF_RUN_INPUT", `Invalid ${label}`);
42
+ return value;
43
+ }
44
+ export function managedObject(value: unknown, allowed: readonly string[]): asserts value is Record<string, unknown> {
45
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some(key => !allowed.includes(key))) managedFail("AF_RUN_INPUT", "Unexpected or invalid request fields");
46
+ }
47
+ export function managedText(value: unknown, label: string, max = 12000): string {
48
+ if (typeof value !== "string" || !value.trim() || value.length > max) managedFail("AF_RUN_INPUT", `Invalid ${label}`);
49
+ return value;
50
+ }
51
+ export function managedPaths(value: unknown, label: string): string[] {
52
+ if (!Array.isArray(value) || !value.length || value.length > 100 || new Set(value).size !== value.length || value.some(path => typeof path !== "string" || !path || path.length > 512 || path.includes("\\") || path.startsWith("/") || path.split("/").some((part: string) => !part || part === "." || part === ".." || part === ".git") || path.includes(":") || path.toLowerCase() === ".forge" || path.toLowerCase().startsWith(".forge/"))) managedFail("AF_RUN_INPUT", `Invalid ${label}`);
53
+ return value as string[];
54
+ }
55
+ export function validateManagedSpec(value: unknown): ManagedRunSpec {
56
+ managedObject(value, ["requestId", "goal", "scope", "workflow", "executors", "publish", "environment"]);
57
+ if (value.environment !== undefined) {
58
+ managedObject(value.environment, ["mode", "ignoreScripts", "registry", "timeoutMs"]);
59
+ const environment = value.environment;
60
+ if (environment.mode !== undefined && !["auto", "none"].includes(environment.mode as string)) managedFail("AF_RUN_ENVIRONMENT", "Invalid environment mode");
61
+ if (environment.ignoreScripts !== undefined && typeof environment.ignoreScripts !== "boolean") managedFail("AF_RUN_ENVIRONMENT", "ignoreScripts must be boolean");
62
+ if (environment.timeoutMs !== undefined && (!Number.isSafeInteger(environment.timeoutMs) || (environment.timeoutMs as number) < 100 || (environment.timeoutMs as number) > 1800000)) managedFail("AF_RUN_ENVIRONMENT", "Invalid environment deadline");
63
+ if (environment.registry !== undefined) {
64
+ let url: URL; try { url = new URL(managedText(environment.registry, "registry", 2048)); } catch { managedFail("AF_RUN_ENVIRONMENT", "Invalid registry URL"); }
65
+ if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) managedFail("AF_RUN_ENVIRONMENT", "Registry requires HTTPS without embedded credentials");
66
+ }
67
+ }
68
+ managedId(value.requestId, "requestId"); managedText(value.goal, "goal"); const scope = managedPaths(value.scope, "scope");
69
+ managedObject(value.workflow, ["workflowId", "nodes", "limits"]);
70
+ const workflow = createWorkflow(value.workflow as unknown as ManagedRunSpec["workflow"]);
71
+ if (workflow.nodes.length > 32 || workflow.limits.maxConcurrency > 4 || workflow.limits.maxTotalAttempts > 100 || workflow.limits.maxRevisions > 20) managedFail("AF_RUN_LIMIT", "Managed workflow exceeds its limits");
72
+ if (!Array.isArray(value.executors) || value.executors.length !== workflow.nodes.length) managedFail("AF_RUN_INPUT", "Every node requires exactly one executor");
73
+ const executors = value.executors as ManagedExecutorSpec[]; const ids = new Set<string>();
74
+ for (const executor of executors) {
75
+ managedObject(executor, ["nodeId", "type", "role", "prompt", "writeScope", "model", "argv", "timeoutMs"]);
76
+ const id = managedId(executor.nodeId, "nodeId"); const node = workflow.nodes.find(item => item.nodeId === id);
77
+ if (!node || ids.has(id)) managedFail("AF_RUN_INPUT", "Invalid executor node"); ids.add(id);
78
+ if (node.outputContract?.requiredEvidenceKinds?.some(kind => kind !== "executor-observed")) managedFail("AF_RUN_EVIDENCE", "Managed executors support only executor-observed evidence; agent reports are separate from proof");
79
+ if (executor.timeoutMs !== undefined && (!Number.isSafeInteger(executor.timeoutMs) || executor.timeoutMs < 100 || executor.timeoutMs > 1800000)) managedFail("AF_RUN_LIMIT", "Invalid executor deadline");
80
+ if (executor.writeScope !== undefined) {
81
+ managedPaths(executor.writeScope, "writeScope");
82
+ if (executor.writeScope.some(path => !scope.some(parent => path === parent || path.startsWith(`${parent}/`)))) managedFail("AF_RUN_SCOPE", "Write scope exceeds task scope");
83
+ }
84
+ if (executor.type === "codex") {
85
+ if (!["implementer", "reviewer", "investigator", "decision"].includes(executor.role ?? "") || executor.argv !== undefined) managedFail("AF_RUN_INPUT", "Invalid Codex executor");
86
+ managedText(executor.prompt, "prompt"); if (executor.model !== undefined) managedText(executor.model, "model", 100);
87
+ if (executor.role !== "implementer" && executor.writeScope !== undefined) managedFail("AF_RUN_SCOPE", "Read-only workers cannot declare write scope");
88
+ if (executor.role === "implementer" && !executor.writeScope) managedFail("AF_RUN_SCOPE", "Implementer requires a write scope");
89
+ if ((node.kind === "decision") !== (executor.role === "decision")) managedFail("AF_RUN_INPUT", "Decision nodes require decision workers");
90
+ } else if (executor.type === "command") {
91
+ if (executor.role !== undefined || executor.prompt !== undefined || executor.model !== undefined || executor.writeScope !== undefined || node.kind === "decision") managedFail("AF_RUN_INPUT", "Invalid command executor");
92
+ if (!Array.isArray(executor.argv) || !executor.argv.length || executor.argv.length > 40 || executor.argv.some(arg => typeof arg !== "string" || !arg || arg.length > 4096 || arg.includes("\0"))) managedFail("AF_RUN_INPUT", "Command argv required");
93
+ } else managedFail("AF_RUN_INPUT", "Unknown executor type");
94
+ }
95
+ if (value.publish !== undefined && typeof value.publish !== "boolean") managedFail("AF_RUN_INPUT", "publish must be boolean");
96
+ const writers = executors.filter(item => item.role === "implementer");
97
+ if (writers.length && value.publish !== false) {
98
+ const ancestor = (nodeId: string, target: string): boolean => workflow.nodes.find(node => node.nodeId === nodeId)!.dependsOn.some(id => id === target || ancestor(id, target));
99
+ if (!executors.some(item => item.role === "reviewer" && workflow.nodes.find(node => node.nodeId === item.nodeId)!.required && writers.every(writer => ancestor(item.nodeId, writer.nodeId)))) managedFail("AF_RUN_REVIEW_REQUIRED", "Publishing requires a required independent review of every writer");
100
+ if (!executors.some(item => item.type === "command" && workflow.nodes.find(node => node.nodeId === item.nodeId)!.required && writers.every(writer => ancestor(item.nodeId, writer.nodeId)))) managedFail("AF_RUN_CHECK_REQUIRED", "Publishing requires a required command verification of every writer");
101
+ }
102
+ return structuredClone(value) as unknown as ManagedRunSpec;
103
+ }