forgeos 0.1.0-alpha.2 → 0.1.0-alpha.21
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.
- package/.npmignore +4 -0
- package/AGENTS.md +168 -81
- package/CHANGELOG.md +211 -0
- package/README.md +88 -14
- package/adapters/go/README.md +23 -0
- package/adapters/go/go.mod +3 -0
- package/adapters/go/http.go +149 -0
- package/adapters/go/registry.go +234 -0
- package/adapters/go/types.go +136 -0
- package/adapters/java/README.md +68 -0
- package/adapters/java/pom.xml +34 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Auth.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Diagnostic.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Entry.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/EntryKind.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ErrorInfo.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Forge.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeCall.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeContext.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHandler.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHttpHandler.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$EntryOption.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegisteredEntry.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegistryOption.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Json.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Manifest.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/RequestEnvelope.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/ResponseEnvelope.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Risk.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Schemas.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/Service.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/TransactionMode.class +0 -0
- package/adapters/java/target/classes/dev/forgeos/adapter/TypedForgeHandler.class +0 -0
- package/adapters/java/target/forge-java-adapter-0.1.0-alpha.11.jar +0 -0
- package/adapters/java/target/maven-archiver/pom.properties +3 -0
- package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +23 -0
- package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +20 -0
- package/adapters/java-spring-boot-starter/README.md +32 -0
- package/adapters/java-spring-boot-starter/pom.xml +36 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
- package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
- package/adapters/java-spring-boot-starter/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeCommand.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeExternalService.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeQuery.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.class +0 -0
- package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringRuntime.class +0 -0
- package/adapters/java-spring-boot-starter/target/forge-java-spring-boot-starter-0.1.0-alpha.11.jar +0 -0
- package/adapters/java-spring-boot-starter/target/maven-archiver/pom.properties +3 -0
- package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +6 -0
- package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +6 -0
- package/bin/forge.mjs +18 -0
- package/docs/changelog.md +242 -0
- package/docs/forge-protocol.md +189 -0
- package/examples/go-billing/go.mod +7 -0
- package/examples/go-billing/main.go +120 -0
- package/examples/java-billing/pom.xml +52 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/CreateInvoiceInput.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Invoice.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$EmptyInput.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$Options.class +0 -0
- package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main.class +0 -0
- package/examples/java-billing/target/java-billing-0.1.0-alpha.11-all.jar +0 -0
- package/examples/java-billing/target/java-billing-0.1.0-alpha.11.jar +0 -0
- package/examples/java-billing/target/maven-archiver/pom.properties +3 -0
- package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +5 -0
- package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +3 -0
- package/package.json +29 -7
- package/schemas/forge-manifest.schema.json +57 -0
- package/src/forge/_generated/releaseManifest.json +1 -2
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/agent-adapters/index.ts +1511 -123
- package/src/forge/agent-adapters/types.ts +216 -1
- package/src/forge/agent-memory/bridge.ts +1245 -0
- package/src/forge/agent-memory/context-pack.ts +151 -0
- package/src/forge/agent-memory/hook-runner.ts +312 -0
- package/src/forge/agent-memory/mcp.ts +224 -0
- package/src/forge/agent-memory/normalize.ts +498 -0
- package/src/forge/agent-memory/redaction.ts +103 -0
- package/src/forge/agent-memory/sources/claude-code.ts +51 -0
- package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
- package/src/forge/agent-memory/sources/codex.ts +119 -0
- package/src/forge/agent-memory/sources/cursor.ts +35 -0
- package/src/forge/agent-memory/types.ts +191 -0
- package/src/forge/bench.ts +248 -0
- package/src/forge/brownfield-import/index.ts +801 -0
- package/src/forge/brownfield-import/types.ts +127 -0
- package/src/forge/cair/action-journal.ts +61 -0
- package/src/forge/cair/action-parser.ts +314 -0
- package/src/forge/cair/action-validator.ts +40 -0
- package/src/forge/cair/actions.ts +1818 -0
- package/src/forge/cair/format.ts +77 -0
- package/src/forge/cair/index.ts +106 -0
- package/src/forge/cair/query.ts +478 -0
- package/src/forge/cair/snapshot.ts +315 -0
- package/src/forge/cair/types.ts +248 -0
- package/src/forge/cli/ai.ts +671 -3
- package/src/forge/cli/auth.ts +36 -1
- package/src/forge/cli/build.ts +20 -4
- package/src/forge/cli/changed.ts +300 -0
- package/src/forge/cli/codex-app-server.ts +877 -0
- package/src/forge/cli/commands.ts +1285 -7
- package/src/forge/cli/db.ts +121 -2
- package/src/forge/cli/deps.ts +79 -12
- package/src/forge/cli/dev.ts +502 -38
- package/src/forge/cli/docs.ts +265 -0
- package/src/forge/cli/handoff.ts +250 -0
- package/src/forge/cli/index.ts +1 -0
- package/src/forge/cli/main.ts +49 -3
- package/src/forge/cli/new.ts +3 -1
- package/src/forge/cli/next-actions.ts +23 -0
- package/src/forge/cli/output.ts +290 -1
- package/src/forge/cli/parse.ts +770 -36
- package/src/forge/cli/query.ts +32 -0
- package/src/forge/cli/release.ts +35 -11
- package/src/forge/cli/rls.ts +568 -17
- package/src/forge/cli/run.ts +41 -0
- package/src/forge/cli/secrets.ts +46 -1
- package/src/forge/cli/security.ts +381 -0
- package/src/forge/cli/self-host.ts +56 -14
- package/src/forge/cli/studio.ts +2163 -0
- package/src/forge/cli/verify.ts +1422 -32
- package/src/forge/compiler/agent-contract/build.ts +725 -41
- package/src/forge/compiler/agent-contract/types.ts +85 -0
- package/src/forge/compiler/ai-registry/build.ts +62 -1
- package/src/forge/compiler/ai-registry/constants.ts +1 -1
- package/src/forge/compiler/ai-registry/parse.ts +168 -5
- package/src/forge/compiler/api-surface/build.ts +47 -0
- package/src/forge/compiler/app-graph/build.ts +68 -8
- package/src/forge/compiler/app-graph/extract.ts +107 -0
- package/src/forge/compiler/app-graph/forge-apis.ts +1 -0
- package/src/forge/compiler/app-graph/module-graph.ts +73 -78
- package/src/forge/compiler/app-graph/parser.ts +24 -24
- package/src/forge/compiler/app-graph/profile.ts +26 -0
- package/src/forge/compiler/app-graph/versions.ts +1 -1
- package/src/forge/compiler/classifier/capabilities.ts +3 -2
- package/src/forge/compiler/classifier/classify.ts +32 -8
- package/src/forge/compiler/classifier/secrets.ts +3 -2
- package/src/forge/compiler/classifier/signals.ts +91 -1
- package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
- package/src/forge/compiler/client-sdk/render-client.ts +188 -13
- package/src/forge/compiler/data-graph/parse.ts +3 -3
- package/src/forge/compiler/data-graph/sql/ddl.ts +60 -2
- package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
- package/src/forge/compiler/data-graph/sql/types.ts +1 -0
- package/src/forge/compiler/dev-manifest/build.ts +3 -0
- package/src/forge/compiler/diagnostics/codes.ts +35 -0
- package/src/forge/compiler/diagnostics/create.ts +8 -3
- package/src/forge/compiler/diagnostics/index.ts +2 -0
- package/src/forge/compiler/emitter/barrel.ts +3 -0
- package/src/forge/compiler/emitter/render.ts +5 -0
- package/src/forge/compiler/external-manifest/registry.ts +205 -0
- package/src/forge/compiler/external-manifest/types.ts +91 -0
- package/src/forge/compiler/external-manifest/validate.ts +373 -0
- package/src/forge/compiler/frontend-graph/build.ts +85 -13
- package/src/forge/compiler/integration/add.ts +498 -22
- package/src/forge/compiler/integration/snapshot.ts +2 -0
- package/src/forge/compiler/make-registry/build.ts +19 -7
- package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
- package/src/forge/compiler/orchestrator/plan.ts +78 -7
- package/src/forge/compiler/orchestrator/profile.ts +65 -0
- package/src/forge/compiler/orchestrator/run.ts +97 -31
- package/src/forge/compiler/orchestrator/serialize.ts +101 -8
- package/src/forge/compiler/package-graph/compiler.ts +13 -3
- package/src/forge/compiler/package-manager/adapter.ts +4 -1
- package/src/forge/compiler/package-manager/commands.ts +4 -0
- package/src/forge/compiler/package-manager/executor.ts +30 -1
- package/src/forge/compiler/policy-registry/build.ts +44 -1
- package/src/forge/compiler/test-graph/build.ts +11 -3
- package/src/forge/compiler/types/ai-registry.ts +25 -1
- package/src/forge/compiler/types/app-graph.ts +9 -2
- package/src/forge/compiler/types/cli.ts +76 -1
- package/src/forge/compiler/types/dev-manifest.ts +3 -0
- package/src/forge/compiler/types/frontend-graph.ts +2 -2
- package/src/forge/delta/classifier.ts +52 -0
- package/src/forge/delta/explain.ts +126 -0
- package/src/forge/delta/git-observer.ts +43 -0
- package/src/forge/delta/ids.ts +44 -0
- package/src/forge/delta/index.ts +13 -0
- package/src/forge/delta/recorder.ts +402 -0
- package/src/forge/delta/redaction.ts +50 -0
- package/src/forge/delta/schema.ts +240 -0
- package/src/forge/delta/session.ts +142 -0
- package/src/forge/delta/status.ts +489 -0
- package/src/forge/delta/store.ts +2975 -0
- package/src/forge/delta/timeline.ts +104 -0
- package/src/forge/dev/server.ts +768 -15
- package/src/forge/dev/types.ts +15 -1
- package/src/forge/dev/watch.ts +17 -7
- package/src/forge/dev-console/cycle.ts +233 -21
- package/src/forge/dev-console/types.ts +46 -1
- package/src/forge/impact/index.ts +46 -8
- package/src/forge/impact/types.ts +6 -0
- package/src/forge/intent/index.ts +35 -16
- package/src/forge/make/index.ts +149 -6
- package/src/forge/make/templates.ts +343 -2
- package/src/forge/make/types.ts +3 -1
- package/src/forge/refactor/index.ts +1 -0
- package/src/forge/repair/rules/index.ts +2 -2
- package/src/forge/review/index.ts +158 -12
- package/src/forge/review/types.ts +15 -0
- package/src/forge/runtime/ai/context.ts +210 -5
- package/src/forge/runtime/ai/types.ts +70 -0
- package/src/forge/runtime/auth/claims.ts +32 -0
- package/src/forge/runtime/auth/errors.ts +2 -0
- package/src/forge/runtime/context/create-context.ts +30 -6
- package/src/forge/runtime/db/generated-client.ts +13 -2
- package/src/forge/runtime/db/memory-adapter.ts +2 -2
- package/src/forge/runtime/db/pglite-adapter.ts +77 -2
- package/src/forge/runtime/db/postgres-adapter.ts +6 -3
- package/src/forge/runtime/executor.ts +112 -2
- package/src/forge/runtime/external/bridge.ts +649 -0
- package/src/forge/runtime/runner/run-entry.ts +16 -7
- package/src/forge/runtime/telemetry/scrubber.ts +91 -10
- package/src/forge/runtime/webhooks/security.ts +184 -0
- package/src/forge/server.ts +100 -2
- package/src/forge/version.ts +1 -1
- package/src/forge/vue/index.ts +407 -0
- package/src/forge/workspace/change-summary.ts +209 -0
- package/src/forge/workspace/forge-cli.ts +14 -0
- package/src/forge/workspace/git-summary.ts +279 -0
- package/templates/agent-workroom/AGENTS.md +29 -0
- package/templates/agent-workroom/README.md +34 -0
- package/templates/agent-workroom/forge.config.ts +3 -0
- package/templates/agent-workroom/package.json +33 -0
- package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
- package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
- package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
- package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
- package/templates/agent-workroom/src/forge/schema.ts +54 -0
- package/templates/agent-workroom/src/policies.ts +6 -0
- package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
- package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
- package/templates/agent-workroom/tsconfig.json +16 -0
- package/templates/agent-workroom/web/index.html +12 -0
- package/templates/agent-workroom/web/package.json +21 -0
- package/templates/agent-workroom/web/src/App.tsx +345 -0
- package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
- package/templates/agent-workroom/web/src/main.tsx +13 -0
- package/templates/agent-workroom/web/src/styles.css +545 -0
- package/templates/agent-workroom/web/tsconfig.json +27 -0
- package/templates/b2b-support-web/package.json +2 -0
- package/templates/b2b-support-web/tsconfig.json +4 -1
- package/templates/b2b-support-web/web/package.json +1 -1
- package/templates/minimal-web/package.json +2 -1
- package/templates/minimal-web/tsconfig.json +3 -1
- package/templates/minimal-web/web/package.json +2 -2
- package/src/forge/_generated/actionSubscriptions.json +0 -2
- package/src/forge/_generated/actionSubscriptions.ts +0 -10
- package/src/forge/_generated/agentAdapterManifest.json +0 -2
- package/src/forge/_generated/agentAdapterManifest.ts +0 -73
- package/src/forge/_generated/agentContract.json +0 -2
- package/src/forge/_generated/agentContract.ts +0 -7696
- package/src/forge/_generated/agentQuickstart.md +0 -32
- package/src/forge/_generated/aiContext.ts +0 -59
- package/src/forge/_generated/aiModels.json +0 -2
- package/src/forge/_generated/aiModels.ts +0 -35
- package/src/forge/_generated/aiProviders.json +0 -2
- package/src/forge/_generated/aiProviders.ts +0 -23
- package/src/forge/_generated/aiRegistry.json +0 -2
- package/src/forge/_generated/aiRegistry.ts +0 -29
- package/src/forge/_generated/api.json +0 -2
- package/src/forge/_generated/api.ts +0 -8
- package/src/forge/_generated/appGraph.json +0 -2
- package/src/forge/_generated/appGraph.ts +0 -14667
- package/src/forge/_generated/appMap.md +0 -35
- package/src/forge/_generated/artifactManifest.json +0 -2
- package/src/forge/_generated/artifactManifest.ts +0 -7
- package/src/forge/_generated/authClaims.json +0 -2
- package/src/forge/_generated/authClaims.ts +0 -13
- package/src/forge/_generated/authConfig.json +0 -2
- package/src/forge/_generated/authConfig.ts +0 -17
- package/src/forge/_generated/authContext.ts +0 -23
- package/src/forge/_generated/authRegistry.json +0 -2
- package/src/forge/_generated/authRegistry.ts +0 -25
- package/src/forge/_generated/buildInfo.json +0 -2
- package/src/forge/_generated/buildInfo.ts +0 -9
- package/src/forge/_generated/capabilityMap.json +0 -2
- package/src/forge/_generated/capabilityMap.md +0 -15
- package/src/forge/_generated/capabilityMap.ts +0 -17
- package/src/forge/_generated/client.ts +0 -282
- package/src/forge/_generated/clientApi.ts +0 -9
- package/src/forge/_generated/clientManifest.json +0 -2
- package/src/forge/_generated/clientManifest.ts +0 -39
- package/src/forge/_generated/clientTypes.ts +0 -78
- package/src/forge/_generated/configRegistry.json +0 -2
- package/src/forge/_generated/configRegistry.ts +0 -4
- package/src/forge/_generated/dataGraph.json +0 -2
- package/src/forge/_generated/dataGraph.ts +0 -8
- package/src/forge/_generated/db.json +0 -2
- package/src/forge/_generated/db.ts +0 -2
- package/src/forge/_generated/dbSecurityManifest.json +0 -2
- package/src/forge/_generated/dbSecurityManifest.ts +0 -15
- package/src/forge/_generated/dbSessionContext.json +0 -2
- package/src/forge/_generated/dbSessionContext.ts +0 -39
- package/src/forge/_generated/deployManifest.json +0 -2
- package/src/forge/_generated/deployManifest.ts +0 -14
- package/src/forge/_generated/devManifest.json +0 -2
- package/src/forge/_generated/devManifest.ts +0 -47
- package/src/forge/_generated/envSchema.json +0 -2
- package/src/forge/_generated/envSchema.ts +0 -59
- package/src/forge/_generated/frontendGraph.json +0 -2
- package/src/forge/_generated/frontendGraph.ts +0 -27
- package/src/forge/_generated/importGuards.json +0 -2
- package/src/forge/_generated/importGuards.ts +0 -686
- package/src/forge/_generated/index.ts +0 -67
- package/src/forge/_generated/liveProductionManifest.json +0 -2
- package/src/forge/_generated/liveProductionManifest.ts +0 -23
- package/src/forge/_generated/liveProtocol.json +0 -2
- package/src/forge/_generated/liveProtocol.ts +0 -21
- package/src/forge/_generated/liveQueryRegistry.json +0 -2
- package/src/forge/_generated/liveQueryRegistry.ts +0 -9
- package/src/forge/_generated/liveTransportConfig.json +0 -2
- package/src/forge/_generated/liveTransportConfig.ts +0 -19
- package/src/forge/_generated/makeRegistry.json +0 -2
- package/src/forge/_generated/makeRegistry.ts +0 -163
- package/src/forge/_generated/makeTemplates.json +0 -2
- package/src/forge/_generated/makeTemplates.ts +0 -61
- package/src/forge/_generated/mockMap.json +0 -2
- package/src/forge/_generated/mockMap.ts +0 -7
- package/src/forge/_generated/operationPlaybooks.md +0 -147
- package/src/forge/_generated/packageGraph.json +0 -2
- package/src/forge/_generated/packageGraph.ts +0 -245249
- package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
- package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
- package/src/forge/_generated/permissionMatrix.json +0 -2
- package/src/forge/_generated/permissionMatrix.ts +0 -7
- package/src/forge/_generated/policyRegistry.json +0 -2
- package/src/forge/_generated/policyRegistry.ts +0 -11
- package/src/forge/_generated/queryRegistry.json +0 -2
- package/src/forge/_generated/queryRegistry.ts +0 -9
- package/src/forge/_generated/react.d.ts +0 -22
- package/src/forge/_generated/react.ts +0 -29
- package/src/forge/_generated/reactManifest.json +0 -2
- package/src/forge/_generated/reactManifest.ts +0 -19
- package/src/forge/_generated/rlsPolicies.json +0 -2
- package/src/forge/_generated/rlsPolicies.sql +0 -34
- package/src/forge/_generated/rlsPolicies.ts +0 -6
- package/src/forge/_generated/runtimeGraph.json +0 -2
- package/src/forge/_generated/runtimeGraph.ts +0 -8
- package/src/forge/_generated/runtimeMatrix.json +0 -2
- package/src/forge/_generated/runtimeMatrix.ts +0 -327385
- package/src/forge/_generated/runtimeRegistry.ts +0 -2
- package/src/forge/_generated/runtimeRules.md +0 -79
- package/src/forge/_generated/secretRegistry.json +0 -2
- package/src/forge/_generated/secretRegistry.ts +0 -50
- package/src/forge/_generated/secretsContext.ts +0 -11
- package/src/forge/_generated/serverApi.ts +0 -10
- package/src/forge/_generated/sourceMapManifest.json +0 -2
- package/src/forge/_generated/sourceMapManifest.ts +0 -7
- package/src/forge/_generated/sqlPlan.json +0 -2
- package/src/forge/_generated/sqlPlan.ts +0 -88
- package/src/forge/_generated/subscriptionManifest.json +0 -2
- package/src/forge/_generated/subscriptionManifest.ts +0 -7
- package/src/forge/_generated/symbolicationManifest.json +0 -2
- package/src/forge/_generated/symbolicationManifest.ts +0 -17
- package/src/forge/_generated/telemetryRegistry.json +0 -2
- package/src/forge/_generated/telemetryRegistry.ts +0 -9
- package/src/forge/_generated/telemetrySinks.json +0 -2
- package/src/forge/_generated/telemetrySinks.ts +0 -11
- package/src/forge/_generated/tenantScope.json +0 -2
- package/src/forge/_generated/tenantScope.ts +0 -8
- package/src/forge/_generated/testGraph.json +0 -2
- package/src/forge/_generated/testGraph.ts +0 -3108
- package/src/forge/_generated/testPlanRegistry.json +0 -2
- package/src/forge/_generated/testPlanRegistry.ts +0 -33
- package/src/forge/_generated/uiRoutes.json +0 -2
- package/src/forge/_generated/uiRoutes.ts +0 -16
- package/src/forge/_generated/uiScenarios.json +0 -2
- package/src/forge/_generated/uiScenarios.ts +0 -30
- package/src/forge/_generated/uiTestManifest.json +0 -2
- package/src/forge/_generated/uiTestManifest.ts +0 -27
- package/src/forge/_generated/workflowRegistry.json +0 -2
- package/src/forge/_generated/workflowRegistry.ts +0 -9
- package/src/forge/_generated/workflowSubscriptions.json +0 -2
- package/src/forge/_generated/workflowSubscriptions.ts +0 -10
package/src/forge/dev/server.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
2102
|
+
const handle: DevServerHandle = {
|
|
1359
2103
|
host,
|
|
1360
2104
|
port,
|
|
1361
2105
|
url,
|
|
1362
|
-
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 {
|