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,348 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, open, readFile } from "node:fs/promises";
3
+ import { isAbsolute, resolve } from "node:path";
4
+ import type { DbAdapter } from "../runtime/db/adapter.ts";
5
+ import { sha256Digest, stableStringify } from "./canonical.ts";
6
+ import { AgentFabricError } from "./errors.ts";
7
+ import { localFabricPath } from "./local-paths.ts";
8
+ import type { Digest } from "./types.ts";
9
+
10
+ const CREATE_EFFECTS_TABLE = `
11
+ CREATE TABLE IF NOT EXISTS _forge_agent_fabric_local_effects (
12
+ request_digest TEXT PRIMARY KEY,
13
+ challenge_digest TEXT NOT NULL,
14
+ task_id TEXT NOT NULL,
15
+ subject_digest TEXT NOT NULL,
16
+ artifact_digest TEXT NOT NULL,
17
+ owner_id TEXT NOT NULL,
18
+ authorization_digest TEXT NOT NULL,
19
+ state TEXT NOT NULL CHECK (state IN ('intent', 'receipted')),
20
+ intent_at BIGINT NOT NULL,
21
+ receipt_digest TEXT,
22
+ receipt_at BIGINT
23
+ )`;
24
+
25
+ const DIGEST = /^sha256:[0-9a-f]{64}$/u;
26
+ const TASK_ID = /^task:[0-9a-f]{64}$/u;
27
+
28
+ /** The only effect admitted by this local broker. The destination is never client supplied. */
29
+ export interface LocalArtifactEffectRequest {
30
+ kind: "immutable_local_artifact_v1";
31
+ taskId: string;
32
+ subjectDigest: Digest;
33
+ content: string;
34
+ }
35
+
36
+ export interface LocalEffectAuthorization {
37
+ ownerId: string;
38
+ challengeDigest: Digest;
39
+ expiresAt: number;
40
+ proof: string;
41
+ }
42
+
43
+ export interface LocalEffectChallenge {
44
+ repositoryRoot: string;
45
+ targetPath: string;
46
+ request: LocalArtifactEffectRequest;
47
+ requestDigest: Digest;
48
+ challengeDigest: Digest;
49
+ artifactDigest: Digest;
50
+ expiresAt: number;
51
+ }
52
+
53
+ export interface LocalEffectReceipt {
54
+ requestDigest: Digest;
55
+ artifactDigest: Digest;
56
+ readbackDigest: Digest;
57
+ receiptDigest: Digest;
58
+ receivedAt: number;
59
+ }
60
+
61
+ export type LocalEffectObservation =
62
+ | { state: "not_dispatched"; requestDigest: Digest }
63
+ | { state: "receipt_unknown" | "incomplete_materialization" | "materialized_without_receipt" | "receipt_mismatch";
64
+ requestDigest: Digest; expectedArtifactDigest: Digest; observedArtifactDigest?: Digest }
65
+ | { state: "receipted"; requestDigest: Digest; receipt: LocalEffectReceipt };
66
+
67
+ export interface LocalEffectOwnerVerifier {
68
+ (challenge: LocalEffectChallenge, authorization: LocalEffectAuthorization): Promise<boolean> | boolean;
69
+ }
70
+
71
+ export type LocalEffectBoundary = "after_intent" | "after_open" | "after_write" | "after_readback" | "after_receipt";
72
+
73
+ export interface LocalEffectBrokerOptions {
74
+ adapter: DbAdapter;
75
+ repositoryRoot: string;
76
+ ownerId: string;
77
+ verifyOwner: LocalEffectOwnerVerifier;
78
+ /** Trusted clock; injectable only by the owner service for deterministic tests. */
79
+ now?: () => number;
80
+ /** Fault injection is for deterministic crash-boundary tests. Never exposes a client route. */
81
+ onBoundary?: (boundary: LocalEffectBoundary) => Promise<void> | void;
82
+ }
83
+
84
+ interface StoredEffect {
85
+ requestDigest: Digest;
86
+ challengeDigest: Digest;
87
+ taskId: string;
88
+ subjectDigest: Digest;
89
+ artifactDigest: Digest;
90
+ ownerId: string;
91
+ authorizationDigest: Digest;
92
+ state: "intent" | "receipted";
93
+ intentAt: number;
94
+ receiptDigest: Digest | null;
95
+ receiptAt: number | null;
96
+ }
97
+
98
+ function reject(message: string): never {
99
+ throw new AgentFabricError("AF_PERMIT_REJECTED", message);
100
+ }
101
+
102
+ function exactKeys(value: Record<string, unknown>, expected: readonly string[]): boolean {
103
+ return Object.keys(value).sort().join("\0") === [...expected].sort().join("\0");
104
+ }
105
+
106
+ function validateRequest(input: unknown): LocalArtifactEffectRequest {
107
+ if (!input || typeof input !== "object" || Array.isArray(input)) reject("Effect request must be an object");
108
+ const value = input as Record<string, unknown>;
109
+ if (!exactKeys(value, ["kind", "taskId", "subjectDigest", "content"]) ||
110
+ value.kind !== "immutable_local_artifact_v1" ||
111
+ typeof value.taskId !== "string" || !TASK_ID.test(value.taskId) ||
112
+ typeof value.subjectDigest !== "string" || !DIGEST.test(value.subjectDigest) ||
113
+ typeof value.content !== "string" ||
114
+ Buffer.byteLength(value.content, "utf8") > 4096) {
115
+ reject("Effect is outside the bounded immutable artifact contract");
116
+ }
117
+ return value as unknown as LocalArtifactEffectRequest;
118
+ }
119
+
120
+ function artifactBytes(request: LocalArtifactEffectRequest): string {
121
+ return stableStringify({
122
+ version: 1, taskId: request.taskId, subjectDigest: request.subjectDigest,
123
+ content: request.content,
124
+ });
125
+ }
126
+
127
+ function digestBytes(bytes: string | Buffer): Digest {
128
+ return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
129
+ }
130
+
131
+ /**
132
+ * One PGlite owner, one effect kind. Authorization is supplied by trusted owner ingress,
133
+ * and the immutable target is derived only from the request digest. An intent is committed
134
+ * before materialization; replay of an uncertain intent never dispatches it again.
135
+ */
136
+ export class LocalEffectBroker {
137
+ private schemaReady?: Promise<unknown>;
138
+ private tail: Promise<void> = Promise.resolve();
139
+ private readonly repositoryRoot: string;
140
+
141
+ constructor(private readonly options: LocalEffectBrokerOptions) {
142
+ if (options.adapter.kind !== "pglite" || !options.ownerId ||
143
+ typeof options.verifyOwner !== "function" ||
144
+ !isAbsolute(options.repositoryRoot)) {
145
+ throw new AgentFabricError("AF_INVALID_STATE", "Local effect broker requires a PGlite owner and absolute repository root");
146
+ }
147
+ this.repositoryRoot = resolve(options.repositoryRoot);
148
+ }
149
+
150
+ private async serialized<T>(operation: () => Promise<T>): Promise<T> {
151
+ const previous = this.tail;
152
+ let release!: () => void;
153
+ this.tail = new Promise<void>((done) => { release = done; });
154
+ await previous;
155
+ try {
156
+ this.schemaReady ??= this.options.adapter.query(CREATE_EFFECTS_TABLE);
157
+ await this.schemaReady;
158
+ return await operation();
159
+ } finally {
160
+ release();
161
+ }
162
+ }
163
+
164
+ private artifactPath(requestDigest: Digest): string {
165
+ if (!DIGEST.test(requestDigest)) reject("Invalid request digest");
166
+ return localFabricPath(this.repositoryRoot, "effects", `${requestDigest.slice(7)}.json`);
167
+ }
168
+
169
+ private resolve(input: unknown, expiresAt: number): {
170
+ request: LocalArtifactEffectRequest; challenge: LocalEffectChallenge; bytes: string;
171
+ } {
172
+ const request = validateRequest(input);
173
+ if (!Number.isSafeInteger(expiresAt) || expiresAt <= 0) reject("Invalid owner approval expiry");
174
+ const bytes = artifactBytes(request);
175
+ const requestDigest = sha256Digest(stableStringify({
176
+ version: 1, repositoryRoot: this.repositoryRoot, request,
177
+ }));
178
+ const artifactDigest = digestBytes(bytes);
179
+ const challengeDigest = sha256Digest(stableStringify({
180
+ version: 1, requestDigest, artifactDigest, ownerId: this.options.ownerId, expiresAt,
181
+ }));
182
+ return { request, bytes, challenge: {
183
+ repositoryRoot: this.repositoryRoot,
184
+ targetPath: this.artifactPath(requestDigest),
185
+ request, requestDigest, challengeDigest, artifactDigest, expiresAt,
186
+ } };
187
+ }
188
+
189
+ /** Resolve a proposal for display by trusted owner UI before seeking approval. */
190
+ prepare(input: unknown, expiresAt: number): LocalEffectChallenge {
191
+ return this.resolve(input, expiresAt).challenge;
192
+ }
193
+
194
+ private async load(requestDigest: Digest): Promise<StoredEffect | null> {
195
+ const result = await this.options.adapter.query(
196
+ `SELECT * FROM _forge_agent_fabric_local_effects WHERE request_digest = $1`, [requestDigest],
197
+ );
198
+ const row = result.rows[0];
199
+ if (!row) return null;
200
+ if (row.request_digest !== requestDigest || !DIGEST.test(String(row.challenge_digest)) ||
201
+ !TASK_ID.test(String(row.task_id)) || !DIGEST.test(String(row.subject_digest)) ||
202
+ !DIGEST.test(String(row.artifact_digest)) || !DIGEST.test(String(row.authorization_digest)) ||
203
+ (row.state !== "intent" && row.state !== "receipted") ||
204
+ (row.state === "receipted" && (!DIGEST.test(String(row.receipt_digest)) || row.receipt_at === null))) {
205
+ throw new AgentFabricError("AF_INVALID_STATE", "Stored effect row is inconsistent");
206
+ }
207
+ return {
208
+ requestDigest, challengeDigest: row.challenge_digest as Digest,
209
+ taskId: String(row.task_id), subjectDigest: row.subject_digest as Digest,
210
+ artifactDigest: row.artifact_digest as Digest, ownerId: String(row.owner_id),
211
+ authorizationDigest: row.authorization_digest as Digest,
212
+ state: row.state, intentAt: Number(row.intent_at),
213
+ receiptDigest: row.receipt_digest === null ? null : row.receipt_digest as Digest,
214
+ receiptAt: row.receipt_at === null ? null : Number(row.receipt_at),
215
+ };
216
+ }
217
+
218
+ private async readback(requestDigest: Digest): Promise<Digest | null> {
219
+ try {
220
+ return digestBytes(await readFile(this.artifactPath(requestDigest)));
221
+ } catch (error) {
222
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
223
+ throw error;
224
+ }
225
+ }
226
+
227
+ private async observe(stored: StoredEffect | null, requestDigest: Digest): Promise<LocalEffectObservation> {
228
+ if (!stored) return { state: "not_dispatched", requestDigest };
229
+ const observed = await this.readback(requestDigest);
230
+ if (stored.state === "intent") {
231
+ if (observed !== null && observed !== stored.artifactDigest) {
232
+ return { state: "incomplete_materialization", requestDigest,
233
+ expectedArtifactDigest: stored.artifactDigest, observedArtifactDigest: observed };
234
+ }
235
+ return observed === null
236
+ ? { state: "receipt_unknown", requestDigest, expectedArtifactDigest: stored.artifactDigest }
237
+ : { state: "materialized_without_receipt", requestDigest,
238
+ expectedArtifactDigest: stored.artifactDigest, observedArtifactDigest: observed };
239
+ }
240
+ if (observed !== null && observed !== stored.artifactDigest) {
241
+ return { state: "receipt_mismatch", requestDigest,
242
+ expectedArtifactDigest: stored.artifactDigest, observedArtifactDigest: observed };
243
+ }
244
+ if (observed === null || stored.receiptDigest === null || stored.receiptAt === null) {
245
+ return { state: "receipt_mismatch", requestDigest, expectedArtifactDigest: stored.artifactDigest };
246
+ }
247
+ const receipt: LocalEffectReceipt = {
248
+ requestDigest, artifactDigest: stored.artifactDigest, readbackDigest: observed,
249
+ receiptDigest: stored.receiptDigest, receivedAt: stored.receiptAt,
250
+ };
251
+ const expected = sha256Digest(stableStringify({
252
+ version: 1, requestDigest, artifactDigest: stored.artifactDigest,
253
+ readbackDigest: observed, receivedAt: stored.receiptAt,
254
+ }));
255
+ if (expected !== stored.receiptDigest) {
256
+ return { state: "receipt_mismatch", requestDigest, expectedArtifactDigest: stored.artifactDigest,
257
+ observedArtifactDigest: observed };
258
+ }
259
+ return { state: "receipted", requestDigest, receipt };
260
+ }
261
+
262
+ /** Independent disk readback. This never mutates or retries an effect. */
263
+ async inspect(requestDigest: Digest): Promise<LocalEffectObservation> {
264
+ if (!DIGEST.test(requestDigest)) reject("Invalid request digest");
265
+ return this.serialized(async () => this.observe(await this.load(requestDigest), requestDigest));
266
+ }
267
+
268
+ /** Reconciliation is read-only; an ambiguous receipt remains ambiguous. */
269
+ async reconcile(requestDigest: Digest): Promise<LocalEffectObservation> {
270
+ return this.inspect(requestDigest);
271
+ }
272
+
273
+ async dispatch(input: unknown, authorization: LocalEffectAuthorization): Promise<LocalEffectObservation> {
274
+ if (!authorization || typeof authorization !== "object" ||
275
+ !exactKeys(authorization as unknown as Record<string, unknown>,
276
+ ["ownerId", "challengeDigest", "expiresAt", "proof"]) ||
277
+ typeof authorization.proof !== "string" || !authorization.proof ||
278
+ authorization.proof.length > 4096) reject("Invalid owner authorization");
279
+ const { request, challenge, bytes } = this.resolve(input, authorization.expiresAt);
280
+ if (authorization.ownerId !== this.options.ownerId ||
281
+ authorization.challengeDigest !== challenge.challengeDigest) {
282
+ reject("Owner authorization does not bind this effect");
283
+ }
284
+ return this.serialized(async () => {
285
+ const existing = await this.load(challenge.requestDigest);
286
+ if (existing) return this.observe(existing, challenge.requestDigest);
287
+ const readClock = () => this.options.now?.() ?? Date.now();
288
+ const isCurrent = () => {
289
+ const current = readClock();
290
+ return Number.isSafeInteger(current) && current >= 0 && current < authorization.expiresAt;
291
+ };
292
+ if (!isCurrent()) reject("Owner authorization expired before effect dispatch");
293
+ if (!(await this.options.verifyOwner(challenge, authorization))) reject("Owner authorization was not verified");
294
+ if (!isCurrent()) reject("Owner authorization expired during effect verification");
295
+ const authorizationDigest = sha256Digest(stableStringify({
296
+ version: 1, ownerId: authorization.ownerId, challengeDigest: challenge.challengeDigest,
297
+ proofDigest: digestBytes(authorization.proof),
298
+ }));
299
+ const dir = localFabricPath(this.repositoryRoot, "effects");
300
+ await mkdir(dir, { recursive: true });
301
+ this.artifactPath(challenge.requestDigest); // reject symbolic-link substitution before intent
302
+ const now = readClock();
303
+ if (!Number.isSafeInteger(now) || now < 0 || now >= authorization.expiresAt) {
304
+ reject("Owner authorization expired before effect intent");
305
+ }
306
+ const inserted = await this.options.adapter.query(
307
+ `INSERT INTO _forge_agent_fabric_local_effects
308
+ (request_digest, challenge_digest, task_id, subject_digest, artifact_digest,
309
+ owner_id, authorization_digest, state, intent_at)
310
+ VALUES ($1,$2,$3,$4,$5,$6,$7,'intent',$8) ON CONFLICT (request_digest) DO NOTHING`,
311
+ [challenge.requestDigest, challenge.challengeDigest, request.taskId, request.subjectDigest,
312
+ challenge.artifactDigest, authorization.ownerId, authorizationDigest, now],
313
+ );
314
+ if (inserted.rowCount !== 1) {
315
+ return this.observe(await this.load(challenge.requestDigest), challenge.requestDigest);
316
+ }
317
+ // From here on, any failure is uncertain. A second dispatch only observes.
318
+ await this.options.onBoundary?.("after_intent");
319
+ const handle = await open(this.artifactPath(challenge.requestDigest), "wx", 0o600);
320
+ try {
321
+ await this.options.onBoundary?.("after_open");
322
+ await handle.writeFile(bytes, "utf8");
323
+ await handle.sync();
324
+ } finally {
325
+ await handle.close();
326
+ }
327
+ await this.options.onBoundary?.("after_write");
328
+ const observed = await this.readback(challenge.requestDigest);
329
+ await this.options.onBoundary?.("after_readback");
330
+ if (observed !== challenge.artifactDigest) {
331
+ return this.observe(await this.load(challenge.requestDigest), challenge.requestDigest);
332
+ }
333
+ const receivedAt = Date.now();
334
+ const receiptDigest = sha256Digest(stableStringify({
335
+ version: 1, requestDigest: challenge.requestDigest, artifactDigest: challenge.artifactDigest,
336
+ readbackDigest: observed, receivedAt,
337
+ }));
338
+ await this.options.adapter.query(
339
+ `UPDATE _forge_agent_fabric_local_effects
340
+ SET state = 'receipted', receipt_digest = $2, receipt_at = $3
341
+ WHERE request_digest = $1 AND state = 'intent'`,
342
+ [challenge.requestDigest, receiptDigest, receivedAt],
343
+ );
344
+ await this.options.onBoundary?.("after_receipt");
345
+ return this.observe(await this.load(challenge.requestDigest), challenge.requestDigest);
346
+ });
347
+ }
348
+ }
@@ -0,0 +1,81 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { spawn } from "node:child_process";
3
+ import { createServer } from "node:http";
4
+ import type { AddressInfo } from "node:net";
5
+ import { digestCanonical, sha256Digest } from "./canonical.ts";
6
+ import type { EvolutionOwnerVerifier, OwnerDecisionChallenge } from "./local-evolution-registry.ts";
7
+
8
+ function html(value: string): string {
9
+ return value.replace(/[&<>"']/gu, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[character]!);
10
+ }
11
+
12
+ async function launchBrowser(url: string): Promise<void> {
13
+ const command = process.platform === "win32" ? "explorer.exe" : process.platform === "darwin" ? "open" : "xdg-open";
14
+ await new Promise<void>((resolve, reject) => {
15
+ const child = spawn(command, [url], { detached: true, stdio: "ignore", windowsHide: true });
16
+ child.once("error", reject);
17
+ child.once("spawn", () => { child.unref(); resolve(); });
18
+ });
19
+ }
20
+
21
+ /** Cooperative human review on loopback; same-account UI/shell control is outside this boundary. */
22
+ export function localEvolutionOwnerVerifier(options: {
23
+ openBrowser?: (url: string) => Promise<void>;
24
+ timeoutMs?: number;
25
+ } = {}): EvolutionOwnerVerifier {
26
+ return { async verify(challenge: Readonly<OwnerDecisionChallenge>) {
27
+ const token = randomBytes(32).toString("hex");
28
+ const digest = digestCanonical(challenge, sha256Digest);
29
+ const timeoutMs = options.timeoutMs ?? 300_000;
30
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 300_000) throw new Error("Invalid owner review timeout");
31
+ let origin = "";
32
+ let decided = false;
33
+ let settle!: (approved: boolean) => void;
34
+ let fail!: (error: Error) => void;
35
+ const outcome = new Promise<boolean>((resolve, reject) => { settle = resolve; fail = reject; });
36
+ const server = createServer((request, response) => {
37
+ response.setHeader("Cache-Control", "no-store");
38
+ response.setHeader("Referrer-Policy", "no-referrer");
39
+ response.setHeader("X-Content-Type-Options", "nosniff");
40
+ response.setHeader("X-Frame-Options", "DENY");
41
+ response.setHeader("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'");
42
+ if (request.headers.host !== origin.slice(7)) { response.writeHead(400).end(); return; }
43
+ if (request.method === "GET" && request.url === `/${token}` && !decided) {
44
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
45
+ response.writeHead(200).end(`<!doctype html><html lang="en"><meta charset="utf-8"><title>Forge evolution review</title><style>body{font:16px system-ui;background:#f4f6f8;color:#14202b;padding:24px}main{max-width:720px;margin:auto;background:white;padding:24px;border-radius:12px}dt{font-weight:bold;margin-top:14px}dd{margin:4px 0;overflow-wrap:anywhere}button{padding:12px;margin:12px 8px 0 0;cursor:pointer}</style><main><h1>Review extension decision</h1><p>This local decision changes the selected extension version. Inspect the fixed evaluation and version status before approving.</p><dl><dt>Action</dt><dd>${html(challenge.action)}</dd><dt>Extension</dt><dd>${html(challenge.extensionKey)}</dd><dt>Version</dt><dd>${html(challenge.versionId)}</dd><dt>Expected current selection</dt><dd>${html(challenge.expectedSelection ?? "none")}</dd><dt>Evaluation evidence</dt><dd>${html(challenge.evaluationDigest ?? "none")}</dd><dt>Decision digest</dt><dd>${html(digest)}</dd></dl><form method="post" action="/decision/${token}"><input type="hidden" name="digest" value="${html(digest)}"><button name="decision" value="approved">Approve ${html(challenge.action)}</button><button name="decision" value="rejected">Reject</button></form></main></html>`);
46
+ return;
47
+ }
48
+ const sameOrigin = request.headers.origin === origin ||
49
+ (request.headers.origin === "null" && request.headers["sec-fetch-site"] === "same-origin");
50
+ if (request.method !== "POST" || request.url !== `/decision/${token}` || decided || !sameOrigin ||
51
+ request.headers["content-type"] !== "application/x-www-form-urlencoded") {
52
+ response.writeHead(403).end(); return;
53
+ }
54
+ let body = "";
55
+ request.on("data", (part: Buffer) => { body += part.toString("utf8"); if (body.length > 2048) request.destroy(); });
56
+ request.on("end", () => {
57
+ const values = new URLSearchParams(body);
58
+ const decision = values.get("decision");
59
+ if (values.size !== 2 || values.get("digest") !== digest ||
60
+ (decision !== "approved" && decision !== "rejected")) { response.writeHead(400).end(); return; }
61
+ decided = true;
62
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
63
+ response.writeHead(200).end(`<meta charset="utf-8"><p>${decision === "approved" ? "Approved" : "Rejected"}. You may close this window.</p>`);
64
+ response.once("finish", () => settle(decision === "approved"));
65
+ });
66
+ });
67
+ await new Promise<void>((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); });
68
+ origin = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
69
+ const timer = setTimeout(() => fail(new Error("Owner review timed out")), timeoutMs);
70
+ try {
71
+ await (options.openBrowser ?? launchBrowser)(`${origin}/${token}`);
72
+ if (!await outcome) throw new Error("Owner rejected evolution decision");
73
+ return { verifierId: "local-owner-window", challengeDigest: digest,
74
+ evidenceDigest: sha256Digest(`owner-approved:${digest}:${token}`) };
75
+ } finally {
76
+ clearTimeout(timer);
77
+ server.closeAllConnections();
78
+ await new Promise<void>((resolve) => server.close(() => resolve()));
79
+ }
80
+ } };
81
+ }
@@ -0,0 +1,87 @@
1
+ import { stableStringify } from "./canonical.ts";
2
+ import { AgentFabricError } from "./errors.ts";
3
+
4
+ export const LOCAL_ADAPTIVE_EXTENSION_KEY = "local-adaptive-input-profile";
5
+ const HARNESS_INPUT_LIMIT = 256;
6
+ const LABEL = /^[a-z][a-z0-9-]{0,23}$/u;
7
+
8
+ export interface LocalAdaptiveInputRule {
9
+ maxLength: number;
10
+ requiredLabel: string;
11
+ }
12
+
13
+ /** Data-only restrictions on the two fixed local adaptive worker inputs. */
14
+ export interface LocalAdaptiveInputProfile {
15
+ schemaVersion: 1;
16
+ kind: "local-adaptive-input-profile";
17
+ inventory: LocalAdaptiveInputRule;
18
+ constraints: LocalAdaptiveInputRule;
19
+ }
20
+
21
+ function invalid(message: string): never {
22
+ throw new AgentFabricError("AF_INVALID_STATE", message);
23
+ }
24
+
25
+ function exactKeys(value: unknown, keys: readonly string[]): value is Record<string, unknown> {
26
+ return !!value && typeof value === "object" && !Array.isArray(value) &&
27
+ Object.keys(value).sort().join(",") === [...keys].sort().join(",");
28
+ }
29
+
30
+ function validRule(value: unknown): value is LocalAdaptiveInputRule {
31
+ if (!exactKeys(value, ["maxLength", "requiredLabel"])) return false;
32
+ const label = value.requiredLabel;
33
+ return typeof label === "string" && LABEL.test(label) &&
34
+ Number.isSafeInteger(value.maxLength) &&
35
+ (value.maxLength as number) >= label.length + 3 &&
36
+ (value.maxLength as number) <= HARNESS_INPUT_LIMIT;
37
+ }
38
+
39
+ /** Require one canonical UTF-8 JSON document; duplicate/unknown keys and code are excluded. */
40
+ export function parseLocalAdaptiveInputProfile(bytes: Buffer): LocalAdaptiveInputProfile {
41
+ if (!Buffer.isBuffer(bytes) || bytes.length < 1 || bytes.length > 1024) {
42
+ invalid("Local adaptive profile size is invalid");
43
+ }
44
+ let value: unknown;
45
+ let raw: string;
46
+ try { raw = new TextDecoder("utf-8", { fatal: true }).decode(bytes); }
47
+ catch { invalid("Local adaptive profile is not UTF-8"); }
48
+ try { value = JSON.parse(raw); } catch { invalid("Local adaptive profile is invalid JSON"); }
49
+ if (!exactKeys(value, ["schemaVersion", "kind", "inventory", "constraints"]) ||
50
+ value.schemaVersion !== 1 || value.kind !== "local-adaptive-input-profile" ||
51
+ !validRule(value.inventory) || !validRule(value.constraints) ||
52
+ raw !== `${stableStringify(value)}\n`) {
53
+ invalid("Local adaptive profile is outside the v1 data contract");
54
+ }
55
+ return value as unknown as LocalAdaptiveInputProfile;
56
+ }
57
+
58
+ export function acceptsLocalAdaptiveInput(rule: LocalAdaptiveInputRule, value: string): boolean {
59
+ return typeof value === "string" && value.length <= rule.maxLength &&
60
+ value.startsWith(`${rule.requiredLabel}:`) &&
61
+ value.length > rule.requiredLabel.length + 1;
62
+ }
63
+
64
+ export function assertLocalAdaptiveInputs(profile: LocalAdaptiveInputProfile,
65
+ inventory: string, constraints: string): void {
66
+ if (!acceptsLocalAdaptiveInput(profile.inventory, inventory) ||
67
+ !acceptsLocalAdaptiveInput(profile.constraints, constraints)) {
68
+ invalid("Local adaptive inputs do not satisfy the selected data profile");
69
+ }
70
+ }
71
+
72
+ /** Fixed behavioral probes are evaluated without running artifact code or worker code. */
73
+ export function evaluateLocalAdaptiveProfile(bytes: Buffer): {
74
+ contract: boolean; acceptsValid: boolean; rejectsWrongLabel: boolean; rejectsOverLimit: boolean;
75
+ } {
76
+ let profile: LocalAdaptiveInputProfile;
77
+ try { profile = parseLocalAdaptiveInputProfile(bytes); }
78
+ catch { return { contract: false, acceptsValid: false, rejectsWrongLabel: false, rejectsOverLimit: false }; }
79
+ const rules = [profile.inventory, profile.constraints];
80
+ return {
81
+ contract: true,
82
+ acceptsValid: rules.every((rule) => acceptsLocalAdaptiveInput(rule, `${rule.requiredLabel}:x`)),
83
+ rejectsWrongLabel: rules.every((rule) => !acceptsLocalAdaptiveInput(rule, `wrong:${rule.requiredLabel}:x`)),
84
+ rejectsOverLimit: rules.every((rule) =>
85
+ !acceptsLocalAdaptiveInput(rule, `${rule.requiredLabel}:${"x".repeat(rule.maxLength)}`)),
86
+ };
87
+ }