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,253 @@
1
+ import { createHash } from "node:crypto";
2
+ import { execFile } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+ import { chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
5
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
+ import { tmpdir } from "node:os";
7
+ import { stableStringify } from "./canonical.ts";
8
+ import { verifyManagedEnvironment, type ManagedEnvironment } from "./managed-environment.ts";
9
+
10
+ export interface ManagedBase { root: string; runId: string; head: string; scope: string[]; digest: string; baselineDirectory: string; contextScope?: string[]; contextDigest?: string }
11
+ export interface ManagedFile { path: string; beforeDigest: string | null; contentBase64: string | null; mode?: number }
12
+ export interface ManagedArtifact { digest: string; files: ManagedFile[] }
13
+ interface Entry { path: string; digest: string; mode: number; contentBase64?: string; readonlySize?: number }
14
+ interface Inventory { files: Entry[]; digest: string }
15
+ interface WorkspaceMetadata { baseDigest: string; runId: string; attemptId: string; directory: string; input: Inventory }
16
+ const runFile = promisify(execFile);
17
+ // Baselines include generated Forge graphs (~10MiB); output artifacts retain the smaller bound.
18
+ const MAX_FILES = 20_000, MAX_BYTES = 128 * 1024 * 1024, MAX_SNAPSHOT_FILE_BYTES = 16 * 1024 * 1024, MAX_ARTIFACT_FILE_BYTES = 8 * 1024 * 1024;
19
+ // Large assets outside the editable/context scope stay in Git clones. Only their
20
+ // streaming fingerprints enter inventories; editable snapshots retain their limits.
21
+ const MAX_READONLY_ASSET_BYTES = 512 * 1024 * 1024, MAX_READONLY_BYTES = 1024 * 1024 * 1024;
22
+ const hash = (data: string | Uint8Array) => `sha256:${createHash("sha256").update(data).digest("hex")}`;
23
+ function assert(value: unknown, message: string): asserts value { if (!value) throw new Error(`managed workspace: ${message}`); }
24
+ function pathName(value: string): string {
25
+ assert(typeof value === "string" && value.length > 0 && !isAbsolute(value) && !/[\\:\x00]/.test(value), "invalid relative path");
26
+ const parts = value.split("/"); assert(parts.every(part => part && part !== "." && part !== ".." && part.toLowerCase() !== ".git"), "unsafe path");
27
+ assert(!parts.some(part => /[. ]$/.test(part) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part)), "unsafe Windows path");
28
+ return value;
29
+ }
30
+ function scopes(input: string[]): string[] { assert(Array.isArray(input) && input.length > 0 && input.length <= 1000, "scope required"); const result = input.map(pathName); assert(result.every(path => path.toLowerCase() !== ".forge" && !path.toLowerCase().startsWith(".forge/")), "owned .forge metadata cannot be task scope"); assert(new Set(result.map(path => path.toLowerCase())).size === result.length, "duplicate scope"); return result.sort(); }
31
+ const withinScope = (path: string, scope: string[]) => scope.some(item => path === item || path.startsWith(`${item}/`));
32
+ async function safePath(root: string, path: string): Promise<string> {
33
+ pathName(path); const absolute = resolve(root, path), rel = relative(resolve(root), absolute);
34
+ assert(rel && !rel.startsWith(`..${sep}`) && !isAbsolute(rel), "path escapes directory");
35
+ let cursor = resolve(root); assert(!(await lstat(cursor)).isSymbolicLink(), "symlink root rejected");
36
+ for (const part of path.split("/")) { cursor = join(cursor, part); try { assert(!(await lstat(cursor)).isSymbolicLink(), "symlink rejected"); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") break; throw error; } }
37
+ return absolute;
38
+ }
39
+ async function git(root: string, args: string[]) { return (await runFile("git", ["-C", root, ...args], { windowsHide: true, maxBuffer: 16 * 1024 * 1024 })).stdout.trim(); }
40
+ const contextNames = ["package.json", "package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", "yarn.lock", ".yarnrc.yml", "bun.lock", "bun.lockb", "bunfig.toml", "forge.manifest.json"];
41
+ async function readonlyContextPaths(root: string, scope: string[]): Promise<string[]> {
42
+ const tracked = (await git(root, ["ls-files", "-z"])).split("\0").filter(Boolean);
43
+ const result = new Set(tracked.filter(path => contextNames.includes(path.split("/").at(-1)!) && !withinScope(path, scope)));
44
+ for (const name of contextNames) try { await lstat(join(root, name)); if (!withinScope(name, scope)) result.add(name); } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
45
+ const paths = [...result].sort(); assert(paths.length <= 1000, "context metadata count exceeded"); for (const path of paths) pathName(path); return paths;
46
+ }
47
+ async function currentContextMatches(base: ManagedBase): Promise<boolean> {
48
+ return base.contextScope === undefined || (await inventory(base.root, base.contextScope)).digest === base.contextDigest;
49
+ }
50
+ function inventoryDigest(files: Entry[]): string {
51
+ return hash(JSON.stringify(files.map(({ path, digest, mode, readonlySize }) => ({ path, digest, mode, ...(readonlySize === undefined ? {} : { readonlySize }) }))));
52
+ }
53
+ function entryContent(file: Entry): string { assert(typeof file.contentBase64 === "string" && file.readonlySize === undefined, "read-only asset cannot be materialized or published"); return file.contentBase64; }
54
+ async function readonlyAssetDigest(absolute: string, size: number): Promise<string> {
55
+ const handle = await open(absolute, "r");
56
+ try {
57
+ const before = await handle.stat();
58
+ assert(before.isFile() && before.size === size, "asset changed before hashing");
59
+ const digest = createHash("sha256"); let bytes = 0;
60
+ for await (const chunk of handle.createReadStream({ autoClose: false, highWaterMark: 256 * 1024 })) {
61
+ bytes += chunk.length; assert(bytes <= size && bytes <= MAX_READONLY_ASSET_BYTES, "asset changed or size bound exceeded"); digest.update(chunk);
62
+ }
63
+ const after = await handle.stat(), current = await lstat(absolute);
64
+ assert(bytes === size && after.size === before.size && after.mtimeMs === before.mtimeMs && after.ctimeMs === before.ctimeMs
65
+ && current.isFile() && !current.isSymbolicLink() && current.ino === before.ino && current.size === before.size
66
+ && current.mtimeMs === before.mtimeMs && current.ctimeMs === before.ctimeMs, "asset changed during hashing");
67
+ return `sha256:${digest.digest("hex")}`;
68
+ } finally { await handle.close(); }
69
+ }
70
+ async function inventory(root: string, scope?: string[], excluded: string[] = [], boundedScope: string[] = []): Promise<Inventory> {
71
+ const files: Entry[] = []; let bytes = 0, readonlyBytes = 0;
72
+ const visit = async (directory: string, prefix: string) => {
73
+ for (const name of (await readdir(directory)).sort()) {
74
+ if (!prefix && name.toLowerCase() === ".git") continue;
75
+ const path = prefix ? `${prefix}/${name}` : name;
76
+ if (excluded.some(item => path === item || path.startsWith(`${item}/`))) continue;
77
+ if (scope && !withinScope(path, scope) && !scope.some(item => item.startsWith(`${path}/`))) continue;
78
+ pathName(path); const absolute = await safePath(root, path), stat = await lstat(absolute);
79
+ assert(!stat.isSymbolicLink(), "symlink rejected");
80
+ if (stat.isDirectory()) await visit(absolute, path);
81
+ else {
82
+ assert(stat.isFile(), "special file rejected"); assert(files.length < MAX_FILES, "snapshot bound exceeded");
83
+ if (stat.size > MAX_SNAPSHOT_FILE_BYTES && !scope && !withinScope(path, boundedScope)) {
84
+ readonlyBytes += stat.size;
85
+ assert(stat.size <= MAX_READONLY_ASSET_BYTES && readonlyBytes <= MAX_READONLY_BYTES, "read-only asset size bound exceeded");
86
+ files.push({ path, digest: await readonlyAssetDigest(absolute, stat.size), mode: stat.mode & 0o111 ? 0o755 : 0o644, readonlySize: stat.size });
87
+ continue;
88
+ }
89
+ assert(stat.size <= MAX_SNAPSHOT_FILE_BYTES, "file size bound exceeded");
90
+ const content = await readFile(absolute); bytes += content.length;
91
+ assert(bytes <= MAX_BYTES && files.length < MAX_FILES, "snapshot bound exceeded");
92
+ files.push({ path, digest: hash(content), mode: stat.mode & 0o111 ? 0o755 : 0o644, contentBase64: content.toString("base64") });
93
+ }
94
+ }
95
+ };
96
+ await visit(resolve(root), ""); files.sort((a, b) => a.path.localeCompare(b.path));
97
+ assert(new Set(files.map(file => file.path.toLowerCase())).size === files.length, "case-colliding paths rejected");
98
+ return { files, digest: inventoryDigest(files) };
99
+ }
100
+ function workspaceInventory(base: ManagedBase, directory: string, excluded: string[] = []): Promise<Inventory> {
101
+ return inventory(directory, undefined, excluded, [...base.scope, ...(base.contextScope ?? [])]);
102
+ }
103
+ async function setFile(root: string, file: { path: string; contentBase64?: string | null; mode?: number }) {
104
+ const absolute = await safePath(root, file.path);
105
+ if (file.contentBase64 === null) { try { const stat = await lstat(absolute); assert(stat.isFile(), "delete target is not a regular file"); await rm(absolute); } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } return; }
106
+ assert(typeof file.contentBase64 === "string", "read-only asset cannot be materialized or published");
107
+ await mkdir(dirname(absolute), { recursive: true }); await writeFile(absolute, Buffer.from(file.contentBase64, "base64")); await chmod(absolute, file.mode ?? 0o644);
108
+ }
109
+ async function clone(root: string, prefix: string): Promise<string> {
110
+ const owned = await mkdtemp(join(tmpdir(), prefix)), directory = join(owned, "checkout");
111
+ await runFile("git", ["clone", "--quiet", "--no-hardlinks", "--no-checkout", "--", root, directory], { windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
112
+ return directory;
113
+ }
114
+ function artifactDigest(files: ManagedFile[]) { return hash(stableStringify(files)); }
115
+ function validateArtifact(artifact: ManagedArtifact, scope: string[]) {
116
+ assert(artifact && Object.keys(artifact).every(key => ["digest", "files"].includes(key)) && Array.isArray(artifact.files), "invalid artifact");
117
+ assert(artifact.files.length <= MAX_FILES, "artifact count bound exceeded"); let bytes = 0;
118
+ const paths = new Set<string>();
119
+ for (const file of artifact.files) {
120
+ assert(file && Object.keys(file).every(key => ["path", "beforeDigest", "contentBase64", "mode"].includes(key)), "invalid artifact file");
121
+ pathName(file.path); assert(withinScope(file.path, scope) && !paths.has(file.path.toLowerCase()), "out-of-scope or duplicate artifact path"); paths.add(file.path.toLowerCase());
122
+ assert(file.beforeDigest === null || /^sha256:[a-f0-9]{64}$/.test(file.beforeDigest), "invalid before digest");
123
+ assert(file.mode === undefined || file.mode === 0o644 || file.mode === 0o755, "invalid file mode");
124
+ assert(file.contentBase64 === null || typeof file.contentBase64 === "string", "invalid artifact bytes");
125
+ if (file.contentBase64 !== null) { const content = Buffer.from(file.contentBase64, "base64"); assert(content.toString("base64") === file.contentBase64 && content.length <= MAX_ARTIFACT_FILE_BYTES, "invalid or excessive base64"); bytes += content.length; }
126
+ }
127
+ assert(bytes <= MAX_BYTES && artifact.digest === artifactDigest(artifact.files), "artifact digest or size mismatch");
128
+ }
129
+ function applyEntries(input: Entry[], artifacts: ManagedArtifact[], scope: string[]): Entry[] {
130
+ const map = new Map(input.map(file => [file.path, { ...file }]));
131
+ for (const artifact of artifacts) { validateArtifact(artifact, scope); for (const file of artifact.files) {
132
+ const before = map.get(file.path); assert((before?.digest ?? null) === file.beforeDigest, `conflicting beforeimage: ${file.path}`);
133
+ assert(before?.readonlySize === undefined, "read-only asset cannot be changed by artifacts");
134
+ if (file.contentBase64 === null) { assert(before, "deletion of missing file"); map.delete(file.path); }
135
+ else map.set(file.path, { path: file.path, digest: hash(Buffer.from(file.contentBase64, "base64")), mode: file.mode ?? before?.mode ?? 0o644, contentBase64: file.contentBase64 });
136
+ } }
137
+ const result = [...map.values()].sort((a, b) => a.path.localeCompare(b.path));
138
+ assert(new Set(result.map(file => file.path.toLowerCase())).size === result.length, "case-colliding artifact paths");
139
+ for (const file of result) assert(!result.some(other => other.path.startsWith(`${file.path}/`)), "file/directory artifact collision");
140
+ return result;
141
+ }
142
+ async function validateBase(base: ManagedBase) {
143
+ scopes(base.scope); assert(base.root === resolve(base.root) && base.baselineDirectory === resolve(base.baselineDirectory), "invalid base paths");
144
+ const marker = JSON.parse(await readFile(join(base.baselineDirectory, ".git", "forge-managed-base.json"), "utf8")) as ManagedBase;
145
+ assert(stableStringify(marker) === stableStringify(base), "base identity mismatch");
146
+ if (base.contextScope !== undefined) { assert(Array.isArray(base.contextScope) && base.contextScope.length <= 1000 && /^sha256:[a-f0-9]{64}$/.test(base.contextDigest ?? ""), "invalid context metadata"); for (const path of base.contextScope) pathName(path); }
147
+ const baselineDigest = await readFile(join(base.baselineDirectory, ".git", "forge-managed-baseline-digest"), "utf8");
148
+ assert((await workspaceInventory(base, base.baselineDirectory)).digest === baselineDigest && (await inventory(base.baselineDirectory, base.scope)).digest === base.digest, "immutable baseline modified");
149
+ }
150
+ export async function captureManagedBase(root: string, runId: string, scope: string[]): Promise<ManagedBase> {
151
+ root = resolve(root); assert(typeof runId === "string" && runId.trim().length > 0, "runId required"); scope = scopes(scope);
152
+ for (const path of scope) await safePath(root, path);
153
+ const contextScope = await readonlyContextPaths(root, scope), head = await git(root, ["rev-parse", "HEAD"]), snapshot = await inventory(root, scope), context = await inventory(root, contextScope), capturedSource = await inventory(root, [...scope, ...contextScope]), baselineDirectory = await clone(root, "forge-managed-base-");
154
+ await git(baselineDirectory, ["checkout", "--quiet", "--detach", head]);
155
+ const tracked = await inventory(baselineDirectory, [...scope, ...contextScope]);
156
+ const captured = new Map(capturedSource.files.map(file => [file.path, file])), prior = new Map(tracked.files.map(file => [file.path, file]));
157
+ for (const file of tracked.files) if (!captured.has(file.path)) await setFile(baselineDirectory, { path: file.path, contentBase64: null });
158
+ for (const file of capturedSource.files) if (prior.get(file.path)?.digest !== file.digest || prior.get(file.path)?.mode !== file.mode) await setFile(baselineDirectory, file);
159
+ assert((await inventory(root, scope)).digest === snapshot.digest && (await inventory(root, contextScope)).digest === context.digest && await git(root, ["rev-parse", "HEAD"]) === head, "source changed during capture");
160
+ const base: ManagedBase = { root, runId, head, scope, digest: snapshot.digest, baselineDirectory, ...(contextScope.length ? { contextScope, contextDigest: context.digest } : {}) };
161
+ await writeFile(join(baselineDirectory, ".git", "forge-managed-base.json"), JSON.stringify(base));
162
+ await writeFile(join(baselineDirectory, ".git", "forge-managed-baseline-digest"), (await workspaceInventory(base, baselineDirectory)).digest); return base;
163
+ }
164
+ export async function prepareManagedWorkspace(base: ManagedBase, attemptId: string, dependencies: ManagedArtifact[]): Promise<{ directory: string; inputDigest: string }> {
165
+ await validateBase(base); assert(typeof attemptId === "string" && attemptId.trim().length > 0, "attemptId required");
166
+ const baseline = await workspaceInventory(base, base.baselineDirectory), desired = applyEntries(baseline.files, dependencies, base.scope), directory = await clone(base.baselineDirectory, "forge-managed-attempt-");
167
+ await git(directory, ["checkout", "--quiet", "--detach", base.head]);
168
+ const initial = await workspaceInventory(base, directory);
169
+ const desiredByPath = new Map(desired.map(file => [file.path, file])), initialByPath = new Map(initial.files.map(file => [file.path, file]));
170
+ for (const file of initial.files) if (!desiredByPath.has(file.path)) await setFile(directory, { path: file.path, contentBase64: null });
171
+ for (const file of desired) if (initialByPath.get(file.path)?.digest !== file.digest || initialByPath.get(file.path)?.mode !== file.mode) await setFile(directory, file);
172
+ const input = await workspaceInventory(base, directory), metadata: WorkspaceMetadata = { baseDigest: base.digest, runId: base.runId, attemptId, directory, input };
173
+ await writeFile(join(directory, ".git", "forge-managed-input.json"), JSON.stringify(metadata)); return { directory, inputDigest: input.digest };
174
+ }
175
+ export async function captureManagedArtifact(base: ManagedBase, workspaceDirectory: string, writeScope: string[], expectedInputDigest: string, environment?: ManagedEnvironment, generatedPaths: string[] = []): Promise<ManagedArtifact> {
176
+ await validateBase(base); assert(Array.isArray(writeScope), "write scope must be an array"); writeScope = writeScope.length ? scopes(writeScope) : []; assert(writeScope.every(path => withinScope(path, base.scope)), "write scope exceeds base");
177
+ workspaceDirectory = resolve(workspaceDirectory);
178
+ const metadata = JSON.parse(await readFile(join(workspaceDirectory, ".git", "forge-managed-input.json"), "utf8")) as WorkspaceMetadata;
179
+ assert(metadata.directory === workspaceDirectory && metadata.baseDigest === base.digest && metadata.runId === base.runId, "workspace identity mismatch");
180
+ assert(/^sha256:[a-f0-9]{64}$/.test(expectedInputDigest) && metadata.input.digest === expectedInputDigest && metadata.input.digest === inventoryDigest(metadata.input.files)
181
+ && metadata.input.files.every(file => file.readonlySize === undefined ? typeof file.contentBase64 === "string" && hash(Buffer.from(file.contentBase64, "base64")) === file.digest
182
+ : file.contentBase64 === undefined && Number.isSafeInteger(file.readonlySize) && file.readonlySize > MAX_SNAPSHOT_FILE_BYTES && file.readonlySize <= MAX_READONLY_ASSET_BYTES
183
+ && !withinScope(file.path, [...base.scope, ...(base.contextScope ?? [])]) && /^sha256:[a-f0-9]{64}$/.test(file.digest)), "input inventory corrupted or coordinator digest mismatch");
184
+ if (environment) { assert(environment.directory === workspaceDirectory, "environment belongs to another checkout"); await verifyManagedEnvironment(environment); assert(!metadata.input.files.some(file => environment.derivedPaths.some(path => file.path === path || file.path.startsWith(`${path}/`))), "derived environment would conceal captured source"); }
185
+ if (generatedPaths.length) { scopes(generatedPaths); assert(!generatedPaths.some(path => withinScope(path, base.scope) || base.scope.some(scope => withinScope(scope, [path]))) && !metadata.input.files.some(file => withinScope(file.path, generatedPaths)), "generated paths would conceal candidate/source"); }
186
+ const current = await workspaceInventory(base, workspaceDirectory, [...(environment?.derivedPaths ?? []), ...generatedPaths]), prior = new Map(metadata.input.files.map(file => [file.path, file])), after = new Map(current.files.map(file => [file.path, file]));
187
+ const files: ManagedFile[] = [];
188
+ for (const path of [...new Set([...prior.keys(), ...after.keys()])].sort()) {
189
+ const before = prior.get(path), next = after.get(path); if (before?.digest === next?.digest && before?.mode === next?.mode) continue;
190
+ assert(withinScope(path, writeScope), `unauthorized change: ${path}`);
191
+ files.push({ path, beforeDigest: before?.digest ?? null, contentBase64: next ? entryContent(next) : null, ...(next ? { mode: next.mode } : {}) });
192
+ }
193
+ const artifact = { digest: artifactDigest(files), files }; validateArtifact(artifact, writeScope); return artifact;
194
+ }
195
+ export async function publishManagedArtifacts(base: ManagedBase, artifacts: ManagedArtifact[], controls?: { beforeWrite?: () => Promise<void> }): Promise<{ digest: string; changedFiles: string[] }> {
196
+ await validateBase(base); const before = await inventory(base.root, base.scope);
197
+ assert(before.digest === base.digest && await currentContextMatches(base) && await git(base.root, ["rev-parse", "HEAD"]) === base.head, "source changed since capture (including readonly environment context)");
198
+ const after = applyEntries(before.files, artifacts, base.scope), old = new Map(before.files.map(file => [file.path, file])), desired = new Map(after.map(file => [file.path, file]));
199
+ const changedFiles = [...new Set([...old.keys(), ...desired.keys()])].filter(path => old.get(path)?.digest !== desired.get(path)?.digest || old.get(path)?.mode !== desired.get(path)?.mode).sort();
200
+ for (const path of changedFiles) {
201
+ const target = await safePath(base.root, path);
202
+ try { assert((await lstat(target)).isFile(), "publish target is not a regular file"); } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
203
+ }
204
+ const stageParent = await safePath(base.root, ".forge/local/agent-fabric/publication-staging");
205
+ await mkdir(stageParent, { recursive: true }); await safePath(base.root, ".forge/local/agent-fabric/publication-staging");
206
+ const stage = await mkdtemp(join(stageParent, "publication-"));
207
+ for (let index = 0; index < changedFiles.length; index++) { const file = desired.get(changedFiles[index]); if (file) await writeFile(join(stage, `${index}`), Buffer.from(entryContent(file), "base64")); }
208
+ assert((await inventory(base.root, base.scope)).digest === before.digest && await currentContextMatches(base), "source or context changed before publication");
209
+ const applied: { path: string; writtenDigest: string | null; writtenMode: number | null }[] = [];
210
+ try {
211
+ for (let index = 0; index < changedFiles.length; index++) {
212
+ await controls?.beforeWrite?.();
213
+ const path = changedFiles[index], file = desired.get(path), target = await safePath(base.root, path);
214
+ let currentDigest: string | null = null, currentMode: number | null = null;
215
+ try { const stat = await lstat(target); assert(stat.isFile(), "publication target changed type"); currentDigest = hash(await readFile(target)); currentMode = stat.mode & 0o111 ? 0o755 : 0o644; } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
216
+ assert(currentDigest === (old.get(path)?.digest ?? null) && currentMode === (old.get(path)?.mode ?? null), `concurrent publication edit: ${path}`);
217
+ if (!file) { await setFile(base.root, { path, contentBase64: null }); applied.push({ path, writtenDigest: null, writtenMode: null }); }
218
+ else {
219
+ await mkdir(dirname(target), { recursive: true }); const staged = join(stage, `${index}`), stagedMode = (await lstat(staged)).mode & 0o111 ? 0o755 : 0o644;
220
+ await rename(staged, target); const written = { path, writtenDigest: file.digest, writtenMode: stagedMode }; applied.push(written);
221
+ await chmod(target, file.mode); written.writtenMode = file.mode;
222
+ }
223
+ }
224
+ } catch (error) {
225
+ const uncertain: string[] = [];
226
+ for (const written of applied.reverse()) {
227
+ try {
228
+ const target = await safePath(base.root, written.path); let currentDigest: string | null = null, currentMode: number | null = null;
229
+ try { const stat = await lstat(target); assert(stat.isFile(), "rollback target changed type"); currentDigest = hash(await readFile(target)); currentMode = stat.mode & 0o111 ? 0o755 : 0o644; }
230
+ catch (readError) { if ((readError as NodeJS.ErrnoException).code !== "ENOENT") throw readError; }
231
+ if (currentDigest !== written.writtenDigest || currentMode !== written.writtenMode) { uncertain.push(written.path); continue; }
232
+ const file = old.get(written.path); await setFile(base.root, file ?? { path: written.path, contentBase64: null });
233
+ } catch { uncertain.push(written.path); }
234
+ }
235
+ if (uncertain.length) throw new Error(`managed workspace: uncertain publication; external edits or rollback failure preserved at ${uncertain.join(", ")}; evidence retained at ${stage}`, { cause: error });
236
+ throw error;
237
+ }
238
+ await rm(stage, { recursive: true }); return { digest: (await inventory(base.root, base.scope)).digest, changedFiles };
239
+ }
240
+ /** Compute publication outcome from captured source without changing the user's checkout. */
241
+ export async function previewManagedArtifacts(base: ManagedBase, artifacts: ManagedArtifact[]): Promise<{ digest: string; changedFiles: string[] }> {
242
+ await validateBase(base); const before = await inventory(base.baselineDirectory, base.scope), after = applyEntries(before.files, artifacts, base.scope);
243
+ const old = new Map(before.files.map(file => [file.path, file])), desired = new Map(after.map(file => [file.path, file]));
244
+ return { digest: inventoryDigest(after), changedFiles: [...new Set([...old.keys(), ...desired.keys()])].filter(path => old.get(path)?.digest !== desired.get(path)?.digest || old.get(path)?.mode !== desired.get(path)?.mode).sort() };
245
+ }
246
+ /** Crash recovery confirms only a completely materialized publication. It never writes. */
247
+ export async function confirmManagedPublication(base: ManagedBase, artifacts: ManagedArtifact[]): Promise<boolean> {
248
+ const expected = await previewManagedArtifacts(base, artifacts);
249
+ return (await inventory(base.root, base.scope)).digest === expected.digest && await currentContextMatches(base) && await git(base.root, ["rev-parse", "HEAD"]) === base.head;
250
+ }
251
+ export async function confirmManagedBaseline(base: ManagedBase): Promise<boolean> {
252
+ await validateBase(base); return (await inventory(base.root, base.scope)).digest === base.digest && await currentContextMatches(base) && await git(base.root, ["rev-parse", "HEAD"]) === base.head;
253
+ }
@@ -0,0 +1,104 @@
1
+ import { ForgeAgentConductor } from "./hardened-conductor.ts";
2
+ import type {
3
+ AgentAdapter,
4
+ AttemptExecutionPermit,
5
+ AttemptUncertaintyObservation,
6
+ AuthoritativeOutcomeCommit,
7
+ WorkerResultReport,
8
+ } from "./types.ts";
9
+
10
+ export interface ExecuteP0aActivityInput {
11
+ conductor: ForgeAgentConductor;
12
+ adapter: AgentAdapter;
13
+ permit: AttemptExecutionPermit;
14
+ }
15
+
16
+ export type P0aActivityExecutionResult =
17
+ | AuthoritativeOutcomeCommit
18
+ | { status: "unknown"; observation: AttemptUncertaintyObservation };
19
+
20
+ function describeUnknown(error: unknown): string {
21
+ return error instanceof Error ? `${error.name}: ${error.message}` : "adapter_operation_threw";
22
+ }
23
+
24
+ function recordUncertainty(
25
+ conductor: ForgeAgentConductor,
26
+ permit: AttemptExecutionPermit,
27
+ phase: "startup" | "outcome",
28
+ reason: string,
29
+ ): P0aActivityExecutionResult {
30
+ return {
31
+ status: "unknown",
32
+ observation: conductor.recordAttemptUncertainty(permit, phase, reason),
33
+ };
34
+ }
35
+
36
+ export async function executeP0aActivity(
37
+ input: ExecuteP0aActivityInput,
38
+ ): Promise<P0aActivityExecutionResult> {
39
+ input.conductor.authorizeAttemptDispatch(input.permit);
40
+
41
+ let startup;
42
+ try {
43
+ startup = await input.adapter.startAttempt(input.permit);
44
+ } catch (error) {
45
+ return recordUncertainty(
46
+ input.conductor,
47
+ input.permit,
48
+ "startup",
49
+ describeUnknown(error),
50
+ );
51
+ }
52
+
53
+ if (startup.status === "unknown") {
54
+ return recordUncertainty(
55
+ input.conductor,
56
+ input.permit,
57
+ "startup",
58
+ startup.reason,
59
+ );
60
+ }
61
+
62
+ try {
63
+ input.conductor.acceptStartupReport(input.permit, startup.report);
64
+ } catch (error) {
65
+ return recordUncertainty(
66
+ input.conductor,
67
+ input.permit,
68
+ "startup",
69
+ `startup_report_rejected:${describeUnknown(error)}`,
70
+ );
71
+ }
72
+
73
+ let outcome;
74
+ try {
75
+ outcome = await input.adapter.collectOutcome(input.permit.attemptId);
76
+ } catch (error) {
77
+ return recordUncertainty(
78
+ input.conductor,
79
+ input.permit,
80
+ "outcome",
81
+ describeUnknown(error),
82
+ );
83
+ }
84
+
85
+ if (outcome.status === "unknown") {
86
+ return recordUncertainty(
87
+ input.conductor,
88
+ input.permit,
89
+ "outcome",
90
+ outcome.reason,
91
+ );
92
+ }
93
+
94
+ try {
95
+ return input.conductor.commitOutcome(outcome.report as WorkerResultReport);
96
+ } catch (error) {
97
+ return recordUncertainty(
98
+ input.conductor,
99
+ input.permit,
100
+ "outcome",
101
+ `outcome_report_rejected:${describeUnknown(error)}`,
102
+ );
103
+ }
104
+ }