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,283 @@
1
+ import { createHash } from "node:crypto";
2
+ import { stableStringify } from "./canonical.ts";
3
+ import { programGraph } from "./program-structure.ts";
4
+ import { validateVisualPopulation } from "./program-evidence.ts";
5
+ import { validateProgramTypes } from "./program-types.ts";
6
+
7
+ export type ProgramValue = null | boolean | number | string | ProgramValue[] | { [key: string]: ProgramValue };
8
+ export interface ProgramRef { id: string; version: string }
9
+ export interface ProgramExpr { $expr: string; args: unknown[] }
10
+ export interface ProgramOperation { kind: string; id: string; options: Record<string, unknown> }
11
+ export interface WorkflowProgramV2 {
12
+ operatorVersion?: 2;
13
+ mode?: "data" | "candidate";
14
+ schemaVersion: 2; id: string; version: number; inputSchema: ProgramRef; outputSchema: ProgramRef;
15
+ acceptance: ProgramRef; population?: ProgramRef; policy: ProgramRef; steps: ProgramOperation[]; result: unknown;
16
+ }
17
+ export type ProgramSchema = { type?: string | string[]; enum?: ProgramValue[]; const?: ProgramValue;
18
+ properties?: Record<string, ProgramSchema>; required?: string[]; additionalProperties?: boolean;
19
+ items?: ProgramSchema; minItems?: number; maxItems?: number; minLength?: number; maxLength?: number;
20
+ minimum?: number; maximum?: number; anyOf?: ProgramSchema[] };
21
+ export interface ProgramLexicalContext { item?: ProgramSchema; state?: ProgramSchema }
22
+ export interface ProgramExecutor {
23
+ id: string; version: string; kind: "command" | "codex"; effect: "read" | "isolated-write" | "idempotent-effect" | "non-idempotent-effect";
24
+ argv?: string[]; prompt?: string; role?: "implementer" | "reviewer" | "investigator" | "decision";
25
+ model?: string; timeoutMs: number; writeScope: string[]; allowedGeneratedPaths?: string[];
26
+ network: "disabled" | "host"; isolation: "cooperative" | "sandbox"; schema: ProgramRef;
27
+ allowedExitCodes?: number[]; environment?: Record<string, string>;
28
+ inputSchema?: ProgramRef;
29
+ dependencies?: "none" | "auto";
30
+ cache?: "none" | "workspace";
31
+ }
32
+ export interface ProgramPolicy {
33
+ allowFencedReplan?: boolean;
34
+ captureScope?: string[];
35
+ id: string; version: string; maxItems: number; concurrency: number; maxAttempts: number;
36
+ maxOperations: number; maxDepth: number; deadlineMs: number; maxOutputBytes: number;
37
+ writeScope: string[]; executors: string[]; allowCooperativeCommands: boolean; allowNetwork: boolean;
38
+ }
39
+ export interface ProgramAcceptance {
40
+ id: string; version: string; criteria: string[]; writeScope: string[];
41
+ requiredChecks: string[]; requireReview: boolean; allowNoWork: boolean;
42
+ requiredChecksByScope?: { item: string[]; final: string[] };
43
+ assessmentBindings?: Record<string, "item" | "final">;
44
+ obligations?: { id: string; scope: "item" | "final"; criteria: string[]; requiredEvidence?: string[] }[];
45
+ allowPartial?: boolean;
46
+ requireHumanApproval?: boolean;
47
+ }
48
+ export interface ProgramVisualCase { itemKey: string; route: string; viewport: string; state: string; width: number; height: number }
49
+ export interface ProgramPopulation {
50
+ visualCases?: ProgramVisualCase[];
51
+ inventoryRoots?: string[]; extensions?: string[];
52
+ id: string; version: string; members: string[]; exclusions: { id: string; reason: string }[];
53
+ baselineDigest: string; evidence: string; allowNoWork: boolean;
54
+ }
55
+ export interface ProgramRegistry {
56
+ schemas: Record<string, ProgramSchema>; executors: Record<string, ProgramExecutor>;
57
+ policies: Record<string, ProgramPolicy>; acceptance: Record<string, ProgramAcceptance>;
58
+ populations: Record<string, ProgramPopulation>; programs?: Record<string, WorkflowProgramV2>;
59
+ }
60
+ export interface ProgramAssessment {
61
+ receiptId?: string; generation?: number; operationId?: string;
62
+ semanticDigest?: string; inputDigest?: string;
63
+ phase?: "item" | "final"; obligationIds?: string[]; satisfiedObligationIds?: string[]; unsatisfiedObligationIds?: string[];
64
+ environmentRef?: string; evidenceRefs?: string[]; contractDigest?: string;
65
+ candidateDigest: string; verdict: "approved" | "changes_requested" | "inconclusive";
66
+ findings: unknown[]; checks: { id: string; passed: boolean }[]; attemptIds: string[];
67
+ }
68
+ export interface ProgramCandidate {
69
+ candidateId: string; digest: string; baselineDigest: string; parents: string[];
70
+ deltas: string[]; producerAttempts: string[];
71
+ }
72
+ export type ProgramRepairResult =
73
+ | { status: "accepted"; acceptedCandidate: ProgramCandidate; assessment: ProgramAssessment }
74
+ | { status: "exhausted" | "stalled" | "uncertain" | "canceled"; lastCandidate: ProgramCandidate; reason: string };
75
+ export interface ProgramAttempt {
76
+ attemptId: string; operationId: string; generation: number; inputDigest: string;
77
+ executorDigest: string; startedAt: string; completedAt?: string;
78
+ outcome: "running" | "completed" | "infrastructure_failed" | "invalid_output" | "uncertain" | "canceled_confirmed";
79
+ outputRef?: string; threadId?: string; reason?: string;
80
+ reservation?: { scopes: { id: string; limit: number }[]; status: "held" | "released"; ownerEpoch: string };
81
+ usage?: unknown;
82
+ }
83
+ export interface ProgramOperationRecord {
84
+ kind?: string;
85
+ retired?: boolean;
86
+ dependencies?: string[];
87
+ id: string; semanticDigest: string; inputDigest: string; generation: number;
88
+ status: "declared" | "ready" | "running" | "waiting" | "completed" | "skipped" | "needs-attention" | "uncertain";
89
+ outputRef?: string; attempts: string[]; reason?: string; reusedFromGeneration?: number;
90
+ }
91
+ export interface ProgramSignal {
92
+ id: string; target: string; generation: number; type: string; correlation: string;
93
+ payloadRef: string; authorization?: string; subjectDigest?: string; expiresAt?: string; status: "pending" | "consumed" | "pending-after-apply" | "stale";
94
+ }
95
+ export interface ProgramApplyIntent {
96
+ requestId: string; requestDigest: string;
97
+ id: string; candidateDigest: string; gateRef: string; semanticVersion: number;
98
+ programDigest: string; acceptanceDigest: string; policyDigest: string; populationDigest: string;
99
+ sealRefs: string[]; authorization: string; expectedDigest: string; changedFiles: string[];
100
+ }
101
+ export interface ProgramRunV2 {
102
+ schemaVersion: 2; runId: string; requestId: string; requestDigest: string;
103
+ program: WorkflowProgramV2; registry: ProgramRegistry; programDigest: string; registryDigest: string;
104
+ inputRef: string; baselineDigest: string; baseRef?: string; createdAt: string; deadlineAt: string;
105
+ version: number; semanticVersion: number; revision: number; planRevision: number;
106
+ status: "executing" | "waiting" | "paused" | "needs-attention" | "completed" | "acceptance-ready" | "applying" | "applied" | "apply-uncertain" | "canceled";
107
+ operations: Record<string, ProgramOperationRecord>; attempts: Record<string, ProgramAttempt>;
108
+ seals: Record<string, string>; signals: ProgramSignal[]; candidates: Record<string, ProgramCandidate>;
109
+ assessments: Record<string, ProgramAssessment>; coverageReceipts: Record<string, { populationDigest: string; baselineDigest: string; ids: string[]; status: string }>;
110
+ deltas: Record<string, { artifactRef: string; inputCandidateDigest: string; outputCandidateDigest: string; producerAttemptId: string }>;
111
+ waits: Record<string, { generation: number; deadlineAt?: string; signalId?: string; outputRef?: string }>;
112
+ repairs: Record<string, { semanticDigest: string; inputDigest: string; candidate: ProgramCandidate; rounds: number; assessments: number; infrastructure: number; unchanged: number; repeated: number; priorFindings: string; feedback: unknown; phase: "implement" | "implementation-active" | "assess" | "assessment-active" | "decide" | "accepted" | "exhausted" | "stalled"; assessment?: ProgramAssessment }>;
113
+ collections: Record<string, { seal: string; coverageReceiptId: string | null; generation: number; ids: string[]; acceptedAssessmentIds: string[]; acceptedCandidateIds: string[]; obligationsSatisfied: boolean }>;
114
+ resultRef?: string; gateRef?: string; intent?: ProgramApplyIntent; receiptRef?: string;
115
+ acceptedCandidate?: ProgramCandidate;
116
+ scopeResults?: Record<string, { outcomes: { id: string; status: string; reason?: string }[]; status: "completed" | "waiting" | "failed"; resultRef?: string }>;
117
+ artifactRecords?: Record<string, ProgramCaptureReceipt>;
118
+ queue?: { id: string; generation: number; at: string }[];
119
+ totalAttempts: number; reason?: string;
120
+ }
121
+ export interface ProgramAssessmentContext {
122
+ visualCases?: ProgramVisualCase[];
123
+ runId: string; invocationId: string; assessmentId: string; phase: "item" | "final";
124
+ candidateRef: ProgramCandidate; obligationIds: string[]; itemKey?: string;
125
+ environmentRef: string; evidenceRefs: string[]; input: unknown;
126
+ }
127
+ export type ProgramCollectionItem = { key: string; outcome: "completed"; value: unknown } | { key: string; outcome: "failed" | "waiting"; reason?: string };
128
+ export interface ProgramCollectionResult {
129
+ items: ProgramCollectionItem[]; results: unknown[]; failures: { id: string; reason: string }[];
130
+ seal: string; coverage: unknown; status: "completed" | "partial" | "no-work";
131
+ }
132
+ export interface ProgramCaptureReceipt {
133
+ receiptId: string; artifactRef: string; candidateDigest: string; environmentRef: string;
134
+ itemKey: string; buildDigest: string; mime: string; width: number; height: number;
135
+ route: string; viewport: string; state: string; producerAttemptId: string;
136
+ }
137
+ export class ProgramError extends Error {
138
+ constructor(readonly code: string, message: string) { super(message); this.name = "ProgramError"; }
139
+ }
140
+ export function programAssert(condition: unknown, message: string, code = "AF_PROGRAM_INVALID"): asserts condition {
141
+ if (!condition) throw new ProgramError(code, message);
142
+ }
143
+ export function programDigest(value: unknown): string {
144
+ return `sha256:${createHash("sha256").update(stableStringify(value)).digest("hex")}`;
145
+ }
146
+ /** Count before cloning/expanding; serialization is not a memory guard. */
147
+ export function programValueBytes(value: unknown, maxBytes: number, maxItems = 10000): number {
148
+ let bytes = 0, nodes = 0;
149
+ const walk = (entry: unknown, depth: number): void => {
150
+ programAssert(depth <= 40 && ++nodes <= 1000000, "Value nesting/node budget exceeded");
151
+ if (typeof entry === "string") bytes += Buffer.byteLength(entry) + 2;
152
+ else if (entry === null || typeof entry === "number" || typeof entry === "boolean") bytes += 24;
153
+ else if (Array.isArray(entry)) { programAssert(entry.length <= maxItems, "Value item budget exceeded"); bytes += 2; for (const item of entry) walk(item, depth + 1); }
154
+ else if (entry && typeof entry === "object") { bytes += 2; for (const [key, child] of Object.entries(entry)) { bytes += Buffer.byteLength(key) + 4; walk(child, depth + 1); } }
155
+ else programAssert(false, "Non-data value rejected");
156
+ programAssert(bytes <= maxBytes, "Value byte budget exceeded");
157
+ };
158
+ walk(value, 0); return bytes;
159
+ }
160
+ export function refKey(ref: ProgramRef): string {
161
+ programAssert(ref && typeof ref.id === "string" && typeof ref.version === "string", "Versioned reference required");
162
+ return `${ref.id}@${ref.version}`;
163
+ }
164
+ export function registryEntry<T>(entries: Record<string, T>, ref: ProgramRef): T {
165
+ const key = refKey(ref); programAssert(Object.hasOwn(entries, key), `Unregistered reference ${key}`); return entries[key];
166
+ }
167
+ export function validateProgramData(value: unknown, schema: ProgramSchema, depth = 0): void {
168
+ programAssert(depth <= 32, "Schema/data depth exceeded");
169
+ if (schema.anyOf) {
170
+ programAssert(schema.anyOf.some(option => { try { validateProgramData(value, option, depth + 1); return true; } catch { return false; } }), "No schema alternative matched");
171
+ }
172
+ const type = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
173
+ if (schema.type) programAssert((Array.isArray(schema.type) ? schema.type : [schema.type]).some(expected => expected === type || expected === "integer" && typeof value === "number" && Number.isSafeInteger(value)), `Expected ${schema.type}, got ${type}`);
174
+ if (schema.enum) programAssert(schema.enum.some(entry => programDigest(entry) === programDigest(value)), "Value outside enum");
175
+ if (Object.hasOwn(schema, "const")) programAssert(programDigest(value) === programDigest(schema.const), "Const mismatch");
176
+ if (typeof value === "number") programAssert(Number.isFinite(value) && (schema.minimum === undefined || value >= schema.minimum) && (schema.maximum === undefined || value <= schema.maximum), "Number outside bounds");
177
+ if (typeof value === "string") programAssert((schema.minLength === undefined || value.length >= schema.minLength) && (schema.maxLength === undefined || value.length <= schema.maxLength), "String outside bounds");
178
+ if (Array.isArray(value)) {
179
+ programAssert((schema.minItems === undefined || value.length >= schema.minItems) && (schema.maxItems === undefined || value.length <= schema.maxItems), "Array outside bounds");
180
+ if (schema.items) for (const entry of value) validateProgramData(entry, schema.items, depth + 1);
181
+ } else if (value && typeof value === "object") {
182
+ const data = value as Record<string, unknown>;
183
+ for (const key of schema.required ?? []) programAssert(Object.hasOwn(data, key), `Missing field ${key}`);
184
+ for (const [key, entry] of Object.entries(data)) {
185
+ if (schema.properties && Object.hasOwn(schema.properties, key)) validateProgramData(entry, schema.properties[key], depth + 1);
186
+ else programAssert(schema.additionalProperties !== false, `Unexpected field ${key}`);
187
+ }
188
+ }
189
+ }
190
+ export function programPath(path: string): void {
191
+ programAssert(typeof path === "string" && path.length > 0 && path.length < 2048 && !path.includes("\\") && !path.includes("\0") && !path.startsWith("/") && !path.includes(":") && path.split("/").every(part => part !== ".." && part !== "." && part !== ""), "Invalid relative path");
192
+ programAssert(path !== ".git" && !path.startsWith(".git/") && !path.startsWith(".forge/local/"), "Protected path");
193
+ }
194
+ export function programWithin(path: string, scope: string[]): boolean { return scope.some(prefix => path === prefix || path.startsWith(`${prefix}/`)); }
195
+ export function validateWorkflowProgram(program: WorkflowProgramV2, registry: ProgramRegistry, ancestry: Set<string> = new Set(), lexical: ProgramLexicalContext = {}): void {
196
+ programAssert(program?.schemaVersion === 2 && typeof program.id === "string" && program.id.length > 0 && Number.isSafeInteger(program.version) && program.version > 0, "Program v2 required");
197
+ programAssert(program.operatorVersion === undefined || program.operatorVersion === 2, "Unsupported operator version");
198
+ programAssert(program.mode === undefined || ["data", "candidate"].includes(program.mode), "Invalid workflow mode");
199
+ programValueBytes(program, 4 * 1024 * 1024); programValueBytes(registry, 4 * 1024 * 1024);
200
+ const identity = programDigest(program); programAssert(!ancestry.has(identity), "Recursive subworkflow cycle"); ancestry = new Set([...ancestry, identity]); programAssert(ancestry.size <= 32, "Subworkflow depth exceeded");
201
+ const schemaKeys = new Set(["type", "enum", "const", "properties", "required", "additionalProperties", "items", "minItems", "maxItems", "minLength", "maxLength", "minimum", "maximum", "anyOf"]);
202
+ function validateSchema(schema: ProgramSchema, depth: number): void {
203
+ programAssert(schema && typeof schema === "object" && !Array.isArray(schema) && depth <= 32 && Object.keys(schema).every(key => schemaKeys.has(key)), "Unsupported/invalid schema keyword");
204
+ if (schema.type) programAssert((Array.isArray(schema.type) ? schema.type : [schema.type]).every(type => ["object", "array", "string", "number", "integer", "boolean", "null"].includes(type)), "Unsupported schema type");
205
+ if (schema.properties) for (const child of Object.values(schema.properties)) validateSchema(child, depth + 1);
206
+ if (schema.items) validateSchema(schema.items, depth + 1);
207
+ if (schema.anyOf) { programAssert(Array.isArray(schema.anyOf) && schema.anyOf.length > 0 && schema.anyOf.length <= 32, "Invalid schema alternatives"); for (const child of schema.anyOf) validateSchema(child, depth + 1); }
208
+ if (schema.required) programAssert(Array.isArray(schema.required) && schema.required.every(key => typeof key === "string"), "Invalid required fields");
209
+ }
210
+ for (const schema of Object.values(registry.schemas)) validateSchema(schema, 0);
211
+ registryEntry(registry.schemas, program.inputSchema); registryEntry(registry.schemas, program.outputSchema);
212
+ const policy = registryEntry(registry.policies, program.policy), acceptance = registryEntry(registry.acceptance, program.acceptance);
213
+ for (const field of ["maxItems", "concurrency", "maxAttempts", "maxOperations", "maxDepth", "deadlineMs", "maxOutputBytes"] as const) programAssert(Number.isSafeInteger(policy[field]) && policy[field] > 0, `Invalid limit ${field}`);
214
+ programAssert(policy.concurrency <= 32 && policy.maxDepth <= 32 && policy.maxItems <= 10000 && policy.maxAttempts <= 10000 && policy.maxOperations <= 20000 && policy.maxOutputBytes <= 4 * 1024 * 1024, "Physical limits exceeded");
215
+ for (const path of [...policy.writeScope, ...acceptance.writeScope, ...(policy.captureScope ?? [])]) programPath(path);
216
+ programAssert(acceptance.criteria.length > 0 && new Set(acceptance.criteria).size === acceptance.criteria.length, "Acceptance criteria required and unique");
217
+ if (program.population) { const population = registryEntry(registry.populations, program.population); programAssert(population.members.length <= policy.maxItems && new Set(population.members).size === population.members.length && population.evidence.length > 0, "Population inventory invalid"); validateVisualPopulation(population); }
218
+ const kinds = new Set(["agent", "command", "map", "branch", "loop", "compose", "gate", "repair", "subworkflow", "waitEvent", "value"]);
219
+ const expressions: Record<string, [number, number]> = { workflowInput: [0, 0], item: [0, 0], loopState: [0, 0], output: [1, 1], field: [2, 2], literal: [1, 1], object: [1, 1], array: [0, 10000], eq: [2, 2], and: [1, 100], or: [1, 100], not: [1, 1], concat: [1, 100], filter: [3, 3], unique: [2, 2], sort: [2, 2], take: [2, 2], length: [1, 1], population: [0, 0], acceptance: [0, 0], acceptedCandidate: [1, 1], acceptedCandidates: [1, 1], outputCandidate: [1, 1], candidateFromBaseline: [0, 1], coverageFor: [2, 2], coverageReceipt: [1, 1], acceptanceWriteScope: [1, 1], approvedChecksFor: [1, 1], acceptanceChecks: [1, 1] };
220
+ function inspect(value: unknown, resolve: (id: string) => string | undefined, dependencies: Set<string>, depth = 0): void {
221
+ programAssert(depth <= 32, "Expression nesting exceeded");
222
+ if (!value || typeof value !== "object") return;
223
+ if (Array.isArray(value)) { for (const entry of value) inspect(entry, resolve, dependencies, depth + 1); return; }
224
+ const object = value as Record<string, unknown>;
225
+ if (Object.hasOwn(object, "$expr")) {
226
+ const expression = object as unknown as ProgramExpr, limits = expressions[expression.$expr];
227
+ programAssert(limits && Array.isArray(expression.args) && expression.args.length >= limits[0] && expression.args.length <= limits[1] && Object.keys(object).every(key => key === "$expr" || key === "args"), "Unknown expression/arity");
228
+ if (["output", "acceptedCandidate", "acceptedCandidates", "outputCandidate", "coverageReceipt"].includes(expression.$expr)) { const target = resolve(String(expression.args[0])); programAssert(target, `Unknown output ${expression.args[0]}`); dependencies.add(target); }
229
+ if (expression.$expr !== "literal") for (const arg of expression.args) inspect(arg, resolve, dependencies, depth + 1); return;
230
+ }
231
+ for (const entry of Object.values(object)) inspect(entry, resolve, dependencies, depth + 1);
232
+ }
233
+ const options: Record<string, string[]> = {
234
+ value: ["value"], agent: ["executor", "input", "candidate", "writeScope"], command: ["executor", "input", "candidate", "writeScope"],
235
+ map: ["items", "key", "coverage", "concurrency", "completion", "quorum", "body", "order"],
236
+ branch: ["condition", "then", "else"], loop: ["initialState", "maxRounds", "body", "next", "until"],
237
+ repair: ["recipe", "entryMode", "initialCandidate", "writeScope", "implement", "review", "checks", "input", "assessmentScope", "evidence", "maxRepairRounds", "maxAssessmentAttempts", "maxInfrastructureAttempts", "progressPolicy"],
238
+ compose: ["candidates", "onConflict"], gate: ["candidate", "coverage", "authorization"], subworkflow: ["program", "input"],
239
+ waitEvent: ["schema", "type", "correlation", "subject", "timeoutMs"], sequence: ["steps", "result"], parallel: ["steps", "result", "onFailure"],
240
+ };
241
+ kinds.add("sequence"); kinds.add("parallel");
242
+ const entries = programGraph(program);
243
+ programAssert(entries.length <= policy.maxOperations, "Template limit exceeded");
244
+ for (const entry of entries) {
245
+ const step = entry.step;
246
+ programAssert(step && kinds.has(step.kind) && /^[A-Za-z][A-Za-z0-9_-]{0,79}$/.test(step.id) && step.options && typeof step.options === "object", "Invalid operation");
247
+ programAssert(Object.keys(step.options).every(key => [...options[step.kind], "after", "label"].includes(key)), `Unknown ${step.kind} option`);
248
+ programAssert(entries.filter(other => other.id === entry.id).length === 1, "Duplicate operation ID");
249
+ inspect(Object.fromEntries(Object.entries(step.options).filter(([key]) => !["body", "steps", "result", "then", "else", "next", "until"].includes(key))), id => id, new Set());
250
+ programAssert(entry.id.split("/").length <= policy.maxDepth * 3 + 1, "Template depth exceeded");
251
+ if (step.kind === "map") {
252
+ programAssert(["all-required", "partial", "quorum"].includes(String(step.options.completion)), "Explicit map completion required");
253
+ programAssert(step.options.order === undefined || ["key", "input"].includes(String(step.options.order)), "Invalid map order");
254
+ if (step.options.completion !== "all-required") programAssert(acceptance.allowPartial, "Partial/quorum requires owner acceptance permission");
255
+ if (step.options.completion === "quorum") programAssert(step.options.quorum && typeof step.options.quorum === "object" && Number.isSafeInteger((step.options.quorum as { minAccepted: number }).minAccepted) && (step.options.quorum as { minAccepted: number }).minAccepted > 0, "Quorum minAccepted required");
256
+ else programAssert(step.options.quorum === undefined, "Quorum only applies to quorum completion");
257
+ }
258
+ if (["agent", "command"].includes(step.kind)) { const executor = registryEntry(registry.executors, step.options.executor as ProgramRef); if (executor.inputSchema) registryEntry(registry.schemas, executor.inputSchema); }
259
+ if (step.kind === "repair") {
260
+ registryEntry(registry.executors, step.options.implement as ProgramRef); registryEntry(registry.executors, step.options.review as ProgramRef);
261
+ programAssert(["implement-first", "assess-first"].includes(String(step.options.entryMode)), "Repair entryMode required");
262
+ programAssert((step.options.recipe as ProgramRef)?.id === "repair" && (step.options.recipe as ProgramRef)?.version === "v2", "Unsupported repair recipe");
263
+ const phase = step.options.assessmentScope as "item" | "final" | undefined;
264
+ programAssert(phase === undefined || phase === "item" || phase === "final", "Invalid assessment scope");
265
+ if (acceptance.requiredChecksByScope) {
266
+ programAssert(phase && acceptance.assessmentBindings?.[step.id] === phase, "Assessment scope must match owner operation binding");
267
+ if (phase === "item") programAssert(lexical.item || entries.some(ancestor => ancestor.step.kind === "map" && entry.id.startsWith(`${ancestor.id}/body/`)), "Item assessment requires a map context");
268
+ }
269
+ const required = phase && acceptance.requiredChecksByScope ? acceptance.requiredChecksByScope[phase] : acceptance.requiredChecks;
270
+ programAssert(Array.isArray(step.options.checks) && required.every(check => (step.options.checks as unknown[]).includes(check)), "Repair cannot omit required checks");
271
+ for (const ref of (step.options.evidence ?? []) as ProgramRef[]) programAssert(registryEntry(registry.executors, ref).effect === "read", "Evidence executor must be readonly");
272
+ }
273
+ if (step.kind === "parallel") programAssert(["collect-all", "cancel-siblings"].includes(String(step.options.onFailure)), "Parallel failure policy required");
274
+ if (step.kind === "compose") programAssert(step.options.onConflict === undefined || step.options.onConflict === "needs-resolution", "Unsupported conflict policy");
275
+ if (step.kind === "waitEvent") registryEntry(registry.schemas, step.options.schema as ProgramRef);
276
+ if (step.kind === "subworkflow") { const child = registryEntry(registry.programs ?? {}, step.options.program as ProgramRef); programAssert(programDigest(child.policy) === programDigest(program.policy) && programDigest(child.acceptance) === programDigest(program.acceptance), "Child cannot replace inherited policy/acceptance"); programAssert(!child.population || program.population && programDigest(child.population) === programDigest(program.population), "Child cannot replace inherited population"); validateWorkflowProgram(child, registry, ancestry, { ...lexical, ...(entries.some(ancestor => ancestor.step.kind === "map" && entry.id.startsWith(`${ancestor.id}/body/`)) ? { item: {} } : {}), ...(entries.some(ancestor => ancestor.step.kind === "loop" && entry.id.startsWith(`${ancestor.id}/body/`)) ? { state: {} } : {}) }); }
277
+ }
278
+ programAssert(Object.hasOwn(program, "result"), "Explicit program result required");
279
+ inspect(program.result, id => program.steps.some(step => step.id === id) ? id : undefined, new Set());
280
+ if (acceptance.requiredChecksByScope) programAssert(acceptance.obligations?.length && new Set(acceptance.obligations.map(obligation => obligation.id)).size === acceptance.obligations.length, "Scoped acceptance requires unique owner obligations");
281
+ validateProgramTypes(program, registry, lexical);
282
+ programDigest(program); programDigest(registry);
283
+ }
@@ -0,0 +1,126 @@
1
+ import type { WorkflowRef, WorkflowOptions, WorkflowOperation, WorkflowDefinition, WorkflowExpression, WorkflowValue, WorkflowResolved, WorkflowActivityOptions, WorkflowBody, WorkflowCollection } from "./program-api.ts";
2
+ export type { WorkflowRef, WorkflowOptions, WorkflowOperation, WorkflowDefinition, WorkflowExpression, WorkflowValue, WorkflowResolved, WorkflowActivityOptions, WorkflowBody, WorkflowCollection } from "./program-api.ts";
3
+ import ts from "typescript";
4
+ import { programAssert, programValueBytes, type ProgramExpr, type ProgramOperation, type WorkflowProgramV2 } from "./program-contract.ts";
5
+
6
+ export const PROGRAM_DSL_VERSION = "forge-workflow-dsl/2";
7
+ export const expressionNames = ["workflowInput", "item", "loopState", "output", "field", "literal", "object", "array", "eq", "and", "or", "not", "concat", "filter", "unique", "sort", "take", "length", "population", "acceptance", "acceptedCandidate", "acceptedCandidates", "outputCandidate", "candidateFromBaseline", "coverageFor", "coverageReceipt", "acceptanceWriteScope", "approvedChecksFor", "acceptanceChecks"];
8
+ export const referenceNames = ["schemaRef", "executorRef", "policyRef", "acceptanceRef", "populationRef", "recipeRef", "programRef"];
9
+ export const operationNames = ["agent", "command", "map", "branch", "loop", "compose", "gate", "repair", "subworkflow", "waitEvent", "value", "sequence", "parallel"];
10
+ export function workflowExpression(name: string, ...args: unknown[]): ProgramExpr { return { $expr: name, args }; }
11
+ export function workflowOperation(kind: string, id: string, options: Record<string, unknown>): ProgramOperation { return { kind, id, options }; }
12
+ export function schemaRef<T = unknown>(id: string, version: string): WorkflowRef<"schema", unknown, T> { return { id, version } as WorkflowRef<"schema", unknown, T>; }
13
+ export const executorRef = <Input = unknown, Output = unknown>(id: string, version: string) => ({ id, version } as WorkflowRef<"executor", Input, Output>);
14
+ export const policyRef = (id: string, version: string) => ({ id, version } as WorkflowRef<"policy">);
15
+ export const acceptanceRef = (id: string, version: string) => ({ id, version } as WorkflowRef<"acceptance">);
16
+ export const populationRef = (id: string, version: string) => ({ id, version } as WorkflowRef<"population">);
17
+ export const recipeRef = (id: string, version: string) => ({ id, version } as WorkflowRef<"recipe">);
18
+ export const programRef = <Input = unknown, Output = unknown>(id: string, version: string) => ({ id, version } as WorkflowRef<"program", Input, Output>);
19
+ export const workflowInput = <T = unknown>() => workflowExpression("workflowInput") as WorkflowExpression<T>;
20
+ export const item = <T = unknown>() => workflowExpression("item") as WorkflowExpression<T>;
21
+ export const loopState = <T = unknown>() => workflowExpression("loopState") as WorkflowExpression<T>;
22
+ export function output<T>(operation: WorkflowOperation<keyof WorkflowOptions, T>): WorkflowExpression<T>;
23
+ export function output(id: string): WorkflowExpression;
24
+ export function output(operation: string | WorkflowOperation): WorkflowExpression { return workflowExpression("output", typeof operation === "string" ? operation : operation.id); }
25
+ export function field<T, K extends (unknown extends T ? string : keyof T & string)>(value: WorkflowExpression<T>, key: K): WorkflowExpression<unknown extends T ? unknown : T[K & keyof T]>;
26
+ export function field<T, K extends keyof T & string>(value: T, key: K): WorkflowExpression<T[K]>;
27
+ export function field(value: unknown, key: string): WorkflowExpression { return workflowExpression("field", value, key); }
28
+ export const literal = <T>(value: T) => workflowExpression("literal", value) as WorkflowExpression<T>;
29
+ export const object = <T extends Record<string, unknown>>(value: T) => workflowExpression("object", value) as WorkflowExpression<WorkflowResolved<T>>;
30
+ export const array = <T extends unknown[]>(...values: T) => workflowExpression("array", ...values) as WorkflowExpression<WorkflowResolved<T>>;
31
+ export const eq = (left: unknown, right: unknown) => workflowExpression("eq", left, right);
32
+ export const and = (...values: unknown[]) => workflowExpression("and", ...values);
33
+ export const or = (...values: unknown[]) => workflowExpression("or", ...values);
34
+ export const not = (value: unknown) => workflowExpression("not", value);
35
+ export const concat = (...values: unknown[]) => workflowExpression("concat", ...values);
36
+ export const filter = (values: unknown, key: string, equals: unknown) => workflowExpression("filter", values, key, equals);
37
+ export const unique = (values: unknown, key: string) => workflowExpression("unique", values, key);
38
+ export const sort = (values: unknown, key: string) => workflowExpression("sort", values, key);
39
+ export const take = (values: unknown, count: number) => workflowExpression("take", values, count);
40
+ export const length = (value: unknown) => workflowExpression("length", value);
41
+ export const population = () => workflowExpression("population");
42
+ export const acceptance = () => workflowExpression("acceptance");
43
+ export const acceptedCandidate = (id: string) => workflowExpression("acceptedCandidate", id);
44
+ export const acceptedCandidates = (id: string) => workflowExpression("acceptedCandidates", id);
45
+ export const outputCandidate = (id: string) => workflowExpression("outputCandidate", id);
46
+ export const candidateFromBaseline = (entry?: unknown) => entry === undefined ? workflowExpression("candidateFromBaseline") : workflowExpression("candidateFromBaseline", entry);
47
+ export const coverageFor = (contract: unknown, discovered: unknown) => workflowExpression("coverageFor", contract, discovered);
48
+ export const coverageReceipt = (id: string) => workflowExpression("coverageReceipt", id);
49
+ export const acceptanceWriteScope = (id: string) => workflowExpression("acceptanceWriteScope", id);
50
+ export const approvedChecksFor = (entry: unknown) => workflowExpression("approvedChecksFor", entry);
51
+ export const acceptanceChecks = (id: string) => workflowExpression("acceptanceChecks", id);
52
+ export const agent = <Input = unknown, Output = unknown>(id: string, options: WorkflowActivityOptions<Input, Output>) => workflowOperation("agent", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"agent", Output>;
53
+ export const command = <Input = unknown, Output = unknown>(id: string, options: WorkflowActivityOptions<Input, Output>) => workflowOperation("command", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"command", Output>;
54
+ export const map = <T = unknown>(id: string, options: Omit<WorkflowOptions["map"], "body"> & { body: WorkflowBody<T> }) => workflowOperation("map", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"map", WorkflowCollection<T>>;
55
+ export const branch = (id: string, options: WorkflowOptions["branch"]) => workflowOperation("branch", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"branch">;
56
+ export const loop = (id: string, options: WorkflowOptions["loop"]) => workflowOperation("loop", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"loop">;
57
+ export const compose = (id: string, options: WorkflowOptions["compose"]) => workflowOperation("compose", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"compose">;
58
+ export const gate = (id: string, options: WorkflowOptions["gate"]) => workflowOperation("gate", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"gate">;
59
+ export const repair = (id: string, options: WorkflowOptions["repair"]) => workflowOperation("repair", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"repair">;
60
+ export const subworkflow = <Input = unknown, Output = unknown>(id: string, options: Omit<WorkflowOptions["subworkflow"], "program" | "input"> & { program: WorkflowRef<"program", Input, Output>; input: WorkflowValue<NoInfer<Input>> }) => workflowOperation("subworkflow", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"subworkflow", Output>;
61
+ export const waitEvent = <T = unknown>(id: string, options: Omit<WorkflowOptions["waitEvent"], "schema"> & { schema: WorkflowRef<"schema", unknown, T> }) => workflowOperation("waitEvent", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"waitEvent", T>;
62
+ export const value = <T>(id: string, options: Omit<WorkflowOptions["value"], "value"> & { value: T }) => workflowOperation("value", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"value", WorkflowResolved<T>>;
63
+ export function defineWorkflow(program: WorkflowDefinition): WorkflowProgramV2 { return { schemaVersion: 2, operatorVersion: 2, ...program }; }
64
+
65
+ /** Parse a finite AST. Never transpile, import, evaluate, or call authored JavaScript. */
66
+ export const sequence = (id: string, options: WorkflowOptions["sequence"]) => workflowOperation("sequence", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"sequence">;
67
+ export const parallel = (id: string, options: WorkflowOptions["parallel"]) => workflowOperation("parallel", id, options as unknown as Record<string, unknown>) as WorkflowOperation<"parallel">;
68
+ export function lowerWorkflowSource(source: string): WorkflowProgramV2 {
69
+ programAssert(Buffer.byteLength(source) <= 256 * 1024, "DSL source too large");
70
+ const file = ts.createSourceFile("workflow.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
71
+ programAssert((file as ts.SourceFile & { parseDiagnostics: readonly ts.Diagnostic[] }).parseDiagnostics.length === 0, "Malformed workflow source");
72
+ const bindings = new Map<string, unknown>(), imports = new Map<string, string>(); let program: WorkflowProgramV2 | undefined, nodes = 0, bindingBytes = 0;
73
+ function evaluateList(entries: readonly ts.Expression[], depth: number): unknown[] { let bytes = 0; const values: unknown[] = []; for (const entry of entries) { const value = evaluate(entry, depth); bytes += programValueBytes(value, 4 * 1024 * 1024); programAssert(bytes <= 4 * 1024 * 1024, "DSL expansion byte budget exceeded"); values.push(value); } return values; }
74
+ function evaluate(node: ts.Expression, depth = 0): unknown {
75
+ programAssert(++nodes <= 100000 && depth <= 32, "DSL AST budget exceeded");
76
+ const next = (expression: ts.Expression) => evaluate(expression, depth + 1);
77
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
78
+ if (ts.isNumericLiteral(node)) return Number(node.text);
79
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
80
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
81
+ if (node.kind === ts.SyntaxKind.NullKeyword) return null;
82
+ if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(node.operand)) return -Number(node.operand.text);
83
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) return next(node.expression);
84
+ if (ts.isIdentifier(node)) { programAssert(bindings.has(node.text), `Unknown binding ${node.text}`); programValueBytes(bindings.get(node.text), 4 * 1024 * 1024); return structuredClone(bindings.get(node.text)); }
85
+ if (ts.isArrayLiteralExpression(node)) { programAssert(node.elements.every(element => !ts.isSpreadElement(element)), "Spread forbidden"); return evaluateList(node.elements, depth + 1); }
86
+ if (ts.isObjectLiteralExpression(node)) {
87
+ const object: Record<string, unknown> = Object.create(null), explicit = new Set<string>(); let bytes = 0;
88
+ for (const property of node.properties) {
89
+ if (ts.isSpreadAssignment(property)) {
90
+ programAssert(ts.isIdentifier(property.expression) && bindings.has(property.expression.text), "Only finite const data object spreads allowed");
91
+ const spread = bindings.get(property.expression.text); programAssert(spread && typeof spread === "object" && !Array.isArray(spread) && !Object.hasOwn(spread, "$expr"), "Object spread requires const data");
92
+ bytes += programValueBytes(spread, 4 * 1024 * 1024); programAssert(bytes <= 4 * 1024 * 1024, "DSL expansion byte budget exceeded"); Object.assign(object, structuredClone(spread)); continue;
93
+ }
94
+ programAssert(ts.isPropertyAssignment(property) && (ts.isIdentifier(property.name) || ts.isStringLiteral(property.name)), "Only literal data properties allowed");
95
+ const key = property.name.text; programAssert(!explicit.has(key) && !["__proto__", "prototype", "constructor"].includes(key), "Duplicate/protected DSL key"); explicit.add(key); const value = next(property.initializer); bytes += Buffer.byteLength(key) + programValueBytes(value, 4 * 1024 * 1024); programAssert(bytes <= 4 * 1024 * 1024, "DSL expansion byte budget exceeded"); object[key] = value;
96
+ }
97
+ return object;
98
+ }
99
+ if (ts.isCallExpression(node)) {
100
+ programAssert(ts.isIdentifier(node.expression), "Only DSL constructors allowed");
101
+ const name = imports.get(node.expression.text); programAssert(name, `Unimported constructor ${node.expression.text}`);
102
+ const args = evaluateList(node.arguments, depth + 1);
103
+ if (expressionNames.includes(name)) { if (name === "output" && args[0] && typeof args[0] === "object" && "kind" in args[0]) args[0] = (args[0] as ProgramOperation).id; return { $expr: name, args }; }
104
+ if (referenceNames.includes(name)) { programAssert(args.length === 2 && args.every(arg => typeof arg === "string"), "Versioned registry reference required"); return { id: args[0], version: args[1] }; }
105
+ if (operationNames.includes(name)) { programAssert(args.length === 2 && typeof args[0] === "string" && args[1] && typeof args[1] === "object", "Operation ID and options required"); return { kind: name, id: args[0], options: args[1] }; }
106
+ programAssert(name === "defineWorkflow" && args.length === 1 && args[0] && typeof args[0] === "object", "Invalid workflow constructor"); return { schemaVersion: 2, operatorVersion: 2, ...args[0] };
107
+ }
108
+ programAssert(false, `Unsupported authored syntax ${ts.SyntaxKind[node.kind]}`);
109
+ }
110
+ for (const statement of file.statements) {
111
+ if (ts.isImportDeclaration(statement)) {
112
+ programAssert(ts.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === "forgeos/agent-fabric/workflows" && statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings), "Only versioned Forge DSL named imports allowed");
113
+ for (const element of statement.importClause.namedBindings.elements) {
114
+ const name = element.propertyName?.text ?? element.name.text;
115
+ if (statement.importClause.isTypeOnly || element.isTypeOnly) { programAssert(["WorkflowOptions", "WorkflowRef", "WorkflowOperation", "WorkflowDefinition", "WorkflowExpression", "WorkflowValue", "WorkflowResolved", "WorkflowActivityOptions", "WorkflowBody", "WorkflowCollection"].includes(name), "Unknown DSL author type"); continue; }
116
+ programAssert(["defineWorkflow", ...expressionNames, ...referenceNames, ...operationNames].includes(name) && !imports.has(element.name.text), "Unknown/duplicate DSL import"); imports.set(element.name.text, name);
117
+ }
118
+ } else if (ts.isVariableStatement(statement)) {
119
+ programAssert((statement.declarationList.flags & ts.NodeFlags.Const) !== 0, "Only const bindings allowed");
120
+ for (const declaration of statement.declarationList.declarations) { programAssert(ts.isIdentifier(declaration.name) && declaration.initializer && !bindings.has(declaration.name.text) && !imports.has(declaration.name.text), "Invalid const declaration"); const value = evaluate(declaration.initializer); bindingBytes += programValueBytes(value, 4 * 1024 * 1024); programAssert(bindingBytes <= 4 * 1024 * 1024, "DSL cumulative binding budget exceeded"); bindings.set(declaration.name.text, value); }
121
+ } else if (ts.isExportAssignment(statement)) {
122
+ programAssert(!program && !statement.isExportEquals, "Only one export default workflow allowed"); program = evaluate(statement.expression) as WorkflowProgramV2;
123
+ } else programAssert(false, "Only DSL imports, const data bindings, and export default allowed");
124
+ }
125
+ programAssert(program?.schemaVersion === 2, "export default defineWorkflow required"); programValueBytes(program, 4 * 1024 * 1024); return program;
126
+ }
@@ -0,0 +1,22 @@
1
+ import { programAssert, programDigest, type ProgramAcceptance, type ProgramCaptureReceipt, type ProgramPopulation, type ProgramVisualCase } from "./program-contract.ts";
2
+ import { canonicalItemKey } from "./program-structure.ts";
3
+
4
+ export function validateVisualPopulation(population: ProgramPopulation): void {
5
+ if (!population.visualCases) return;
6
+ const keys = population.members.map(canonicalItemKey), cases = population.visualCases;
7
+ programAssert(cases.length === keys.length && new Set(cases.map(entry => canonicalItemKey(entry.itemKey))).size === keys.length && cases.every(entry => keys.includes(canonicalItemKey(entry.itemKey))), "Visual cases must cover exactly owner population");
8
+ const tuples = cases.map(entry => programDigest([entry.route, entry.viewport, entry.state]));
9
+ programAssert(new Set(tuples).size === tuples.length, "Duplicate visual route/viewport/state case");
10
+ for (const entry of cases) programAssert([entry.route, entry.viewport, entry.state].every(value => typeof value === "string" && value.length > 0 && value.length <= 2048) && [entry.width, entry.height].every(value => Number.isSafeInteger(value) && value > 0 && value <= 16384) && entry.width * entry.height <= 64 * 1024 * 1024, "Invalid expected visual case");
11
+ }
12
+ export function captureKeys(acceptance: ProgramAcceptance, phase: "item" | "final", members: string[], itemKey?: string): string[] {
13
+ const required = (acceptance.obligations ?? []).some(obligation => (phase === "final" || obligation.scope === "item") && obligation.requiredEvidence?.includes("capture"));
14
+ if (!required) return [];
15
+ programAssert(members.length > 0 && (phase !== "item" || itemKey && members.includes(itemKey)), "Capture obligations require an owner population");
16
+ return phase === "item" ? [itemKey!] : members;
17
+ }
18
+ export function validateVisualCapture(capture: Pick<ProgramCaptureReceipt, "itemKey" | "route" | "viewport" | "state" | "width" | "height">, cases?: ProgramVisualCase[]): void {
19
+ if (!cases) return;
20
+ const expected = cases.find(entry => canonicalItemKey(entry.itemKey) === canonicalItemKey(capture.itemKey));
21
+ programAssert(expected && ["route", "viewport", "state", "width", "height"].every(key => expected[key as keyof ProgramVisualCase] === capture[key as keyof typeof capture]), "Capture does not match owner visual case");
22
+ }
@@ -0,0 +1,15 @@
1
+ import { programAssert } from "./program-contract.ts";
2
+
3
+ /** Bounded image header validation. Pixel decoding/render acceptance belongs to the registered checker. */
4
+ export function programImage(bytes: Uint8Array): { mime: string; width: number; height: number } {
5
+ const data = Buffer.from(bytes); programAssert(data.length >= 24 && data.length <= 8 * 1024 * 1024, "Invalid image byte size");
6
+ let mime = "", width = 0, height = 0;
7
+ if (data.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10])) && data.toString("ascii",12,16) === "IHDR") { mime = "image/png"; width = data.readUInt32BE(16); height = data.readUInt32BE(20); }
8
+ else if (data[0] === 255 && data[1] === 216) {
9
+ mime = "image/jpeg"; let offset = 2;
10
+ while (offset + 9 < data.length) { if (data[offset++] !== 255) break; const marker = data[offset++], size = data.readUInt16BE(offset); if ([192,193,194].includes(marker)) { height = data.readUInt16BE(offset+3); width = data.readUInt16BE(offset+5); break; } if (size < 2) break; offset += size; }
11
+ } else if (data.toString("ascii",0,4) === "RIFF" && data.toString("ascii",8,12) === "WEBP" && data.toString("ascii",12,16) === "VP8X" && data.length >= 30) {
12
+ mime = "image/webp"; width = 1 + data.readUIntLE(24,3); height = 1 + data.readUIntLE(27,3);
13
+ }
14
+ programAssert(width > 0 && height > 0 && width <= 16384 && height <= 16384 && width * height <= 64 * 1024 * 1024, "Unsupported/invalid image header or pixel budget"); return { mime, width, height };
15
+ }
@@ -0,0 +1,50 @@
1
+ import { link, open, readFile, unlink } from "node:fs/promises";
2
+ import { readFileSync, unlinkSync } from "node:fs";
3
+ import { randomUUID } from "node:crypto";
4
+ import { programAssert } from "./program-contract.ts";
5
+
6
+ const code = (error: unknown) => (error as NodeJS.ErrnoException).code;
7
+ const transient = (error: unknown) => ["ENOENT", "EEXIST", "EPERM", "EACCES", "EBUSY"].includes(code(error) ?? "");
8
+ function dead(bytes: string): boolean {
9
+ const holder = JSON.parse(bytes) as { pid: number };
10
+ if (!Number.isSafeInteger(holder.pid) || holder.pid <= 0) return false;
11
+ try { process.kill(holder.pid, 0); return false; } catch (error) { return code(error) === "ESRCH"; }
12
+ }
13
+ /** Shared local hardlink protocol. Only a confirmed dead holder may be reclaimed. */
14
+ export class ProgramFileLease {
15
+ private constructor(readonly path: string, readonly token: string, private bytes: string, private lostMessage: string) {}
16
+ static async acquire(path: string, options: { waitMs?: number; busyCode?: string; busyMessage?: string; lostMessage?: string } = {}): Promise<ProgramFileLease> {
17
+ const token = randomUUID(), bytes = JSON.stringify({ pid: process.pid, token });
18
+ const candidate = `${path}.${token}.candidate`, reclaim = `${path}.reclaim`, deadline = Date.now() + (options.waitMs ?? 0);
19
+ const handle = await open(candidate, "wx", 0o600);
20
+ try { await handle.writeFile(bytes); await handle.sync(); } finally { await handle.close(); }
21
+ try {
22
+ for (;;) {
23
+ try { await link(candidate, path); return new ProgramFileLease(path, token, bytes, options.lostMessage ?? "Program lock ownership changed"); }
24
+ catch (error) { if (!transient(error)) throw error; }
25
+ let reclaimed = false;
26
+ try {
27
+ const old = await readFile(path, "utf8");
28
+ if (dead(old)) {
29
+ try { const guard = readFileSync(reclaim, "utf8"); if (dead(guard) && readFileSync(reclaim, "utf8") === guard) unlinkSync(reclaim); }
30
+ catch (error) { if (!transient(error) && !(error instanceof SyntaxError)) throw error; }
31
+ let claimed = false;
32
+ try {
33
+ await link(candidate, reclaim); claimed = true;
34
+ if (readFileSync(path, "utf8") === old) { unlinkSync(path); reclaimed = true; }
35
+ } finally {
36
+ if (claimed) { programAssert(readFileSync(reclaim, "utf8") === bytes, "Reclaim identity changed"); unlinkSync(reclaim); }
37
+ }
38
+ }
39
+ } catch (error) { if (!transient(error) && !(error instanceof SyntaxError)) throw error; }
40
+ if (reclaimed) continue;
41
+ programAssert(Date.now() < deadline, options.busyMessage ?? "Program lock busy", options.busyCode ?? "AF_PROGRAM_CONFLICT");
42
+ await new Promise(resolve => setTimeout(resolve, 20));
43
+ }
44
+ } finally { await unlink(candidate).catch(() => {}); }
45
+ }
46
+ async assert(): Promise<void> { programAssert(await readFile(this.path, "utf8") === this.bytes, this.lostMessage); }
47
+ async close(): Promise<void> {
48
+ programAssert(readFileSync(this.path, "utf8") === this.bytes, this.lostMessage); unlinkSync(this.path);
49
+ }
50
+ }
@@ -0,0 +1,16 @@
1
+ import { join } from "node:path";
2
+ import { ProgramFileLease } from "./program-lock.ts";
3
+
4
+ /** Owner and transactions use the same crash-recoverable admission protocol. */
5
+ export class ProgramOwnerLease {
6
+ private constructor(private lease: ProgramFileLease) {}
7
+ get path(): string { return this.lease.path; }
8
+ get epoch(): string { return this.lease.token; }
9
+ static async acquire(directory: string): Promise<ProgramOwnerLease> {
10
+ return new ProgramOwnerLease(await ProgramFileLease.acquire(join(directory, "owner.lock"), {
11
+ busyCode: "AF_PROGRAM_OWNER_BUSY", busyMessage: "Another program owner is active or reclaiming", lostMessage: "Owner admission lease lost",
12
+ }));
13
+ }
14
+ async assert(): Promise<void> { await this.lease.assert(); }
15
+ async close(): Promise<void> { await this.lease.close(); }
16
+ }
@@ -0,0 +1,38 @@
1
+ import { programAssert } from "./program-contract.ts";
2
+
3
+ export interface ActivityScope { id: string; limit: number }
4
+ interface Ticket { runId: string; runLimit: number; scopes: ActivityScope[]; resolve: (release: () => void) => void; reject: (error: Error) => void; signal: AbortSignal; abort: () => void }
5
+ /** Round robin between eligible runs; parent controls never hold activity capacity. */
6
+ export class ProgramActivityScheduler {
7
+ private tickets: Ticket[] = [];
8
+ private active = new Map<string, number>();
9
+ private scopes = new Map<string, number>();
10
+ private count = 0;
11
+ private lastRun = "";
12
+ constructor(readonly ownerLimit = 4) { programAssert(Number.isSafeInteger(ownerLimit) && ownerLimit > 0 && ownerLimit <= 32, "Invalid owner capacity"); }
13
+ restore(runId: string, scopes: ActivityScope[]): () => void {
14
+ this.count++; this.active.set(runId, (this.active.get(runId) ?? 0) + 1);
15
+ for (const scope of scopes) this.scopes.set(scope.id, (this.scopes.get(scope.id) ?? 0) + 1);
16
+ let released = false;
17
+ return () => { if (released) return; released = true; this.count--; this.active.set(runId, this.active.get(runId)! - 1); for (const scope of scopes) this.scopes.set(scope.id, this.scopes.get(scope.id)! - 1); this.drain(); };
18
+ }
19
+ acquire(runId: string, runLimit: number, scopes: ActivityScope[], signal: AbortSignal): Promise<() => void> {
20
+ return new Promise((resolve, reject) => {
21
+ const ticket: Ticket = { runId, runLimit, scopes, resolve, reject, signal, abort: () => { this.tickets = this.tickets.filter(entry => entry !== ticket); reject(new Error("Activity admission canceled")); this.drain(); } };
22
+ if (signal.aborted) { ticket.abort(); return; }
23
+ signal.addEventListener("abort", ticket.abort, { once: true }); this.tickets.push(ticket); this.drain();
24
+ });
25
+ }
26
+ private drain(): void {
27
+ while (this.count < this.ownerLimit) {
28
+ const eligible = this.tickets.filter(ticket => !ticket.signal.aborted && (this.active.get(ticket.runId) ?? 0) < ticket.runLimit && ticket.scopes.every(scope => (this.scopes.get(scope.id) ?? 0) < scope.limit));
29
+ const ticket = eligible.find(entry => entry.runId !== this.lastRun) ?? eligible[0]; if (!ticket) return;
30
+ this.tickets.splice(this.tickets.indexOf(ticket), 1); ticket.signal.removeEventListener("abort", ticket.abort);
31
+ this.lastRun = ticket.runId; this.count++; this.active.set(ticket.runId, (this.active.get(ticket.runId) ?? 0) + 1);
32
+ for (const scope of ticket.scopes) this.scopes.set(scope.id, (this.scopes.get(scope.id) ?? 0) + 1);
33
+ let released = false;
34
+ ticket.resolve(() => { if (released) return; released = true; this.count--; this.active.set(ticket.runId, this.active.get(ticket.runId)! - 1); for (const scope of ticket.scopes) this.scopes.set(scope.id, this.scopes.get(scope.id)! - 1); this.drain(); });
35
+ }
36
+ }
37
+ snapshot(): { active: number; queued: number; ownerLimit: number } { return { active: this.count, queued: this.tickets.length, ownerLimit: this.ownerLimit }; }
38
+ }