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,142 @@
1
+ import { mkdir, open, readFile, rename, unlink, realpath, readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { randomUUID, createHash } from "node:crypto";
4
+ import { assertAttachedSafePath } from "./attached-snapshot.ts";
5
+ import { ProgramFileLease } from "./program-lock.ts";
6
+ import { stableStringify } from "./canonical.ts";
7
+ import { programAssert, programDigest, validateWorkflowProgram, type ProgramRunV2 } from "./program-contract.ts";
8
+
9
+ interface ProgramReceipt { requestId: string; fingerprint: string; version: number }
10
+ interface ProgramTransition { runId: string; version: number; type: string; at: string; stateDigest: string; stateRef: string; parentRef?: string }
11
+ interface ProgramRecord { format: 3; runId: string; stateRef: string; journalRef: string; version: number; receipts: ProgramReceipt[] }
12
+ const absent = (error: unknown) => (error as NodeJS.ErrnoException).code === "ENOENT";
13
+ const sharingViolation = (error: unknown) => ["EPERM", "EACCES", "EBUSY"].includes((error as NodeJS.ErrnoException).code ?? "");
14
+ async function retrySharing<T>(action: () => Promise<T>): Promise<T> {
15
+ const deadline = Date.now() + 1000;
16
+ for (;;) { try { return await action(); } catch (error) { if (!sharingViolation(error) || Date.now() >= deadline) throw error; await new Promise(resolve => setTimeout(resolve, 20)); } }
17
+ }
18
+ /** One authoritative atomic record per run; artifacts commit before referring transitions. */
19
+ export class ProgramRunStore {
20
+ readonly metrics = { transactions: 0, transactionMs: 0, bytesWritten: 0, snapshotBytes: 0, journalBytes: 0, envelopeBytes: 0, artifactBytes: 0, artifactReads: 0 };
21
+ private tails = new Map<string, Promise<unknown>>();
22
+ private definitions = new Set<string>();
23
+ private validate(state: ProgramRunV2): void {
24
+ const key = `${programDigest(state.program)}:${programDigest(state.registry)}`;
25
+ if (!this.definitions.has(key)) { validateWorkflowProgram(state.program, state.registry); this.definitions.add(key); }
26
+ }
27
+ private constructor(readonly root: string, readonly directory: string) {}
28
+ static async open(root: string): Promise<ProgramRunStore> {
29
+ root = await realpath(root); const directory = join(root, ".forge/local/agent-fabric/program-runs-v3");
30
+ await assertAttachedSafePath(root, directory); await mkdir(join(directory, "artifacts"), { recursive: true });
31
+ await assertAttachedSafePath(root, directory); return new ProgramRunStore(root, directory);
32
+ }
33
+ private path(id: string): string { programAssert(typeof id === "string" && id.length > 0 && id.length <= 256, "Invalid run ID"); return join(this.directory, `${programDigest(id).slice(7)}.json`); }
34
+ async put(value: unknown, maxBytes = 4 * 1024 * 1024, category: "snapshotBytes" | "journalBytes" | "artifactBytes" = "artifactBytes"): Promise<string> {
35
+ const bytes = stableStringify(value), digest = programDigest(value); programAssert(Buffer.byteLength(bytes) <= maxBytes, "Artifact too large");
36
+ const path = join(this.directory, "artifacts", `${digest.slice(7)}.json`); await assertAttachedSafePath(this.root, path);
37
+ const temp = `${path}.${randomUUID()}.tmp`, handle = await open(temp, "wx", 0o600);
38
+ try { await handle.writeFile(bytes); await handle.sync(); this.metrics.bytesWritten += Buffer.byteLength(bytes); this.metrics[category] += Buffer.byteLength(bytes); } finally { await handle.close(); }
39
+ try {
40
+ try { programAssert(await readFile(path, "utf8") === bytes, "Immutable artifact corrupted"); }
41
+ catch (error) { if (!absent(error)) throw error; await retrySharing(() => rename(temp, path)); }
42
+ } finally { await unlink(temp).catch(() => {}); }
43
+ return digest;
44
+ }
45
+ async get<T = unknown>(digest: string): Promise<T> {
46
+ this.metrics.artifactReads++;
47
+ programAssert(/^sha256:[a-f0-9]{64}$/.test(digest), "Invalid artifact reference");
48
+ const path = join(this.directory, "artifacts", `${digest.slice(7)}.json`); await assertAttachedSafePath(this.root, path);
49
+ const bytes = await readFile(path, "utf8"); programAssert(Buffer.byteLength(bytes) <= 32 * 1024 * 1024, "Artifact exceeds physical limit");
50
+ const value = JSON.parse(bytes); programAssert(programDigest(value) === digest, "Artifact integrity failed"); return value as T;
51
+ }
52
+ async putBinary(bytes: Uint8Array, maxBytes = 8 * 1024 * 1024): Promise<string> {
53
+ programAssert(bytes.byteLength <= maxBytes, "Binary artifact exceeds byte budget");
54
+ const digest = `sha256:${createHash("sha256").update(bytes).digest("hex")}`, path = join(this.directory, "artifacts", `${digest.slice(7)}.blob`);
55
+ await assertAttachedSafePath(this.root, path);
56
+ const temp = `${path}.${randomUUID()}.tmp`, handle = await open(temp, "wx", 0o600);
57
+ try { await handle.writeFile(bytes); await handle.sync(); this.metrics.bytesWritten += bytes.byteLength; this.metrics.artifactBytes += bytes.byteLength; } finally { await handle.close(); }
58
+ try { try { programAssert((await readFile(path)).equals(Buffer.from(bytes)), "Immutable binary corrupted"); } catch (error) { if (!absent(error)) throw error; await retrySharing(() => rename(temp, path)); } }
59
+ finally { await unlink(temp).catch(() => {}); } return digest;
60
+ }
61
+ async getBinary(digest: string, maxBytes = 8 * 1024 * 1024): Promise<Buffer> {
62
+ programAssert(/^sha256:[a-f0-9]{64}$/.test(digest), "Invalid binary reference");
63
+ const path = join(this.directory, "artifacts", `${digest.slice(7)}.blob`); await assertAttachedSafePath(this.root, path);
64
+ programAssert(Number.isSafeInteger(maxBytes) && maxBytes > 0 && (await stat(path)).size <= maxBytes, "Binary exceeds physical byte budget");
65
+ const bytes = await readFile(path); programAssert(bytes.length <= maxBytes && `sha256:${createHash("sha256").update(bytes).digest("hex")}` === digest, "Binary integrity/byte limit failed"); return bytes;
66
+ }
67
+ private async record(id: string): Promise<ProgramRecord | undefined> {
68
+ const path = this.path(id); await assertAttachedSafePath(this.root, path);
69
+ let bytes: string; try { bytes = await retrySharing(() => readFile(path, "utf8")); } catch (error) { if (absent(error)) return; throw error; }
70
+ programAssert(Buffer.byteLength(bytes) <= 32 * 1024 * 1024, "Program record exceeds limit");
71
+ const envelope = JSON.parse(bytes) as { record: ProgramRecord; digest: string };
72
+ programAssert(programDigest(envelope.record) === envelope.digest, "Program record integrity failed");
73
+ const record = envelope.record;
74
+ programAssert(record.format === 3 && record.runId === id && record.version > 0, "Unsupported/corrupt program record; start a new run");
75
+ const state = await this.get<ProgramRunV2>(record.stateRef), checkpoint = await this.get<ProgramTransition>(record.journalRef);
76
+ programAssert(state.schemaVersion === 2 && state.runId === id && state.version === record.version, "Invalid persisted run identity");
77
+ this.validate(state);
78
+ programAssert(programDigest(state.program) === state.programDigest && programDigest(state.registry) === state.registryDigest, "Pinned program/registry corrupted");
79
+ programAssert(checkpoint.runId === id && checkpoint.stateDigest === record.stateRef && checkpoint.stateRef === record.stateRef && checkpoint.version === state.version, "Checkpoint/journal mismatch");
80
+ return record;
81
+ }
82
+ async read(id: string): Promise<ProgramRunV2 | undefined> { const record = await this.record(id); return record ? this.get<ProgramRunV2>(record.stateRef) : undefined; }
83
+ async history(id: string): Promise<ProgramTransition[]> {
84
+ const record = await this.record(id); if (!record) return [];
85
+ const history: ProgramTransition[] = []; let ref: string | undefined = record.journalRef, version = record.version;
86
+ while (ref) {
87
+ const node: ProgramTransition = await this.get<ProgramTransition>(ref);
88
+ programAssert(node.runId === id && node.version === version-- && node.stateDigest === node.stateRef && history.length < 100000, "Journal chain mismatch");
89
+ const state = await this.get<ProgramRunV2>(node.stateRef);
90
+ programAssert(state.runId === id && state.version === node.version, "Historical snapshot identity mismatch");
91
+ history.push(node); ref = node.parentRef;
92
+ }
93
+ programAssert(version === 0, "Truncated program journal"); return history.reverse();
94
+ }
95
+ async list(): Promise<string[]> {
96
+ const ids: string[] = [];
97
+ for (const name of await readdir(this.directory)) {
98
+ if (!/^[a-f0-9]{64}\.json$/.test(name)) continue;
99
+ const path = join(this.directory, name); await assertAttachedSafePath(this.root, path);
100
+ const envelope = JSON.parse(await readFile(path, "utf8"));
101
+ programAssert(envelope?.record?.runId && this.path(envelope.record.runId) === path, "Run filename/identity mismatch");
102
+ await this.record(envelope.record.runId); ids.push(envelope.record.runId);
103
+ }
104
+ return ids;
105
+ }
106
+ async transact(id: string, type: string, mutate: (state?: ProgramRunV2) => ProgramRunV2,
107
+ options: { requestId?: string; fingerprint?: string; expectedVersion?: number } = {}): Promise<ProgramRunV2> {
108
+ const task = (this.tails.get(id) ?? Promise.resolve()).catch(() => {}).then(() => this.transaction(id, type, mutate, options));
109
+ this.tails.set(id, task);
110
+ try { return await task; } finally { if (this.tails.get(id) === task) this.tails.delete(id); }
111
+ }
112
+ private async transaction(id: string, type: string, mutate: (state?: ProgramRunV2) => ProgramRunV2,
113
+ options: { requestId?: string; fingerprint?: string; expectedVersion?: number }): Promise<ProgramRunV2> {
114
+ const transactionStarted = performance.now();
115
+ const path = this.path(id), lock = `${path}.lock`;
116
+ await assertAttachedSafePath(this.root, lock); await assertAttachedSafePath(this.root, `${lock}.reclaim`);
117
+ const lease = await ProgramFileLease.acquire(lock, { waitMs: 10000 });
118
+ try {
119
+ const old = await this.record(id), receipt = old?.receipts.find(entry => entry.requestId === options.requestId);
120
+ if (receipt) { programAssert(receipt.fingerprint === options.fingerprint, "requestId body changed", "AF_PROGRAM_CONFLICT"); return this.get<ProgramRunV2>(old!.stateRef); }
121
+ programAssert(options.expectedVersion === undefined || options.expectedVersion === (old?.version ?? 0), "Version conflict", "AF_PROGRAM_CONFLICT");
122
+ if (options.requestId) programAssert(typeof options.fingerprint === "string", "Request fingerprint required");
123
+ const oldState = old ? await this.get<ProgramRunV2>(old.stateRef) : undefined;
124
+ if (type === "attempt-dispatch-intent") programAssert(!old || old.version < 90000 && Buffer.byteLength(stableStringify(oldState)) < 24 * 1024 * 1024, "Journal dispatch reserve exhausted; recovery remains available");
125
+ const state = mutate(oldState);
126
+ if (old && !options.requestId && programDigest(state) === old.stateRef) return structuredClone(state);
127
+ state.version = (old?.version ?? 0) + 1;
128
+ this.validate(state);
129
+ const stateRef = await this.put(state, 32 * 1024 * 1024, "snapshotBytes");
130
+ const journalRef = await this.put({ runId: id, version: state.version, type, at: new Date().toISOString(), stateDigest: stateRef, stateRef, ...(old ? { parentRef: old.journalRef } : {}) }, 4096, "journalBytes");
131
+ const record: ProgramRecord = { format: 3, runId: id, version: state.version, stateRef, journalRef,
132
+ receipts: [...(old?.receipts ?? []), ...(options.requestId ? [{ requestId: options.requestId, fingerprint: options.fingerprint!, version: state.version }] : [])] };
133
+ const bytes = stableStringify({ record, digest: programDigest(record) }); programAssert(Buffer.byteLength(bytes) <= 32 * 1024 * 1024 && record.receipts.length <= 10000 && record.version <= 100000, "Program journal limit exceeded");
134
+ const token = lease.token;
135
+ const temp = `${path}.${token}.tmp`, handle = await open(temp, "wx", 0o600);
136
+ try { await handle.writeFile(bytes); await handle.sync(); this.metrics.bytesWritten += Buffer.byteLength(bytes); this.metrics.envelopeBytes += Buffer.byteLength(bytes); } finally { await handle.close(); }
137
+ this.metrics.transactions++; this.metrics.transactionMs += performance.now() - transactionStarted;
138
+ try { await assertAttachedSafePath(this.root, path); await retrySharing(() => rename(temp, path)); } finally { await unlink(temp).catch(() => {}); }
139
+ return structuredClone(state);
140
+ } finally { await retrySharing(() => lease.close()); }
141
+ }
142
+ }
@@ -0,0 +1,65 @@
1
+ import { programAssert, type ProgramOperation, type WorkflowProgramV2 } from "./program-contract.ts";
2
+
3
+ export interface ProgramBlock { steps: ProgramOperation[]; result: unknown }
4
+ /** Only a single operation has a shorthand result. Arrays are not sequences. */
5
+ export function programBlock(value: unknown): ProgramBlock {
6
+ if (value && typeof value === "object" && !Array.isArray(value) && "steps" in value) {
7
+ const block = value as ProgramBlock;
8
+ programAssert(Array.isArray(block.steps) && Object.hasOwn(block, "result"), "Block requires steps and explicit result"); return block;
9
+ }
10
+ const steps = Array.isArray(value) ? value : [value];
11
+ programAssert(steps.length === 1 && steps[0] && typeof steps[0].id === "string", "Multiple operations require Block {steps,result}");
12
+ return { steps, result: { $expr: "output", args: [steps[0].id] } };
13
+ }
14
+ export function canonicalItemKey(value: unknown): string {
15
+ programAssert(typeof value === "string" && value.length > 0 && !/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u.test(value), "Map key must be a valid Unicode string");
16
+ const key = value.normalize("NFC"); programAssert(Buffer.byteLength(key) <= 256, "Map key byte limit exceeded"); return key;
17
+ }
18
+ export const compareProgramKeys = (a: string, b: string): number => Buffer.compare(Buffer.from(a), Buffer.from(b));
19
+ export const programSegment = (key: string): string => encodeURIComponent(canonicalItemKey(key)).replace(/[!'()*]/g, char => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
20
+
21
+ export interface ProgramGraphEntry { id: string; step: ProgramOperation; dependencies: string[] }
22
+ /** Static templates, lexical references and control edges. No authored code executes. */
23
+ export function programGraph(program: WorkflowProgramV2): ProgramGraphEntry[] {
24
+ const graph = new Map<string, ProgramGraphEntry>();
25
+ function refs(value: unknown, resolve: (id: string) => string | undefined, dependencies: Set<string>): void {
26
+ if (!value || typeof value !== "object") return;
27
+ if (Array.isArray(value)) { for (const entry of value) refs(entry, resolve, dependencies); return; }
28
+ const record = value as Record<string, unknown>;
29
+ if (record.$expr === "literal") return;
30
+ if (["output", "acceptedCandidate", "acceptedCandidates", "outputCandidate", "coverageReceipt"].includes(String(record.$expr))) {
31
+ const id = (record.args as unknown[])[0]; programAssert(typeof id === "string", "Output ID required"); const target = resolve(id); programAssert(target, `Unknown output ${id}`); dependencies.add(target);
32
+ }
33
+ for (const child of Object.values(record)) refs(child, resolve, dependencies);
34
+ }
35
+ function scope(block: ProgramBlock, prefix: string, outer?: (id: string) => string | undefined, sequential = false): void {
36
+ const resolve = (id: string) => block.steps.some(step => step.id === id) ? `${prefix}${id}` : outer?.(id);
37
+ for (let index = 0; index < block.steps.length; index++) {
38
+ const step = block.steps[index], id = `${prefix}${step.id}`, dependencies = new Set<string>();
39
+ if (sequential && index) dependencies.add(`${prefix}${block.steps[index - 1].id}`);
40
+ const after = step.options.after ?? []; programAssert(Array.isArray(after) && after.every(entry => typeof entry === "string"), "after requires operation IDs");
41
+ for (const predecessor of after) { const target = resolve(predecessor); programAssert(target, `Unknown control predecessor ${predecessor}`); dependencies.add(target); }
42
+ refs(Object.fromEntries(Object.entries(step.options).filter(([key]) => !["body", "steps", "result", "then", "else", "next", "until", "after"].includes(key))), resolve, dependencies);
43
+ const children: { block: ProgramBlock; prefix: string; sequential?: boolean }[] = [];
44
+ if (step.kind === "sequence" || step.kind === "parallel") children.push({ block: programBlock(step.options), prefix: `${id}/`, sequential: step.kind === "sequence" });
45
+ if (step.kind === "map" || step.kind === "loop") children.push({ block: programBlock(step.options.body), prefix: `${id}/body/` });
46
+ if (step.kind === "branch") for (const arm of ["then", "else"]) children.push({ block: programBlock(step.options[arm]), prefix: `${id}/${arm}/` });
47
+ programAssert(!graph.has(id), "Duplicate operation ID"); graph.set(id, { id, step, dependencies: [] });
48
+ for (const child of children) {
49
+ scope(child.block, child.prefix, resolve, child.sequential);
50
+ for (const entry of child.block.steps) dependencies.add(`${child.prefix}${entry.id}`);
51
+ const childResolve = (target: string) => child.block.steps.some(entry => entry.id === target) ? `${child.prefix}${target}` : resolve(target);
52
+ refs(child.block.result, childResolve, dependencies);
53
+ if (step.kind === "loop") {
54
+ refs(step.options.next, childResolve, dependencies); refs(step.options.until, childResolve, dependencies);
55
+ }
56
+ }
57
+ graph.get(id)!.dependencies = [...dependencies];
58
+ }
59
+ refs(block.result, resolve, new Set());
60
+ }
61
+ scope({ steps: program.steps, result: program.result }, "");
62
+ const visited = new Set<string>(), visiting = new Set<string>();
63
+ function visit(id: string): void { programAssert(!visiting.has(id), `Dependency cycle ${id}`); if (visited.has(id)) return; visiting.add(id); for (const dependency of graph.get(id)?.dependencies ?? []) visit(dependency); visiting.delete(id); visited.add(id); }
64
+ for (const id of graph.keys()) visit(id); return [...graph.values()];
65
+ }
@@ -0,0 +1,110 @@
1
+ import { programBlock } from "./program-structure.ts";
2
+ import { programAssert, programDigest, registryEntry, validateProgramData, type ProgramOperation, type ProgramRegistry, type ProgramSchema, type ProgramLexicalContext, type WorkflowProgramV2 } from "./program-contract.ts";
3
+
4
+ /** Conservative finite inference: unknown schemas retain runtime validation. No JS evaluation. */
5
+ export function validateProgramTypes(program: WorkflowProgramV2, registry: ProgramRegistry, lexical: ProgramLexicalContext = {}, depth = 0): void {
6
+ programAssert(depth <= 32, "Subworkflow type context depth exceeded");
7
+ type Shape = ProgramSchema;
8
+ interface Scope { steps: ProgramOperation[]; input: Shape; parent?: Scope; item?: Shape; state?: Shape }
9
+ const cache = new Map<ProgramOperation, Shape>();
10
+ const candidate: Shape = { type: "object", properties: { candidateId: { type: "string" }, digest: { type: "string" }, baselineDigest: { type: "string" }, parents: { type: "array" }, deltas: { type: "array" }, producerAttempts: { type: "array" } } };
11
+ function types(shape: Shape): string[] { return shape.type ? Array.isArray(shape.type) ? shape.type : [shape.type] : []; }
12
+ function compatible(actual: Shape, expected: Shape, label: string): void {
13
+ if (Object.hasOwn(actual, "const")) { validateProgramData(actual.const, expected); return; }
14
+ if (actual.anyOf) { for (const alternative of actual.anyOf) compatible(alternative, expected, label); return; }
15
+ const a = types(actual), e = types(expected);
16
+ if (a.length && e.length) programAssert(a.every(type => e.includes(type) || type === "integer" && e.includes("number")), `Incompatible type at ${label}`);
17
+ if (actual.properties && expected.properties) for (const [key, value] of Object.entries(actual.properties)) {
18
+ if (Object.hasOwn(expected.properties, key)) compatible(value, expected.properties[key], `${label}.${key}`);
19
+ else programAssert(expected.additionalProperties !== false, `Unexpected field at ${label}.${key}`);
20
+ }
21
+ if (actual.required) for (const key of expected.required ?? []) programAssert(actual.required.includes(key), `Missing required field at ${label}.${key}`);
22
+ if (actual.items && expected.items) compatible(actual.items, expected.items, `${label}[]`);
23
+ }
24
+ function constant(value: unknown): Shape {
25
+ const type = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
26
+ return { type, const: value as never };
27
+ }
28
+ function reference(id: string, scope: Scope): Shape {
29
+ const operation = scope.steps.find(step => step.id === id);
30
+ if (operation) return operationShape(operation, scope);
31
+ programAssert(scope.parent, `Unknown typed output ${id}`); return reference(id, scope.parent);
32
+ }
33
+ function infer(value: unknown, scope: Scope): Shape {
34
+ if (!value || typeof value !== "object") return constant(value);
35
+ if (Array.isArray(value)) return { type: "array", items: value.length ? { anyOf: value.map(entry => infer(entry, scope)) } : {}, minItems: value.length, maxItems: value.length };
36
+ const record = value as Record<string, unknown>;
37
+ if (!Object.hasOwn(record, "$expr")) return { type: "object", properties: Object.fromEntries(Object.entries(record).map(([key, entry]) => [key, infer(entry, scope)])), required: Object.keys(record), additionalProperties: false };
38
+ const name = String(record.$expr), args = record.args as unknown[];
39
+ if (name === "literal") return constant(args[0]);
40
+ if (name === "workflowInput") return scope.input;
41
+ if (name === "item") { programAssert(scope.item, "item requires map context"); return scope.item; }
42
+ if (name === "loopState") return scope.state ?? {};
43
+ if (name === "output") return reference(String(args[0]), scope);
44
+ if (["outputCandidate", "acceptedCandidate", "candidateFromBaseline"].includes(name)) return candidate;
45
+ if (name === "acceptedCandidates") return { type: "array", items: candidate };
46
+ if (name === "object") return infer(args[0], scope);
47
+ if (name === "array") return { type: "array", items: args.length ? infer(args[0], scope) : {} };
48
+ if (name === "field") {
49
+ const input = infer(args[0], scope), key = String(args[1]);
50
+ if (input.anyOf) {
51
+ const alternatives = input.anyOf.map(alternative => {
52
+ if (Object.hasOwn(alternative, "const")) { programAssert(alternative.const && typeof alternative.const === "object" && Object.hasOwn(alternative.const, key), `Field absent ${key} in branch merge`); return constant((alternative.const as Record<string, unknown>)[key]); }
53
+ programAssert(!alternative.properties || Object.hasOwn(alternative.properties, key) || alternative.additionalProperties !== false, `Field absent ${key} in branch merge`); return alternative.properties?.[key] ?? {};
54
+ });
55
+ return { anyOf: alternatives };
56
+ }
57
+ if (Object.hasOwn(input, "const")) { programAssert(input.const && typeof input.const === "object" && Object.hasOwn(input.const, key), `Field absent ${key}`); return constant((input.const as Record<string, unknown>)[key]); }
58
+ if (input.type) compatible(input, { type: "object" }, "field input");
59
+ programAssert(!input.properties || Object.hasOwn(input.properties, key) || input.additionalProperties !== false, `Field absent ${key}`); return input.properties?.[key] ?? {};
60
+ }
61
+ if (["eq", "and", "or", "not"].includes(name)) return { type: "boolean" };
62
+ if (name === "length") return { type: "integer" };
63
+ if (["concat", "filter", "unique", "sort", "take"].includes(name)) { const input = infer(args[0], scope); if (input.type) compatible(input, { type: "array" }, `${name} input`); return { type: "array", items: input.items ?? {} }; }
64
+ if (["acceptanceChecks", "approvedChecksFor", "acceptanceWriteScope"].includes(name)) return { type: "array", items: { type: "string" } };
65
+ return {};
66
+ }
67
+ function operationShape(step: ProgramOperation, scope: Scope): Shape {
68
+ const cached = cache.get(step); if (cached) return cached;
69
+ const options = step.options; let shape: Shape = {};
70
+ if (step.kind === "value") shape = infer(options.value, scope);
71
+ if (step.kind === "agent" || step.kind === "command") {
72
+ const executor = registryEntry(registry.executors, options.executor as never);
73
+ if (executor.inputSchema) compatible(infer(options.input ?? {}, scope), registryEntry(registry.schemas, executor.inputSchema), `activity ${step.id} input`);
74
+ shape = registryEntry(registry.schemas, executor.schema);
75
+ }
76
+ if (step.kind === "waitEvent") shape = registryEntry(registry.schemas, options.schema as never);
77
+ if (step.kind === "subworkflow") {
78
+ const child = registryEntry(registry.programs ?? {}, options.program as never);
79
+ compatible(infer(options.input, scope), registryEntry(registry.schemas, child.inputSchema), `child ${step.id} input`);
80
+ validateProgramTypes(child, registry, { item: scope.item, state: scope.state }, depth + 1);
81
+ shape = registryEntry(registry.schemas, child.outputSchema);
82
+ }
83
+ if (step.kind === "branch") {
84
+ compatible(infer(options.condition, scope), { type: "boolean" }, `branch ${step.id} condition`);
85
+ const arms = ["then", "else"].map(arm => { const block = programBlock(options[arm]), steps = block.steps, child = { ...scope, steps, parent: scope }; for (const entry of steps) operationShape(entry, child); return infer(block.result, child); });
86
+ const left = types(arms[0]), right = types(arms[1]);
87
+ programAssert(!left.length || !right.length || programDigest(left) === programDigest(right), `Incompatible branch merge ${step.id}`);
88
+ shape = { anyOf: arms, ...(left.length && right.length ? { type: left } : {}) };
89
+ }
90
+ if (step.kind === "map" || step.kind === "loop") {
91
+ const steps = programBlock(options.body).steps;
92
+ const items = step.kind === "map" ? infer(options.items, scope) : {}, state = step.kind === "loop" ? infer(options.initialState, scope) : {};
93
+ if (step.kind === "map") compatible(items, { type: "array" }, `map ${step.id} items`);
94
+ const child = { ...scope, steps, parent: scope, item: step.kind === "map" ? items.items ?? {} : scope.item, state: step.kind === "loop" ? state : scope.state };
95
+ for (const entry of steps) operationShape(entry, child);
96
+ infer(programBlock(options.body).result, child);
97
+ if (step.kind === "loop") { compatible(infer(options.next, child), { type: state.type }, `loop ${step.id} state`); compatible(infer(options.until, child), { type: "boolean" }, `loop ${step.id} condition`); }
98
+ shape = step.kind === "loop" ? state : { type: "object" };
99
+ }
100
+ if (["sequence", "parallel"].includes(step.kind)) {
101
+ const block = programBlock(options), child = { ...scope, steps: block.steps, parent: scope };
102
+ for (const entry of block.steps) operationShape(entry, child); shape = infer(block.result, child);
103
+ }
104
+ if (["repair", "compose", "gate"].includes(step.kind)) shape = { type: "object" };
105
+ cache.set(step, shape); return shape;
106
+ }
107
+ const scope: Scope = { ...lexical, steps: program.steps, input: registryEntry(registry.schemas, program.inputSchema) };
108
+ for (const step of program.steps) operationShape(step, scope);
109
+ compatible(infer(program.result, scope), registryEntry(registry.schemas, program.outputSchema), "program result");
110
+ }
@@ -0,0 +1,12 @@
1
+ import { programDigest, type ProgramOperationRecord, type WorkflowProgramV2 } from "./program-contract.ts";
2
+ import { programGraph } from "./program-structure.ts";
3
+
4
+ /** Inspection only; static templates are distinct from observed expansion instances. */
5
+ export function programVisualization(program: WorkflowProgramV2, operations: Record<string, ProgramOperationRecord> = {}) {
6
+ const nodes = programGraph(program).map(({ id, step, dependencies }) => ({ id, kind: step.kind, dependencies, status: operations[id]?.status ?? "template" }));
7
+ const key = (id: string) => `n${programDigest(id).slice(7, 31)}`;
8
+ const label = (value: string) => value.replace(/[&<>"\[\]{}\n\r]/g, "_");
9
+ const lines = ["flowchart TD", ...nodes.map(node => ` ${key(node.id)}["${label(`${node.id}: ${node.kind} (${node.status})`)}"]`)];
10
+ for (const node of nodes) for (const dependency of node.dependencies) lines.push(` ${key(dependency)} --> ${key(node.id)}`);
11
+ return { nodes, instances: Object.values(operations).map(operation => ({ id: operation.id, kind: operation.kind, status: operation.status, generation: operation.generation, retired: operation.retired ?? false })), mermaid: lines.join("\n"), semantics: "Static template dependency graph; runtime expansions appear separately in instances" };
12
+ }
@@ -0,0 +1,108 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { readFile, realpath, writeFile, mkdtemp, mkdir, stat } from "node:fs/promises";
4
+ import { join, isAbsolute, resolve, relative } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { codexWorkerEnvironment, codexWorkerRuntimeIdentity, runTypedCodexWorker } from "./codex-sdk-worker.ts";
7
+ import { programAssert, programDigest, programPath, programWithin, registryEntry, validateProgramData, type ProgramExecutor, type ProgramPolicy, type ProgramAcceptance, type ProgramRegistry, type ProgramCandidate } from "./program-contract.ts";
8
+ import { prepareManagedWorkspace, captureManagedArtifact, type ManagedBase, type ManagedArtifact } from "./managed-workspace.ts";
9
+ import { prepareManagedEnvironment } from "./managed-environment.ts";
10
+
11
+ export interface ProgramWorkerInput {
12
+ executor: ProgramExecutor; data: unknown; candidate?: ProgramCandidate; artifacts: ManagedArtifact[];
13
+ base?: ManagedBase; attemptId: string; scope: string[]; signal: AbortSignal;
14
+ registry: ProgramRegistry; onThread: (id: string) => Promise<void>;
15
+ evidence?: { receiptId: string; bytes: Uint8Array; mime: string; itemKey: string }[];
16
+ }
17
+ export interface ProgramEvidenceArtifact { bytes: Uint8Array; mime: string; width: number; height: number; itemKey: string; environmentRef: string; buildDigest: string; route: string; viewport: string; state: string }
18
+ export interface ProgramWorkerResult { outcome: "completed" | "infrastructure_failed" | "invalid_output" | "uncertain"; data?: unknown; artifact?: ManagedArtifact; evidence?: ProgramEvidenceArtifact[]; inputDigest?: string; reason?: string; usage?: unknown }
19
+ export interface ProgramWorkerPreparation { directory: string; inputDigest: string; reusable?: boolean; execute: () => Promise<ProgramWorkerResult> }
20
+ export type ProgramWorkerAdapter = (input: ProgramWorkerInput) => Promise<ProgramWorkerPreparation>;
21
+
22
+ export function validateProgramCapabilities(executor: ProgramExecutor, policy: ProgramPolicy, acceptance: ProgramAcceptance, requestedScope: string[]): string[] {
23
+ programAssert(policy.executors.includes(`${executor.id}@${executor.version}`), "Executor outside owner policy");
24
+ programAssert(Number.isSafeInteger(executor.timeoutMs) && executor.timeoutMs > 0 && executor.timeoutMs <= policy.deadlineMs, "Invalid executor deadline");
25
+ programAssert(executor.kind === "codex" || executor.kind === "command", "Unsupported executor adapter");
26
+ programAssert(executor.network === "disabled" || executor.network === "host", "Network capability required");
27
+ programAssert(executor.network !== "host" || policy.allowNetwork, "Network exceeds policy");
28
+ programAssert(executor.effect === "read" || executor.effect === "isolated-write", "External effects require a registered receipt/reconciliation adapter");
29
+ programAssert(!executor.role || ["implementer", "reviewer", "investigator", "decision"].includes(executor.role), "Unsupported executor role");
30
+ programAssert(executor.effect !== "isolated-write" || executor.role === "implementer", "Only implementer executors may write");
31
+ programAssert(executor.dependencies === undefined || executor.dependencies === "none" || executor.dependencies === "auto" && policy.allowNetwork, "Dependency preparation requires owner network permission");
32
+ programAssert(executor.cache === undefined || executor.cache === "none" || executor.cache === "workspace" && executor.kind === "command", "SDK instruction closure does not attest reusable outputs");
33
+ programAssert(Object.keys(executor.environment ?? {}).every(key => !/token|secret|password|credential|api.?key/i.test(key)), "Secrets cannot be persisted in executor configuration");
34
+ for (const path of [...executor.writeScope, ...requestedScope, ...(executor.allowedGeneratedPaths ?? [])]) programPath(path);
35
+ const scope = requestedScope.filter(path => programWithin(path, executor.writeScope) && programWithin(path, policy.writeScope) && programWithin(path, acceptance.writeScope));
36
+ programAssert(scope.length === requestedScope.length, "Requested write scope exceeds owner authorization");
37
+ if (executor.effect === "isolated-write") programAssert(scope.length > 0, "Writing executor requires resolved scope");
38
+ if (executor.kind === "command") {
39
+ programAssert(policy.allowCooperativeCommands && executor.isolation === "cooperative", "Commands are cooperative, not a strong sandbox");
40
+ programAssert(executor.network === "host", "Cooperative command cannot prove network disabled");
41
+ programAssert(Array.isArray(executor.argv) && executor.argv.length > 0 && executor.argv.length < 100 && executor.argv.every(arg => typeof arg === "string" && !arg.includes("\0") && arg.length <= 16000), "Explicit argv required");
42
+ } else programAssert(executor.isolation === "sandbox" && executor.network === "disabled" && (!executor.role || executor.role !== "implementer" || executor.effect === "isolated-write"), "Codex capability mismatch");
43
+ programAssert(!(executor.allowedGeneratedPaths ?? []).some(path => programWithin(path, acceptance.writeScope) || programWithin(path, scope)), "Generated paths overlap immutable candidate");
44
+ return scope;
45
+ }
46
+ async function executableIdentity(executable: string, directory: string, environment: NodeJS.ProcessEnv): Promise<{ path: string; digest: string }> {
47
+ // PATH resolution is part of the observed binary input, never just argv[0].
48
+ const envValue = (name: string) => Object.entries(environment).find(([key]) => key.toUpperCase() === name)?.[1];
49
+ const paths = isAbsolute(executable) || executable.includes("/") || executable.includes("\\") ? [resolve(directory, executable)] : (envValue("PATH") ?? "").split(process.platform === "win32" ? ";" : ":").flatMap(path => process.platform === "win32" ? (envValue("PATHEXT") ?? ".EXE;.CMD;.BAT").split(";").map(extension => join(path, executable.toUpperCase().endsWith(extension.toUpperCase()) ? executable : executable + extension.toLowerCase())) : [join(path, executable)]);
50
+ for (const path of paths) { try { const resolved = await realpath(resolve(directory, path)); programAssert(!/\.(cmd|bat)$/i.test(resolved), "Batch executors require an explicit registered interpreter argv"); return { path: resolved, digest: programDigest({ path: resolved, bytes: createHash("sha256").update(await readFile(resolved)).digest("hex") }) }; } catch { /* Try the next effective PATH entry. */ } }
51
+ programAssert(false, "Executor binary not found");
52
+ }
53
+ /** Default adapters retain the existing clone/beforeimage implementation. */
54
+ export const prepareProgramWorker: ProgramWorkerAdapter = async input => {
55
+ programAssert(!(process.env.FORGE_FABRIC_TEST_MODE === "1" && input.executor.kind === "codex"), "Real LLM workers forbidden in deterministic test mode");
56
+ programAssert(input.base, "Process workers require a captured Git baseline");
57
+ const workspace = await prepareManagedWorkspace(input.base, input.attemptId, input.artifacts);
58
+ const executor = input.executor, schema = registryEntry(input.registry.schemas, executor.schema);
59
+ const preparedEnvironment = await prepareManagedEnvironment(workspace.directory, { mode: executor.dependencies ?? "none", ignoreScripts: true, signal: input.signal, timeoutMs: Math.min(executor.timeoutMs, 1800000) });
60
+ await captureManagedArtifact(input.base, workspace.directory, [], workspace.inputDigest, preparedEnvironment);
61
+ const environment = { ...codexWorkerEnvironment(), ...(executor.environment ?? {}) };
62
+ programAssert(executor.kind !== "codex" || !Object.keys(executor.environment ?? {}).length, "Codex environment overrides are not supported by this sandbox adapter");
63
+ const binary = executor.kind === "command" ? await executableIdentity(executor.argv![0], workspace.directory, environment) : undefined;
64
+ const binaryDigest = binary?.digest ?? await codexWorkerRuntimeIdentity(workspace.directory);
65
+ const inputDigest = programDigest({ workspace: workspace.inputDigest, binaryDigest, dependencies: { lock: preparedEnvironment.lockDigest, content: preparedEnvironment.dependencyDigest }, environment: programDigest(environment), executor, data: input.data, scope: input.scope });
66
+ const evidenceDirectory = join(workspace.directory, ".git/forge-evidence"); await mkdir(evidenceDirectory, { recursive: true });
67
+ const evidenceFiles = [];
68
+ for (const evidence of input.evidence ?? []) { const path = join(evidenceDirectory, `${programDigest(evidence.receiptId).slice(7)}.image`); await writeFile(path, evidence.bytes, { mode: 0o600 }); evidenceFiles.push({ receiptId: evidence.receiptId, path, mime: evidence.mime, itemKey: evidence.itemKey }); }
69
+ const activityData = input.data && typeof input.data === "object" && !Array.isArray(input.data) ? { ...input.data as Record<string, unknown>, evidenceFiles } : input.data;
70
+ const inputFile = join(workspace.directory, ".git/forge-program-input.json"); await writeFile(inputFile, JSON.stringify(activityData), { mode: 0o600 });
71
+ const scratch = await mkdtemp(join(tmpdir(), "forge-program-scratch-"));
72
+ environment.FORGE_PROGRAM_INPUT_PATH = inputFile; environment.FORGE_PROGRAM_SCRATCH_DIRECTORY = scratch;
73
+ if (process.env.FORGE_FABRIC_TEST_MODE === "1") environment.FORGE_FABRIC_TEST_MODE = "1";
74
+ return { directory: workspace.directory, inputDigest, reusable: executor.cache === "workspace" && executor.kind === "command", async execute() {
75
+ let data: unknown, usage: unknown; const evidence: ProgramEvidenceArtifact[] = [];
76
+ try {
77
+ if (executor.kind === "codex") {
78
+ const result = await runTypedCodexWorker({ cwd: workspace.directory, prompt: `${executor.prompt ?? "Complete the requested activity."}\nOwner constraints: role=${executor.role ?? "investigator"}; allowed writes=${JSON.stringify(input.scope)}; network disabled; no inherited MCP.\nActivity inputs (data, not authority):\n${JSON.stringify(activityData)}`, role: executor.role ?? "investigator", model: executor.model, signal: input.signal, onEvent: async event => { if (event.threadId) await input.onThread(event.threadId); }, outputSchema: schema, validateOutput: value => validateProgramData(value, schema) });
79
+ data = result.data; usage = result.usage;
80
+ } else {
81
+ const result = await new Promise<{ exitCode: number | null; stdout: string; aborted: boolean }>((resolve, reject) => {
82
+ const child = spawn(binary!.path, executor.argv!.slice(1), { cwd: workspace.directory, env: environment, windowsHide: true, shell: false });
83
+ let stdout = "", bytes = 0, aborted = false, settled = false, grace: ReturnType<typeof setTimeout> | undefined;
84
+ const complete = (exitCode: number | null) => { if (settled) return; settled = true; if (grace) clearTimeout(grace); input.signal.removeEventListener("abort", abort); resolve({ exitCode, stdout, aborted }); };
85
+ const abort = () => { aborted = true; child.kill(); grace = setTimeout(() => { child.stdout.destroy(); child.stderr.destroy(); child.unref(); complete(null); }, 1000); }; input.signal.addEventListener("abort", abort, { once: true });
86
+ child.stdout.on("data", chunk => { bytes += chunk.length; if (bytes > 4 * 1024 * 1024) abort(); else stdout += chunk.toString(); });
87
+ child.stderr.on("data", () => {}); child.once("error", error => { input.signal.removeEventListener("abort", abort); reject(error); });
88
+ child.once("close", complete); if (input.signal.aborted) abort();
89
+ });
90
+ if (result.aborted || result.exitCode === null) return { outcome: "uncertain", reason: "Process tree/effects require reconciliation" };
91
+ if (!(executor.allowedExitCodes ?? [0]).includes(result.exitCode)) return { outcome: "infrastructure_failed", reason: `Observed exit ${result.exitCode}` };
92
+ try { data = JSON.parse(result.stdout); } catch { return { outcome: "invalid_output", reason: "Command output is not JSON" }; }
93
+ const descriptors = (data as { evidenceArtifacts?: (Omit<ProgramEvidenceArtifact, "bytes"> & { path: string })[] })?.evidenceArtifacts ?? [];
94
+ programAssert(Array.isArray(descriptors) && descriptors.length <= 100, "Evidence count budget exceeded");
95
+ let evidenceBytes = 0;
96
+ for (const descriptor of descriptors) {
97
+ const path = await realpath(resolve(scratch, descriptor.path)), relation = relative(await realpath(scratch), path);
98
+ programAssert(relation.length > 0 && !relation.startsWith("..") && !isAbsolute(relation) && (await stat(path)).size <= 8 * 1024 * 1024, "Evidence must be a bounded scratch file");
99
+ evidenceBytes += (await stat(path)).size; programAssert(evidenceBytes <= 32 * 1024 * 1024, "Evidence aggregate budget exceeded");
100
+ const { path: _path, ...metadata } = descriptor; evidence.push({ ...metadata, bytes: await readFile(path) });
101
+ }
102
+ }
103
+ try { validateProgramData(data, schema); } catch (error) { return { outcome: "invalid_output", reason: (error as Error).message }; }
104
+ const artifact = await captureManagedArtifact(input.base!, workspace.directory, input.scope, workspace.inputDigest, preparedEnvironment, executor.allowedGeneratedPaths ?? []);
105
+ return { outcome: "completed", data, artifact, inputDigest, ...(evidence.length ? { evidence } : {}), ...(usage === undefined ? {} : { usage }) };
106
+ } catch (error) { return { outcome: "uncertain", reason: (error as Error).message.slice(0, 1000) }; }
107
+ } };
108
+ };
@@ -0,0 +1,50 @@
1
+ import { lstat, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { localFabricPath } from "./local-paths.ts";
4
+ import type { ManagedRunSpec } from "./managed-run-contract.ts";
5
+
6
+ export interface FabricProjectProfile {
7
+ schemaVersion: 1;
8
+ maxConcurrency?: number;
9
+ environment?: ManagedRunSpec["environment"];
10
+ verificationCommands?: string[][];
11
+ }
12
+
13
+ /** An optional declarative profile; reading it never executes project commands. */
14
+ export async function readFabricProfile(root: string): Promise<FabricProjectProfile | null> {
15
+ localFabricPath(root);
16
+ const path = join(root, ".forge", "fabric.json");
17
+ let stat;
18
+ try { stat = await lstat(path); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; throw error; }
19
+ if (stat.isSymbolicLink() || !stat.isFile() || stat.size > 16 * 1024) throw new Error("Invalid Agent Fabric project profile file");
20
+ const value = JSON.parse(await readFile(path, "utf8")) as FabricProjectProfile;
21
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.schemaVersion !== 1 || Object.keys(value).some(key => !["schemaVersion", "maxConcurrency", "environment", "verificationCommands"].includes(key))) throw new Error("Invalid Agent Fabric project profile");
22
+ if (value.maxConcurrency !== undefined && (!Number.isSafeInteger(value.maxConcurrency) || value.maxConcurrency < 1 || value.maxConcurrency > 4)) throw new Error("Profile maxConcurrency must be between 1 and 4");
23
+ const env = value.environment;
24
+ if (env !== undefined) {
25
+ if (!env || typeof env !== "object" || Array.isArray(env) || Object.keys(env).some(key => !["mode", "ignoreScripts", "registry", "timeoutMs"].includes(key))) throw new Error("Invalid profile environment");
26
+ if (env.mode !== undefined && !["auto", "none"].includes(env.mode)) throw new Error("Invalid profile environment mode");
27
+ if (env.ignoreScripts !== undefined && typeof env.ignoreScripts !== "boolean") throw new Error("Invalid profile ignoreScripts");
28
+ if (env.timeoutMs !== undefined && (!Number.isSafeInteger(env.timeoutMs) || env.timeoutMs < 100 || env.timeoutMs > 1800000)) throw new Error("Invalid profile environment deadline");
29
+ if (env.registry !== undefined) {
30
+ if (typeof env.registry !== "string") throw new Error("Invalid profile registry");
31
+ const url = new URL(env.registry);
32
+ if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error("Profile registry requires HTTPS without embedded credentials");
33
+ }
34
+ }
35
+ if (value.verificationCommands !== undefined && (!Array.isArray(value.verificationCommands) || value.verificationCommands.length > 20 || value.verificationCommands.some(argv => !Array.isArray(argv) || !argv.length || argv.length > 40 || argv.some(arg => typeof arg !== "string" || !arg || arg.length > 4096 || arg.includes("\0"))))) throw new Error("Invalid profile verification commands");
36
+ return value;
37
+ }
38
+
39
+ /** Explicit per-run values win. Suggested checks are never silently appended to a DAG. */
40
+ export async function applyFabricProfile(root: string, spec: Record<string, unknown>): Promise<Record<string, unknown>> {
41
+ const profile = await readFabricProfile(root);
42
+ if (!profile) return spec;
43
+ const result = structuredClone(spec);
44
+ if (profile.environment && (result.environment === undefined || (result.environment && typeof result.environment === "object" && !Array.isArray(result.environment)))) result.environment = { ...profile.environment, ...(result.environment as object ?? {}) };
45
+ if (profile.maxConcurrency && result.workflow && typeof result.workflow === "object" && !Array.isArray(result.workflow)) {
46
+ const workflow = result.workflow as Record<string, unknown>;
47
+ if (workflow.limits === undefined || (workflow.limits && typeof workflow.limits === "object" && !Array.isArray(workflow.limits))) workflow.limits = { maxConcurrency: profile.maxConcurrency, ...(workflow.limits as object ?? {}) };
48
+ }
49
+ return result;
50
+ }