forgeos 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (406) hide show
  1. package/.npmignore +4 -0
  2. package/AGENTS.md +168 -81
  3. package/CHANGELOG.md +199 -0
  4. package/README.md +88 -14
  5. package/adapters/go/README.md +23 -0
  6. package/adapters/go/go.mod +3 -0
  7. package/adapters/go/http.go +149 -0
  8. package/adapters/go/registry.go +234 -0
  9. package/adapters/go/types.go +136 -0
  10. package/adapters/java/README.md +68 -0
  11. package/adapters/java/pom.xml +34 -0
  12. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  13. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  14. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  15. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  16. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  17. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  32. package/adapters/java/target/classes/dev/forgeos/adapter/Auth.class +0 -0
  33. package/adapters/java/target/classes/dev/forgeos/adapter/Diagnostic.class +0 -0
  34. package/adapters/java/target/classes/dev/forgeos/adapter/Entry.class +0 -0
  35. package/adapters/java/target/classes/dev/forgeos/adapter/EntryKind.class +0 -0
  36. package/adapters/java/target/classes/dev/forgeos/adapter/ErrorInfo.class +0 -0
  37. package/adapters/java/target/classes/dev/forgeos/adapter/Forge.class +0 -0
  38. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeCall.class +0 -0
  39. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeContext.class +0 -0
  40. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHandler.class +0 -0
  41. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHttpHandler.class +0 -0
  42. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$EntryOption.class +0 -0
  43. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegisteredEntry.class +0 -0
  44. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegistryOption.class +0 -0
  45. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry.class +0 -0
  46. package/adapters/java/target/classes/dev/forgeos/adapter/Json.class +0 -0
  47. package/adapters/java/target/classes/dev/forgeos/adapter/Manifest.class +0 -0
  48. package/adapters/java/target/classes/dev/forgeos/adapter/RequestEnvelope.class +0 -0
  49. package/adapters/java/target/classes/dev/forgeos/adapter/ResponseEnvelope.class +0 -0
  50. package/adapters/java/target/classes/dev/forgeos/adapter/Risk.class +0 -0
  51. package/adapters/java/target/classes/dev/forgeos/adapter/Schemas.class +0 -0
  52. package/adapters/java/target/classes/dev/forgeos/adapter/Service.class +0 -0
  53. package/adapters/java/target/classes/dev/forgeos/adapter/TransactionMode.class +0 -0
  54. package/adapters/java/target/classes/dev/forgeos/adapter/TypedForgeHandler.class +0 -0
  55. package/adapters/java/target/forge-java-adapter-0.1.0-alpha.11.jar +0 -0
  56. package/adapters/java/target/maven-archiver/pom.properties +3 -0
  57. package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +23 -0
  58. package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +20 -0
  59. package/adapters/java-spring-boot-starter/README.md +32 -0
  60. package/adapters/java-spring-boot-starter/pom.xml +36 -0
  61. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  62. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  63. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  64. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  65. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  66. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  67. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  68. package/adapters/java-spring-boot-starter/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  69. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeCommand.class +0 -0
  70. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeExternalService.class +0 -0
  71. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeQuery.class +0 -0
  72. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.class +0 -0
  73. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.class +0 -0
  74. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringRuntime.class +0 -0
  75. package/adapters/java-spring-boot-starter/target/forge-java-spring-boot-starter-0.1.0-alpha.11.jar +0 -0
  76. package/adapters/java-spring-boot-starter/target/maven-archiver/pom.properties +3 -0
  77. package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +6 -0
  78. package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +6 -0
  79. package/bin/forge.mjs +18 -0
  80. package/docs/changelog.md +212 -0
  81. package/docs/forge-protocol.md +189 -0
  82. package/examples/go-billing/go.mod +7 -0
  83. package/examples/go-billing/main.go +120 -0
  84. package/examples/java-billing/pom.xml +52 -0
  85. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  86. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  87. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  88. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/CreateInvoiceInput.class +0 -0
  89. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Invoice.class +0 -0
  90. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$EmptyInput.class +0 -0
  91. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$Options.class +0 -0
  92. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main.class +0 -0
  93. package/examples/java-billing/target/java-billing-0.1.0-alpha.11-all.jar +0 -0
  94. package/examples/java-billing/target/java-billing-0.1.0-alpha.11.jar +0 -0
  95. package/examples/java-billing/target/maven-archiver/pom.properties +3 -0
  96. package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +5 -0
  97. package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +3 -0
  98. package/package.json +29 -7
  99. package/schemas/forge-manifest.schema.json +57 -0
  100. package/src/forge/_generated/releaseManifest.json +1 -2
  101. package/src/forge/_generated/releaseManifest.ts +3 -3
  102. package/src/forge/agent-adapters/index.ts +1511 -123
  103. package/src/forge/agent-adapters/types.ts +216 -1
  104. package/src/forge/agent-memory/bridge.ts +1192 -0
  105. package/src/forge/agent-memory/context-pack.ts +151 -0
  106. package/src/forge/agent-memory/hook-runner.ts +312 -0
  107. package/src/forge/agent-memory/mcp.ts +224 -0
  108. package/src/forge/agent-memory/normalize.ts +498 -0
  109. package/src/forge/agent-memory/redaction.ts +103 -0
  110. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  111. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +84 -0
  112. package/src/forge/agent-memory/sources/codex.ts +119 -0
  113. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  114. package/src/forge/agent-memory/types.ts +191 -0
  115. package/src/forge/bench.ts +248 -0
  116. package/src/forge/brownfield-import/index.ts +736 -0
  117. package/src/forge/brownfield-import/types.ts +127 -0
  118. package/src/forge/cair/action-journal.ts +61 -0
  119. package/src/forge/cair/action-parser.ts +314 -0
  120. package/src/forge/cair/action-validator.ts +40 -0
  121. package/src/forge/cair/actions.ts +1818 -0
  122. package/src/forge/cair/format.ts +77 -0
  123. package/src/forge/cair/index.ts +106 -0
  124. package/src/forge/cair/query.ts +478 -0
  125. package/src/forge/cair/snapshot.ts +315 -0
  126. package/src/forge/cair/types.ts +248 -0
  127. package/src/forge/cli/ai.ts +671 -3
  128. package/src/forge/cli/auth.ts +36 -1
  129. package/src/forge/cli/build.ts +20 -4
  130. package/src/forge/cli/changed.ts +300 -0
  131. package/src/forge/cli/codex-app-server.ts +877 -0
  132. package/src/forge/cli/commands.ts +1285 -7
  133. package/src/forge/cli/db.ts +121 -2
  134. package/src/forge/cli/deps.ts +79 -12
  135. package/src/forge/cli/dev.ts +502 -38
  136. package/src/forge/cli/docs.ts +265 -0
  137. package/src/forge/cli/handoff.ts +250 -0
  138. package/src/forge/cli/index.ts +1 -0
  139. package/src/forge/cli/main.ts +49 -3
  140. package/src/forge/cli/new.ts +3 -1
  141. package/src/forge/cli/next-actions.ts +23 -0
  142. package/src/forge/cli/output.ts +290 -1
  143. package/src/forge/cli/parse.ts +770 -36
  144. package/src/forge/cli/query.ts +32 -0
  145. package/src/forge/cli/release.ts +35 -11
  146. package/src/forge/cli/rls.ts +568 -17
  147. package/src/forge/cli/run.ts +41 -0
  148. package/src/forge/cli/secrets.ts +46 -1
  149. package/src/forge/cli/security.ts +381 -0
  150. package/src/forge/cli/self-host.ts +56 -14
  151. package/src/forge/cli/studio.ts +2163 -0
  152. package/src/forge/cli/verify.ts +1422 -32
  153. package/src/forge/compiler/agent-contract/build.ts +725 -41
  154. package/src/forge/compiler/agent-contract/types.ts +85 -0
  155. package/src/forge/compiler/ai-registry/build.ts +62 -1
  156. package/src/forge/compiler/ai-registry/constants.ts +1 -1
  157. package/src/forge/compiler/ai-registry/parse.ts +168 -5
  158. package/src/forge/compiler/api-surface/build.ts +47 -0
  159. package/src/forge/compiler/app-graph/build.ts +68 -8
  160. package/src/forge/compiler/app-graph/extract.ts +107 -0
  161. package/src/forge/compiler/app-graph/forge-apis.ts +1 -0
  162. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  163. package/src/forge/compiler/app-graph/parser.ts +24 -24
  164. package/src/forge/compiler/app-graph/profile.ts +26 -0
  165. package/src/forge/compiler/app-graph/versions.ts +1 -1
  166. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  167. package/src/forge/compiler/classifier/classify.ts +32 -8
  168. package/src/forge/compiler/classifier/secrets.ts +3 -2
  169. package/src/forge/compiler/classifier/signals.ts +91 -1
  170. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  171. package/src/forge/compiler/client-sdk/render-client.ts +188 -13
  172. package/src/forge/compiler/data-graph/parse.ts +3 -3
  173. package/src/forge/compiler/data-graph/sql/ddl.ts +60 -2
  174. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  175. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  176. package/src/forge/compiler/dev-manifest/build.ts +3 -0
  177. package/src/forge/compiler/diagnostics/codes.ts +35 -0
  178. package/src/forge/compiler/diagnostics/create.ts +8 -3
  179. package/src/forge/compiler/diagnostics/index.ts +2 -0
  180. package/src/forge/compiler/emitter/barrel.ts +3 -0
  181. package/src/forge/compiler/emitter/render.ts +5 -0
  182. package/src/forge/compiler/external-manifest/registry.ts +205 -0
  183. package/src/forge/compiler/external-manifest/types.ts +91 -0
  184. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  185. package/src/forge/compiler/frontend-graph/build.ts +85 -13
  186. package/src/forge/compiler/integration/add.ts +498 -22
  187. package/src/forge/compiler/integration/snapshot.ts +2 -0
  188. package/src/forge/compiler/make-registry/build.ts +19 -7
  189. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  190. package/src/forge/compiler/orchestrator/plan.ts +78 -7
  191. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  192. package/src/forge/compiler/orchestrator/run.ts +97 -31
  193. package/src/forge/compiler/orchestrator/serialize.ts +101 -8
  194. package/src/forge/compiler/package-graph/compiler.ts +13 -3
  195. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  196. package/src/forge/compiler/package-manager/commands.ts +4 -0
  197. package/src/forge/compiler/package-manager/executor.ts +30 -1
  198. package/src/forge/compiler/policy-registry/build.ts +44 -1
  199. package/src/forge/compiler/test-graph/build.ts +11 -3
  200. package/src/forge/compiler/types/ai-registry.ts +25 -1
  201. package/src/forge/compiler/types/app-graph.ts +9 -2
  202. package/src/forge/compiler/types/cli.ts +76 -1
  203. package/src/forge/compiler/types/dev-manifest.ts +3 -0
  204. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  205. package/src/forge/delta/classifier.ts +52 -0
  206. package/src/forge/delta/explain.ts +126 -0
  207. package/src/forge/delta/git-observer.ts +43 -0
  208. package/src/forge/delta/ids.ts +44 -0
  209. package/src/forge/delta/index.ts +13 -0
  210. package/src/forge/delta/recorder.ts +402 -0
  211. package/src/forge/delta/redaction.ts +50 -0
  212. package/src/forge/delta/schema.ts +240 -0
  213. package/src/forge/delta/session.ts +142 -0
  214. package/src/forge/delta/status.ts +489 -0
  215. package/src/forge/delta/store.ts +2975 -0
  216. package/src/forge/delta/timeline.ts +104 -0
  217. package/src/forge/dev/server.ts +768 -15
  218. package/src/forge/dev/types.ts +15 -1
  219. package/src/forge/dev/watch.ts +17 -7
  220. package/src/forge/dev-console/cycle.ts +233 -21
  221. package/src/forge/dev-console/types.ts +46 -1
  222. package/src/forge/impact/index.ts +46 -8
  223. package/src/forge/impact/types.ts +6 -0
  224. package/src/forge/intent/index.ts +35 -16
  225. package/src/forge/make/index.ts +149 -6
  226. package/src/forge/make/templates.ts +343 -2
  227. package/src/forge/make/types.ts +3 -1
  228. package/src/forge/refactor/index.ts +1 -0
  229. package/src/forge/repair/rules/index.ts +2 -2
  230. package/src/forge/review/index.ts +158 -12
  231. package/src/forge/review/types.ts +15 -0
  232. package/src/forge/runtime/ai/context.ts +210 -5
  233. package/src/forge/runtime/ai/types.ts +70 -0
  234. package/src/forge/runtime/auth/claims.ts +32 -0
  235. package/src/forge/runtime/auth/errors.ts +2 -0
  236. package/src/forge/runtime/context/create-context.ts +30 -6
  237. package/src/forge/runtime/db/generated-client.ts +13 -2
  238. package/src/forge/runtime/db/memory-adapter.ts +2 -2
  239. package/src/forge/runtime/db/pglite-adapter.ts +77 -2
  240. package/src/forge/runtime/db/postgres-adapter.ts +6 -3
  241. package/src/forge/runtime/executor.ts +112 -2
  242. package/src/forge/runtime/external/bridge.ts +649 -0
  243. package/src/forge/runtime/runner/run-entry.ts +16 -7
  244. package/src/forge/runtime/telemetry/scrubber.ts +91 -10
  245. package/src/forge/runtime/webhooks/security.ts +184 -0
  246. package/src/forge/server.ts +100 -2
  247. package/src/forge/version.ts +1 -1
  248. package/src/forge/vue/index.ts +407 -0
  249. package/src/forge/workspace/change-summary.ts +209 -0
  250. package/src/forge/workspace/forge-cli.ts +14 -0
  251. package/src/forge/workspace/git-summary.ts +279 -0
  252. package/templates/agent-workroom/AGENTS.md +29 -0
  253. package/templates/agent-workroom/README.md +34 -0
  254. package/templates/agent-workroom/forge.config.ts +3 -0
  255. package/templates/agent-workroom/package.json +33 -0
  256. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  257. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  258. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  259. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  260. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  261. package/templates/agent-workroom/src/policies.ts +6 -0
  262. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  263. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  264. package/templates/agent-workroom/tsconfig.json +16 -0
  265. package/templates/agent-workroom/web/index.html +12 -0
  266. package/templates/agent-workroom/web/package.json +21 -0
  267. package/templates/agent-workroom/web/src/App.tsx +345 -0
  268. package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
  269. package/templates/agent-workroom/web/src/main.tsx +13 -0
  270. package/templates/agent-workroom/web/src/styles.css +545 -0
  271. package/templates/agent-workroom/web/tsconfig.json +27 -0
  272. package/templates/b2b-support-web/package.json +2 -0
  273. package/templates/b2b-support-web/tsconfig.json +4 -1
  274. package/templates/b2b-support-web/web/package.json +1 -1
  275. package/templates/minimal-web/package.json +2 -1
  276. package/templates/minimal-web/tsconfig.json +3 -1
  277. package/templates/minimal-web/web/package.json +2 -2
  278. package/src/forge/_generated/actionSubscriptions.json +0 -2
  279. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  280. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  281. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  282. package/src/forge/_generated/agentContract.json +0 -2
  283. package/src/forge/_generated/agentContract.ts +0 -7696
  284. package/src/forge/_generated/agentQuickstart.md +0 -32
  285. package/src/forge/_generated/aiContext.ts +0 -59
  286. package/src/forge/_generated/aiModels.json +0 -2
  287. package/src/forge/_generated/aiModels.ts +0 -35
  288. package/src/forge/_generated/aiProviders.json +0 -2
  289. package/src/forge/_generated/aiProviders.ts +0 -23
  290. package/src/forge/_generated/aiRegistry.json +0 -2
  291. package/src/forge/_generated/aiRegistry.ts +0 -29
  292. package/src/forge/_generated/api.json +0 -2
  293. package/src/forge/_generated/api.ts +0 -8
  294. package/src/forge/_generated/appGraph.json +0 -2
  295. package/src/forge/_generated/appGraph.ts +0 -14667
  296. package/src/forge/_generated/appMap.md +0 -35
  297. package/src/forge/_generated/artifactManifest.json +0 -2
  298. package/src/forge/_generated/artifactManifest.ts +0 -7
  299. package/src/forge/_generated/authClaims.json +0 -2
  300. package/src/forge/_generated/authClaims.ts +0 -13
  301. package/src/forge/_generated/authConfig.json +0 -2
  302. package/src/forge/_generated/authConfig.ts +0 -17
  303. package/src/forge/_generated/authContext.ts +0 -23
  304. package/src/forge/_generated/authRegistry.json +0 -2
  305. package/src/forge/_generated/authRegistry.ts +0 -25
  306. package/src/forge/_generated/buildInfo.json +0 -2
  307. package/src/forge/_generated/buildInfo.ts +0 -9
  308. package/src/forge/_generated/capabilityMap.json +0 -2
  309. package/src/forge/_generated/capabilityMap.md +0 -15
  310. package/src/forge/_generated/capabilityMap.ts +0 -17
  311. package/src/forge/_generated/client.ts +0 -282
  312. package/src/forge/_generated/clientApi.ts +0 -9
  313. package/src/forge/_generated/clientManifest.json +0 -2
  314. package/src/forge/_generated/clientManifest.ts +0 -39
  315. package/src/forge/_generated/clientTypes.ts +0 -78
  316. package/src/forge/_generated/configRegistry.json +0 -2
  317. package/src/forge/_generated/configRegistry.ts +0 -4
  318. package/src/forge/_generated/dataGraph.json +0 -2
  319. package/src/forge/_generated/dataGraph.ts +0 -8
  320. package/src/forge/_generated/db.json +0 -2
  321. package/src/forge/_generated/db.ts +0 -2
  322. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  323. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  324. package/src/forge/_generated/dbSessionContext.json +0 -2
  325. package/src/forge/_generated/dbSessionContext.ts +0 -39
  326. package/src/forge/_generated/deployManifest.json +0 -2
  327. package/src/forge/_generated/deployManifest.ts +0 -14
  328. package/src/forge/_generated/devManifest.json +0 -2
  329. package/src/forge/_generated/devManifest.ts +0 -47
  330. package/src/forge/_generated/envSchema.json +0 -2
  331. package/src/forge/_generated/envSchema.ts +0 -59
  332. package/src/forge/_generated/frontendGraph.json +0 -2
  333. package/src/forge/_generated/frontendGraph.ts +0 -27
  334. package/src/forge/_generated/importGuards.json +0 -2
  335. package/src/forge/_generated/importGuards.ts +0 -686
  336. package/src/forge/_generated/index.ts +0 -67
  337. package/src/forge/_generated/liveProductionManifest.json +0 -2
  338. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  339. package/src/forge/_generated/liveProtocol.json +0 -2
  340. package/src/forge/_generated/liveProtocol.ts +0 -21
  341. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  342. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  343. package/src/forge/_generated/liveTransportConfig.json +0 -2
  344. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  345. package/src/forge/_generated/makeRegistry.json +0 -2
  346. package/src/forge/_generated/makeRegistry.ts +0 -163
  347. package/src/forge/_generated/makeTemplates.json +0 -2
  348. package/src/forge/_generated/makeTemplates.ts +0 -61
  349. package/src/forge/_generated/mockMap.json +0 -2
  350. package/src/forge/_generated/mockMap.ts +0 -7
  351. package/src/forge/_generated/operationPlaybooks.md +0 -147
  352. package/src/forge/_generated/packageGraph.json +0 -2
  353. package/src/forge/_generated/packageGraph.ts +0 -245249
  354. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  355. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  356. package/src/forge/_generated/permissionMatrix.json +0 -2
  357. package/src/forge/_generated/permissionMatrix.ts +0 -7
  358. package/src/forge/_generated/policyRegistry.json +0 -2
  359. package/src/forge/_generated/policyRegistry.ts +0 -11
  360. package/src/forge/_generated/queryRegistry.json +0 -2
  361. package/src/forge/_generated/queryRegistry.ts +0 -9
  362. package/src/forge/_generated/react.d.ts +0 -22
  363. package/src/forge/_generated/react.ts +0 -29
  364. package/src/forge/_generated/reactManifest.json +0 -2
  365. package/src/forge/_generated/reactManifest.ts +0 -19
  366. package/src/forge/_generated/rlsPolicies.json +0 -2
  367. package/src/forge/_generated/rlsPolicies.sql +0 -34
  368. package/src/forge/_generated/rlsPolicies.ts +0 -6
  369. package/src/forge/_generated/runtimeGraph.json +0 -2
  370. package/src/forge/_generated/runtimeGraph.ts +0 -8
  371. package/src/forge/_generated/runtimeMatrix.json +0 -2
  372. package/src/forge/_generated/runtimeMatrix.ts +0 -327385
  373. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  374. package/src/forge/_generated/runtimeRules.md +0 -79
  375. package/src/forge/_generated/secretRegistry.json +0 -2
  376. package/src/forge/_generated/secretRegistry.ts +0 -50
  377. package/src/forge/_generated/secretsContext.ts +0 -11
  378. package/src/forge/_generated/serverApi.ts +0 -10
  379. package/src/forge/_generated/sourceMapManifest.json +0 -2
  380. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  381. package/src/forge/_generated/sqlPlan.json +0 -2
  382. package/src/forge/_generated/sqlPlan.ts +0 -88
  383. package/src/forge/_generated/subscriptionManifest.json +0 -2
  384. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  385. package/src/forge/_generated/symbolicationManifest.json +0 -2
  386. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  387. package/src/forge/_generated/telemetryRegistry.json +0 -2
  388. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  389. package/src/forge/_generated/telemetrySinks.json +0 -2
  390. package/src/forge/_generated/telemetrySinks.ts +0 -11
  391. package/src/forge/_generated/tenantScope.json +0 -2
  392. package/src/forge/_generated/tenantScope.ts +0 -8
  393. package/src/forge/_generated/testGraph.json +0 -2
  394. package/src/forge/_generated/testGraph.ts +0 -3108
  395. package/src/forge/_generated/testPlanRegistry.json +0 -2
  396. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  397. package/src/forge/_generated/uiRoutes.json +0 -2
  398. package/src/forge/_generated/uiRoutes.ts +0 -16
  399. package/src/forge/_generated/uiScenarios.json +0 -2
  400. package/src/forge/_generated/uiScenarios.ts +0 -30
  401. package/src/forge/_generated/uiTestManifest.json +0 -2
  402. package/src/forge/_generated/uiTestManifest.ts +0 -27
  403. package/src/forge/_generated/workflowRegistry.json +0 -2
  404. package/src/forge/_generated/workflowRegistry.ts +0 -9
  405. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  406. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -6,9 +6,11 @@ import {
6
6
  type ServerResponse,
7
7
  } from "node:http";
8
8
  import { join } from "node:path";
9
+ import { pathToFileURL } from "node:url";
9
10
  import { createDiagnostic } from "../compiler/diagnostics/create.ts";
10
11
  import {
11
12
  FORGE_AUTH_DEV_HEADERS_IN_PRODUCTION,
13
+ FORGE_DEV_DB_RESET,
12
14
  FORGE_DEV_INVOKE_FAILED,
13
15
  FORGE_DEV_SERVER_ERROR,
14
16
  FORGE_POLICY_DENIED,
@@ -22,12 +24,15 @@ import type { SqlPlan } from "../compiler/data-graph/sql/types.ts";
22
24
  import { GENERATED_DIR } from "../compiler/emitter/constants.ts";
23
25
  import { stripDeterministicHeader } from "../compiler/primitives/header.ts";
24
26
  import type { DevManifest } from "../compiler/types/dev-manifest.ts";
27
+ import type { Diagnostic } from "../compiler/types/diagnostic.ts";
25
28
  import type { RuntimeGraph } from "../compiler/types/runtime-graph.ts";
26
29
  import { createDbAdapter } from "../runtime/db/factory.ts";
27
- import { applyMigrations } from "../runtime/db/migrate.ts";
30
+ import { applyMigrations, getMigrationStatus, resetDatabase } from "../runtime/db/migrate.ts";
28
31
  import {
32
+ disposeRuntimeModuleNamespace,
29
33
  listEntries,
30
34
  prepareRuntimeEnvironment,
35
+ refreshRuntimeModuleNamespace,
31
36
  runEntry,
32
37
  } from "../runtime/executor.ts";
33
38
  import { listQueries, runQuery } from "../runtime/query/run-query.ts";
@@ -48,7 +53,7 @@ import { loadWorkflowRegistry } from "../runtime/workflows/registry.ts";
48
53
  import { retryWorkflowRun } from "../runtime/workflows/retry-run.ts";
49
54
  import { hashStable } from "../compiler/primitives/hash.ts";
50
55
  import { canonicalJson } from "../compiler/primitives/serialize.ts";
51
- import type { DevServerHandle, DevServerOptions, DevServerState } from "./types.ts";
56
+ import type { DevServerHandle, DevServerOptions, DevServerReloadResult, DevServerState } from "./types.ts";
52
57
  import { getTelemetrySummary, inspectTrace } from "../runtime/telemetry/flush.ts";
53
58
  import { processTelemetryBatch } from "../runtime/telemetry/process.ts";
54
59
  import { getRuntimeEnvStore } from "../runtime/context/create-context.ts";
@@ -59,9 +64,12 @@ import {
59
64
  import { checkAiProviders, loadAiRegistry } from "../runtime/ai/check.ts";
60
65
  import { isMockAiEnabled } from "../runtime/ai/state.ts";
61
66
  import { createAiContext } from "../runtime/ai/context.ts";
67
+ import type { ForgeAiProvider, ForgeAiToolDefinition } from "../runtime/ai/types.ts";
68
+ import { resolveLanguageModel } from "../runtime/ai/providers.ts";
62
69
  import { createRuntimeSecretsBundle } from "../runtime/secrets/runtime-bundle.ts";
63
- import { createNoopTelemetryContext } from "../runtime/telemetry/context.ts";
70
+ import { createNoopTelemetryContext, createTelemetryContext } from "../runtime/telemetry/context.ts";
64
71
  import { generateTraceId } from "../runtime/telemetry/correlation.ts";
72
+ import type { AgentToolRegistry } from "../compiler/agent-contract/types.ts";
65
73
  import { loadLiveQueryRegistry } from "../runtime/live/registry.ts";
66
74
  import { createLiveSubscriptionManager } from "../runtime/live/subscription-manager.ts";
67
75
  import { createSseResponse } from "../runtime/live/sse.ts";
@@ -71,6 +79,37 @@ import {
71
79
  } from "../runtime/live/invalidation-log.ts";
72
80
  import { currentReleaseInfo } from "../runtime/release/runtime.ts";
73
81
  import { DEFAULT_LIVE_LIMITS } from "../runtime/live/types.ts";
82
+ import {
83
+ loadExternalServiceGraph,
84
+ runExternalEntry,
85
+ } from "../runtime/external/bridge.ts";
86
+
87
+ async function applyDevMigrations(
88
+ adapter: NonNullable<Awaited<ReturnType<typeof createDbAdapter>>["adapter"]>,
89
+ sqlPlan: SqlPlan,
90
+ dbMode: DevServerOptions["db"],
91
+ ): Promise<Diagnostic[]> {
92
+ const shouldAutoReset =
93
+ (dbMode === "pglite" || dbMode === "memory") &&
94
+ process.env.FORGE_DEV_AUTO_RESET_DB !== "0";
95
+
96
+ if (shouldAutoReset) {
97
+ const status = await getMigrationStatus(adapter).catch(() => null);
98
+ const appliedChecksum = status?.applied.at(-1)?.checksum ?? null;
99
+ if (appliedChecksum && appliedChecksum !== sqlPlan.checksum) {
100
+ return [
101
+ createDiagnostic({
102
+ severity: "warning",
103
+ code: FORGE_DEV_DB_RESET,
104
+ message: "local dev database schema changed; reset app tables automatically",
105
+ }),
106
+ ...(await resetDatabase(adapter, sqlPlan)),
107
+ ];
108
+ }
109
+ }
110
+
111
+ return applyMigrations(adapter, sqlPlan);
112
+ }
74
113
 
75
114
  interface FetchServer {
76
115
  hostname?: string;
@@ -92,7 +131,7 @@ async function readIncomingBody(request: IncomingMessage): Promise<Buffer | unde
92
131
 
93
132
  async function nodeRequestToFetch(
94
133
  request: IncomingMessage,
95
- input: { host: string; port: number },
134
+ input: { host: string; port: number; signal?: AbortSignal },
96
135
  ): Promise<Request> {
97
136
  const headers = new Headers();
98
137
  for (const [name, value] of Object.entries(request.headers)) {
@@ -109,6 +148,7 @@ async function nodeRequestToFetch(
109
148
  method: request.method,
110
149
  headers,
111
150
  body: body ? new Blob([new Uint8Array(body)]) : undefined,
151
+ signal: input.signal,
112
152
  ...(body ? { duplex: "half" as const } : {}),
113
153
  };
114
154
  return new Request(url, init);
@@ -151,10 +191,13 @@ async function createNodeFetchServer(
151
191
  ): Promise<FetchServer> {
152
192
  let actualPort = input.port;
153
193
  const nodeServer: NodeHttpServer = createServer(async (request, response) => {
194
+ const controller = new AbortController();
195
+ response.once("close", () => controller.abort());
154
196
  try {
155
197
  const fetchRequest = await nodeRequestToFetch(request, {
156
198
  host: input.hostname,
157
199
  port: actualPort,
200
+ signal: controller.signal,
158
201
  });
159
202
  const fetchResponse = await fetchHandler(fetchRequest);
160
203
  await writeFetchResponse(response, fetchResponse);
@@ -402,6 +445,276 @@ function parseInvokeName(pathname: string, prefix: string): string | null {
402
445
  return name.length > 0 ? decodeURIComponent(name) : null;
403
446
  }
404
447
 
448
+ function parseExternalInvoke(pathname: string): {
449
+ serviceName: string;
450
+ kind: "command" | "query";
451
+ entryName: string;
452
+ } | null {
453
+ const match = pathname.match(/^\/external\/([^/]+)\/(commands|queries)\/([^/]+)$/);
454
+ if (!match) {
455
+ return null;
456
+ }
457
+ return {
458
+ serviceName: decodeURIComponent(match[1]!),
459
+ kind: match[2] === "commands" ? "command" : "query",
460
+ entryName: decodeURIComponent(match[3]!),
461
+ };
462
+ }
463
+
464
+ function loadAgentToolRegistry(workspaceRoot: string): AgentToolRegistry | null {
465
+ return readGeneratedJson<AgentToolRegistry>(
466
+ workspaceRoot,
467
+ `${GENERATED_DIR}/agentTools.json`,
468
+ );
469
+ }
470
+
471
+ async function listDatabaseTables(adapter: NonNullable<DevServerState["adapter"]>): Promise<string[]> {
472
+ const result = await adapter.query(
473
+ `SELECT c.relname AS table_name
474
+ FROM pg_catalog.pg_class c
475
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
476
+ WHERE n.nspname = 'public'
477
+ AND c.relkind IN ('r', 'p')
478
+ ORDER BY c.relname`,
479
+ );
480
+ return result.rows.map((row) => String(row.table_name));
481
+ }
482
+
483
+ type RuntimeAgentDefinition = {
484
+ provider?: ForgeAiProvider;
485
+ model: string;
486
+ instructions: string;
487
+ tools?: Record<string, ForgeAiToolDefinition> | string[];
488
+ stopWhen?: { kind: "stepCount"; maxSteps: number } | { kind: "toolCall"; toolName: string };
489
+ maxSteps?: number;
490
+ };
491
+
492
+ async function loadNamedAgentDefinition(
493
+ workspaceRoot: string,
494
+ name: string | undefined,
495
+ ): Promise<RuntimeAgentDefinition | null> {
496
+ if (!name) {
497
+ return null;
498
+ }
499
+ const registry = loadAiRegistry(workspaceRoot);
500
+ const metadata = registry?.agents.find((agent) => agent.name === name);
501
+ if (!metadata) {
502
+ throw new Error(`unknown Forge AI agent '${name}'`);
503
+ }
504
+
505
+ const mod = (await import(pathToFileURL(join(workspaceRoot, metadata.file)).href)) as Record<
506
+ string,
507
+ unknown
508
+ >;
509
+ const definition = mod[metadata.name];
510
+ if (!definition || typeof definition !== "object") {
511
+ throw new Error(`Forge AI agent '${name}' was not exported from ${metadata.file}`);
512
+ }
513
+
514
+ const agent = definition as Partial<RuntimeAgentDefinition>;
515
+ if (!agent.model || !agent.instructions) {
516
+ throw new Error(`Forge AI agent '${name}' is missing model or instructions`);
517
+ }
518
+ return {
519
+ provider: agent.provider ?? metadata.provider,
520
+ model: agent.model,
521
+ instructions: agent.instructions,
522
+ tools: agent.tools,
523
+ stopWhen:
524
+ agent.stopWhen ??
525
+ (metadata.stopWhen.kind === "default" ? undefined : metadata.stopWhen),
526
+ maxSteps: agent.maxSteps,
527
+ };
528
+ }
529
+
530
+ function explicitAgentTools(
531
+ definition: RuntimeAgentDefinition | null,
532
+ ): Record<string, ForgeAiToolDefinition> {
533
+ if (!definition?.tools || Array.isArray(definition.tools)) {
534
+ return {};
535
+ }
536
+ return Object.fromEntries(
537
+ Object.entries(definition.tools).filter(([, toolDefinition]) =>
538
+ Boolean(
539
+ toolDefinition &&
540
+ typeof toolDefinition === "object" &&
541
+ "handler" in toolDefinition &&
542
+ "inputSchema" in toolDefinition,
543
+ ),
544
+ ),
545
+ );
546
+ }
547
+
548
+ function buildAutoAgentTools(input: {
549
+ workspaceRoot: string;
550
+ registry: AgentToolRegistry | null;
551
+ selected?: string[];
552
+ adapter: DevServerState["adapter"];
553
+ tableMap: Record<string, TableMapEntry>;
554
+ auth: Awaited<ReturnType<typeof authenticateHeaders>>;
555
+ liveManager?: ReturnType<typeof createLiveSubscriptionManager> | null;
556
+ json: boolean;
557
+ mock: boolean;
558
+ }): Record<string, ForgeAiToolDefinition> {
559
+ const selected = new Set(input.selected ?? []);
560
+ const includeAll = selected.size === 0;
561
+ const tools: Record<string, ForgeAiToolDefinition> = {};
562
+ for (const toolInfo of input.registry?.autoTools ?? []) {
563
+ if (!includeAll && !selected.has(toolInfo.name) && !selected.has(toolInfo.sourceName)) {
564
+ continue;
565
+ }
566
+ tools[toolInfo.name] = {
567
+ description: `${toolInfo.readOnly ? "Read" : "Execute"} Forge ${toolInfo.sourceKind} '${toolInfo.sourceName}'. Policy: ${toolInfo.policy ?? "none"}.`,
568
+ inputSchema: {
569
+ type: "object",
570
+ properties: {
571
+ args: {
572
+ type: "object",
573
+ additionalProperties: true,
574
+ },
575
+ },
576
+ additionalProperties: true,
577
+ },
578
+ risk: toolInfo.readOnly ? "read" : "write",
579
+ needsApproval: !toolInfo.readOnly,
580
+ handler: async (_ctx, rawInput) => {
581
+ const objectInput =
582
+ rawInput && typeof rawInput === "object" && !Array.isArray(rawInput)
583
+ ? rawInput as { args?: unknown }
584
+ : {};
585
+ const args = objectInput.args ?? rawInput ?? {};
586
+ if (!input.adapter) {
587
+ throw new Error("database not connected");
588
+ }
589
+ if (toolInfo.sourceKind === "query" || toolInfo.sourceKind === "liveQuery") {
590
+ const result = await runQuery(
591
+ input.workspaceRoot,
592
+ toolInfo.sourceName,
593
+ { args, auth: input.auth },
594
+ {
595
+ adapter: input.adapter,
596
+ tableMap: input.tableMap,
597
+ },
598
+ );
599
+ if (!result.ok) {
600
+ throw new Error(result.diagnostics.map((diagnostic) => diagnostic.message).join("; "));
601
+ }
602
+ return { result: result.result, traceId: result.traceId };
603
+ }
604
+ const result = await runEntry(input.workspaceRoot, toolInfo.sourceName, {
605
+ json: input.json,
606
+ mock: input.mock,
607
+ args,
608
+ db: input.adapter,
609
+ auth: input.auth,
610
+ liveManager: input.liveManager ?? undefined,
611
+ });
612
+ if (!result.ok) {
613
+ throw new Error(result.diagnostics.map((diagnostic) => diagnostic.message).join("; "));
614
+ }
615
+ return { result: result.result, traceId: result.traceId };
616
+ },
617
+ };
618
+ }
619
+ return tools;
620
+ }
621
+
622
+ async function buildAiSdkTools(input: {
623
+ tools: Record<string, ForgeAiToolDefinition>;
624
+ provider: ForgeAiProvider;
625
+ model: string;
626
+ purpose?: string;
627
+ traceId: string;
628
+ secrets: ReturnType<typeof createRuntimeSecretsBundle>["secrets"];
629
+ telemetry: ReturnType<typeof createTelemetryContext> | ReturnType<typeof createNoopTelemetryContext>;
630
+ env: Record<string, string | undefined>;
631
+ auth: Awaited<ReturnType<typeof authenticateHeaders>>;
632
+ deltaRecorder?: DevServerOptions["deltaRecorder"];
633
+ }): Promise<Record<string, unknown>> {
634
+ const { tool } = await import("ai");
635
+ return Object.fromEntries(
636
+ Object.entries(input.tools).map(([name, definition]) => {
637
+ const needsApproval = definition.needsApproval;
638
+ return [
639
+ name,
640
+ tool({
641
+ description: definition.description,
642
+ inputSchema: definition.inputSchema as never,
643
+ ...(definition.outputSchema ? { outputSchema: definition.outputSchema as never } : {}),
644
+ ...(definition.strict !== undefined ? { strict: definition.strict } : {}),
645
+ ...(needsApproval !== undefined
646
+ ? {
647
+ needsApproval:
648
+ typeof needsApproval === "function"
649
+ ? async ({ args }: { args: unknown }) =>
650
+ Boolean(await needsApproval(args as never))
651
+ : needsApproval,
652
+ }
653
+ : {}),
654
+ execute: async (args: unknown) => {
655
+ const startedAt = Date.now();
656
+ await input.telemetry.capture("forge.ai.tool.started", {
657
+ traceId: input.traceId,
658
+ provider: input.provider,
659
+ model: input.model,
660
+ purpose: input.purpose,
661
+ tool: name,
662
+ risk: definition.risk ?? "external",
663
+ });
664
+ try {
665
+ const output = await definition.handler(
666
+ {
667
+ secrets: input.secrets,
668
+ env: input.env,
669
+ telemetry: input.telemetry,
670
+ auth: input.auth,
671
+ },
672
+ args as never,
673
+ );
674
+ await input.telemetry.capture("forge.ai.tool.completed", {
675
+ traceId: input.traceId,
676
+ provider: input.provider,
677
+ model: input.model,
678
+ purpose: input.purpose,
679
+ tool: name,
680
+ latencyMs: Date.now() - startedAt,
681
+ status: "completed",
682
+ });
683
+ await input.deltaRecorder?.recordAgentTool({
684
+ toolName: name,
685
+ risk: definition.risk ?? "external",
686
+ status: "completed",
687
+ traceId: input.traceId,
688
+ durationMs: Date.now() - startedAt,
689
+ });
690
+ return output;
691
+ } catch (error) {
692
+ await input.telemetry.capture("forge.ai.tool.failed", {
693
+ traceId: input.traceId,
694
+ provider: input.provider,
695
+ model: input.model,
696
+ purpose: input.purpose,
697
+ tool: name,
698
+ latencyMs: Date.now() - startedAt,
699
+ status: "failed",
700
+ error: error instanceof Error ? error.message : String(error),
701
+ });
702
+ await input.deltaRecorder?.recordAgentTool({
703
+ toolName: name,
704
+ risk: definition.risk ?? "external",
705
+ status: "failed",
706
+ traceId: input.traceId,
707
+ durationMs: Date.now() - startedAt,
708
+ });
709
+ throw error;
710
+ }
711
+ },
712
+ } as never),
713
+ ];
714
+ }),
715
+ );
716
+ }
717
+
405
718
  async function parseRequestArgs(request: Request): Promise<unknown> {
406
719
  const contentType = request.headers.get("content-type") ?? "";
407
720
  if (!contentType.includes("application/json")) {
@@ -420,6 +733,7 @@ export async function startDevServer(
420
733
  options: DevServerOptions,
421
734
  ): Promise<DevServerHandle> {
422
735
  const workspaceRoot = options.workspaceRoot.replace(/\\/g, "/");
736
+ const deltaRecorder = options.deltaRecorder;
423
737
  const authConfig = loadAuthConfigFromEnv(workspaceRoot, {
424
738
  defaultMode: "dev-headers",
425
739
  });
@@ -480,7 +794,7 @@ export async function startDevServer(
480
794
  );
481
795
 
482
796
  if (sqlPlan) {
483
- const migrationDiagnostics = await applyMigrations(adapter, sqlPlan);
797
+ const migrationDiagnostics = await applyDevMigrations(adapter, sqlPlan, dbMode);
484
798
  const errors = migrationDiagnostics.filter(
485
799
  (diagnostic) => diagnostic.severity === "error",
486
800
  );
@@ -500,6 +814,7 @@ export async function startDevServer(
500
814
  mockAi: options.mockAi,
501
815
  db: serverState.adapter,
502
816
  });
817
+ refreshRuntimeModuleNamespace("dev-start");
503
818
 
504
819
  function loadArtifacts(): {
505
820
  devManifest: DevManifest;
@@ -533,12 +848,85 @@ export async function startDevServer(
533
848
  }
534
849
 
535
850
  const initialArtifacts = loadArtifacts();
851
+ let currentRoutes = devManifest.routes;
852
+ let currentRuntimeEntryCount = runtimeGraph.entries.length;
853
+
854
+ async function reloadGeneratedArtifacts(reason = "manual"): Promise<DevServerReloadResult> {
855
+ try {
856
+ refreshRuntimeModuleNamespace(reason);
857
+ const fresh = loadArtifacts();
858
+ currentRoutes = fresh.devManifest.routes;
859
+ currentRuntimeEntryCount = fresh.runtimeGraph.entries.length;
860
+ const diagnostics: Diagnostic[] = [];
861
+ let migrated = false;
862
+
863
+ if (serverState.adapter) {
864
+ const sqlPlan = readGeneratedJson<SqlPlan>(
865
+ workspaceRoot,
866
+ `${GENERATED_DIR}/sqlPlan.json`,
867
+ );
868
+ if (sqlPlan) {
869
+ migrated = true;
870
+ diagnostics.push(...(await applyDevMigrations(serverState.adapter, sqlPlan, options.db)));
871
+ }
872
+ if (diagnostics.every((diagnostic) => diagnostic.severity !== "error")) {
873
+ await ensureLiveInvalidationSchema(serverState.adapter);
874
+ }
875
+ }
876
+
877
+ if (
878
+ options.worker &&
879
+ serverState.adapter &&
880
+ diagnostics.every((diagnostic) => diagnostic.severity !== "error")
881
+ ) {
882
+ serverState.outboxWorker?.stop();
883
+ serverState.outboxWorker = startOutboxWorker(
884
+ serverState.adapter,
885
+ workspaceRoot,
886
+ fresh.tableMap,
887
+ fresh.runtimeGraph.entries,
888
+ { mock: options.mock, intervalMs: 2_000, telemetrySinks, workspaceRoot },
889
+ );
890
+ }
891
+
892
+ return {
893
+ ok: diagnostics.every((diagnostic) => diagnostic.severity !== "error"),
894
+ reason,
895
+ migrated,
896
+ routes: currentRoutes.length,
897
+ runtimeEntries: currentRuntimeEntryCount,
898
+ worker: serverState.outboxWorker?.isRunning() ? "running" : "stopped",
899
+ diagnostics,
900
+ };
901
+ } catch (error) {
902
+ return {
903
+ ok: false,
904
+ reason,
905
+ migrated: false,
906
+ routes: currentRoutes.length,
907
+ runtimeEntries: currentRuntimeEntryCount,
908
+ worker: serverState.outboxWorker?.isRunning() ? "running" : "stopped",
909
+ diagnostics: [
910
+ createDiagnostic({
911
+ severity: "error",
912
+ code: FORGE_DEV_SERVER_ERROR,
913
+ message: error instanceof Error ? error.message : "failed to reload generated artifacts",
914
+ }),
915
+ ],
916
+ };
917
+ }
918
+ }
919
+
536
920
  const liveManager = serverState.adapter
537
921
  ? createLiveSubscriptionManager({
538
922
  workspaceRoot,
539
923
  adapter: serverState.adapter,
540
924
  loadTableMap: () => loadArtifacts().tableMap,
541
925
  enablePolling: true,
926
+ limits: {
927
+ ...DEFAULT_LIVE_LIMITS,
928
+ maxSubscriptionsPerClient: Number(process.env.FORGE_DEV_LIVE_MAX_SUBSCRIPTIONS ?? "250"),
929
+ },
542
930
  pollIntervalMs: Number(process.env.FORGE_LIVE_POLL_INTERVAL_MS ?? "1000"),
543
931
  })
544
932
  : null;
@@ -548,7 +936,7 @@ export async function startDevServer(
548
936
  serverState.adapter,
549
937
  workspaceRoot,
550
938
  initialArtifacts.tableMap,
551
- runtimeGraph.entries,
939
+ initialArtifacts.runtimeGraph.entries,
552
940
  { mock: options.mock, intervalMs: 2_000, telemetrySinks, workspaceRoot },
553
941
  );
554
942
  }
@@ -572,6 +960,7 @@ export async function startDevServer(
572
960
  const listed = listEntries(workspaceRoot);
573
961
  const queries = listQueries(workspaceRoot);
574
962
  const liveQueries = loadLiveQueryRegistry(workspaceRoot);
963
+ const external = loadExternalServiceGraph(workspaceRoot);
575
964
  const entries = [
576
965
  ...queries.queries.map((query) => ({
577
966
  name: query.name,
@@ -594,6 +983,14 @@ export async function startDevServer(
594
983
  : `/actions/${entry.name}`,
595
984
  method: "POST",
596
985
  })),
986
+ ...(external.graph?.services.flatMap((service) =>
987
+ service.entries.map((entry) => ({
988
+ name: `${service.name}.${entry.name}`,
989
+ kind: `external:${entry.kind}`,
990
+ path: `/external/${encodeURIComponent(service.name)}/${entry.kind === "command" ? "commands" : "queries"}/${encodeURIComponent(entry.name)}`,
991
+ method: entry.method ?? "POST",
992
+ }))
993
+ ) ?? []),
597
994
  ].sort((a, b) =>
598
995
  a.path === b.path ? a.kind.localeCompare(b.kind) : a.path.localeCompare(b.path),
599
996
  );
@@ -615,6 +1012,7 @@ export async function startDevServer(
615
1012
  ...listed.diagnostics,
616
1013
  ...queries.diagnostics,
617
1014
  ...(liveQueries.registry?.diagnostics ?? []),
1015
+ ...external.diagnostics,
618
1016
  ],
619
1017
  };
620
1018
 
@@ -797,6 +1195,13 @@ export async function startDevServer(
797
1195
  "NaN",
798
1196
  );
799
1197
  let subscriptionId: string | null = null;
1198
+ const cleanupSubscription = () => {
1199
+ if (subscriptionId) {
1200
+ liveManager.unsubscribe(subscriptionId);
1201
+ subscriptionId = null;
1202
+ }
1203
+ };
1204
+ request.signal.addEventListener("abort", cleanupSubscription, { once: true });
800
1205
 
801
1206
  return createSseResponse(
802
1207
  async (send, close) => {
@@ -808,6 +1213,11 @@ export async function startDevServer(
808
1213
  send,
809
1214
  });
810
1215
  subscriptionId = subscription.id;
1216
+ if (request.signal.aborted) {
1217
+ cleanupSubscription();
1218
+ close();
1219
+ return;
1220
+ }
811
1221
  const known = loadLiveQueryRegistry(workspaceRoot).liveQueries.some(
812
1222
  (liveQuery) => liveQuery.name === name,
813
1223
  );
@@ -816,9 +1226,7 @@ export async function startDevServer(
816
1226
  }
817
1227
  },
818
1228
  () => {
819
- if (subscriptionId) {
820
- liveManager.unsubscribe(subscriptionId);
821
- }
1229
+ cleanupSubscription();
822
1230
  },
823
1231
  {
824
1232
  heartbeatIntervalMs: Number(process.env.FORGE_LIVE_HEARTBEAT_MS ?? "15000"),
@@ -877,6 +1285,283 @@ export async function startDevServer(
877
1285
  }
878
1286
  }
879
1287
 
1288
+ if (request.method === "POST" && pathname === "/ai/agents/run") {
1289
+ const body = (await request.json().catch(() => ({}))) as {
1290
+ agent?: string;
1291
+ provider?: string;
1292
+ model?: string;
1293
+ instructions?: string;
1294
+ prompt?: string;
1295
+ purpose?: string;
1296
+ maxSteps?: number;
1297
+ tools?: string[];
1298
+ };
1299
+ if (!body.prompt) {
1300
+ return jsonResponse(
1301
+ {
1302
+ ok: false,
1303
+ diagnostics: [
1304
+ createDiagnostic({
1305
+ severity: "error",
1306
+ code: FORGE_DEV_INVOKE_FAILED,
1307
+ message: "POST /ai/agents/run requires a prompt",
1308
+ fixHint: "Send JSON like {\"prompt\":\"...\",\"instructions\":\"...\",\"tools\":[\"forge_query_listTickets\"]}.",
1309
+ }),
1310
+ ],
1311
+ },
1312
+ 400,
1313
+ );
1314
+ }
1315
+ const auth = await authenticateHeaders(request.headers, authConfig);
1316
+ const envStore = getRuntimeEnvStore(workspaceRoot);
1317
+ const secretRegistry = loadSecretRegistry(workspaceRoot);
1318
+ const bundle = createRuntimeSecretsBundle({
1319
+ store: envStore,
1320
+ registry: secretRegistry,
1321
+ envSchema: null,
1322
+ runtimeKind: "server",
1323
+ });
1324
+ const traceId = generateTraceId();
1325
+ const telemetry = serverState.adapter
1326
+ ? createTelemetryContext({
1327
+ adapter: serverState.adapter,
1328
+ traceId,
1329
+ runtime: { kind: "endpoint", name: "ai.agent.run" },
1330
+ bufferInTransaction: false,
1331
+ workspaceRoot,
1332
+ sinks: telemetrySinks,
1333
+ })
1334
+ : createNoopTelemetryContext(traceId);
1335
+ const ai = createAiContext({
1336
+ secrets: bundle.secrets,
1337
+ telemetry,
1338
+ runtimeKind: "endpoint",
1339
+ mockAi: isMockAiEnabled({ mockAi: options.mockAi }),
1340
+ envelope: { traceId, tenantId: auth.kind === "user" || auth.kind === "system" ? auth.tenantId : undefined },
1341
+ toolContext: {
1342
+ env: envStore.snapshot(),
1343
+ auth,
1344
+ },
1345
+ });
1346
+ try {
1347
+ const namedAgent = await loadNamedAgentDefinition(workspaceRoot, body.agent);
1348
+ const tools = {
1349
+ ...explicitAgentTools(namedAgent),
1350
+ ...buildAutoAgentTools({
1351
+ workspaceRoot,
1352
+ registry: loadAgentToolRegistry(workspaceRoot),
1353
+ selected: body.tools,
1354
+ adapter: serverState.adapter,
1355
+ tableMap,
1356
+ auth,
1357
+ liveManager,
1358
+ json: options.json,
1359
+ mock: options.mock,
1360
+ }),
1361
+ };
1362
+ const result = await ai.runAgent({
1363
+ provider: (body.provider ?? namedAgent?.provider ?? "gateway") as ForgeAiProvider,
1364
+ model: body.model ?? namedAgent?.model ?? "openai/gpt-5.4",
1365
+ instructions:
1366
+ body.instructions ??
1367
+ namedAgent?.instructions ??
1368
+ "You are a ForgeOS app agent. Use available Forge tools when useful.",
1369
+ prompt: body.prompt,
1370
+ purpose: body.purpose ?? "dev_agent_run",
1371
+ stopWhen: namedAgent?.stopWhen,
1372
+ maxSteps: body.maxSteps ?? namedAgent?.maxSteps,
1373
+ tools,
1374
+ });
1375
+ return jsonResponse(
1376
+ {
1377
+ ok: true,
1378
+ result,
1379
+ traceId,
1380
+ tools: Object.keys(tools).sort(),
1381
+ },
1382
+ 200,
1383
+ { "x-forge-trace-id": traceId },
1384
+ );
1385
+ } catch (error) {
1386
+ const message = error instanceof Error ? error.message : String(error);
1387
+ return jsonResponse(
1388
+ {
1389
+ ok: false,
1390
+ traceId,
1391
+ diagnostics: [
1392
+ createDiagnostic({
1393
+ severity: "error",
1394
+ code: FORGE_DEV_INVOKE_FAILED,
1395
+ message,
1396
+ fixHint: `Inspect with forge ai trace ${traceId} --json or GET /telemetry/traces/${traceId}.`,
1397
+ }),
1398
+ ],
1399
+ },
1400
+ 400,
1401
+ { "x-forge-trace-id": traceId },
1402
+ );
1403
+ }
1404
+ }
1405
+
1406
+ if (request.method === "POST" && pathname === "/ai/agents/chat") {
1407
+ const body = (await request.json().catch(() => ({}))) as {
1408
+ agent?: string;
1409
+ provider?: string;
1410
+ model?: string;
1411
+ instructions?: string;
1412
+ purpose?: string;
1413
+ maxSteps?: number;
1414
+ tools?: string[];
1415
+ messages?: unknown[];
1416
+ };
1417
+ if (!Array.isArray(body.messages)) {
1418
+ return jsonResponse(
1419
+ {
1420
+ ok: false,
1421
+ diagnostics: [
1422
+ createDiagnostic({
1423
+ severity: "error",
1424
+ code: FORGE_DEV_INVOKE_FAILED,
1425
+ message: "POST /ai/agents/chat requires AI SDK UI messages",
1426
+ fixHint: "Use @ai-sdk/react useChat with DefaultChatTransport({ api: `${forgeUrl}/ai/agents/chat` }).",
1427
+ }),
1428
+ ],
1429
+ },
1430
+ 400,
1431
+ );
1432
+ }
1433
+
1434
+ const purpose = body.purpose ?? "dev_agent_chat";
1435
+ const auth = await authenticateHeaders(request.headers, authConfig);
1436
+ const envStore = getRuntimeEnvStore(workspaceRoot);
1437
+ const secretRegistry = loadSecretRegistry(workspaceRoot);
1438
+ const bundle = createRuntimeSecretsBundle({
1439
+ store: envStore,
1440
+ registry: secretRegistry,
1441
+ envSchema: null,
1442
+ runtimeKind: "server",
1443
+ });
1444
+ const traceId = generateTraceId();
1445
+ const telemetry = serverState.adapter
1446
+ ? createTelemetryContext({
1447
+ adapter: serverState.adapter,
1448
+ traceId,
1449
+ runtime: { kind: "endpoint", name: "ai.agent.chat" },
1450
+ bufferInTransaction: false,
1451
+ workspaceRoot,
1452
+ sinks: telemetrySinks,
1453
+ })
1454
+ : createNoopTelemetryContext(traceId);
1455
+ let provider = (body.provider ?? "gateway") as ForgeAiProvider;
1456
+ let model = body.model ?? "openai/gpt-5.4";
1457
+
1458
+ try {
1459
+ const namedAgent = await loadNamedAgentDefinition(workspaceRoot, body.agent);
1460
+ provider = (body.provider ?? namedAgent?.provider ?? "gateway") as ForgeAiProvider;
1461
+ model = body.model ?? namedAgent?.model ?? "openai/gpt-5.4";
1462
+ await telemetry.capture("forge.ai.agent.started", {
1463
+ traceId,
1464
+ provider,
1465
+ model,
1466
+ purpose,
1467
+ mode: "stream",
1468
+ });
1469
+ const languageModel = await resolveLanguageModel(provider, model, bundle.secrets);
1470
+ const forgeTools = {
1471
+ ...explicitAgentTools(namedAgent),
1472
+ ...buildAutoAgentTools({
1473
+ workspaceRoot,
1474
+ registry: loadAgentToolRegistry(workspaceRoot),
1475
+ selected: body.tools,
1476
+ adapter: serverState.adapter,
1477
+ tableMap,
1478
+ auth,
1479
+ liveManager,
1480
+ json: options.json,
1481
+ mock: options.mock,
1482
+ }),
1483
+ };
1484
+ const tools = await buildAiSdkTools({
1485
+ tools: forgeTools,
1486
+ provider,
1487
+ model,
1488
+ purpose,
1489
+ traceId,
1490
+ secrets: bundle.secrets,
1491
+ telemetry,
1492
+ env: envStore.snapshot(),
1493
+ auth,
1494
+ deltaRecorder,
1495
+ });
1496
+ const { ToolLoopAgent, createAgentUIStreamResponse, hasToolCall, stepCountIs } = await import("ai");
1497
+ const stopWhen =
1498
+ namedAgent?.stopWhen?.kind === "toolCall"
1499
+ ? (hasToolCall(namedAgent.stopWhen.toolName) as never)
1500
+ : namedAgent?.stopWhen?.kind === "stepCount"
1501
+ ? (stepCountIs(body.maxSteps ?? namedAgent.stopWhen.maxSteps) as never)
1502
+ : (stepCountIs(body.maxSteps ?? namedAgent?.maxSteps ?? 20) as never);
1503
+ const agent = new ToolLoopAgent({
1504
+ model: languageModel as never,
1505
+ instructions:
1506
+ body.instructions ??
1507
+ namedAgent?.instructions ??
1508
+ "You are a ForgeOS app agent. Use available Forge tools when useful.",
1509
+ tools: tools as never,
1510
+ stopWhen,
1511
+ });
1512
+ const response = await createAgentUIStreamResponse({
1513
+ agent: agent as never,
1514
+ uiMessages: body.messages,
1515
+ onStepFinish: async () => {
1516
+ await telemetry.capture("forge.ai.agent.step.completed", {
1517
+ traceId,
1518
+ provider,
1519
+ model,
1520
+ purpose,
1521
+ });
1522
+ },
1523
+ headers: {
1524
+ "Access-Control-Allow-Origin": "*",
1525
+ "x-forge-trace-id": traceId,
1526
+ },
1527
+ });
1528
+ await telemetry.capture("forge.ai.agent.completed", {
1529
+ traceId,
1530
+ provider,
1531
+ model,
1532
+ purpose,
1533
+ status: "streaming",
1534
+ });
1535
+ return response;
1536
+ } catch (error) {
1537
+ const message = error instanceof Error ? error.message : String(error);
1538
+ await telemetry.capture("forge.ai.agent.failed", {
1539
+ traceId,
1540
+ provider,
1541
+ model,
1542
+ purpose,
1543
+ status: "failed",
1544
+ error: message,
1545
+ });
1546
+ return jsonResponse(
1547
+ {
1548
+ ok: false,
1549
+ traceId,
1550
+ diagnostics: [
1551
+ createDiagnostic({
1552
+ severity: "error",
1553
+ code: FORGE_DEV_INVOKE_FAILED,
1554
+ message,
1555
+ fixHint: `Inspect with forge ai trace ${traceId} --json or GET /telemetry/traces/${traceId}.`,
1556
+ }),
1557
+ ],
1558
+ },
1559
+ 400,
1560
+ { "x-forge-trace-id": traceId },
1561
+ );
1562
+ }
1563
+ }
1564
+
880
1565
  if (request.method === "GET" && pathname === "/telemetry") {
881
1566
  if (!serverState.adapter) {
882
1567
  return jsonResponse({
@@ -978,12 +1663,11 @@ export async function startDevServer(
978
1663
 
979
1664
  if (request.method === "GET" && pathname === "/db/tables") {
980
1665
  if (serverState.adapter) {
981
- const result = await serverState.adapter.query(
982
- `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' ORDER BY table_name`,
983
- );
984
1666
  return jsonResponse({
985
1667
  ok: true,
986
- tables: result.rows.map((row) => String(row.table_name)),
1668
+ tables: serverState.adapter.kind === "memory"
1669
+ ? Object.keys(tableMap).sort()
1670
+ : await listDatabaseTables(serverState.adapter),
987
1671
  });
988
1672
  }
989
1673
 
@@ -994,6 +1678,15 @@ export async function startDevServer(
994
1678
  }
995
1679
 
996
1680
  if (request.method === "GET") {
1681
+ const externalInvoke = parseExternalInvoke(pathname);
1682
+ if (externalInvoke) {
1683
+ return methodHelpResponse({
1684
+ kind: externalInvoke.kind,
1685
+ name: `${externalInvoke.serviceName}.${externalInvoke.entryName}`,
1686
+ path: `/external/${externalInvoke.serviceName}/${externalInvoke.kind === "command" ? "commands" : "queries"}/${externalInvoke.entryName}`,
1687
+ });
1688
+ }
1689
+
997
1690
  const queryName = parseInvokeName(pathname, "/queries/");
998
1691
  if (queryName) {
999
1692
  return methodHelpResponse({
@@ -1151,6 +1844,43 @@ export async function startDevServer(
1151
1844
  return jsonResponse({ ok: true, ...result });
1152
1845
  }
1153
1846
 
1847
+ const externalInvoke = parseExternalInvoke(pathname);
1848
+ if (externalInvoke) {
1849
+ const args = await parseRequestArgs(request);
1850
+ const auth = await authenticateHeaders(request.headers, authConfig);
1851
+ const result = await runExternalEntry(
1852
+ workspaceRoot,
1853
+ {
1854
+ ...externalInvoke,
1855
+ args,
1856
+ auth,
1857
+ requestHeaders: request.headers,
1858
+ },
1859
+ { adapter: serverState.adapter },
1860
+ );
1861
+ const policyDenied = result.diagnostics.some(
1862
+ (diagnostic) => diagnostic.code === FORGE_POLICY_DENIED,
1863
+ );
1864
+ await deltaRecorder?.recordRuntimeCall({
1865
+ entryName: `${externalInvoke.serviceName}.${externalInvoke.entryName}`,
1866
+ entryKind: externalInvoke.kind,
1867
+ service: externalInvoke.serviceName,
1868
+ result: policyDenied ? "denied" : result.ok ? "success" : "failed",
1869
+ diagnostics: result.diagnostics,
1870
+ traceId: result.traceId,
1871
+ });
1872
+ return jsonResponse(
1873
+ {
1874
+ ok: result.ok,
1875
+ result: result.result,
1876
+ diagnostics: result.diagnostics,
1877
+ traceId: result.traceId,
1878
+ },
1879
+ result.ok ? 200 : policyDenied ? 403 : 400,
1880
+ result.traceId ? { "x-forge-trace-id": result.traceId } : {},
1881
+ );
1882
+ }
1883
+
1154
1884
  let entryName: string | null = null;
1155
1885
  let expectedKind: "command" | "action" | null = null;
1156
1886
  let queryName: string | null = null;
@@ -1200,6 +1930,13 @@ export async function startDevServer(
1200
1930
  const policyDenied = result.diagnostics.some(
1201
1931
  (diagnostic) => diagnostic.code === FORGE_POLICY_DENIED,
1202
1932
  );
1933
+ await deltaRecorder?.recordRuntimeCall({
1934
+ entryName: queryName,
1935
+ entryKind: "query",
1936
+ result: policyDenied ? "denied" : result.ok ? "success" : "failed",
1937
+ diagnostics: result.diagnostics,
1938
+ traceId: result.traceId,
1939
+ });
1203
1940
 
1204
1941
  return jsonResponse(
1205
1942
  {
@@ -1270,6 +2007,13 @@ export async function startDevServer(
1270
2007
  const policyDenied = result.diagnostics.some(
1271
2008
  (diagnostic) => diagnostic.code === FORGE_POLICY_DENIED,
1272
2009
  );
2010
+ await deltaRecorder?.recordRuntimeCall({
2011
+ entryName,
2012
+ entryKind: entry.kind,
2013
+ result: policyDenied ? "denied" : result.ok ? "success" : "failed",
2014
+ diagnostics: result.diagnostics,
2015
+ traceId: result.traceId,
2016
+ });
1273
2017
 
1274
2018
  if (policyDenied) {
1275
2019
  const denied = result.diagnostics.find(
@@ -1355,23 +2099,32 @@ export async function startDevServer(
1355
2099
  const protocol = "http";
1356
2100
  const url = `${protocol}://${host}:${port}`;
1357
2101
 
1358
- return {
2102
+ const handle: DevServerHandle = {
1359
2103
  host,
1360
2104
  port,
1361
2105
  url,
1362
- routes: devManifest.routes,
2106
+ routes: currentRoutes,
1363
2107
  state: serverState,
1364
2108
  outboxWorker: serverState.outboxWorker,
2109
+ reload: async (reason?: string) => {
2110
+ const result = await reloadGeneratedArtifacts(reason);
2111
+ handle.routes = currentRoutes;
2112
+ handle.outboxWorker = serverState.outboxWorker;
2113
+ return result;
2114
+ },
1365
2115
  stop: () => {
1366
2116
  serverState.outboxWorker?.stop();
1367
2117
  liveManager?.stop();
1368
2118
  server.stop(true);
1369
2119
  restoreRuntimeEnvironment();
2120
+ disposeRuntimeModuleNamespace();
1370
2121
  const adapter = serverState.adapter;
1371
2122
  serverState.adapter = null;
1372
2123
  void adapter?.close().catch(() => undefined);
1373
2124
  },
1374
2125
  };
2126
+
2127
+ return handle;
1375
2128
  }
1376
2129
 
1377
2130
  export function resolveDevPort(explicit?: number): number {