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,863 @@
1
+ import { getOwn, setOwn } from "./dictionary.ts";
2
+ import {
3
+ assertAuthorizationCurrent,
4
+ assertGrantAttenuated,
5
+ assertGrantLineageCurrent,
6
+ assertRootGrantAuthorized,
7
+ } from "./authority.ts";
8
+ import { digestCanonical, sha256Digest, stableStringify } from "./canonical.ts";
9
+ import { AgentFabricError } from "./errors.ts";
10
+ import { computeControlEventDigest } from "./journal.ts";
11
+ import { applyPlanDelta, computeRunPlanContentDigest } from "./planning.ts";
12
+ import type {
13
+ ControlEventEnvelope,
14
+ ControlState,
15
+ ExecutionGrant,
16
+ ReplayTrustContext,
17
+ ResourceDefinition,
18
+ ResourceReservation,
19
+ } from "./types.ts";
20
+ import { validateControlEventEnvelope } from "./validation.ts";
21
+
22
+ export function createEmptyControlState(): ControlState {
23
+ return {
24
+ lastSequence: 0,
25
+ lastEventId: null,
26
+ lastEventDigest: null,
27
+ lastOccurredAt: null,
28
+ authorizations: {},
29
+ authorizationVerifications: {},
30
+ revokedAuthorizations: {},
31
+ resourceDefinitions: {},
32
+ resourceReserved: {},
33
+ resourceConsumed: {},
34
+ resourceOwnerReserved: {},
35
+ resourceOwnerConsumed: {},
36
+ resourceReservations: {},
37
+ goals: {},
38
+ grants: {},
39
+ revokedGrants: {},
40
+ planDeltas: {},
41
+ planRevisions: {},
42
+ activePlanRevisionByExecution: {},
43
+ dispatchIntents: {},
44
+ claims: {},
45
+ activeClaimByIntent: {},
46
+ claimByAttemptId: {},
47
+ permits: {},
48
+ attempts: {},
49
+ uncertaintyObservations: {},
50
+ outcomes: {},
51
+ };
52
+ }
53
+
54
+ function assertEnvelopeContinuity(state: ControlState, event: ControlEventEnvelope): void {
55
+ validateControlEventEnvelope(event);
56
+ if (event.sequence !== state.lastSequence + 1) {
57
+ throw new AgentFabricError(
58
+ "AF_INVALID_EVENT",
59
+ `Expected event sequence ${state.lastSequence + 1}, received ${event.sequence}`,
60
+ );
61
+ }
62
+ if (event.predecessorEventId !== state.lastEventId) {
63
+ throw new AgentFabricError("AF_INVALID_EVENT", `Event ${event.eventId} has an invalid predecessor`);
64
+ }
65
+ if (event.predecessorEventDigest !== state.lastEventDigest) {
66
+ throw new AgentFabricError(
67
+ "AF_INVALID_EVENT",
68
+ `Event ${event.eventId} has an invalid predecessor digest`,
69
+ );
70
+ }
71
+ if (state.lastOccurredAt !== null && event.occurredAt < state.lastOccurredAt) {
72
+ throw new AgentFabricError("AF_INVALID_EVENT", "Control event timestamps are not monotonic");
73
+ }
74
+ const { eventDigest: _eventDigest, ...withoutDigest } = event;
75
+ if (computeControlEventDigest(withoutDigest) !== event.eventDigest) {
76
+ throw new AgentFabricError("AF_INVALID_EVENT", `Event ${event.eventId} digest does not match`);
77
+ }
78
+ }
79
+
80
+ function sortedDefinitions(definitions: readonly ResourceDefinition[]): ResourceDefinition[] {
81
+ const byName = new Map<string, ResourceDefinition>();
82
+ for (const definition of definitions) {
83
+ if (byName.has(definition.resource)) {
84
+ throw new AgentFabricError("AF_INVALID_EVENT", `Duplicate resource definition ${definition.resource}`);
85
+ }
86
+ if (!Number.isFinite(definition.limit) || definition.limit < 0) {
87
+ throw new AgentFabricError("AF_INVALID_EVENT", `Invalid resource limit ${definition.resource}`);
88
+ }
89
+ byName.set(definition.resource, { ...definition });
90
+ }
91
+ return [...byName.values()].sort((a, b) => a.resource.localeCompare(b.resource));
92
+ }
93
+
94
+ function resourceDefinitionMap(definitions: readonly ResourceDefinition[]) {
95
+ return Object.fromEntries(definitions.map((definition) => [definition.resource, definition]));
96
+ }
97
+
98
+ function cloneNested(
99
+ record: Readonly<Record<string, Readonly<Record<string, number>>>>,
100
+ ): Record<string, Record<string, number>> {
101
+ return Object.fromEntries(
102
+ Object.entries(record).map(([owner, values]) => [owner, { ...values }]),
103
+ );
104
+ }
105
+
106
+ function reservationMatchesGrant(reservation: ResourceReservation, grant: ExecutionGrant): boolean {
107
+ if (!grant.parentGrantId || !grant.reservationId) return false;
108
+ if (
109
+ reservation.reservationId !== grant.reservationId ||
110
+ reservation.ownerId !== grant.parentGrantId ||
111
+ reservation.status === "released"
112
+ ) {
113
+ return false;
114
+ }
115
+ const seen = new Set<string>();
116
+ const requestCeilings: Record<string, number> = {};
117
+ for (const request of reservation.requests) {
118
+ if (seen.has(request.resource)) return false;
119
+ seen.add(request.resource);
120
+ setOwn(requestCeilings, request.resource, request.amount);
121
+ }
122
+ return stableStringify(requestCeilings) === stableStringify(grant.resourceCeilings);
123
+ }
124
+
125
+ function applyReservationToState(
126
+ state: ControlState,
127
+ reservation: ResourceReservation,
128
+ ): Pick<
129
+ ControlState,
130
+ | "resourceReserved"
131
+ | "resourceConsumed"
132
+ | "resourceOwnerReserved"
133
+ | "resourceOwnerConsumed"
134
+ | "resourceReservations"
135
+ > {
136
+ if (getOwn(state.resourceReservations, reservation.reservationId)) {
137
+ throw new AgentFabricError(
138
+ "AF_INVALID_EVENT",
139
+ `Reservation already exists: ${reservation.reservationId}`,
140
+ );
141
+ }
142
+ const reserved = { ...state.resourceReserved };
143
+ const consumed = { ...state.resourceConsumed };
144
+ const ownerReserved = cloneNested(state.resourceOwnerReserved);
145
+ const ownerConsumed = cloneNested(state.resourceOwnerConsumed);
146
+ const ownerReservedBucket = getOwn(ownerReserved, reservation.ownerId) ?? {};
147
+ const ownerConsumedBucket = getOwn(ownerConsumed, reservation.ownerId) ?? {};
148
+ setOwn(ownerReserved, reservation.ownerId, ownerReservedBucket);
149
+ setOwn(ownerConsumed, reservation.ownerId, ownerConsumedBucket);
150
+
151
+ const seen = new Set<string>();
152
+ let hasNonCounter = false;
153
+ for (const request of reservation.requests) {
154
+ if (seen.has(request.resource)) {
155
+ throw new AgentFabricError(
156
+ "AF_INVALID_EVENT",
157
+ `Reservation ${reservation.reservationId} repeats resource ${request.resource}`,
158
+ );
159
+ }
160
+ seen.add(request.resource);
161
+ const definition = getOwn(state.resourceDefinitions, request.resource);
162
+ if (!definition) {
163
+ throw new AgentFabricError("AF_INVALID_EVENT", `Unknown resource ${request.resource}`);
164
+ }
165
+ if (!Number.isFinite(request.amount) || request.amount <= 0) {
166
+ throw new AgentFabricError("AF_INVALID_EVENT", `Invalid resource amount ${request.resource}`);
167
+ }
168
+ if (definition.semantics === "counter") {
169
+ const projected = (getOwn(consumed, request.resource) ?? 0) + request.amount;
170
+ if (projected > definition.limit) {
171
+ throw new AgentFabricError(
172
+ "AF_INVALID_EVENT",
173
+ `Reservation exceeds global counter ${request.resource}`,
174
+ );
175
+ }
176
+ } else {
177
+ hasNonCounter = true;
178
+ const projected =
179
+ (getOwn(reserved, request.resource) ?? 0) +
180
+ (getOwn(consumed, request.resource) ?? 0) +
181
+ request.amount;
182
+ if (projected > definition.limit) {
183
+ throw new AgentFabricError(
184
+ "AF_INVALID_EVENT",
185
+ `Reservation exceeds global resource ${request.resource}`,
186
+ );
187
+ }
188
+ }
189
+ }
190
+
191
+ const expectedStatus = hasNonCounter ? "active" : "consumed";
192
+ if (reservation.status !== expectedStatus) {
193
+ throw new AgentFabricError(
194
+ "AF_INVALID_EVENT",
195
+ `Reservation ${reservation.reservationId} has invalid initial status`,
196
+ );
197
+ }
198
+
199
+ for (const request of reservation.requests) {
200
+ const definition = getOwn(state.resourceDefinitions, request.resource)!;
201
+ if (definition.semantics === "counter") {
202
+ setOwn(consumed, request.resource, (getOwn(consumed, request.resource) ?? 0) + request.amount);
203
+ setOwn(ownerConsumedBucket, request.resource, (getOwn(ownerConsumedBucket, request.resource) ?? 0) + request.amount);
204
+ } else {
205
+ setOwn(reserved, request.resource, (getOwn(reserved, request.resource) ?? 0) + request.amount);
206
+ setOwn(ownerReservedBucket, request.resource, (getOwn(ownerReservedBucket, request.resource) ?? 0) + request.amount);
207
+ }
208
+ }
209
+
210
+ return {
211
+ resourceReserved: reserved,
212
+ resourceConsumed: consumed,
213
+ resourceOwnerReserved: ownerReserved,
214
+ resourceOwnerConsumed: ownerConsumed,
215
+ resourceReservations: {
216
+ ...state.resourceReservations,
217
+ [reservation.reservationId]: reservation,
218
+ },
219
+ };
220
+ }
221
+
222
+ function transitionReservation(
223
+ state: ControlState,
224
+ reservationId: string,
225
+ transition: "consume" | "release",
226
+ ): Pick<
227
+ ControlState,
228
+ | "resourceReserved"
229
+ | "resourceConsumed"
230
+ | "resourceOwnerReserved"
231
+ | "resourceOwnerConsumed"
232
+ | "resourceReservations"
233
+ > {
234
+ const current = getOwn(state.resourceReservations, reservationId);
235
+ if (!current) {
236
+ throw new AgentFabricError("AF_INVALID_EVENT", `Unknown reservation ${reservationId}`);
237
+ }
238
+ const reserved = { ...state.resourceReserved };
239
+ const consumed = { ...state.resourceConsumed };
240
+ const ownerReserved = cloneNested(state.resourceOwnerReserved);
241
+ const ownerConsumed = cloneNested(state.resourceOwnerConsumed);
242
+ const ownerReservedBucket = getOwn(ownerReserved, current.ownerId) ?? {};
243
+ const ownerConsumedBucket = getOwn(ownerConsumed, current.ownerId) ?? {};
244
+ setOwn(ownerReserved, current.ownerId, ownerReservedBucket);
245
+ setOwn(ownerConsumed, current.ownerId, ownerConsumedBucket);
246
+
247
+ if (transition === "consume") {
248
+ if (current.status === "released") {
249
+ throw new AgentFabricError("AF_INVALID_EVENT", "Released reservation cannot be consumed");
250
+ }
251
+ if (current.status === "active") {
252
+ for (const request of current.requests) {
253
+ const definition = getOwn(state.resourceDefinitions, request.resource)!;
254
+ if (definition.semantics === "consumable") {
255
+ setOwn(reserved, request.resource, (getOwn(reserved, request.resource) ?? 0) - request.amount);
256
+ setOwn(consumed, request.resource, (getOwn(consumed, request.resource) ?? 0) + request.amount);
257
+ setOwn(ownerReservedBucket, request.resource, (getOwn(ownerReservedBucket, request.resource) ?? 0) - request.amount);
258
+ setOwn(ownerConsumedBucket, request.resource, (getOwn(ownerConsumedBucket, request.resource) ?? 0) + request.amount);
259
+ }
260
+ }
261
+ }
262
+ return {
263
+ resourceReserved: reserved,
264
+ resourceConsumed: consumed,
265
+ resourceOwnerReserved: ownerReserved,
266
+ resourceOwnerConsumed: ownerConsumed,
267
+ resourceReservations: {
268
+ ...state.resourceReservations,
269
+ [reservationId]: { ...current, status: "consumed" },
270
+ },
271
+ };
272
+ }
273
+
274
+ if (current.status !== "released") {
275
+ if (current.status === "active") {
276
+ for (const request of current.requests) {
277
+ const definition = getOwn(state.resourceDefinitions, request.resource)!;
278
+ if (definition.semantics !== "counter") {
279
+ setOwn(reserved, request.resource, (getOwn(reserved, request.resource) ?? 0) - request.amount);
280
+ setOwn(ownerReservedBucket, request.resource, (getOwn(ownerReservedBucket, request.resource) ?? 0) - request.amount);
281
+ }
282
+ }
283
+ } else {
284
+ for (const request of current.requests) {
285
+ const definition = getOwn(state.resourceDefinitions, request.resource)!;
286
+ if (definition.semantics === "capacity") {
287
+ setOwn(reserved, request.resource, (getOwn(reserved, request.resource) ?? 0) - request.amount);
288
+ setOwn(ownerReservedBucket, request.resource, (getOwn(ownerReservedBucket, request.resource) ?? 0) - request.amount);
289
+ }
290
+ }
291
+ }
292
+ }
293
+ return {
294
+ resourceReserved: reserved,
295
+ resourceConsumed: consumed,
296
+ resourceOwnerReserved: ownerReserved,
297
+ resourceOwnerConsumed: ownerConsumed,
298
+ resourceReservations: {
299
+ ...state.resourceReservations,
300
+ [reservationId]: { ...current, status: "released" },
301
+ },
302
+ };
303
+ }
304
+
305
+ function issuedAttemptsForGrant(state: ControlState, grantId: string): number {
306
+ return Object.values(state.permits).filter((permit) => permit.grantId === grantId).length;
307
+ }
308
+
309
+ function delegatedAttemptBudget(state: ControlState, grantId: string): number {
310
+ return Object.values(state.grants)
311
+ .filter((candidate) => candidate.parentGrantId === grantId)
312
+ .reduce((total, candidate) => total + candidate.maximumAttempts, 0);
313
+ }
314
+
315
+ export function reduceControlEvent(
316
+ state: ControlState,
317
+ event: ControlEventEnvelope,
318
+ trust: ReplayTrustContext,
319
+ ): ControlState {
320
+ assertEnvelopeContinuity(state, event);
321
+ const next: ControlState = {
322
+ ...state,
323
+ lastSequence: event.sequence,
324
+ lastEventId: event.eventId,
325
+ lastEventDigest: event.eventDigest,
326
+ lastOccurredAt: event.occurredAt,
327
+ };
328
+ const payload = event.payload;
329
+
330
+ if (payload.type === "owner_authorization_registered") {
331
+ const authorization = payload.authorization;
332
+ if (getOwn(state.authorizations, authorization.authorizationId)) {
333
+ throw new AgentFabricError(
334
+ "AF_INVALID_EVENT",
335
+ `Owner authorization already exists: ${authorization.authorizationId}`,
336
+ );
337
+ }
338
+ if (authorization.rootExecutionId !== event.rootExecutionId) {
339
+ throw new AgentFabricError("AF_INVALID_EVENT", "Owner authorization is in the wrong root execution");
340
+ }
341
+ if (authorization.expiresAt <= authorization.notBefore) {
342
+ throw new AgentFabricError("AF_INVALID_EVENT", "Owner authorization has an invalid time window");
343
+ }
344
+ const expectedAuthorizationDigest = digestCanonical(authorization, sha256Digest);
345
+ if (
346
+ payload.verification.authorizationDigest !== expectedAuthorizationDigest ||
347
+ !trust.ownerAuthorizationVerifier.verifyRecorded(authorization, payload.verification)
348
+ ) {
349
+ throw new AgentFabricError("AF_INVALID_EVENT", "Owner authorization admission is not trusted");
350
+ }
351
+ return {
352
+ ...next,
353
+ authorizations: { ...state.authorizations, [authorization.authorizationId]: authorization },
354
+ authorizationVerifications: {
355
+ ...state.authorizationVerifications,
356
+ [authorization.authorizationId]: payload.verification,
357
+ },
358
+ };
359
+ }
360
+
361
+ if (payload.type === "owner_authorization_revoked") {
362
+ if (!getOwn(state.authorizations, payload.authorizationId)) {
363
+ throw new AgentFabricError("AF_INVALID_EVENT", `Unknown owner authorization: ${payload.authorizationId}`);
364
+ }
365
+ return {
366
+ ...next,
367
+ revokedAuthorizations: {
368
+ ...state.revokedAuthorizations,
369
+ [payload.authorizationId]: payload.reason,
370
+ },
371
+ };
372
+ }
373
+
374
+ if (payload.type === "resource_ledger_initialized") {
375
+ if (Object.keys(state.resourceDefinitions).length > 0) {
376
+ throw new AgentFabricError("AF_INVALID_EVENT", "Resource ledger is already initialized");
377
+ }
378
+ if (!trust.resourceDefinitions) {
379
+ throw new AgentFabricError("AF_INVALID_EVENT", "Replay lacks trusted resource definitions");
380
+ }
381
+ const recorded = sortedDefinitions(payload.definitions);
382
+ const trusted = sortedDefinitions(trust.resourceDefinitions);
383
+ if (stableStringify(recorded) !== stableStringify(trusted)) {
384
+ throw new AgentFabricError("AF_INVALID_EVENT", "Resource definitions do not match replay trust");
385
+ }
386
+ const definitions = resourceDefinitionMap(recorded);
387
+ return {
388
+ ...next,
389
+ resourceDefinitions: definitions,
390
+ resourceReserved: Object.fromEntries(recorded.map(({ resource }) => [resource, 0])),
391
+ resourceConsumed: Object.fromEntries(recorded.map(({ resource }) => [resource, 0])),
392
+ };
393
+ }
394
+
395
+ if (payload.type === "resource_reservation_consumed") {
396
+ return { ...next, ...transitionReservation(state, payload.reservationId, "consume") };
397
+ }
398
+
399
+ if (payload.type === "resource_reservation_released") {
400
+ return { ...next, ...transitionReservation(state, payload.reservationId, "release") };
401
+ }
402
+
403
+ if (payload.type === "goal_registered") {
404
+ const authorization = getOwn(state.authorizations, payload.goal.authorityInvocationId);
405
+ if (!authorization) {
406
+ throw new AgentFabricError(
407
+ "AF_INVALID_EVENT",
408
+ `Goal ${payload.goal.goalId} has no registered owner authorization`,
409
+ );
410
+ }
411
+ assertAuthorizationCurrent(
412
+ authorization,
413
+ event.occurredAt,
414
+ getOwn(state.revokedAuthorizations, authorization.authorizationId),
415
+ );
416
+ if (!authorization.goalIds.includes(payload.goal.goalId)) {
417
+ throw new AgentFabricError("AF_INVALID_EVENT", "Owner authorization does not cover this goal");
418
+ }
419
+ if (getOwn(state.goals, payload.goal.goalId)) {
420
+ throw new AgentFabricError("AF_INVALID_EVENT", `Goal already exists: ${payload.goal.goalId}`);
421
+ }
422
+ return { ...next, goals: { ...state.goals, [payload.goal.goalId]: payload.goal } };
423
+ }
424
+
425
+ if (payload.type === "grant_registered") {
426
+ const grant = payload.grant;
427
+ if (getOwn(state.grants, grant.grantId)) {
428
+ throw new AgentFabricError("AF_INVALID_EVENT", `Grant already exists: ${grant.grantId}`);
429
+ }
430
+ if (grant.parentGrantId) {
431
+ const parent = getOwn(state.grants, grant.parentGrantId);
432
+ if (!parent) {
433
+ throw new AgentFabricError(
434
+ "AF_INVALID_EVENT",
435
+ `Derived grant references unknown parent ${grant.parentGrantId}`,
436
+ );
437
+ }
438
+ if (Object.keys(state.resourceDefinitions).length === 0) {
439
+ throw new AgentFabricError("AF_INVALID_EVENT", "Derived grant precedes resource ledger initialization");
440
+ }
441
+ assertGrantLineageCurrent(state, parent.grantId, event.occurredAt);
442
+ assertGrantAttenuated(parent, grant);
443
+ if (!payload.reservation || !reservationMatchesGrant(payload.reservation, grant)) {
444
+ throw new AgentFabricError(
445
+ "AF_INVALID_EVENT",
446
+ `Derived grant ${grant.grantId} is missing its matching reservation`,
447
+ );
448
+ }
449
+ if (
450
+ issuedAttemptsForGrant(state, parent.grantId) +
451
+ delegatedAttemptBudget(state, parent.grantId) +
452
+ grant.maximumAttempts >
453
+ parent.maximumAttempts
454
+ ) {
455
+ throw new AgentFabricError(
456
+ "AF_INVALID_EVENT",
457
+ `Derived grants exceed parent attempt budget ${parent.grantId}`,
458
+ );
459
+ }
460
+ for (const [resource, amount] of Object.entries(grant.resourceCeilings)) {
461
+ const allocated = Object.values(state.grants)
462
+ .filter((candidate) => candidate.parentGrantId === parent.grantId)
463
+ .reduce((total, candidate) => total + (getOwn(candidate.resourceCeilings, resource) ?? 0), 0);
464
+ if (allocated + amount > (getOwn(parent.resourceCeilings, resource) ?? -1)) {
465
+ throw new AgentFabricError(
466
+ "AF_INVALID_EVENT",
467
+ `Derived grants exceed parent resource ${resource}`,
468
+ );
469
+ }
470
+ }
471
+ const accounting = applyReservationToState(state, payload.reservation);
472
+ return {
473
+ ...next,
474
+ ...accounting,
475
+ grants: { ...state.grants, [grant.grantId]: grant },
476
+ };
477
+ }
478
+
479
+ if (payload.reservation !== null) {
480
+ throw new AgentFabricError("AF_INVALID_EVENT", "Root grant cannot carry a child reservation");
481
+ }
482
+ const authorization = getOwn(state.authorizations, grant.rootAuthorizationId);
483
+ if (!authorization) {
484
+ throw new AgentFabricError(
485
+ "AF_INVALID_EVENT",
486
+ `Root grant ${grant.grantId} has no registered owner authorization`,
487
+ );
488
+ }
489
+ if (authorization.rootExecutionId !== event.rootExecutionId) {
490
+ throw new AgentFabricError("AF_INVALID_EVENT", "Root grant authorization is in the wrong execution");
491
+ }
492
+ assertAuthorizationCurrent(
493
+ authorization,
494
+ event.occurredAt,
495
+ getOwn(state.revokedAuthorizations, authorization.authorizationId),
496
+ );
497
+ assertRootGrantAuthorized(authorization, grant);
498
+ const siblingRoots = Object.values(state.grants).filter(
499
+ (candidate) => candidate.parentGrantId === null &&
500
+ candidate.rootAuthorizationId === authorization.authorizationId,
501
+ );
502
+ if (
503
+ siblingRoots.reduce((total, candidate) => total + candidate.maximumAttempts, 0) +
504
+ grant.maximumAttempts >
505
+ authorization.maximumAttempts
506
+ ) {
507
+ throw new AgentFabricError("AF_INVALID_EVENT", "Root grants exceed owner attempt budget");
508
+ }
509
+ for (const [resource, amount] of Object.entries(grant.resourceCeilings)) {
510
+ const allocated = siblingRoots.reduce(
511
+ (total, candidate) => total + (getOwn(candidate.resourceCeilings, resource) ?? 0),
512
+ 0,
513
+ );
514
+ if (allocated + amount > (getOwn(authorization.resourceCeilings, resource) ?? -1)) {
515
+ throw new AgentFabricError(
516
+ "AF_INVALID_EVENT",
517
+ `Root grants exceed owner authorization resource ${resource}`,
518
+ );
519
+ }
520
+ }
521
+ return { ...next, grants: { ...state.grants, [grant.grantId]: grant } };
522
+ }
523
+
524
+ if (payload.type === "grant_revoked") {
525
+ if (!getOwn(state.grants, payload.grantId)) {
526
+ throw new AgentFabricError("AF_INVALID_EVENT", `Unknown grant: ${payload.grantId}`);
527
+ }
528
+ return {
529
+ ...next,
530
+ revokedGrants: { ...state.revokedGrants, [payload.grantId]: payload.reason },
531
+ };
532
+ }
533
+
534
+ if (payload.type === "plan_delta_registered") {
535
+ const delta = payload.delta;
536
+ if (delta.rootExecutionId !== event.rootExecutionId) {
537
+ throw new AgentFabricError("AF_INVALID_EVENT", "Plan delta is in the wrong root execution");
538
+ }
539
+ if (getOwn(state.planDeltas, delta.deltaId)) {
540
+ throw new AgentFabricError("AF_INVALID_EVENT", `Plan delta already exists: ${delta.deltaId}`);
541
+ }
542
+ const base = getOwn(state.planRevisions, delta.baseRevisionId);
543
+ if (!base || getOwn(state.activePlanRevisionByExecution, event.rootExecutionId) !== base.revisionId) {
544
+ throw new AgentFabricError("AF_INVALID_EVENT", "Plan delta does not target the active revision");
545
+ }
546
+ return { ...next, planDeltas: { ...state.planDeltas, [delta.deltaId]: delta } };
547
+ }
548
+
549
+ if (payload.type === "plan_revision_activated") {
550
+ const revision = payload.revision;
551
+ if (revision.rootExecutionId !== event.rootExecutionId) {
552
+ throw new AgentFabricError("AF_INVALID_EVENT", "Plan revision is in the wrong root execution");
553
+ }
554
+ if (!getOwn(state.goals, revision.goalId)) {
555
+ throw new AgentFabricError("AF_INVALID_EVENT", `Unknown goal: ${revision.goalId}`);
556
+ }
557
+ if (getOwn(state.planRevisions, revision.revisionId)) {
558
+ throw new AgentFabricError("AF_INVALID_EVENT", `Plan revision already exists: ${revision.revisionId}`);
559
+ }
560
+ const expectedDigest = computeRunPlanContentDigest(
561
+ revision.programVersionId,
562
+ revision.nodes,
563
+ sha256Digest,
564
+ );
565
+ if (revision.contentDigest !== expectedDigest) {
566
+ throw new AgentFabricError("AF_INVALID_EVENT", "Plan revision content digest does not match");
567
+ }
568
+ const currentId = getOwn(state.activePlanRevisionByExecution, revision.rootExecutionId) ?? null;
569
+ if (revision.parentRevisionId !== currentId) {
570
+ throw new AgentFabricError(
571
+ "AF_INVALID_EVENT",
572
+ `Plan revision ${revision.revisionId} does not extend the active revision`,
573
+ );
574
+ }
575
+ if (currentId === null) {
576
+ if (revision.revisionNumber !== 1 || revision.sourcePlanDeltaId !== null) {
577
+ throw new AgentFabricError("AF_INVALID_EVENT", "Initial plan revision has invalid lineage");
578
+ }
579
+ } else {
580
+ const parent = getOwn(state.planRevisions, currentId)!;
581
+ if (revision.goalId !== parent.goalId || revision.programVersionId !== parent.programVersionId) {
582
+ throw new AgentFabricError(
583
+ "AF_INVALID_EVENT",
584
+ "Plan revision cannot silently change goal or workflow program",
585
+ );
586
+ }
587
+ if (revision.revisionNumber !== parent.revisionNumber + 1 || !revision.sourcePlanDeltaId) {
588
+ throw new AgentFabricError("AF_INVALID_EVENT", "Plan revision has invalid revision lineage");
589
+ }
590
+ const delta = getOwn(state.planDeltas, revision.sourcePlanDeltaId);
591
+ if (
592
+ !delta ||
593
+ delta.rootExecutionId !== revision.rootExecutionId ||
594
+ delta.baseRevisionId !== parent.revisionId ||
595
+ delta.nextRevisionId !== revision.revisionId
596
+ ) {
597
+ throw new AgentFabricError(
598
+ "AF_INVALID_EVENT",
599
+ "Plan revision is not backed by its registered PlanDelta",
600
+ );
601
+ }
602
+ const derived = applyPlanDelta(parent, delta, sha256Digest);
603
+ if (stableStringify(derived) !== stableStringify(revision)) {
604
+ throw new AgentFabricError(
605
+ "AF_INVALID_EVENT",
606
+ "Plan revision content does not match its registered PlanDelta",
607
+ );
608
+ }
609
+ }
610
+ return {
611
+ ...next,
612
+ planRevisions: { ...state.planRevisions, [revision.revisionId]: revision },
613
+ activePlanRevisionByExecution: {
614
+ ...state.activePlanRevisionByExecution,
615
+ [revision.rootExecutionId]: revision.revisionId,
616
+ },
617
+ };
618
+ }
619
+
620
+ if (payload.type === "dispatch_intent_committed") {
621
+ const intent = payload.intent;
622
+ if (intent.rootExecutionId !== event.rootExecutionId) {
623
+ throw new AgentFabricError("AF_INVALID_EVENT", "Dispatch intent is in the wrong root execution");
624
+ }
625
+ if (intent.createdAt > event.occurredAt) {
626
+ throw new AgentFabricError("AF_INVALID_EVENT", "Dispatch intent cannot be created in the future");
627
+ }
628
+ if (getOwn(state.activePlanRevisionByExecution, intent.rootExecutionId) !== intent.planRevisionId) {
629
+ throw new AgentFabricError(
630
+ "AF_INVALID_EVENT",
631
+ `Dispatch intent references a non-current plan revision ${intent.planRevisionId}`,
632
+ );
633
+ }
634
+ const revision = getOwn(state.planRevisions, intent.planRevisionId);
635
+ const goal = revision ? getOwn(state.goals, revision.goalId) : undefined;
636
+ if (!revision || !goal) {
637
+ throw new AgentFabricError("AF_INVALID_EVENT", "Dispatch intent has no active GoalContract");
638
+ }
639
+ if (
640
+ !goal.allowedEffectClasses.includes(intent.effectClass) ||
641
+ goal.prohibitedEffectClasses.includes(intent.effectClass) ||
642
+ (!goal.sourceBoundary.allowExpansion &&
643
+ !intent.sourceIds.every((sourceId) => goal.sourceBoundary.sourceIds.includes(sourceId)))
644
+ ) {
645
+ throw new AgentFabricError("AF_INVALID_EVENT", "Dispatch intent violates its GoalContract");
646
+ }
647
+ if (!revision.nodes.some((node) => node.nodeId === intent.taskNodeId)) {
648
+ throw new AgentFabricError(
649
+ "AF_INVALID_EVENT",
650
+ `Dispatch intent references missing plan node ${intent.taskNodeId}`,
651
+ );
652
+ }
653
+ if (getOwn(state.dispatchIntents, intent.intentId)) {
654
+ throw new AgentFabricError("AF_INVALID_EVENT", `Dispatch intent already exists: ${intent.intentId}`);
655
+ }
656
+ return {
657
+ ...next,
658
+ dispatchIntents: { ...state.dispatchIntents, [intent.intentId]: intent },
659
+ };
660
+ }
661
+
662
+ if (payload.type === "scheduling_claim_committed") {
663
+ const claim = payload.claim;
664
+ const intent = getOwn(state.dispatchIntents, claim.intentId);
665
+ if (!intent) {
666
+ throw new AgentFabricError("AF_INVALID_EVENT", `Claim references unknown intent ${claim.intentId}`);
667
+ }
668
+ if (getOwn(state.claimByAttemptId, claim.attemptId)) {
669
+ throw new AgentFabricError("AF_INVALID_EVENT", `Attempt ID already claimed: ${claim.attemptId}`);
670
+ }
671
+ if (getOwn(state.activePlanRevisionByExecution, event.rootExecutionId) !== intent.planRevisionId) {
672
+ throw new AgentFabricError("AF_INVALID_EVENT", "Claim targets a superseded plan revision");
673
+ }
674
+ if (claim.committedAt !== event.occurredAt || claim.leaseExpiresAt <= claim.committedAt) {
675
+ throw new AgentFabricError("AF_INVALID_EVENT", "Claim timestamps are inconsistent");
676
+ }
677
+ if (Object.values(state.outcomes).some((outcome) => outcome.intentId === claim.intentId)) {
678
+ throw new AgentFabricError(
679
+ "AF_INVALID_EVENT",
680
+ `Claim ${claim.claimId} targets an intent with an existing outcome`,
681
+ );
682
+ }
683
+ if (getOwn(state.claims, claim.claimId)) {
684
+ throw new AgentFabricError("AF_INVALID_EVENT", `Claim already exists: ${claim.claimId}`);
685
+ }
686
+ const currentClaimId = getOwn(state.activeClaimByIntent, claim.intentId);
687
+ const currentClaim = currentClaimId ? getOwn(state.claims, currentClaimId) : undefined;
688
+ if (currentClaim && currentClaim.leaseExpiresAt > event.occurredAt) {
689
+ throw new AgentFabricError("AF_INVALID_EVENT", `Claim ${claim.claimId} overlaps a current lease`);
690
+ }
691
+ if (claim.fencingToken !== (currentClaim?.fencingToken ?? 0) + 1) {
692
+ throw new AgentFabricError("AF_INVALID_EVENT", `Claim ${claim.claimId} has an invalid fencing token`);
693
+ }
694
+ return {
695
+ ...next,
696
+ claims: { ...state.claims, [claim.claimId]: claim },
697
+ activeClaimByIntent: { ...state.activeClaimByIntent, [claim.intentId]: claim.claimId },
698
+ claimByAttemptId: { ...state.claimByAttemptId, [claim.attemptId]: claim.claimId },
699
+ };
700
+ }
701
+
702
+ if (payload.type === "attempt_execution_permit_issued") {
703
+ const permit = payload.permit;
704
+ const claim = getOwn(state.claims, permit.claimId);
705
+ const grant = getOwn(state.grants, permit.grantId);
706
+ const intent = getOwn(state.dispatchIntents, permit.intentId);
707
+ if (!claim || !grant || !intent) {
708
+ throw new AgentFabricError("AF_INVALID_EVENT", "Permit references missing authority state");
709
+ }
710
+ if (getOwn(state.permits, permit.permitId)) {
711
+ throw new AgentFabricError("AF_INVALID_EVENT", `Permit already exists: ${permit.permitId}`);
712
+ }
713
+ if (
714
+ getOwn(state.claimByAttemptId, permit.attemptId) !== claim.claimId ||
715
+ getOwn(state.activeClaimByIntent, permit.intentId) !== claim.claimId ||
716
+ permit.attemptId !== claim.attemptId ||
717
+ permit.workerId !== claim.workerId ||
718
+ permit.fencingToken !== claim.fencingToken ||
719
+ permit.planRevisionId !== intent.planRevisionId ||
720
+ permit.effectiveRunSpecDigest !== intent.effectiveRunSpecDigest
721
+ ) {
722
+ throw new AgentFabricError("AF_INVALID_EVENT", "Permit does not match its claim or intent");
723
+ }
724
+ if (
725
+ grant.subjectId !== permit.workerId ||
726
+ !grant.capabilities.includes(intent.requiredCapability) ||
727
+ !grant.effectClasses.includes(intent.effectClass) ||
728
+ !intent.sourceIds.every((sourceId) => grant.sourceIds.includes(sourceId)) ||
729
+ !grant.targetIds.includes(intent.targetId) ||
730
+ permit.notBefore > event.occurredAt ||
731
+ event.occurredAt >= permit.expiresAt ||
732
+ permit.expiresAt > claim.leaseExpiresAt ||
733
+ permit.expiresAt > grant.expiresAt
734
+ ) {
735
+ throw new AgentFabricError("AF_INVALID_EVENT", "Permit exceeds its grant, claim, or intent");
736
+ }
737
+ assertGrantLineageCurrent(state, grant.grantId, event.occurredAt);
738
+ if (
739
+ issuedAttemptsForGrant(state, grant.grantId) + delegatedAttemptBudget(state, grant.grantId) >=
740
+ grant.maximumAttempts
741
+ ) {
742
+ throw new AgentFabricError("AF_INVALID_EVENT", "Grant attempt budget is exhausted");
743
+ }
744
+ return { ...next, permits: { ...state.permits, [permit.permitId]: permit } };
745
+ }
746
+
747
+ if (payload.type === "attempt_started") {
748
+ const permit = getOwn(state.permits, payload.permitId);
749
+ if (!permit || payload.attemptId !== permit.attemptId) {
750
+ throw new AgentFabricError("AF_INVALID_EVENT", "Attempt does not match a known permit");
751
+ }
752
+ if (getOwn(state.attempts, payload.attemptId)) {
753
+ throw new AgentFabricError("AF_INVALID_EVENT", `Attempt already exists: ${payload.attemptId}`);
754
+ }
755
+ const claim = getOwn(state.claims, permit.claimId);
756
+ const grant = getOwn(state.grants, permit.grantId);
757
+ if (
758
+ !claim ||
759
+ !grant ||
760
+ getOwn(state.claimByAttemptId, payload.attemptId) !== claim.claimId ||
761
+ payload.startedAt < permit.notBefore ||
762
+ payload.startedAt > event.occurredAt ||
763
+ payload.startedAt >= permit.expiresAt ||
764
+ getOwn(state.activeClaimByIntent, permit.intentId) !== claim.claimId ||
765
+ claim.leaseExpiresAt <= event.occurredAt ||
766
+ event.occurredAt < permit.notBefore ||
767
+ event.occurredAt >= permit.expiresAt ||
768
+ getOwn(state.activePlanRevisionByExecution, event.rootExecutionId) !== permit.planRevisionId
769
+ ) {
770
+ throw new AgentFabricError("AF_INVALID_EVENT", "Attempt started without current authority");
771
+ }
772
+ assertGrantLineageCurrent(state, grant.grantId, event.occurredAt);
773
+ return {
774
+ ...next,
775
+ attempts: {
776
+ ...state.attempts,
777
+ [payload.attemptId]: {
778
+ permitId: payload.permitId,
779
+ startupStatus: "started",
780
+ startedAt: payload.startedAt,
781
+ startupReportId: payload.startupReportId,
782
+ },
783
+ },
784
+ };
785
+ }
786
+
787
+ if (payload.type === "attempt_uncertainty_observed") {
788
+ const observation = payload.observation;
789
+ const permit = getOwn(state.permits, observation.permitId);
790
+ if (!permit || observation.attemptId !== permit.attemptId) {
791
+ throw new AgentFabricError("AF_INVALID_EVENT", "Uncertainty observation does not match its permit");
792
+ }
793
+ if (observation.observedAt !== event.occurredAt) {
794
+ throw new AgentFabricError("AF_INVALID_EVENT", "Uncertainty observation timestamp is inconsistent");
795
+ }
796
+ if (observation.phase === "outcome" && !getOwn(state.attempts, observation.attemptId)) {
797
+ throw new AgentFabricError("AF_INVALID_EVENT", "Outcome uncertainty requires a confirmed startup");
798
+ }
799
+ if (getOwn(state.uncertaintyObservations, observation.observationId)) {
800
+ throw new AgentFabricError(
801
+ "AF_INVALID_EVENT",
802
+ `Uncertainty observation already exists: ${observation.observationId}`,
803
+ );
804
+ }
805
+ return {
806
+ ...next,
807
+ uncertaintyObservations: {
808
+ ...state.uncertaintyObservations,
809
+ [observation.observationId]: observation,
810
+ },
811
+ };
812
+ }
813
+
814
+ const outcome = payload.outcome;
815
+ const attempt = getOwn(state.attempts, outcome.attemptId);
816
+ if (!attempt) {
817
+ throw new AgentFabricError("AF_INVALID_EVENT", `Outcome references unknown attempt ${outcome.attemptId}`);
818
+ }
819
+ if (getOwn(state.outcomes, outcome.attemptId)) {
820
+ throw new AgentFabricError("AF_INVALID_EVENT", `Outcome already exists for attempt ${outcome.attemptId}`);
821
+ }
822
+ if (outcome.committedAt !== event.occurredAt) {
823
+ throw new AgentFabricError("AF_INVALID_EVENT", "Outcome commit time must match its event time");
824
+ }
825
+ const permit = getOwn(state.permits, attempt.permitId);
826
+ const claim = permit ? getOwn(state.claims, permit.claimId) : undefined;
827
+ const grant = permit ? getOwn(state.grants, permit.grantId) : undefined;
828
+ const intent = permit ? getOwn(state.dispatchIntents, permit.intentId) : undefined;
829
+ if (
830
+ !permit ||
831
+ !claim ||
832
+ !grant ||
833
+ !intent ||
834
+ outcome.permitId !== permit.permitId ||
835
+ outcome.intentId !== permit.intentId ||
836
+ outcome.planRevisionId !== permit.planRevisionId ||
837
+ outcome.effectiveRunSpecDigest !== permit.effectiveRunSpecDigest ||
838
+ outcome.fencingToken !== permit.fencingToken ||
839
+ getOwn(state.claimByAttemptId, outcome.attemptId) !== claim.claimId ||
840
+ outcome.reportedAt < attempt.startedAt ||
841
+ outcome.reportedAt > event.occurredAt ||
842
+ event.occurredAt < permit.notBefore ||
843
+ event.occurredAt >= permit.expiresAt ||
844
+ getOwn(state.activeClaimByIntent, permit.intentId) !== claim.claimId ||
845
+ claim.fencingToken !== permit.fencingToken ||
846
+ claim.leaseExpiresAt <= event.occurredAt ||
847
+ getOwn(state.activePlanRevisionByExecution, event.rootExecutionId) !== permit.planRevisionId
848
+ ) {
849
+ throw new AgentFabricError("AF_INVALID_EVENT", "Outcome committed without matching current authority");
850
+ }
851
+ assertGrantLineageCurrent(state, grant.grantId, event.occurredAt);
852
+ return {
853
+ ...next,
854
+ outcomes: { ...state.outcomes, [outcome.attemptId]: outcome },
855
+ };
856
+ }
857
+
858
+ export function replayControlState(
859
+ events: readonly ControlEventEnvelope[],
860
+ trust: ReplayTrustContext,
861
+ ): ControlState {
862
+ return events.reduce((state, event) => reduceControlEvent(state, event, trust), createEmptyControlState());
863
+ }