@bevel-software/platform-core-backend 0.14.0 → 0.19.0
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/THIRD-PARTY-NOTICES.md +8 -8
- package/dist/core/catalog-cache-invalidation.d.ts +45 -0
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -0
- package/dist/core/catalog-cache-invalidation.js +76 -0
- package/dist/core/catalog-cache-invalidation.js.map +1 -0
- package/dist/core/catalog-revision.d.ts +39 -0
- package/dist/core/catalog-revision.d.ts.map +1 -0
- package/dist/core/catalog-revision.js +94 -0
- package/dist/core/catalog-revision.js.map +1 -0
- package/dist/core/create-core-server.d.ts +7 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +279 -64
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +66 -2
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +290 -111
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +138 -0
- package/dist/core/lifecycle.d.ts.map +1 -0
- package/dist/core/lifecycle.js +218 -0
- package/dist/core/lifecycle.js.map +1 -0
- package/dist/core/public-config.d.ts +82 -0
- package/dist/core/public-config.d.ts.map +1 -0
- package/dist/core/public-config.js +78 -0
- package/dist/core/public-config.js.map +1 -0
- package/dist/core/readiness.d.ts +86 -0
- package/dist/core/readiness.d.ts.map +1 -0
- package/dist/core/readiness.js +56 -0
- package/dist/core/readiness.js.map +1 -0
- package/dist/core-config.d.ts +30 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +64 -2
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +231 -15
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +83 -12
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +815 -212
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +71 -5
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +161 -13
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-view.d.ts +147 -0
- package/dist/modules/access/access-view.d.ts.map +1 -0
- package/dist/modules/access/access-view.js +211 -0
- package/dist/modules/access/access-view.js.map +1 -0
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +312 -84
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/admin-locked-commit.d.ts +2 -3
- package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
- package/dist/modules/access/admin-locked-commit.js +9 -8
- package/dist/modules/access/admin-locked-commit.js.map +1 -1
- package/dist/modules/access/admin-route-helpers.d.ts.map +1 -1
- package/dist/modules/access/admin-route-helpers.js +4 -4
- package/dist/modules/access/admin-route-helpers.js.map +1 -1
- package/dist/modules/access/change-read-gate.d.ts +53 -0
- package/dist/modules/access/change-read-gate.d.ts.map +1 -0
- package/dist/modules/access/change-read-gate.js +134 -0
- package/dist/modules/access/change-read-gate.js.map +1 -0
- package/dist/modules/access/creator-access.d.ts +33 -39
- package/dist/modules/access/creator-access.d.ts.map +1 -1
- package/dist/modules/access/creator-access.js +92 -119
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +3 -1
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/access/groups-admin.service.d.ts +3 -3
- package/dist/modules/access/groups-admin.service.js +2 -2
- package/dist/modules/access/reference-scan.d.ts +1 -1
- package/dist/modules/access/reference-scan.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.js +5 -4
- package/dist/modules/access/reference-scan.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +72 -13
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +71 -13
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access/synced-groups-committer.d.ts.map +1 -1
- package/dist/modules/access/synced-groups-committer.js +8 -6
- package/dist/modules/access/synced-groups-committer.js.map +1 -1
- package/dist/modules/access/synced-groups-writer.d.ts.map +1 -1
- package/dist/modules/access/synced-groups-writer.js +3 -8
- package/dist/modules/access/synced-groups-writer.js.map +1 -1
- package/dist/modules/access/user-access-removal.service.d.ts +209 -0
- package/dist/modules/access/user-access-removal.service.d.ts.map +1 -0
- package/dist/modules/access/user-access-removal.service.js +662 -0
- package/dist/modules/access/user-access-removal.service.js.map +1 -0
- package/dist/modules/access-model/access-errors.d.ts +32 -1
- package/dist/modules/access-model/access-errors.d.ts.map +1 -1
- package/dist/modules/access-model/access-errors.js +38 -11
- package/dist/modules/access-model/access-errors.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +100 -25
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +162 -48
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/access-splice.d.ts.map +1 -1
- package/dist/modules/access-model/access-splice.js +18 -18
- package/dist/modules/access-model/access-splice.js.map +1 -1
- package/dist/modules/access-model/access-template.d.ts +22 -0
- package/dist/modules/access-model/access-template.d.ts.map +1 -0
- package/dist/modules/access-model/access-template.js +42 -0
- package/dist/modules/access-model/access-template.js.map +1 -0
- package/dist/modules/access-model/change-gate.d.ts +65 -0
- package/dist/modules/access-model/change-gate.d.ts.map +1 -0
- package/dist/modules/access-model/change-gate.js +25 -0
- package/dist/modules/access-model/change-gate.js.map +1 -0
- package/dist/modules/access-model/creator.d.ts +13 -16
- package/dist/modules/access-model/creator.d.ts.map +1 -1
- package/dist/modules/access-model/creator.js.map +1 -1
- package/dist/modules/access-model/frontmatter-lines.d.ts +27 -0
- package/dist/modules/access-model/frontmatter-lines.d.ts.map +1 -0
- package/dist/modules/access-model/frontmatter-lines.js +74 -0
- package/dist/modules/access-model/frontmatter-lines.js.map +1 -0
- package/dist/modules/access-model/group-files.d.ts.map +1 -1
- package/dist/modules/access-model/group-files.js +4 -1
- package/dist/modules/access-model/group-files.js.map +1 -1
- package/dist/modules/access-model/plugin-principals.d.ts +51 -0
- package/dist/modules/access-model/plugin-principals.d.ts.map +1 -0
- package/dist/modules/access-model/plugin-principals.js +119 -0
- package/dist/modules/access-model/plugin-principals.js.map +1 -0
- package/dist/modules/access-model/roles-yaml-guard.d.ts +73 -2
- package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
- package/dist/modules/access-model/roles-yaml-guard.js +153 -3
- package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
- package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
- package/dist/modules/admin/admin-access.service.js +5 -2
- package/dist/modules/admin/admin-access.service.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +21 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.js +38 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -0
- package/dist/modules/agent-instructions/compose.d.ts +71 -0
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -0
- package/dist/modules/agent-instructions/compose.js +234 -0
- package/dist/modules/agent-instructions/compose.js.map +1 -0
- package/dist/modules/agent-instructions/index.d.ts +4 -0
- package/dist/modules/agent-instructions/index.d.ts.map +1 -0
- package/dist/modules/agent-instructions/index.js +4 -0
- package/dist/modules/agent-instructions/index.js.map +1 -0
- package/dist/modules/agent-instructions/read-preamble.d.ts +37 -0
- package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -0
- package/dist/modules/agent-instructions/read-preamble.js +81 -0
- package/dist/modules/agent-instructions/read-preamble.js.map +1 -0
- package/dist/modules/auth/account-erasure.service.d.ts +30 -4
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +96 -10
- package/dist/modules/auth/account-erasure.service.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts +6 -3
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +109 -10
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts +10 -3
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +9 -4
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +89 -5
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +160 -25
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts +46 -3
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +99 -21
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +14 -2
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +56 -7
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/advisory-lock.d.ts +103 -0
- package/dist/modules/database/advisory-lock.d.ts.map +1 -0
- package/dist/modules/database/advisory-lock.js +320 -0
- package/dist/modules/database/advisory-lock.js.map +1 -0
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +9 -1
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +672 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +171 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +26 -6
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.js +4 -2
- package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
- package/dist/modules/diff/diff.routes.d.ts.map +1 -1
- package/dist/modules/diff/diff.routes.js +2 -4
- package/dist/modules/diff/diff.routes.js.map +1 -1
- package/dist/modules/diff/diff.service.d.ts +4 -1
- package/dist/modules/diff/diff.service.d.ts.map +1 -1
- package/dist/modules/diff/diff.service.js +79 -81
- package/dist/modules/diff/diff.service.js.map +1 -1
- package/dist/modules/kb-fs/bevel-ignore.d.ts +35 -0
- package/dist/modules/kb-fs/bevel-ignore.d.ts.map +1 -0
- package/dist/modules/kb-fs/bevel-ignore.js +71 -0
- package/dist/modules/kb-fs/bevel-ignore.js.map +1 -0
- package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -1
- package/dist/modules/kb-fs/file-change-notifier.js +3 -1
- package/dist/modules/kb-fs/file-change-notifier.js.map +1 -1
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +37 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.js +104 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts +113 -18
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +353 -76
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/node-fs.d.ts +23 -0
- package/dist/modules/kb-fs/node-fs.d.ts.map +1 -0
- package/dist/modules/kb-fs/node-fs.js +158 -0
- package/dist/modules/kb-fs/node-fs.js.map +1 -0
- package/dist/modules/kb-fs/read-only-filesystem.d.ts +10 -9
- package/dist/modules/kb-fs/read-only-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/read-only-filesystem.js +18 -9
- package/dist/modules/kb-fs/read-only-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +100 -3
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +195 -3
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-sync/index.d.ts +6 -0
- package/dist/modules/kb-sync/index.d.ts.map +1 -0
- package/dist/modules/kb-sync/index.js +5 -0
- package/dist/modules/kb-sync/index.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +77 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.js +2 -0
- package/dist/modules/kb-sync/kb-sync.interface.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts +70 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.js +137 -0
- package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts +45 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.js +185 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -0
- package/dist/modules/kb-sync/sync-auth.d.ts +59 -0
- package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-auth.js +62 -0
- package/dist/modules/kb-sync/sync-auth.js.map +1 -0
- package/dist/modules/kb-sync/sync-payload.d.ts +32 -0
- package/dist/modules/kb-sync/sync-payload.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-payload.js +105 -0
- package/dist/modules/kb-sync/sync-payload.js.map +1 -0
- package/dist/modules/marketplace/git-http.routes.d.ts +42 -0
- package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/git-http.routes.js +192 -0
- package/dist/modules/marketplace/git-http.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +33 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +88 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts +59 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js +96 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +126 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +192 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts +47 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js +252 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +121 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js +213 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/index.d.ts +6 -0
- package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/index.js +6 -0
- package/dist/modules/marketplace/github-facade/index.js.map +1 -0
- package/dist/modules/marketplace/index.d.ts +4 -0
- package/dist/modules/marketplace/index.d.ts.map +1 -0
- package/dist/modules/marketplace/index.js +4 -0
- package/dist/modules/marketplace/index.js.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts +135 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.js +318 -0
- package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -0
- package/dist/modules/mcp/downstream-pool.d.ts +107 -0
- package/dist/modules/mcp/downstream-pool.d.ts.map +1 -0
- package/dist/modules/mcp/downstream-pool.js +221 -0
- package/dist/modules/mcp/downstream-pool.js.map +1 -0
- package/dist/modules/mcp/downstream-token-refresh.d.ts +77 -0
- package/dist/modules/mcp/downstream-token-refresh.d.ts.map +1 -0
- package/dist/modules/mcp/downstream-token-refresh.js +166 -0
- package/dist/modules/mcp/downstream-token-refresh.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli-args.d.ts +78 -0
- package/dist/modules/mcp/first-call-probe.cli-args.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli-args.js +151 -0
- package/dist/modules/mcp/first-call-probe.cli-args.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli.d.ts +2 -0
- package/dist/modules/mcp/first-call-probe.cli.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli.js +62 -0
- package/dist/modules/mcp/first-call-probe.cli.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.d.ts +165 -0
- package/dist/modules/mcp/first-call-probe.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.js +298 -0
- package/dist/modules/mcp/first-call-probe.js.map +1 -0
- package/dist/modules/mcp/manual-failure-memo.d.ts +42 -17
- package/dist/modules/mcp/manual-failure-memo.d.ts.map +1 -1
- package/dist/modules/mcp/manual-failure-memo.js +79 -23
- package/dist/modules/mcp/manual-failure-memo.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +9 -5
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +24 -16
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +4 -4
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +83 -179
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +218 -61
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +713 -159
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +8 -2
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +14 -8
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +16 -2
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.d.ts +13 -2
- package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.js +3 -1
- package/dist/modules/mcp/oauth/oauth-state.js.map +1 -1
- package/dist/modules/mcp/surface-log-throttle.d.ts +59 -0
- package/dist/modules/mcp/surface-log-throttle.d.ts.map +1 -0
- package/dist/modules/mcp/surface-log-throttle.js +94 -0
- package/dist/modules/mcp/surface-log-throttle.js.map +1 -0
- package/dist/modules/plugins/compile/compile-marketplace.d.ts +104 -0
- package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -0
- package/dist/modules/plugins/compile/compile-marketplace.js +323 -0
- package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +73 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +135 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +37 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +158 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +65 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js +204 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +39 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js +110 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -0
- package/dist/modules/plugins/discovery/native.source.d.ts +16 -0
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/native.source.js +120 -0
- package/dist/modules/plugins/discovery/native.source.js.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts +110 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.js +2 -0
- package/dist/modules/plugins/discovery/plugin-source.js.map +1 -0
- package/dist/modules/plugins/index.d.ts +15 -3
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +13 -2
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts +250 -0
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -0
- package/dist/modules/plugins/join-request-jobs.service.js +602 -0
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -0
- package/dist/modules/plugins/join-request-records.store.d.ts +161 -0
- package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -0
- package/dist/modules/plugins/join-request-records.store.js +179 -0
- package/dist/modules/plugins/join-request-records.store.js.map +1 -0
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +4 -2
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +69 -0
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.js +163 -0
- package/dist/modules/plugins/plugin-links.js.map +1 -0
- package/dist/modules/plugins/plugin-links.service.d.ts +111 -0
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.service.js +295 -0
- package/dist/modules/plugins/plugin-links.service.js.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts +129 -21
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +320 -75
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +71 -0
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-rename.service.js +254 -0
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -0
- package/dist/modules/plugins/plugins.contract.d.ts +78 -4
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +44 -5
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +297 -115
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +82 -3
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +178 -48
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/plugins/plugins.tools.d.ts +25 -0
- package/dist/modules/plugins/plugins.tools.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.tools.js +82 -0
- package/dist/modules/plugins/plugins.tools.js.map +1 -0
- package/dist/modules/plugins/teams.routes.d.ts +42 -0
- package/dist/modules/plugins/teams.routes.d.ts.map +1 -0
- package/dist/modules/plugins/teams.routes.js +79 -0
- package/dist/modules/plugins/teams.routes.js.map +1 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +40 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +202 -37
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +1 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +5 -3
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js +3 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +78 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.js +40 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +106 -52
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/connection-check.d.ts +118 -0
- package/dist/modules/settings/connection-check.d.ts.map +1 -0
- package/dist/modules/settings/connection-check.js +287 -0
- package/dist/modules/settings/connection-check.js.map +1 -0
- package/dist/modules/settings/deployment-settings.service.d.ts +157 -2
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +412 -35
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/git-root-folders.d.ts +54 -0
- package/dist/modules/settings/git-root-folders.d.ts.map +1 -0
- package/dist/modules/settings/git-root-folders.js +130 -0
- package/dist/modules/settings/git-root-folders.js.map +1 -0
- package/dist/modules/settings/oidc-check.d.ts +85 -0
- package/dist/modules/settings/oidc-check.d.ts.map +1 -0
- package/dist/modules/settings/oidc-check.js +172 -0
- package/dist/modules/settings/oidc-check.js.map +1 -0
- package/dist/modules/settings/setup.routes.d.ts +57 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +408 -108
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/allowed-tools-check.d.ts +132 -0
- package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -0
- package/dist/modules/skills/allowed-tools-check.js +260 -0
- package/dist/modules/skills/allowed-tools-check.js.map +1 -0
- package/dist/modules/skills/index.d.ts +4 -1
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +2 -0
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +6 -11
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +24 -71
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +38 -0
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -0
- package/dist/modules/skills/skill-access-requests.routes.js +171 -0
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -0
- package/dist/modules/skills/skills.contract.d.ts +67 -2
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +35 -3
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +57 -4
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +31 -3
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +236 -65
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts +4 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +41 -6
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts +18 -0
- package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -0
- package/dist/modules/tool-auth/connection-key-rejection.js +20 -0
- package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts +14 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts.map +1 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.js +67 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.js.map +1 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +66 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.js +2 -1
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +23 -4
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +79 -19
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +5 -2
- package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.js +5 -2
- package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
- package/dist/modules/tool-auth/key-or-session.middleware.d.ts +23 -0
- package/dist/modules/tool-auth/key-or-session.middleware.d.ts.map +1 -0
- package/dist/modules/tool-auth/key-or-session.middleware.js +39 -0
- package/dist/modules/tool-auth/key-or-session.middleware.js.map +1 -0
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +7 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +8 -5
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts +14 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +28 -6
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +22 -5
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +6 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js +7 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-manuals/index.d.ts +2 -1
- package/dist/modules/tool-manuals/index.d.ts.map +1 -1
- package/dist/modules/tool-manuals/index.js +1 -0
- package/dist/modules/tool-manuals/index.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +36 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +136 -96
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +10 -2
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -15
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts +50 -0
- package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/pending-tools.service.js +149 -0
- package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-delete.service.d.ts +135 -0
- package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/tool-delete.service.js +303 -0
- package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +161 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.js +9 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +19 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +127 -57
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +121 -2
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +415 -92
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +78 -6
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +88 -54
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/event-bus.d.ts +10 -4
- package/dist/modules/workflow/event-bus.d.ts.map +1 -1
- package/dist/modules/workflow/event-bus.js +14 -11
- package/dist/modules/workflow/event-bus.js.map +1 -1
- package/dist/modules/workflow/events.routes.d.ts +17 -2
- package/dist/modules/workflow/events.routes.d.ts.map +1 -1
- package/dist/modules/workflow/events.routes.js +31 -12
- package/dist/modules/workflow/events.routes.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +22 -4
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +27 -4
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/change-request-link.d.ts +20 -0
- package/dist/modules/workflow/git/change-request-link.d.ts.map +1 -0
- package/dist/modules/workflow/git/change-request-link.js +34 -0
- package/dist/modules/workflow/git/change-request-link.js.map +1 -0
- package/dist/modules/workflow/git/git-version.d.ts.map +1 -1
- package/dist/modules/workflow/git/git-version.js +3 -1
- package/dist/modules/workflow/git/git-version.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +303 -6
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +753 -170
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +36 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -0
- package/dist/modules/workflow/git/node-git-runner.js +273 -0
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts +57 -2
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +165 -25
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +11 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +24 -4
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +15 -8
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +107 -58
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +3 -1
- package/dist/modules/workflow/recovery-bot.js.map +1 -1
- package/dist/modules/workflow/review-workflow/approval-lock.d.ts +60 -0
- package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +1 -0
- package/dist/modules/workflow/review-workflow/approval-lock.js +116 -0
- package/dist/modules/workflow/review-workflow/approval-lock.js.map +1 -0
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +54 -7
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +64 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +269 -90
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/sanitize-error.d.ts +5 -2
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
- package/dist/modules/workflow/sanitize-error.js +8 -4
- package/dist/modules/workflow/sanitize-error.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts +2 -0
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +259 -29
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +217 -14
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +1142 -109
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/empty-dirs.d.ts +15 -0
- package/dist/modules/workspace/empty-dirs.d.ts.map +1 -0
- package/dist/modules/workspace/empty-dirs.js +90 -0
- package/dist/modules/workspace/empty-dirs.js.map +1 -0
- package/dist/modules/workspace/file-readers/content-mode.d.ts +50 -0
- package/dist/modules/workspace/file-readers/content-mode.d.ts.map +1 -0
- package/dist/modules/workspace/file-readers/content-mode.js +55 -0
- package/dist/modules/workspace/file-readers/content-mode.js.map +1 -0
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +4 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js +4 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.types.d.ts +5 -2
- package/dist/modules/workspace/file-readers/doc-extract.types.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.types.js +7 -4
- package/dist/modules/workspace/file-readers/doc-extract.types.js.map +1 -1
- package/dist/modules/workspace/file-readers/document-reader.d.ts +2 -0
- package/dist/modules/workspace/file-readers/document-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/document-reader.js +5 -0
- package/dist/modules/workspace/file-readers/document-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/extract-docx.d.ts +6 -0
- package/dist/modules/workspace/file-readers/extract-docx.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/extract-docx.js +6 -0
- package/dist/modules/workspace/file-readers/extract-docx.js.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.d.ts +24 -5
- package/dist/modules/workspace/file-readers/file-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.js +11 -6
- package/dist/modules/workspace/file-readers/file-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.js +6 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.js.map +1 -1
- package/dist/modules/workspace/file-readers/image-reader.d.ts +7 -5
- package/dist/modules/workspace/file-readers/image-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/image-reader.js +9 -5
- package/dist/modules/workspace/file-readers/image-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/ooxml-text.d.ts +3 -0
- package/dist/modules/workspace/file-readers/ooxml-text.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/ooxml-text.js +39 -2
- package/dist/modules/workspace/file-readers/ooxml-text.js.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.d.ts +29 -1
- package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.js +57 -3
- package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
- package/dist/modules/workspace/git-internals.middleware.d.ts +28 -0
- package/dist/modules/workspace/git-internals.middleware.d.ts.map +1 -0
- package/dist/modules/workspace/git-internals.middleware.js +93 -0
- package/dist/modules/workspace/git-internals.middleware.js.map +1 -0
- package/dist/modules/workspace/not-found.d.ts +80 -0
- package/dist/modules/workspace/not-found.d.ts.map +1 -0
- package/dist/modules/workspace/not-found.js +112 -0
- package/dist/modules/workspace/not-found.js.map +1 -0
- package/dist/modules/workspace/session-ontology.gate.d.ts +1 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.js +1 -1
- package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
- package/dist/modules/workspace/startup/beside-checkout.d.ts +42 -0
- package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -0
- package/dist/modules/workspace/startup/beside-checkout.js +110 -0
- package/dist/modules/workspace/startup/beside-checkout.js.map +1 -0
- package/dist/modules/workspace/startup/kb-git.d.ts +7 -12
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-git.js +34 -35
- package/dist/modules/workspace/startup/kb-git.js.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +95 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +257 -55
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +45 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +417 -385
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +48 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js +183 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +51 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +158 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.d.ts +64 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.js +97 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +41 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +85 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +3 -1
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js +4 -12
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +6 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +174 -54
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +96 -24
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +521 -126
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +94 -0
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/template-source.js +160 -0
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts +14 -2
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +657 -246
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +323 -12
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +1006 -257
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +42 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +1590 -146
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +76 -0
- package/dist/modules/workspace/write-denial.d.ts.map +1 -0
- package/dist/modules/workspace/write-denial.js +138 -0
- package/dist/modules/workspace/write-denial.js.map +1 -0
- package/dist/shared/canonical-file-identity.d.ts +64 -0
- package/dist/shared/canonical-file-identity.d.ts.map +1 -0
- package/dist/shared/canonical-file-identity.js +92 -0
- package/dist/shared/canonical-file-identity.js.map +1 -0
- package/dist/shared/domain-errors.d.ts +128 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +166 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/email-identity.d.ts +32 -0
- package/dist/shared/email-identity.d.ts.map +1 -0
- package/dist/shared/email-identity.js +37 -0
- package/dist/shared/email-identity.js.map +1 -0
- package/dist/shared/fs-errors.d.ts +8 -0
- package/dist/shared/fs-errors.d.ts.map +1 -0
- package/dist/shared/fs-errors.js +11 -0
- package/dist/shared/fs-errors.js.map +1 -0
- package/dist/shared/fs-walk.d.ts +20 -6
- package/dist/shared/fs-walk.d.ts.map +1 -1
- package/dist/shared/fs-walk.js +35 -27
- package/dist/shared/fs-walk.js.map +1 -1
- package/dist/shared/fs.contract.d.ts +206 -0
- package/dist/shared/fs.contract.d.ts.map +1 -0
- package/dist/shared/fs.contract.js +79 -0
- package/dist/shared/fs.contract.js.map +1 -0
- package/dist/shared/git-failure.d.ts +80 -0
- package/dist/shared/git-failure.d.ts.map +1 -0
- package/dist/shared/git-failure.js +139 -0
- package/dist/shared/git-failure.js.map +1 -0
- package/dist/shared/git-internals.d.ts +17 -0
- package/dist/shared/git-internals.d.ts.map +1 -0
- package/dist/shared/git-internals.js +123 -0
- package/dist/shared/git-internals.js.map +1 -0
- package/dist/shared/git.contract.d.ts +157 -0
- package/dist/shared/git.contract.d.ts.map +1 -0
- package/dist/shared/git.contract.js +103 -0
- package/dist/shared/git.contract.js.map +1 -0
- package/dist/shared/http-errors.d.ts +33 -0
- package/dist/shared/http-errors.d.ts.map +1 -0
- package/dist/shared/http-errors.js +35 -0
- package/dist/shared/http-errors.js.map +1 -0
- package/dist/shared/kb-walk.d.ts +52 -0
- package/dist/shared/kb-walk.d.ts.map +1 -0
- package/dist/shared/kb-walk.js +76 -0
- package/dist/shared/kb-walk.js.map +1 -0
- package/dist/shared/logger.contract.d.ts +63 -0
- package/dist/shared/logger.contract.d.ts.map +1 -0
- package/dist/shared/logger.contract.js +126 -0
- package/dist/shared/logger.contract.js.map +1 -0
- package/dist/shared/logging.d.ts +31 -0
- package/dist/shared/logging.d.ts.map +1 -0
- package/dist/shared/logging.js +92 -0
- package/dist/shared/logging.js.map +1 -0
- package/dist/shared/path-containment.d.ts +21 -0
- package/dist/shared/path-containment.d.ts.map +1 -0
- package/dist/shared/path-containment.js +34 -0
- package/dist/shared/path-containment.js.map +1 -0
- package/dist/shared/path-order.d.ts +18 -0
- package/dist/shared/path-order.d.ts.map +1 -0
- package/dist/shared/path-order.js +35 -0
- package/dist/shared/path-order.js.map +1 -0
- package/dist/shared/pending-proposals.d.ts +71 -0
- package/dist/shared/pending-proposals.d.ts.map +1 -0
- package/dist/shared/pending-proposals.js +98 -0
- package/dist/shared/pending-proposals.js.map +1 -0
- package/dist/shared/printable.d.ts +22 -0
- package/dist/shared/printable.d.ts.map +1 -0
- package/dist/shared/printable.js +65 -0
- package/dist/shared/printable.js.map +1 -0
- package/dist/shared/redact-secret.d.ts +30 -0
- package/dist/shared/redact-secret.d.ts.map +1 -0
- package/dist/shared/redact-secret.js +114 -0
- package/dist/shared/redact-secret.js.map +1 -0
- package/dist/shared/rename-no-replace.d.ts +119 -0
- package/dist/shared/rename-no-replace.d.ts.map +1 -0
- package/dist/shared/rename-no-replace.js +323 -0
- package/dist/shared/rename-no-replace.js.map +1 -0
- package/dist/shared/ssrf.d.ts.map +1 -1
- package/dist/shared/ssrf.js +49 -18
- package/dist/shared/ssrf.js.map +1 -1
- package/dist/shared/ttl-cache.d.ts +28 -1
- package/dist/shared/ttl-cache.d.ts.map +1 -1
- package/dist/shared/ttl-cache.js +33 -1
- package/dist/shared/ttl-cache.js.map +1 -1
- package/dist/version.d.ts +10 -6
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +10 -6
- package/dist/version.js.map +1 -1
- package/kb-template/.bevelignore +4 -3
- package/kb-template/AGENTS.md +364 -54
- package/kb-template/KnowledgeBase/How to get started.md +1 -1
- package/kb-template/Skills/.gitkeep +0 -0
- package/kb-template/access.md +5 -3
- package/kb-template/mcp-description.md +27 -0
- package/migrations/0005_github_facade.sql +25 -0
- package/migrations/0006_api_tokens_revoked_by.sql +1 -0
- package/migrations/0007_github_facade_registered.sql +1 -0
- package/migrations/0008_change_request_apply_failure.sql +4 -0
- package/migrations/0009_change_request_apply_failure_kind.sql +13 -0
- package/migrations/0010_plugin_join_requests.sql +15 -0
- package/migrations/0011_plugin_join_request_claim.sql +11 -0
- package/migrations/meta/0005_snapshot.json +1717 -0
- package/migrations/meta/0006_snapshot.json +1723 -0
- package/migrations/meta/0007_snapshot.json +1729 -0
- package/migrations/meta/0008_snapshot.json +1753 -0
- package/migrations/meta/0009_snapshot.json +1759 -0
- package/migrations/meta/0010_snapshot.json +1872 -0
- package/migrations/meta/0011_snapshot.json +1884 -0
- package/migrations/meta/_journal.json +49 -0
- package/package.json +8 -7
- package/src/__tests__/core-config.domain.test.ts +67 -2
- package/src/__tests__/kb-layout-config.test.ts +295 -0
- package/src/__tests__/open-change-gate.ts +15 -0
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +226 -0
- package/src/core/__tests__/catalog-revision.test.ts +190 -0
- package/src/core/__tests__/lifecycle.test.ts +369 -0
- package/src/core/__tests__/public-config.test.ts +30 -0
- package/src/core/__tests__/readiness.test.ts +109 -0
- package/src/core/catalog-cache-invalidation.ts +111 -0
- package/src/core/catalog-revision.ts +110 -0
- package/src/core/create-core-server.ts +358 -63
- package/src/core/create-core-services.ts +449 -107
- package/src/core/lifecycle.ts +312 -0
- package/src/core/public-config.ts +86 -0
- package/src/core/readiness.ts +123 -0
- package/src/core-config.ts +70 -4
- package/src/index.ts +15 -0
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +362 -0
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +2 -1
- package/src/modules/access/__tests__/access-control.atref-cache.test.ts +46 -22
- package/src/modules/access/__tests__/access-control.deny-dependents.test.ts +215 -0
- package/src/modules/access/__tests__/access-control.download-read.test.ts +268 -0
- package/src/modules/access/__tests__/access-control.everyone-read.test.ts +145 -0
- package/src/modules/access/__tests__/access-control.group-read.test.ts +163 -0
- package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +88 -0
- package/src/modules/access/__tests__/access-control.platform-restore.test.ts +152 -0
- package/src/modules/access/__tests__/access-control.prospective.test.ts +189 -0
- package/src/modules/access/__tests__/access-control.read-batch.test.ts +2 -1
- package/src/modules/access/__tests__/access-control.service.test.ts +142 -81
- package/src/modules/access/__tests__/access-groups.test.ts +47 -14
- package/src/modules/access/__tests__/access-md-format.test.ts +2 -1
- package/src/modules/access/__tests__/access-mutation.service.test.ts +52 -4
- package/src/modules/access/__tests__/access-own-read-grant.test.ts +2 -0
- package/src/modules/access/__tests__/access-personal-plugin.test.ts +153 -0
- package/src/modules/access/__tests__/access-plugin-principals.test.ts +385 -0
- package/src/modules/access/__tests__/access-restrict-here.test.ts +450 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +139 -0
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +394 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -2
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +3 -2
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +234 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +216 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +7 -2
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +360 -0
- package/src/modules/access/__tests__/change-read-gate.test.ts +272 -0
- package/src/modules/access/__tests__/creator-access.test.ts +81 -83
- package/src/modules/access/__tests__/grant-sources.test.ts +3 -2
- package/src/modules/access/__tests__/groups-admin.service.test.ts +14 -5
- package/src/modules/access/__tests__/roles-admin.security.test.ts +196 -0
- package/src/modules/access/__tests__/roles-admin.service.test.ts +127 -2
- package/src/modules/access/__tests__/roles-capabilities.test.ts +5 -4
- package/src/modules/access/__tests__/roles.routes.test.ts +5 -2
- package/src/modules/access/__tests__/user-access-removal.service.test.ts +402 -0
- package/src/modules/access/__tests__/users-db-double.ts +63 -0
- package/src/modules/access/access-control.interface.ts +269 -18
- package/src/modules/access/access-control.service.ts +943 -226
- package/src/modules/access/access-mutation.service.ts +182 -13
- package/src/modules/access/access-view.ts +316 -0
- package/src/modules/access/access.routes.ts +385 -105
- package/src/modules/access/admin-locked-commit.ts +12 -14
- package/src/modules/access/admin-route-helpers.ts +4 -4
- package/src/modules/access/change-read-gate.ts +148 -0
- package/src/modules/access/creator-access.ts +89 -124
- package/src/modules/access/directory-sync-bot.ts +4 -1
- package/src/modules/access/groups-admin.service.ts +3 -3
- package/src/modules/access/reference-scan.ts +5 -5
- package/src/modules/access/roles-admin.service.ts +94 -15
- package/src/modules/access/synced-groups-committer.ts +10 -15
- package/src/modules/access/synced-groups-writer.ts +3 -12
- package/src/modules/access/user-access-removal.service.ts +711 -0
- package/src/modules/access-model/__tests__/access-grammar.test.ts +18 -0
- package/src/modules/access-model/__tests__/access-splice.test.ts +63 -1
- package/src/modules/access-model/__tests__/frontmatter-fences.test.ts +71 -0
- package/src/modules/access-model/__tests__/plugin-token-canonical.test.ts +40 -0
- package/src/modules/access-model/__tests__/roles-yaml-guard.test.ts +131 -0
- package/src/modules/access-model/access-errors.ts +56 -15
- package/src/modules/access-model/access-grammar.ts +198 -49
- package/src/modules/access-model/access-splice.ts +18 -18
- package/src/modules/access-model/access-template.ts +44 -0
- package/src/modules/access-model/change-gate.ts +80 -0
- package/src/modules/access-model/creator.ts +18 -21
- package/src/modules/access-model/frontmatter-lines.ts +98 -0
- package/src/modules/access-model/group-files.ts +4 -0
- package/src/modules/access-model/plugin-principals.ts +130 -0
- package/src/modules/access-model/roles-yaml-guard.ts +182 -4
- package/src/modules/admin/admin-access.service.ts +6 -2
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +100 -0
- package/src/modules/agent-instructions/__tests__/compose.test.ts +256 -0
- package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +135 -0
- package/src/modules/agent-instructions/agent-instructions.routes.ts +41 -0
- package/src/modules/agent-instructions/compose.ts +271 -0
- package/src/modules/agent-instructions/index.ts +14 -0
- package/src/modules/agent-instructions/read-preamble.ts +88 -0
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +209 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +178 -2
- package/src/modules/auth/__tests__/auth.middleware.test.ts +78 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +338 -5
- package/src/modules/auth/__tests__/oidc-auth-provider.test.ts +352 -12
- package/src/modules/auth/__tests__/onboarding-done.route.test.ts +3 -1
- package/src/modules/auth/account-erasure.service.ts +117 -10
- package/src/modules/auth/account.routes.ts +115 -14
- package/src/modules/auth/auth.routes.ts +20 -7
- package/src/modules/auth/auth.service.ts +187 -28
- package/src/modules/auth/oidc-auth-provider.ts +120 -22
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +109 -1
- package/src/modules/code-mode/code-mode.tool.ts +13 -2
- package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +78 -0
- package/src/modules/connection-probe/connection-probe.service.ts +59 -7
- package/src/modules/database/__tests__/advisory-lease.test.ts +152 -0
- package/src/modules/database/__tests__/advisory-lock.test.ts +147 -0
- package/src/modules/database/advisory-lock.ts +346 -0
- package/src/modules/database/connection.ts +9 -1
- package/src/modules/database/core-schema.ts +177 -0
- package/src/modules/database/migrate.ts +27 -6
- package/src/modules/declared-variables/declared-variables.routes.ts +6 -3
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +3 -0
- package/src/modules/diff/__tests__/diff.service.test.ts +3 -0
- package/src/modules/diff/diff.routes.ts +2 -4
- package/src/modules/diff/diff.service.ts +75 -79
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +472 -9
- package/src/modules/kb-fs/__tests__/node-fs.test.ts +409 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +210 -1
- package/src/modules/{workspace → kb-fs}/bevel-ignore.ts +22 -15
- package/src/modules/kb-fs/file-change-notifier.ts +4 -1
- package/src/modules/kb-fs/git-guarded-filesystem.ts +123 -0
- package/src/modules/kb-fs/locking-filesystem.ts +399 -109
- package/src/modules/kb-fs/node-fs.ts +165 -0
- package/src/modules/kb-fs/read-only-filesystem.ts +18 -9
- package/src/modules/kb-fs/repo-path.ts +213 -3
- package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +297 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +295 -0
- package/src/modules/kb-sync/__tests__/sync-auth.test.ts +118 -0
- package/src/modules/kb-sync/__tests__/sync-payload.test.ts +148 -0
- package/src/modules/kb-sync/index.ts +19 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +79 -0
- package/src/modules/kb-sync/kb-sync.routes.ts +200 -0
- package/src/modules/kb-sync/kb-sync.service.ts +204 -0
- package/src/modules/kb-sync/sync-auth.ts +118 -0
- package/src/modules/kb-sync/sync-payload.ts +143 -0
- package/src/modules/marketplace/__tests__/github-facade.test.ts +575 -0
- package/src/modules/marketplace/__tests__/marketplace-git.test.ts +180 -0
- package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +47 -0
- package/src/modules/marketplace/git-http.routes.ts +196 -0
- package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +110 -0
- package/src/modules/marketplace/github-facade/github-facade-codes.store.ts +145 -0
- package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +263 -0
- package/src/modules/marketplace/github-facade/github-facade.routes.ts +294 -0
- package/src/modules/marketplace/github-facade/github-facade.service.ts +282 -0
- package/src/modules/marketplace/github-facade/index.ts +26 -0
- package/src/modules/marketplace/index.ts +3 -0
- package/src/modules/marketplace/marketplace-repo.service.ts +352 -0
- package/src/modules/mcp/__tests__/downstream-pool.test.ts +278 -0
- package/src/modules/mcp/__tests__/downstream-token-refresh.test.ts +178 -0
- package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +156 -0
- package/src/modules/mcp/__tests__/first-call-probe.cli-args.test.ts +178 -0
- package/src/modules/mcp/__tests__/first-call-probe.test.ts +414 -0
- package/src/modules/mcp/__tests__/manual-failure-memo.test.ts +98 -6
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +8 -5
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +1 -3
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +1055 -59
- package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +182 -0
- package/src/modules/mcp/__tests__/mcp.service.test.ts +274 -6
- package/src/modules/mcp/__tests__/surface-log-throttle.test.ts +169 -0
- package/src/modules/mcp/downstream-pool.ts +256 -0
- package/src/modules/mcp/downstream-token-refresh.ts +170 -0
- package/src/modules/mcp/first-call-probe.cli-args.ts +159 -0
- package/src/modules/mcp/first-call-probe.cli.ts +60 -0
- package/src/modules/mcp/first-call-probe.ts +418 -0
- package/src/modules/mcp/manual-failure-memo.ts +81 -23
- package/src/modules/mcp/mcp-auth.middleware.ts +25 -16
- package/src/modules/mcp/mcp.routes.ts +376 -482
- package/src/modules/mcp/mcp.service.ts +846 -176
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +8 -2
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +28 -6
- package/src/modules/mcp/oauth/oauth-state.ts +15 -3
- package/src/modules/mcp/surface-log-throttle.ts +120 -0
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +166 -0
- package/src/modules/plugins/__tests__/bundle-dialect.test.ts +165 -0
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +423 -0
- package/src/modules/plugins/__tests__/fake-join-request-store.ts +189 -0
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +608 -0
- package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +241 -0
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +201 -16
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +284 -0
- package/src/modules/plugins/__tests__/plugin-names.test.ts +77 -0
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +330 -20
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +456 -0
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +441 -43
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +131 -0
- package/src/modules/plugins/__tests__/teams.routes.test.ts +248 -0
- package/src/modules/plugins/compile/compile-marketplace.ts +500 -0
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +151 -0
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +365 -0
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +161 -0
- package/src/modules/plugins/discovery/bundle-dialect/registry.ts +250 -0
- package/src/modules/plugins/discovery/kb-plugin-source.ts +117 -0
- package/src/modules/plugins/discovery/native.source.ts +140 -0
- package/src/modules/plugins/discovery/plugin-source.ts +113 -0
- package/src/modules/plugins/index.ts +25 -2
- package/src/modules/plugins/join-request-jobs.service.ts +675 -0
- package/src/modules/plugins/join-request-records.store.ts +353 -0
- package/src/modules/plugins/join-requests.service.ts +7 -4
- package/src/modules/plugins/plugin-links.service.ts +350 -0
- package/src/modules/plugins/plugin-links.ts +187 -0
- package/src/modules/plugins/plugin-provision.service.ts +356 -78
- package/src/modules/plugins/plugin-rename.service.ts +310 -0
- package/src/modules/plugins/plugins.contract.ts +79 -4
- package/src/modules/plugins/plugins.routes.ts +320 -123
- package/src/modules/plugins/plugins.service.ts +184 -57
- package/src/modules/plugins/plugins.tools.ts +88 -0
- package/src/modules/plugins/teams.routes.ts +129 -0
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +359 -15
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +248 -0
- package/src/modules/secrets-vault/__tests__/namespace-secrets.test.ts +202 -0
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +217 -35
- package/src/modules/secrets-vault/index.ts +1 -0
- package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +6 -3
- package/src/modules/secrets-vault/secrets-variable-loader.ts +4 -4
- package/src/modules/secrets-vault/secrets-vault.contract.ts +93 -0
- package/src/modules/secrets-vault/secrets-vault.routes.ts +99 -38
- package/src/modules/settings/__tests__/connection-check.test.ts +233 -0
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +240 -1
- package/src/modules/settings/__tests__/git-root-folders.test.ts +247 -0
- package/src/modules/settings/__tests__/oidc-check.test.ts +185 -0
- package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +119 -0
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +170 -0
- package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +555 -0
- package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +77 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +667 -16
- package/src/modules/settings/connection-check.ts +351 -0
- package/src/modules/settings/deployment-settings.service.ts +478 -39
- package/src/modules/settings/git-root-folders.ts +171 -0
- package/src/modules/settings/oidc-check.ts +229 -0
- package/src/modules/settings/setup.routes.ts +480 -110
- package/src/modules/skills/__tests__/allowed-tools-check.test.ts +224 -0
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +210 -0
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -3
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +128 -0
- package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +91 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +308 -2
- package/src/modules/skills/allowed-tools-check.ts +352 -0
- package/src/modules/skills/index.ts +5 -0
- package/src/modules/skills/pending-skills.service.ts +26 -85
- package/src/modules/skills/skill-access-requests.routes.ts +203 -0
- package/src/modules/skills/skills.contract.ts +67 -3
- package/src/modules/skills/skills.routes.ts +75 -4
- package/src/modules/skills/skills.service.ts +258 -66
- package/src/modules/skills/skills.tools.ts +43 -5
- package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +192 -0
- package/src/modules/tool-auth/__tests__/connection-keys-admin.routes.test.ts +116 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +124 -0
- package/src/modules/tool-auth/connection-key-rejection.ts +25 -0
- package/src/modules/tool-auth/connection-keys-admin.routes.ts +76 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +73 -1
- package/src/modules/tool-auth/external-api-key.service.ts +95 -29
- package/src/modules/tool-auth/internal-token.service.ts +5 -2
- package/src/modules/tool-auth/key-or-session.middleware.ts +45 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +16 -7
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +228 -0
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +33 -4
- package/src/modules/tool-helpers/tool-context.ts +46 -7
- package/src/modules/tool-helpers/tool-handler.ts +23 -5
- package/src/modules/tool-helpers/tool.contract.ts +5 -0
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +24 -1
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +411 -0
- package/src/modules/tool-manuals/__tests__/tool-delete.route.test.ts +133 -0
- package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +469 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +21 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +11 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +6 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +254 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +577 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +6 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.pending.route.test.ts +102 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +285 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +178 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +114 -2
- package/src/modules/tool-manuals/index.ts +3 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +164 -95
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +11 -14
- package/src/modules/tool-manuals/pending-tools.service.ts +179 -0
- package/src/modules/tool-manuals/tool-delete.service.ts +401 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +165 -0
- package/src/modules/tool-manuals/tool-manuals.routes.ts +138 -54
- package/src/modules/tool-manuals/tool-manuals.service.ts +440 -104
- package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -6
- package/src/modules/workflow/__tests__/apply-failure.test.ts +1038 -0
- package/src/modules/workflow/__tests__/event-bus.test.ts +61 -9
- package/src/modules/workflow/__tests__/fake-file-lock-db.ts +199 -0
- package/src/modules/workflow/__tests__/file-lock.service.path-identity.test.ts +237 -0
- package/src/modules/workflow/__tests__/format-affected-owners.test.ts +18 -0
- package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +220 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.concurrency.test.ts +187 -0
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +3 -1
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +10 -0
- package/src/modules/workflow/__tests__/workflow.routes.folder-proposals.test.ts +128 -0
- package/src/modules/workflow/__tests__/workflow.routes.fork-point-file.test.ts +149 -0
- package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +305 -0
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +2 -0
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +3 -0
- package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +2 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +296 -27
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +564 -0
- package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +135 -0
- package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +172 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +133 -2
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +442 -0
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +281 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +158 -7
- package/src/modules/workflow/agent-tools/workflow.tools.ts +93 -64
- package/src/modules/workflow/event-bus.ts +26 -28
- package/src/modules/workflow/events.routes.ts +56 -21
- package/src/modules/workflow/file-lock.service.ts +27 -4
- package/src/modules/workflow/git/__tests__/change-request-link.test.ts +43 -0
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +1 -0
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +238 -1
- package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +245 -0
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +35 -0
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +149 -0
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +47 -4
- package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +177 -0
- package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +253 -0
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +114 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +69 -2
- package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +121 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +79 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-update.test.ts +111 -0
- package/src/modules/workflow/git/change-request-link.ts +35 -0
- package/src/modules/workflow/git/git-version.ts +5 -2
- package/src/modules/workflow/git/git.service.ts +922 -199
- package/src/modules/workflow/git/node-git-runner.ts +315 -0
- package/src/modules/workflow/git/pull-request.service.ts +188 -27
- package/src/modules/workflow/pending-commits.service.ts +26 -7
- package/src/modules/workflow/pending-commits.worker.ts +114 -69
- package/src/modules/workflow/recovery-bot.ts +4 -1
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +180 -1
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
- package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +520 -0
- package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +84 -0
- package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +89 -77
- package/src/modules/workflow/review-workflow/approval-lock.ts +122 -0
- package/src/modules/workflow/review-workflow/review-workflow.interface.ts +59 -7
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +309 -105
- package/src/modules/workflow/sanitize-error.ts +9 -4
- package/src/modules/workflow/workflow.routes.ts +273 -29
- package/src/modules/workflow/workflow.service.ts +1341 -174
- package/src/modules/workspace/__tests__/empty-dirs.test.ts +44 -0
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +301 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +792 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +464 -0
- package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +139 -0
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +189 -37
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +51 -11
- package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +161 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +109 -10
- package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +624 -0
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +409 -0
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +327 -0
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +31 -13
- package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +109 -0
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +94 -0
- package/src/modules/workspace/__tests__/workspace.service.missing-branch.test.ts +28 -0
- package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +218 -0
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +98 -13
- package/src/modules/workspace/__tests__/workspace.service.test.ts +811 -42
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +197 -0
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +128 -0
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +212 -0
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +2632 -110
- package/src/modules/workspace/__tests__/write-denial.test.ts +75 -0
- package/src/modules/workspace/empty-dirs.ts +79 -0
- package/src/modules/workspace/file-readers/README.md +32 -0
- package/src/modules/workspace/file-readers/__tests__/doc-extract.test.ts +59 -2
- package/src/modules/workspace/file-readers/__tests__/file-reader.registry.test.ts +133 -4
- package/src/modules/workspace/file-readers/__tests__/fixtures/soft-break.docx +0 -0
- package/src/modules/workspace/file-readers/__tests__/fixtures/tab.docx +0 -0
- package/src/modules/workspace/file-readers/content-mode.ts +87 -0
- package/src/modules/workspace/file-readers/doc-extract.service.ts +4 -1
- package/src/modules/workspace/file-readers/doc-extract.types.ts +8 -4
- package/src/modules/workspace/file-readers/document-reader.ts +6 -0
- package/src/modules/workspace/file-readers/extract-docx.ts +6 -0
- package/src/modules/workspace/file-readers/file-reader.registry.ts +9 -1
- package/src/modules/workspace/file-readers/file-reader.ts +27 -6
- package/src/modules/workspace/file-readers/image-reader.ts +10 -5
- package/src/modules/workspace/file-readers/ooxml-text.ts +38 -2
- package/src/modules/workspace/file-readers/text-reader.ts +61 -4
- package/src/modules/workspace/git-internals.middleware.ts +86 -0
- package/src/modules/workspace/not-found.ts +119 -0
- package/src/modules/workspace/session-ontology.gate.ts +1 -1
- package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +181 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +361 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +53 -16
- package/src/modules/workspace/startup/beside-checkout.ts +112 -0
- package/src/modules/workspace/startup/kb-git.ts +43 -36
- package/src/modules/workspace/startup/kb-startup-runner.ts +303 -63
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +1405 -48
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +460 -417
- package/src/modules/workspace/startup/steps/personal-spaces.step.ts +191 -0
- package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +169 -0
- package/src/modules/workspace/startup/steps/plugin-layout.ts +110 -0
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +90 -0
- package/src/modules/workspace/startup/steps/roles-yaml.step.ts +6 -12
- package/src/modules/workspace/startup/steps/seed-tree.ts +177 -54
- package/src/modules/workspace/startup/steps/template-files.step.ts +581 -126
- package/src/modules/workspace/startup/steps/template-source.ts +168 -0
- package/src/modules/workspace/workspace.routes.ts +691 -269
- package/src/modules/workspace/workspace.service.ts +1071 -292
- package/src/modules/workspace/workspace.tools.ts +1787 -180
- package/src/modules/workspace/write-denial.ts +189 -0
- package/src/shared/__tests__/canonical-file-identity.test.ts +154 -0
- package/src/shared/__tests__/email-identity.test.ts +35 -0
- package/src/shared/__tests__/filename.test.ts +0 -0
- package/src/shared/__tests__/frontmatter-id.test.ts +5 -0
- package/src/shared/__tests__/git-failure.test.ts +198 -0
- package/src/shared/__tests__/git-internals.test.ts +115 -0
- package/src/shared/__tests__/http-errors.test.ts +42 -0
- package/src/shared/__tests__/logging.test.ts +185 -0
- package/src/shared/__tests__/path-containment.test.ts +60 -0
- package/src/shared/__tests__/path-order.test.ts +24 -0
- package/src/shared/__tests__/pending-proposals.readAt.test.ts +102 -0
- package/src/shared/__tests__/printable.test.ts +64 -0
- package/src/shared/__tests__/redact-secret.test.ts +122 -0
- package/src/shared/__tests__/rename-no-replace.test.ts +466 -0
- package/src/shared/__tests__/ssrf.test.ts +20 -0
- package/src/shared/__tests__/ttl-cache.test.ts +55 -0
- package/src/shared/canonical-file-identity.ts +92 -0
- package/src/shared/domain-errors.ts +178 -0
- package/src/shared/email-identity.ts +38 -0
- package/src/shared/fs.contract.ts +225 -0
- package/src/shared/git-failure.ts +196 -0
- package/src/shared/git-internals.ts +124 -0
- package/src/shared/git.contract.ts +170 -0
- package/src/shared/http-errors.ts +36 -0
- package/src/shared/logger.contract.ts +141 -0
- package/src/shared/logging.ts +96 -0
- package/src/shared/path-containment.ts +34 -0
- package/src/shared/path-order.ts +33 -0
- package/src/shared/pending-proposals.ts +161 -0
- package/src/shared/printable.ts +68 -0
- package/src/shared/redact-secret.ts +118 -0
- package/src/shared/rename-no-replace.ts +386 -0
- package/src/shared/ssrf.ts +44 -16
- package/src/shared/ttl-cache.ts +33 -1
- package/src/version.ts +10 -6
- package/src/modules/diff/diff-paths.ts +0 -9
- package/src/modules/mcp/__tests__/mcp-session-store.test.ts +0 -151
- package/src/modules/mcp/__tests__/mcp.routes.session.test.ts +0 -226
- package/src/modules/mcp/mcp-session-store.ts +0 -162
- package/src/shared/__tests__/fs-walk.test.ts +0 -42
- package/src/shared/fs-walk.ts +0 -30
- package/src/shared/hash-email.ts +0 -14
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { logger } from '../../shared/logging.js';
|
|
3
|
+
|
|
4
|
+
const log = logger('mcp');
|
|
2
5
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
6
|
import {
|
|
5
7
|
CallToolRequestSchema,
|
|
6
8
|
ListToolsRequestSchema,
|
|
@@ -8,6 +10,7 @@ import {
|
|
|
8
10
|
GetPromptRequestSchema,
|
|
9
11
|
McpError,
|
|
10
12
|
ErrorCode,
|
|
13
|
+
isInitializeRequest,
|
|
11
14
|
type CallToolResult,
|
|
12
15
|
type Tool as McpTool,
|
|
13
16
|
type Prompt,
|
|
@@ -18,6 +21,7 @@ import '@utcp/mcp'; // side effect: registers the 'mcp' protocol (native MCP-ser
|
|
|
18
21
|
import {
|
|
19
22
|
UtcpClientConfigSerializer,
|
|
20
23
|
CallTemplateSerializer,
|
|
24
|
+
UtcpManualSchema,
|
|
21
25
|
type CallTemplate,
|
|
22
26
|
type Tool as UtcpTool,
|
|
23
27
|
} from '@utcp/sdk';
|
|
@@ -28,9 +32,11 @@ import {
|
|
|
28
32
|
dispatchMetaTool,
|
|
29
33
|
dispatchToolCall,
|
|
30
34
|
registerManual,
|
|
35
|
+
installSessionRecovery,
|
|
31
36
|
flattenManualTool,
|
|
32
37
|
toListedTool,
|
|
33
38
|
toolError,
|
|
39
|
+
retiredToolMessage,
|
|
34
40
|
needsAuthorizationResult,
|
|
35
41
|
skillPromptText,
|
|
36
42
|
type ProxiedTool,
|
|
@@ -38,19 +44,33 @@ import {
|
|
|
38
44
|
type LoadedSkill,
|
|
39
45
|
} from '@bevel-software/platform-mcp-core';
|
|
40
46
|
import { bevelSecretsLoaderConfig } from '../secrets-vault/index.js';
|
|
41
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
scopesCovered,
|
|
49
|
+
type ForcedRefreshOutcome,
|
|
50
|
+
type ISecretsVaultService,
|
|
51
|
+
} from '../secrets-vault/secrets-vault.contract.js';
|
|
42
52
|
import { EXTERNAL_KB_MANUAL_NAME } from '../tool-manuals/tool-manuals.contract.js';
|
|
43
53
|
import type { IToolManualService } from '../tool-manuals/tool-manuals.contract.js';
|
|
44
54
|
import type { SpillStore } from '../workspace/spill-store.js';
|
|
45
55
|
import { seedBevelHostedManualVars } from '../../shared/utcp-namespace.js';
|
|
46
|
-
import type { McpSessionStore } from './mcp-session-store.js';
|
|
47
56
|
import type { InternalTokenService } from '../tool-auth/internal-token.service.js';
|
|
48
57
|
import { ManualFailureMemo } from './manual-failure-memo.js';
|
|
58
|
+
import { DownstreamPool, POOL_KEY_SEPARATOR, type DownstreamPoolOptions, type Lease } from './downstream-pool.js';
|
|
59
|
+
import { SurfaceLogThrottle } from './surface-log-throttle.js';
|
|
60
|
+
import { DownstreamRefreshGuard, isDownstreamTokenRejection } from './downstream-token-refresh.js';
|
|
61
|
+
import { printable } from '../../shared/printable.js';
|
|
62
|
+
import {
|
|
63
|
+
composeAgentInstructions,
|
|
64
|
+
prefixToolDescription,
|
|
65
|
+
PREFIXED_TOOLS,
|
|
66
|
+
type AgentPreambleReader,
|
|
67
|
+
type ComposedAgentInstructions,
|
|
68
|
+
} from '../agent-instructions/index.js';
|
|
49
69
|
|
|
50
70
|
/**
|
|
51
71
|
* Configuration for the loopback proxy. `loopbackBaseUrl` is the backend's own
|
|
52
72
|
* address (`http://127.0.0.1:<port>`) and `manualName` is BOTH the UTCP manual
|
|
53
|
-
* namespace the per-
|
|
73
|
+
* namespace the per-request client registers under AND the prefix its `${VAR}`
|
|
54
74
|
* placeholders resolve through (`<manualName>_API_URL` / `_CONNECTION_KEY`).
|
|
55
75
|
*/
|
|
56
76
|
export interface McpProxyOptions {
|
|
@@ -60,22 +80,40 @@ export interface McpProxyOptions {
|
|
|
60
80
|
spillStore: SpillStore;
|
|
61
81
|
/** Public web address of the frontend, for the needs-authorization setup link. */
|
|
62
82
|
publicFrontendUrl: string;
|
|
83
|
+
/**
|
|
84
|
+
* Reads `mcp-description.md` on the default branch with platform rights
|
|
85
|
+
* (see modules/agent-instructions). Called on every request that needs it
|
|
86
|
+
* (an `initialize`, or a `tools/list` for the tool prefix), so an edit
|
|
87
|
+
* reaches the next request without a restart. Optional so constructions
|
|
88
|
+
* that never exercise it (tests) keep working: without it every request
|
|
89
|
+
* carries the platform header alone.
|
|
90
|
+
*/
|
|
91
|
+
readAgentPreamble?: AgentPreambleReader;
|
|
92
|
+
/** Bounds of the downstream (`mcp.json`) connection pool; defaults are 4h idle / 5000 entries. */
|
|
93
|
+
downstreamPool?: Pick<DownstreamPoolOptions<unknown>, 'idleTtlMs' | 'maxEntries' | 'now'>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Who a request is from — everything the proxy needs, resolved from that request's own bearer. */
|
|
97
|
+
export interface McpCaller {
|
|
98
|
+
userId: string;
|
|
99
|
+
/** The connection-key id (per-key metering rides on it), or null for an OAuth/JWT bearer. */
|
|
100
|
+
tokenId: string | null;
|
|
101
|
+
/** The raw bearer the request carried. */
|
|
102
|
+
bearer: string;
|
|
63
103
|
}
|
|
64
104
|
|
|
65
105
|
/**
|
|
66
106
|
* Upper bound on one `loopbackJson` round-trip (manual list, skill fetch) so a
|
|
67
|
-
* hung loopback can't stall
|
|
68
|
-
*
|
|
107
|
+
* hung loopback can't stall a request. Generous: these endpoints answer in
|
|
108
|
+
* milliseconds; only a wedged process ever comes near it.
|
|
69
109
|
*/
|
|
70
110
|
const LOOPBACK_TIMEOUT_MS = 15_000;
|
|
71
111
|
|
|
72
112
|
/**
|
|
73
|
-
* Lifetime of the internal token minted as an OAuth/JWT
|
|
74
|
-
* bearer.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* session CAN outlive it — its tool calls then fail at the loopback and the
|
|
78
|
-
* client recovers by re-initializing, which mints a fresh token.
|
|
113
|
+
* Lifetime of the internal token minted as an OAuth/JWT request's loopback
|
|
114
|
+
* bearer. A request only needs it for as long as it runs, but one `tools/call`
|
|
115
|
+
* can legitimately run for a long time (`ask`, a long `call_tool_chain`), so
|
|
116
|
+
* the lifetime is generous rather than tight.
|
|
79
117
|
*
|
|
80
118
|
* Exported because `POST /api/mcp/local-token` (mcp.routes.ts) performs the
|
|
81
119
|
* same OAuth-access-token → internal-token exchange for the LOCAL MCP server,
|
|
@@ -86,142 +124,260 @@ export const MCP_LOOPBACK_TOKEN_TTL_MS = 5 * 60 * 60 * 1000;
|
|
|
86
124
|
|
|
87
125
|
const callTemplateSerializer = new CallTemplateSerializer();
|
|
88
126
|
|
|
127
|
+
/** What one request's tool calls run against: its UTCP client and the flattened catalog. */
|
|
128
|
+
interface RequestSurface {
|
|
129
|
+
client: CodeModeUtcpClient;
|
|
130
|
+
tools: ProxiedTool[];
|
|
131
|
+
/**
|
|
132
|
+
* Pooled manuals the caller may use but whose tools could not be put on the
|
|
133
|
+
* surface because the caller's sign-in for them is missing or broken AND no
|
|
134
|
+
* tool definitions were ever discovered for them in this process. A call to
|
|
135
|
+
* one of their tools is answered with the /connect link, not "Unknown tool".
|
|
136
|
+
*/
|
|
137
|
+
unavailable: UnavailableManual[];
|
|
138
|
+
/**
|
|
139
|
+
* Registered (rewritten, `utcpManualName`) name → catalog name, for every
|
|
140
|
+
* pooled manual of the caller's. A tool carries the registered name; the
|
|
141
|
+
* credential catalog (`userScopedKeysForManual`) keys by the catalog name.
|
|
142
|
+
* They differ only when the name holds a non-word character (`my-server`).
|
|
143
|
+
*/
|
|
144
|
+
catalogNames: ReadonlyMap<string, string>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A manual off the surface for want of a sign-in — see {@link RequestSurface.unavailable}. */
|
|
148
|
+
interface UnavailableManual {
|
|
149
|
+
/** The name as registered (`utcpManualName`): tools of it are `<utcpName>_<tool>` on the wire. */
|
|
150
|
+
utcpName: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* How a pooled manual's calls are served: lease its pooled connection, and —
|
|
155
|
+
* when a call fails — decide whether a token refresh warrants one retry
|
|
156
|
+
* ({@link RETRY_WITH_REFRESHED_TOKEN}) or which error the caller gets.
|
|
157
|
+
*/
|
|
158
|
+
interface DownstreamRoute {
|
|
159
|
+
acquire: () => Promise<Lease<PooledDownstream>>;
|
|
160
|
+
afterFailure: (err: unknown) => Promise<unknown>;
|
|
161
|
+
}
|
|
162
|
+
|
|
89
163
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
164
|
+
* What a failed downstream operation turned out to be, for the code that has
|
|
165
|
+
* to decide more than "retry or throw" — see {@link McpService.attachDownstream}.
|
|
166
|
+
*/
|
|
167
|
+
type DownstreamFailure =
|
|
168
|
+
| { retry: true }
|
|
169
|
+
/** `credentialTransient`: a token rejection whose refresh could not be settled (network, 5xx, or the once-a-minute guard), so the very next call should try again rather than being written off. */
|
|
170
|
+
| { retry: false; error: unknown; credentialTransient: boolean };
|
|
171
|
+
|
|
172
|
+
/** The {@link DownstreamRoute.afterFailure} answer that means "refreshed — retry once". */
|
|
173
|
+
const RETRY_WITH_REFRESHED_TOKEN = Symbol('retry-with-refreshed-token');
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* What attaching one pooled manual to a request came to. `retryable`: do not
|
|
177
|
+
* remember this failure (it may clear on its own within the minute).
|
|
178
|
+
* `signInMissing`: the caller has no usable sign-in for the manual and this
|
|
179
|
+
* process has never seen its tools — the manual goes on the request's
|
|
180
|
+
* {@link RequestSurface.unavailable} list so a call still gets the sign-in link.
|
|
181
|
+
*/
|
|
182
|
+
type DownstreamAttachResult =
|
|
183
|
+
| { ok: true }
|
|
184
|
+
| { ok: false; error: string; retryable?: boolean; signInMissing?: boolean };
|
|
185
|
+
|
|
186
|
+
/** One pooled downstream connection: a client holding one `mcp` manual, named so it can be deregistered. */
|
|
187
|
+
interface PooledDownstream {
|
|
188
|
+
/** Owns its own `mcp` protocol instance, so its sessions are its alone and `close()` ends exactly them. */
|
|
189
|
+
client: CodeModeUtcpClient;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The MCP server is a GENERIC, STATELESS proxy over the UTCP tool surface. It
|
|
194
|
+
* owns no tool logic and no session: every HTTP request gets its own MCP
|
|
195
|
+
* `Server`, built from that request's bearer. When the request needs tools it
|
|
196
|
+
* stands up a `UtcpClient` pointed at the backend's own `GET /api/agent/all-tools`
|
|
197
|
+
* catalog (over loopback, authenticated as the caller, so ACL filtering lives
|
|
198
|
+
* ONCE behind the REST surface), discovers every tool, and re-exposes each one
|
|
199
|
+
* to the MCP client. A tool call is dispatched straight back through the same
|
|
200
|
+
* REST endpoint the tool's UTCP `tool_call_template` names — so the agent logic,
|
|
96
201
|
* thread continuity, and per-key token metering all live ONCE behind that REST
|
|
97
202
|
* endpoint (`AgentAskService` for `ask`), never duplicated here.
|
|
98
203
|
*
|
|
204
|
+
* Because nothing is held between requests, a platform restart between two
|
|
205
|
+
* requests of one client conversation is undetectable by the client.
|
|
206
|
+
*
|
|
207
|
+
* There is NO catalog cache at this layer, deliberately: the catalog is
|
|
208
|
+
* re-derived per request, and if that ever proves too slow the fix belongs in
|
|
209
|
+
* the source path (all-tools/ACL), which serves every other caller too.
|
|
210
|
+
*
|
|
211
|
+
* The one exception to "nothing between requests" is proxied third-party MCP
|
|
212
|
+
* servers (`mcp` manuals): those are session-ful by nature, so their
|
|
213
|
+
* connections live in a {@link DownstreamPool} keyed by (user, manual) — lazy,
|
|
214
|
+
* single-flight, idle-evicted, bounded, and healed by session recovery. Losing
|
|
215
|
+
* any of it costs a reconnect, never correctness.
|
|
216
|
+
*
|
|
99
217
|
* Dispatch always uses `callToolStreaming`, which is uniform across tool kinds:
|
|
100
218
|
* a plain `http` tool yields exactly one chunk (its final result, emitted as
|
|
101
219
|
* the tool result with no progress), while a `streamable_http` tool yields many
|
|
102
|
-
* (the all-but-last become `notifications/progress
|
|
103
|
-
*
|
|
104
|
-
* touches this file.
|
|
220
|
+
* (the all-but-last become `notifications/progress` on the request's own
|
|
221
|
+
* response stream, the last is the result).
|
|
105
222
|
*
|
|
106
223
|
* Auth note: `/api/agent/*` accepts connection keys and internal tokens only.
|
|
107
|
-
* Connection-key
|
|
108
|
-
* OAuth/JWT
|
|
109
|
-
*
|
|
110
|
-
*
|
|
224
|
+
* Connection-key requests pass the caller's own key through to the loopback;
|
|
225
|
+
* OAuth/JWT requests can't (their bearer would 401 there), so the proxy mints a
|
|
226
|
+
* least-privilege internal token for the resolved user and uses THAT as the
|
|
227
|
+
* request's loopback bearer instead.
|
|
111
228
|
*/
|
|
112
229
|
export class McpService {
|
|
113
230
|
// Circuit breaker for manuals whose credentials just failed — see the memo.
|
|
231
|
+
// Retry policy, not session state: it survives the move to statelessness.
|
|
114
232
|
private readonly manualFailures = new ManualFailureMemo();
|
|
115
|
-
|
|
233
|
+
private readonly surfaceLog = new SurfaceLogThrottle();
|
|
234
|
+
// One forced token refresh per (user, manual) per minute — see downstream-token-refresh.
|
|
235
|
+
private readonly tokenRefreshes: DownstreamRefreshGuard<ForcedRefreshOutcome>;
|
|
116
236
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
237
|
+
* The tools each pooled manual last advertised, by manual definition. A
|
|
238
|
+
* downstream can only be asked what its tools are over a connection, and a
|
|
239
|
+
* caller whose sign-in is missing or broken has no connection — so their
|
|
240
|
+
* tools would silently vanish from the surface, and a call would answer
|
|
241
|
+
* "Unknown tool" instead of "sign in again on /connect". These definitions
|
|
242
|
+
* keep the manual on the surface for such a caller; the call-time credential
|
|
243
|
+
* check then answers with the link, and an interactive client is sent back
|
|
244
|
+
* through authorization. Definitions, never credentials or results. Keyed
|
|
245
|
+
* by catalog name — one entry per manual, replaced when the manual is next
|
|
246
|
+
* discovered — and used only while the manual's definition is the one the
|
|
247
|
+
* tools were discovered from, so an edited `mcp.json` never serves the old
|
|
248
|
+
* server's tools.
|
|
120
249
|
*/
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
250
|
+
private readonly knownDownstreamTools = new Map<string, { definition: string; tools: UtcpTool[] }>();
|
|
251
|
+
|
|
252
|
+
// The downstream connection pool for `mcp` manuals — see the class doc.
|
|
253
|
+
private readonly downstream: DownstreamPool<PooledDownstream>;
|
|
125
254
|
|
|
126
255
|
constructor(
|
|
127
|
-
private readonly sessionStore: McpSessionStore,
|
|
128
256
|
private readonly opts: McpProxyOptions,
|
|
129
257
|
// The vault + manual catalog, used to check a caller's per-user credentials
|
|
130
258
|
// before dispatching a tool. Optional so existing constructions/tests that
|
|
131
259
|
// don't exercise the check keep working (the check is skipped when absent).
|
|
132
260
|
private readonly secretsVault?: ISecretsVaultService,
|
|
133
261
|
private readonly toolManuals?: IToolManualService,
|
|
134
|
-
// Mints the loopback bearer for OAuth/JWT
|
|
135
|
-
// Optional for the same test-compat reason; without it those
|
|
262
|
+
// Mints the loopback bearer for OAuth/JWT requests (see class doc).
|
|
263
|
+
// Optional for the same test-compat reason; without it those requests
|
|
136
264
|
// fall back to the old pass-through (and 401 at the loopback hop).
|
|
137
265
|
private readonly internalTokens?: InternalTokenService,
|
|
138
266
|
// Revokes an MCP OAuth access token (BevelOAuthProvider.revokeByAccessToken)
|
|
139
|
-
// — the reset that sends an interactive
|
|
267
|
+
// — the reset that sends an interactive client back through the browser
|
|
140
268
|
// authorization when one of its TOOL sign-ins breaks. Optional: without
|
|
141
269
|
// it, broken sign-ins surface only as the /connect link in the result.
|
|
142
270
|
private readonly revokeOAuthAccess?: (bearer: string) => Promise<void>,
|
|
143
|
-
) {
|
|
271
|
+
) {
|
|
272
|
+
this.downstream = new DownstreamPool<PooledDownstream>({
|
|
273
|
+
...opts.downstreamPool,
|
|
274
|
+
// Since @utcp/sdk 1.2.0 a client closes only the protocol instances it
|
|
275
|
+
// created, and @utcp/mcp registers as a factory — so this closes exactly
|
|
276
|
+
// this entry's MCP sessions and touches no other client's.
|
|
277
|
+
dispose: (entry) => entry.client.close(),
|
|
278
|
+
});
|
|
279
|
+
// Paced on the pool's clock, so a test that moves one moves both.
|
|
280
|
+
this.tokenRefreshes = new DownstreamRefreshGuard(undefined, opts.downstreamPool?.now);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* A secret changed — wired to the secrets vault's mutation listener.
|
|
285
|
+
* `null` = a shared secret changed (affects everyone).
|
|
286
|
+
*
|
|
287
|
+
* Two things were built from the old value: remembered manual failures (so a
|
|
288
|
+
* just-repaired credential is retried on the very next request) and pooled
|
|
289
|
+
* downstream connections (dialed and discovered with the old credential).
|
|
290
|
+
* Both are dropped; the next request rebuilds whatever it needs.
|
|
291
|
+
*/
|
|
292
|
+
onSecretsChanged(userId: string | null): void {
|
|
293
|
+
this.forgetStaleRefreshWindows(userId);
|
|
294
|
+
if (userId === null) {
|
|
295
|
+
this.manualFailures.clearAll();
|
|
296
|
+
this.downstream.closeAll();
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
this.manualFailures.clearUser(userId);
|
|
300
|
+
this.downstream.evictWhere((key) => key.startsWith(`${userId}${POOL_KEY_SEPARATOR}`));
|
|
301
|
+
}
|
|
144
302
|
|
|
145
303
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
304
|
+
* A credential changed, so a refresh window that FAILED to produce one no
|
|
305
|
+
* longer describes anything: a token the user has just re-authorized (or an
|
|
306
|
+
* admin has just re-keyed) must be tried on the very next rejection, not sit
|
|
307
|
+
* out the remainder of a minute earned by the credential it replaced.
|
|
308
|
+
*
|
|
309
|
+
* Only settled, non-`refreshed` windows are forgotten. A refresh of our own
|
|
310
|
+
* is itself a secrets mutation, and clearing on that would hand a provider
|
|
311
|
+
* that keeps minting tokens the downstream keeps refusing one refresh per
|
|
312
|
+
* call — precisely the loop the once-a-minute guard exists to bound. An
|
|
313
|
+
* in-flight attempt (no outcome yet) is left alone for the same reason: it
|
|
314
|
+
* may be the very refresh that raised this notification.
|
|
148
315
|
*/
|
|
149
|
-
|
|
150
|
-
|
|
316
|
+
private forgetStaleRefreshWindows(userId: string | null): void {
|
|
317
|
+
const mine = (key: string) => userId === null || key.startsWith(`${userId}${POOL_KEY_SEPARATOR}`);
|
|
318
|
+
this.tokenRefreshes.clearWhere(
|
|
319
|
+
(key, outcome) => mine(key) && outcome !== undefined && outcome !== 'refreshed',
|
|
320
|
+
);
|
|
151
321
|
}
|
|
152
322
|
|
|
153
323
|
/**
|
|
154
|
-
* Build
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
324
|
+
* Build the MCP `Server` for ONE request. The route connects it to a fresh
|
|
325
|
+
* stateless transport, lets it answer, and closes both with the response.
|
|
326
|
+
*
|
|
327
|
+
* Cheap by construction: nothing is fetched up front except what the
|
|
328
|
+
* request's own messages need. The agent instructions are read only when the
|
|
329
|
+
* request carries an `initialize` (the only place they ride); the tool
|
|
330
|
+
* catalog is built on the first handler that needs it and shared by every
|
|
331
|
+
* message of the request (a JSON-RPC batch), never beyond it.
|
|
158
332
|
*/
|
|
159
|
-
async
|
|
160
|
-
userId
|
|
161
|
-
tokenId: string | null,
|
|
162
|
-
bearer: string,
|
|
163
|
-
onSessionInitialized: (sessionId: string) => void,
|
|
164
|
-
): Promise<{ transport: StreamableHTTPServerTransport; server: Server }> {
|
|
333
|
+
async createRequestServer(caller: McpCaller, messages: unknown): Promise<Server> {
|
|
334
|
+
const { userId, tokenId, bearer } = caller;
|
|
165
335
|
// The loopback surface (`/api/agent/*`) accepts connection keys and
|
|
166
|
-
// internal tokens only. A connection-key
|
|
167
|
-
// key through (per-key metering rides on it); an OAuth/JWT
|
|
336
|
+
// internal tokens only. A connection-key request passes the caller's own
|
|
337
|
+
// key through (per-key metering rides on it); an OAuth/JWT request's
|
|
168
338
|
// bearer would 401 there, so mint a least-privilege internal token for
|
|
169
339
|
// the resolved user instead — flagged `externalProxy` so the tool-auth
|
|
170
340
|
// verifier resolves it to `source: 'external'`: the caller IS an external
|
|
171
341
|
// agent and must be treated like one (admitted to `start_session`/`ask`,
|
|
172
|
-
// refused from internal-only tools).
|
|
173
|
-
// idle eviction — an evicted session re-initializes and re-mints.
|
|
342
|
+
// refused from internal-only tools).
|
|
174
343
|
const loopbackBearer =
|
|
175
344
|
tokenId == null && this.internalTokens
|
|
176
345
|
? this.internalTokens.mint({ userId, externalProxy: true }, MCP_LOOPBACK_TOKEN_TTL_MS)
|
|
177
346
|
: bearer;
|
|
178
347
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const client = await this.buildClient(loopbackBearer, userId, manuals);
|
|
184
|
-
const tools = await this.discoverTools(client, manuals, userId);
|
|
185
|
-
// Confirms the MCP session was built for a connecting client (vs. an
|
|
186
|
-
// in-process agent code-mode client, which never runs createSession) and
|
|
187
|
-
// how many tools discovery yielded before listing/filtering.
|
|
188
|
-
console.log(
|
|
189
|
-
`[mcp] createSession: user=${userId} tokenId=${tokenId ?? 'none'} — ` +
|
|
190
|
-
`discovered ${tools.length} tool(s) across ${manuals.length} manual(s)`,
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const transport = new StreamableHTTPServerTransport({
|
|
194
|
-
sessionIdGenerator: () => randomUUID(),
|
|
195
|
-
onsessioninitialized: (sessionId) => {
|
|
196
|
-
this.sessionStore.create(sessionId, userId, tokenId);
|
|
197
|
-
onSessionInitialized(sessionId);
|
|
198
|
-
},
|
|
199
|
-
onsessionclosed: (sessionId) => {
|
|
200
|
-
this.sessionStore.delete(sessionId);
|
|
201
|
-
},
|
|
202
|
-
});
|
|
348
|
+
let instructions: Promise<ComposedAgentInstructions> | undefined;
|
|
349
|
+
const agentInstructions = () => (instructions ??= this.composeAgentInstructions());
|
|
350
|
+
let surface: Promise<RequestSurface> | undefined;
|
|
351
|
+
const requestSurface = () => (surface ??= this.buildSurface(userId, tokenId, loopbackBearer));
|
|
203
352
|
|
|
353
|
+
const initializing = (Array.isArray(messages) ? messages : [messages]).some((m) => isInitializeRequest(m));
|
|
204
354
|
const server = new Server(
|
|
205
355
|
{ name: 'bevel-mcp', version: '0.1.0' },
|
|
206
|
-
{
|
|
356
|
+
{
|
|
357
|
+
capabilities: { tools: {}, prompts: {} },
|
|
358
|
+
// `instructions` rides the initialize result; clients that honour it
|
|
359
|
+
// place the text in the model's system prompt without the model acting.
|
|
360
|
+
...(initializing ? { instructions: (await agentInstructions()).instructions } : {}),
|
|
361
|
+
},
|
|
207
362
|
);
|
|
208
363
|
|
|
209
364
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
365
|
+
const [{ tools, catalogNames }, { toolPrefix }] = await Promise.all([requestSurface(), agentInstructions()]);
|
|
210
366
|
// A discovered tool whose name collides with a meta-tool would be
|
|
211
367
|
// listed but never callable (the dispatcher routes the name to the
|
|
212
368
|
// meta-tool first), so drop it from the listing entirely.
|
|
213
369
|
let listed = tools.filter((t) => !META_TOOL_NAMES.has(t.mcpName));
|
|
214
|
-
// Connection-key
|
|
370
|
+
// Connection-key callers are autonomous pipelines — nobody is present
|
|
215
371
|
// to complete a sign-in mid-run, so register ONLY the tools whose
|
|
216
372
|
// per-user credentials are already satisfied. Interactive (OAuth/JWT)
|
|
217
|
-
//
|
|
373
|
+
// callers keep the full listing: they can configure a tool on
|
|
218
374
|
// /connect when the call-time check points them there.
|
|
219
375
|
if (tokenId != null) {
|
|
220
376
|
const ready = await Promise.all(
|
|
221
377
|
// A per-tool check that throws must NOT reject the whole list (which
|
|
222
378
|
// would blank every tool) — fail that one tool closed and move on.
|
|
223
379
|
listed.map((t) =>
|
|
224
|
-
this.missingUserSecrets(userId, t).then(
|
|
380
|
+
this.missingUserSecrets(userId, catalogNames.get(t.manualName) ?? t.manualName).then(
|
|
225
381
|
(missing) => missing.length === 0,
|
|
226
382
|
() => false,
|
|
227
383
|
),
|
|
@@ -247,15 +403,23 @@ export class McpService {
|
|
|
247
403
|
continue;
|
|
248
404
|
}
|
|
249
405
|
seen.add(entry.name);
|
|
250
|
-
|
|
406
|
+
// The four knowledge-base tools carry the purpose prefix: the one
|
|
407
|
+
// pre-call channel every client shows the model, for the clients that
|
|
408
|
+
// drop the handshake's `instructions`. Applied AFTER the credential
|
|
409
|
+
// filter above, so a connection-key caller's listing carries it too.
|
|
410
|
+
// Every other tool, meta-tools included, keeps its description as is.
|
|
411
|
+
direct.push(
|
|
412
|
+
PREFIXED_TOOLS.has(entry.name)
|
|
413
|
+
? { ...entry, description: prefixToolDescription(toolPrefix, entry.description) }
|
|
414
|
+
: entry,
|
|
415
|
+
);
|
|
251
416
|
}
|
|
252
417
|
// Log only when a tool was dropped (name/schema/duplicate) — that's the
|
|
253
418
|
// anomaly worth surfacing, since a downstream client would otherwise hide
|
|
254
|
-
// it by rejecting the whole response.
|
|
255
|
-
// already visible once per session in the createSession log above.
|
|
419
|
+
// it by rejecting the whole response.
|
|
256
420
|
if (dropped.length) {
|
|
257
|
-
|
|
258
|
-
`
|
|
421
|
+
log.warn(
|
|
422
|
+
`tools/list: serving ${CODE_MODE_META_TOOLS.length + direct.length} tool(s); ` +
|
|
259
423
|
`dropped ${dropped.length} non-listable: ${dropped.join(', ')}`,
|
|
260
424
|
);
|
|
261
425
|
}
|
|
@@ -266,36 +430,56 @@ export class McpService {
|
|
|
266
430
|
});
|
|
267
431
|
|
|
268
432
|
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
269
|
-
|
|
270
|
-
|
|
433
|
+
const { client, tools, unavailable, catalogNames } = await requestSurface();
|
|
434
|
+
const toolName = request.params.name;
|
|
435
|
+
if (META_TOOL_NAMES.has(toolName)) {
|
|
436
|
+
return this.dispatchMetaTool(client, toolName, request.params.arguments ?? {});
|
|
271
437
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
438
|
+
/**
|
|
439
|
+
* The needs-authorization answer, when the caller's sign-in for the
|
|
440
|
+
* manual registered as `utcpName` is missing or broken; `null` to
|
|
441
|
+
* proceed. Stops before running a tool whose personal (user-scoped)
|
|
442
|
+
* credentials the caller hasn't provided — a setup link instead of a
|
|
443
|
+
* blank-credential request that would fail opaquely at the provider.
|
|
444
|
+
* The credential catalog keys manuals by their CATALOG name; a tool
|
|
445
|
+
* carries the registered (rewritten) one, so it is translated back.
|
|
446
|
+
*/
|
|
447
|
+
const needsAuthorization = async (utcpName: string): Promise<CallToolResult | null> => {
|
|
448
|
+
const needsAuth = await this.checkUserSecrets(userId, toolName, catalogNames.get(utcpName) ?? utcpName);
|
|
449
|
+
if (!needsAuth) return null;
|
|
281
450
|
// A sign-in that EXISTS but is broken (expired grant, abandoned
|
|
282
|
-
// consent, missing scopes) on an interactive OAuth
|
|
451
|
+
// consent, missing scopes) on an interactive OAuth caller: revoke the
|
|
283
452
|
// agent's own grant too. Its next request then 401s, its refresh
|
|
284
453
|
// fails, and it re-runs the browser authorization — landing the user
|
|
285
454
|
// on /connect where the broken sign-in shows as not-connected, to
|
|
286
455
|
// re-authorize or deselect. Never-configured tools keep the plain
|
|
287
456
|
// link (revoking for those would loop on every poke at a tool the
|
|
288
|
-
// user simply hasn't set up). Connection-key
|
|
457
|
+
// user simply hasn't set up). Connection-key callers have no grant
|
|
289
458
|
// to reset; the revoke no-ops on non-OAuth bearers anyway.
|
|
290
459
|
if (needsAuth.brokenSignIn && tokenId == null && this.revokeOAuthAccess) {
|
|
291
460
|
try {
|
|
292
461
|
await this.revokeOAuthAccess(bearer);
|
|
293
462
|
} catch (err) {
|
|
294
|
-
|
|
463
|
+
log.warn('failed to reset the caller grant for re-auth:', { err });
|
|
295
464
|
}
|
|
296
465
|
}
|
|
297
466
|
return needsAuth.result;
|
|
467
|
+
};
|
|
468
|
+
const proxied = tools.find((t) => t.mcpName === toolName);
|
|
469
|
+
if (!proxied) {
|
|
470
|
+
// Not on the surface — but if the name belongs to a manual that is off
|
|
471
|
+
// it only because this caller's sign-in is gone (and nothing in this
|
|
472
|
+
// process has seen its tools yet), the honest answer is the sign-in
|
|
473
|
+
// link, exactly as if the tool were listed. Longest matching prefix:
|
|
474
|
+
// manual names may themselves contain underscores.
|
|
475
|
+
const owner = unavailable
|
|
476
|
+
.filter((m) => toolName.startsWith(`${m.utcpName}_`))
|
|
477
|
+
.sort((a, b) => b.utcpName.length - a.utcpName.length)[0];
|
|
478
|
+
const answer = owner ? await needsAuthorization(owner.utcpName) : null;
|
|
479
|
+
return answer ?? toolError(retiredToolMessage(toolName) ?? `Unknown tool "${toolName}".`);
|
|
298
480
|
}
|
|
481
|
+
const needsAuth = await needsAuthorization(proxied.manualName);
|
|
482
|
+
if (needsAuth) return needsAuth;
|
|
299
483
|
return this.dispatch(client, proxied, request, extra);
|
|
300
484
|
});
|
|
301
485
|
|
|
@@ -303,7 +487,7 @@ export class McpService {
|
|
|
303
487
|
// in MCP clients). Backed by the same skill endpoints the tools use, over
|
|
304
488
|
// loopback with the caller's key — so the default-branch catalog, access
|
|
305
489
|
// filtering, and progressive disclosure all live ONCE behind that REST
|
|
306
|
-
// surface, never duplicated here.
|
|
490
|
+
// surface, never duplicated here. Neither handler needs the tool catalog.
|
|
307
491
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
308
492
|
const skills = await this.fetchSkillList(loopbackBearer);
|
|
309
493
|
const prompts: Prompt[] = skills.map((s) => ({
|
|
@@ -325,7 +509,58 @@ export class McpService {
|
|
|
325
509
|
};
|
|
326
510
|
});
|
|
327
511
|
|
|
328
|
-
return
|
|
512
|
+
return server;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* The request's tool surface: catalog over loopback, a fresh UTCP client,
|
|
517
|
+
* every manual registered (pooled for `mcp` ones), tools flattened.
|
|
518
|
+
*
|
|
519
|
+
* Logs one line with the time each half took — the per-request overhead
|
|
520
|
+
* the stateless design trades for restart-invisibility, kept visible so it
|
|
521
|
+
* is measured rather than assumed.
|
|
522
|
+
*/
|
|
523
|
+
private async buildSurface(userId: string, tokenId: string | null, loopbackBearer: string): Promise<RequestSurface> {
|
|
524
|
+
const started = performance.now();
|
|
525
|
+
// The list of manuals this caller gets: the KB manual + each `.tool` they
|
|
526
|
+
// can read. Fetched over loopback with the caller's key, so ACL filtering
|
|
527
|
+
// lives ONCE behind the REST surface.
|
|
528
|
+
const manuals = await this.fetchManualTemplates(loopbackBearer);
|
|
529
|
+
const catalogMs = performance.now() - started;
|
|
530
|
+
const client = await this.buildClient(loopbackBearer, userId, manuals);
|
|
531
|
+
const { tools, unavailable, catalogNames } = await this.discoverTools(client, manuals, userId);
|
|
532
|
+
const totalMs = performance.now() - started;
|
|
533
|
+
// Per user: on a shape change or once per interval, never per request —
|
|
534
|
+
// see SurfaceLogThrottle for why both halves matter.
|
|
535
|
+
const decision = this.surfaceLog.decide(userId, { tools: tools.length, manuals: manuals.length });
|
|
536
|
+
if (decision.log) {
|
|
537
|
+
log.info(
|
|
538
|
+
// Both ids come from the request (an identity provider's subject, a
|
|
539
|
+
// connection key's id), so neither is interpolated raw — see printable.
|
|
540
|
+
`request surface: user=${printable(userId)} tokenId=${tokenId ? printable(tokenId) : 'none'} — ` +
|
|
541
|
+
`${tools.length} tool(s) across ${manuals.length} manual(s) in ${totalMs.toFixed(0)}ms ` +
|
|
542
|
+
`(catalog ${catalogMs.toFixed(0)}ms, registration ${(totalMs - catalogMs).toFixed(0)}ms)` +
|
|
543
|
+
(decision.suppressed > 0 ? ` [+${decision.suppressed} identical rebuild(s) since last line]` : ''),
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
return { client, tools, unavailable, catalogNames };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* The instructions and tool prefix. A reader failure (a disk fault; ENOENT
|
|
551
|
+
* is not one, the reader answers null for that) falls back to the header
|
|
552
|
+
* alone and the fixed prefix line with a logged warning: a request never
|
|
553
|
+
* fails over its preamble.
|
|
554
|
+
*/
|
|
555
|
+
private async composeAgentInstructions(): Promise<ComposedAgentInstructions> {
|
|
556
|
+
const read = this.opts.readAgentPreamble;
|
|
557
|
+
if (!read) return composeAgentInstructions(null);
|
|
558
|
+
try {
|
|
559
|
+
return composeAgentInstructions(await read());
|
|
560
|
+
} catch (err) {
|
|
561
|
+
log.warn('could not read mcp-description.md; this request gets the platform header alone:', { err });
|
|
562
|
+
return composeAgentInstructions(null);
|
|
563
|
+
}
|
|
329
564
|
}
|
|
330
565
|
|
|
331
566
|
/** Loopback GET of the default-branch skill catalog (via the `list_skills` tool). */
|
|
@@ -348,10 +583,9 @@ export class McpService {
|
|
|
348
583
|
* The ONE loopback round-trip: fetch `path` on our own REST surface with the
|
|
349
584
|
* caller's bearer, parse JSON. Failures are logged under `label` (never the
|
|
350
585
|
* bearer) and degrade to null — a loopback hiccup must not throw into the
|
|
351
|
-
* MCP
|
|
352
|
-
* Bounded: a hung loopback (e.g. mid-restart) must not block
|
|
353
|
-
*
|
|
354
|
-
* like any other failure.
|
|
586
|
+
* MCP request. GET when `json` is absent, POST with a JSON body when present.
|
|
587
|
+
* Bounded: a hung loopback (e.g. mid-restart) must not block a request
|
|
588
|
+
* indefinitely; a timeout aborts and degrades to null like any other failure.
|
|
355
589
|
*/
|
|
356
590
|
private async loopbackJson(bearer: string, path: string, label: string, json?: unknown): Promise<unknown> {
|
|
357
591
|
try {
|
|
@@ -364,12 +598,12 @@ export class McpService {
|
|
|
364
598
|
signal: AbortSignal.timeout(LOOPBACK_TIMEOUT_MS),
|
|
365
599
|
});
|
|
366
600
|
if (!res.ok) {
|
|
367
|
-
|
|
601
|
+
log.error(`${label} loopback failed: HTTP ${res.status} ${await res.text().catch(() => '')}`);
|
|
368
602
|
return null;
|
|
369
603
|
}
|
|
370
604
|
return await res.json();
|
|
371
605
|
} catch (err) {
|
|
372
|
-
|
|
606
|
+
log.error(`${label} loopback threw:`, { err });
|
|
373
607
|
return null;
|
|
374
608
|
}
|
|
375
609
|
}
|
|
@@ -384,7 +618,7 @@ export class McpService {
|
|
|
384
618
|
* validated into `CallTemplate`s at the boundary. The KB manual is guaranteed
|
|
385
619
|
* present (a fallback covers an unavailable endpoint); a user manual that
|
|
386
620
|
* fails validation is dropped + logged so one bad `.tool` can't break the
|
|
387
|
-
*
|
|
621
|
+
* request. HTTP transport loses the `CallTemplate` type, so we re-validate the
|
|
388
622
|
* received JSON even though the producing endpoint already validated it.
|
|
389
623
|
*/
|
|
390
624
|
private async fetchManualTemplates(bearer: string): Promise<CallTemplate[]> {
|
|
@@ -405,7 +639,7 @@ export class McpService {
|
|
|
405
639
|
} catch (err) {
|
|
406
640
|
const name = String((raw as { name?: unknown })?.name ?? '');
|
|
407
641
|
if (isKb) throw err; // the KB manual must be valid — the core toolset depends on it
|
|
408
|
-
|
|
642
|
+
log.warn(`skipping manual "${name}": ${err instanceof Error ? err.message : String(err)}`);
|
|
409
643
|
}
|
|
410
644
|
}
|
|
411
645
|
// Always include the KB manual, even if `all-tools` was unavailable/regressed.
|
|
@@ -426,83 +660,385 @@ export class McpService {
|
|
|
426
660
|
}
|
|
427
661
|
|
|
428
662
|
/**
|
|
429
|
-
* One `CodeModeUtcpClient` per
|
|
663
|
+
* One `CodeModeUtcpClient` per request. Reserved `API_URL`/`CONNECTION_KEY` are
|
|
430
664
|
* seeded (namespaced) ONLY for Bevel-hosted manuals — the KB manual and inline
|
|
431
665
|
* `.tool` sub-manuals, whose discovery template targets the loopback (`${API_URL}`).
|
|
432
666
|
* Third-party http/mcp `.tool`s point at arbitrary user URLs, so we must NOT seed
|
|
433
667
|
* them the caller's bearer: a malicious `.tool` referencing `${CONNECTION_KEY}`
|
|
434
668
|
* would otherwise exfiltrate the caller's token to its own endpoint. Tool
|
|
435
669
|
* `${SECRET}` refs resolve lazily via the per-user `bevel-secrets` loader.
|
|
670
|
+
*
|
|
671
|
+
* Not `close()`d: a request's client holds no connection of its own (its
|
|
672
|
+
* `mcp` manuals route to the pool, so its own `mcp` protocol instance never
|
|
673
|
+
* opens a session), and dropping the reference is the whole teardown.
|
|
436
674
|
*/
|
|
437
|
-
private async buildClient(
|
|
438
|
-
bearer: string,
|
|
439
|
-
userId: string,
|
|
440
|
-
manuals: CallTemplate[],
|
|
441
|
-
): Promise<CodeModeUtcpClient> {
|
|
442
|
-
// Bevel-hosted manuals (KB + inline `.tool` sub-manuals, discovery url
|
|
443
|
-
// `${API_URL}/…`) get the loopback creds; third-party `.tool` URLs are never
|
|
444
|
-
// seeded so the bearer can't leak. Same rule the in-process agent factory uses.
|
|
675
|
+
private async buildClient(bearer: string, userId: string, manuals: CallTemplate[]): Promise<CodeModeUtcpClient> {
|
|
445
676
|
const variables = seedBevelHostedManualVars(manuals, this.opts.loopbackBaseUrl, bearer);
|
|
446
|
-
|
|
447
|
-
variables,
|
|
448
|
-
load_variables_from: [bevelSecretsLoaderConfig(userId)],
|
|
449
|
-
});
|
|
450
|
-
return CodeModeUtcpClient.create(process.cwd(), config);
|
|
677
|
+
return CodeModeUtcpClient.create(process.cwd(), utcpClientConfig(userId, variables));
|
|
451
678
|
}
|
|
452
679
|
|
|
453
680
|
/**
|
|
454
|
-
* Register EVERY manual
|
|
455
|
-
* the proxy's advertised shape.
|
|
456
|
-
*
|
|
457
|
-
*
|
|
681
|
+
* Register EVERY manual for this request, then flatten each discovered tool
|
|
682
|
+
* into the proxy's advertised shape. Manuals register concurrently — they are
|
|
683
|
+
* independent, and a request pays for the slowest one rather than the sum. A
|
|
684
|
+
* failure registering a user `.tool` is isolated (logged + skipped) so one
|
|
685
|
+
* broken manual never breaks the request; the KB manual failing is fatal (the
|
|
686
|
+
* core toolset is unusable).
|
|
687
|
+
*
|
|
688
|
+
* `mcp` manuals never dial from here: they are attached from the downstream
|
|
689
|
+
* pool (see {@link attachDownstream}).
|
|
458
690
|
*
|
|
459
|
-
* Failures are memoized per (user, manual) for a few minutes (see
|
|
460
|
-
* {@link ManualFailureMemo}):
|
|
461
|
-
* a broken credential (expired OAuth, revoked key) would
|
|
462
|
-
* its provider on every single
|
|
691
|
+
* Failures are memoized per (user, manual definition) for a few minutes (see
|
|
692
|
+
* {@link ManualFailureMemo}): the catalog is rebuilt on every request, and a
|
|
693
|
+
* manual with a broken credential (expired OAuth, revoked key) would
|
|
694
|
+
* otherwise re-dial its provider on every single one. The definition's
|
|
695
|
+
* fingerprint is part of the key, so a manual edited after a failure (a
|
|
696
|
+
* corrected `mcp.json` URL) is tried on the very next request.
|
|
463
697
|
*/
|
|
464
698
|
private async discoverTools(
|
|
465
699
|
client: CodeModeUtcpClient,
|
|
466
700
|
manuals: CallTemplate[],
|
|
467
701
|
userId: string,
|
|
468
|
-
): Promise<ProxiedTool[]> {
|
|
702
|
+
): Promise<{ tools: ProxiedTool[]; unavailable: UnavailableManual[]; catalogNames: Map<string, string> }> {
|
|
703
|
+
const routes = new Map<string, DownstreamRoute>();
|
|
704
|
+
const unavailable: UnavailableManual[] = [];
|
|
705
|
+
const catalogNames = new Map<string, string>();
|
|
469
706
|
for (const m of manuals) {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
707
|
+
if (m.call_template_type === 'mcp') catalogNames.set(utcpManualName(m), String(m.name));
|
|
708
|
+
}
|
|
709
|
+
// The shared layer rewrites every manual name (`[^\w]` → `_`) and tools
|
|
710
|
+
// route by the rewritten prefix, so two manuals whose names rewrite to one
|
|
711
|
+
// identifier would silently share it. Sequential registration used to
|
|
712
|
+
// throw "already registered" for the second; concurrent registration
|
|
713
|
+
// (below) would let both pass the shared layer's pre-check, one
|
|
714
|
+
// overwriting the other in the repository while `routes` kept whichever
|
|
715
|
+
// resolved last — tools/list from one server, tools/call to the other.
|
|
716
|
+
// Decided here, over the whole list, before anything is registered: every
|
|
717
|
+
// manual in a colliding group fails with a message naming the others. The
|
|
718
|
+
// KB manual always keeps its name — a `.tool` colliding with it fails,
|
|
719
|
+
// the KB manual does not.
|
|
720
|
+
const byRewrittenName = new Map<string, string[]>();
|
|
721
|
+
for (const m of manuals) {
|
|
722
|
+
const rewritten = utcpManualName(m);
|
|
723
|
+
byRewrittenName.set(rewritten, [...(byRewrittenName.get(rewritten) ?? []), String(m.name)]);
|
|
724
|
+
}
|
|
725
|
+
const outcomes = await Promise.all(
|
|
726
|
+
manuals.map(async (m) => {
|
|
727
|
+
const isKb = m.name === EXTERNAL_KB_MANUAL_NAME;
|
|
728
|
+
const name = String(m.name);
|
|
729
|
+
// Both taken before registration, which renames the template in place.
|
|
730
|
+
const rewritten = utcpManualName(m);
|
|
731
|
+
const siblings = (byRewrittenName.get(rewritten) ?? []).filter((s) => s !== name);
|
|
732
|
+
// A collision is a property of the LIST, not of this manual's
|
|
733
|
+
// definition, so the memo key carries the siblings: the moment one is
|
|
734
|
+
// renamed away, the key changes and the survivor is tried on the very
|
|
735
|
+
// next request instead of sitting out the memo's TTL.
|
|
736
|
+
const memoKey =
|
|
737
|
+
`${name}${POOL_KEY_SEPARATOR}${templateFingerprint(m)}` +
|
|
738
|
+
(siblings.length > 0 ? `${POOL_KEY_SEPARATOR}collides:${[...siblings].sort().join(',')}` : '');
|
|
739
|
+
if (!isKb) {
|
|
740
|
+
const recent = this.manualFailures.recentFailure(userId, memoKey);
|
|
741
|
+
if (recent !== undefined) {
|
|
742
|
+
log.warn(`skipping manual "${name}" (recent failure, not retried): ${recent}`);
|
|
743
|
+
return { isKb, ok: true as const };
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
// Begun BEFORE the awaited attempt: if a secrets change (or a
|
|
747
|
+
// concurrent success of this same manual) clears the memo while
|
|
748
|
+
// registration is in flight, the stale failure must not resurrect an
|
|
749
|
+
// entry the clear removed.
|
|
750
|
+
const generation = this.manualFailures.beginAttempt();
|
|
751
|
+
try {
|
|
752
|
+
// Neither path throws: a discovery/network failure and a validation
|
|
753
|
+
// failure both come back as `{ ok: false }`, because the retry
|
|
754
|
+
// policy — this memo — is ours, not the shared layer's.
|
|
755
|
+
const result: DownstreamAttachResult =
|
|
756
|
+
!isKb && siblings.length > 0
|
|
757
|
+
? {
|
|
758
|
+
ok: false,
|
|
759
|
+
error:
|
|
760
|
+
`manual name "${name}" rewrites to "${rewritten}", the same identifier as ` +
|
|
761
|
+
`${siblings.map((s) => `"${s}"`).join(', ')} — rename one; a colliding manual is not registered`,
|
|
762
|
+
}
|
|
763
|
+
: m.call_template_type === 'mcp'
|
|
764
|
+
? await this.attachDownstream(client, m, userId, routes)
|
|
765
|
+
: await registerManual(client, m);
|
|
766
|
+
if (!result.ok) {
|
|
767
|
+
if (isKb) return { isKb, ok: false as const, error: result.error };
|
|
768
|
+
// A failure the manual may recover from on its own schedule (a
|
|
769
|
+
// token refresh that hasn't settled) is reported but NOT
|
|
770
|
+
// remembered: the memo's five minutes would outlast the refresh
|
|
771
|
+
// policy's one and hold the manual down after the provider is back.
|
|
772
|
+
if (!result.retryable) this.manualFailures.recordFailure(userId, memoKey, result.error, generation);
|
|
773
|
+
if (result.signInMissing) unavailable.push({ utcpName: rewritten });
|
|
774
|
+
log.warn(`skipping manual "${name}": ${result.error}`);
|
|
775
|
+
} else if (!isKb) {
|
|
776
|
+
this.manualFailures.clear(userId, memoKey);
|
|
777
|
+
}
|
|
778
|
+
} finally {
|
|
779
|
+
this.manualFailures.endAttempt(generation);
|
|
477
780
|
}
|
|
781
|
+
return { isKb, ok: true as const };
|
|
782
|
+
}),
|
|
783
|
+
);
|
|
784
|
+
const kbFailure = outcomes.find((o) => o.isKb && !o.ok);
|
|
785
|
+
if (kbFailure && !kbFailure.ok) throw new Error(`Bevel tool discovery failed: ${kbFailure.error}`);
|
|
786
|
+
if (routes.size > 0) routeToDownstream(client, routes);
|
|
787
|
+
const utcpTools = await client.getTools();
|
|
788
|
+
return {
|
|
789
|
+
tools: utcpTools.map((tool: UtcpTool) => flattenManualTool(tool, EXTERNAL_KB_MANUAL_NAME)),
|
|
790
|
+
unavailable,
|
|
791
|
+
catalogNames,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Make one `mcp` manual's tools part of this request's client, backed by the
|
|
797
|
+
* pooled connection for (user, manual).
|
|
798
|
+
*
|
|
799
|
+
* The pooled client's discovered tools are copied into the request client's
|
|
800
|
+
* repository — so `tools/list`, `list_tools`/`tools_info` and the TypeScript
|
|
801
|
+
* interfaces `call_tool_chain` generates all see them exactly as if they had
|
|
802
|
+
* been registered here — and `routes` records that calls to this manual go to
|
|
803
|
+
* the pool. The route re-acquires at CALL time rather than holding the pooled
|
|
804
|
+
* client, so an entry evicted between discovery and the call is transparently
|
|
805
|
+
* re-created instead of used after its connection was closed. Each use holds
|
|
806
|
+
* a pool lease only for its own duration, so the pool never closes a
|
|
807
|
+
* connection an operation is still using.
|
|
808
|
+
*/
|
|
809
|
+
private async attachDownstream(
|
|
810
|
+
client: CodeModeUtcpClient,
|
|
811
|
+
template: CallTemplate,
|
|
812
|
+
userId: string,
|
|
813
|
+
routes: Map<string, DownstreamRoute>,
|
|
814
|
+
): Promise<DownstreamAttachResult> {
|
|
815
|
+
const key = downstreamPoolKey(userId, template);
|
|
816
|
+
// The catalog name, which is what the manual's per-user variables are keyed by.
|
|
817
|
+
const catalogName = String(template.name ?? '');
|
|
818
|
+
const manualName = utcpManualName(template);
|
|
819
|
+
const definition = templateFingerprint(template);
|
|
820
|
+
const acquire = () => this.downstream.acquire(key, () => this.connectDownstream(userId, template));
|
|
821
|
+
const afterFailure = (err: unknown) => this.afterDownstreamFailure(userId, catalogName, key, err);
|
|
822
|
+
const attach = async (tools: UtcpTool[]) => {
|
|
823
|
+
await client.config.tool_repository.saveManual({ ...template, name: manualName }, UtcpManualSchema.parse({ tools }));
|
|
824
|
+
routes.set(manualName, { acquire, afterFailure });
|
|
825
|
+
};
|
|
826
|
+
// Set when the handshake failed on a credential the provider may yet
|
|
827
|
+
// renew: the caller must NOT remember that as a dead manual (see below).
|
|
828
|
+
let credentialTransient = false;
|
|
829
|
+
try {
|
|
830
|
+
let lease: Lease<PooledDownstream>;
|
|
831
|
+
try {
|
|
832
|
+
lease = await acquire();
|
|
833
|
+
} catch (err) {
|
|
834
|
+
// A connection dialed with a token the server refuses fails right here,
|
|
835
|
+
// at the handshake — the same refresh-and-retry applies.
|
|
836
|
+
const verdict = await this.classifyDownstreamFailure(userId, catalogName, key, err);
|
|
837
|
+
if (!verdict.retry) {
|
|
838
|
+
credentialTransient = verdict.credentialTransient;
|
|
839
|
+
throw verdict.error;
|
|
840
|
+
}
|
|
841
|
+
lease = await acquire();
|
|
478
842
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
843
|
+
let tools: UtcpTool[];
|
|
844
|
+
try {
|
|
845
|
+
tools = (await lease.value.client.getTools()).filter((t) => t.name.startsWith(`${manualName}.`));
|
|
846
|
+
} finally {
|
|
847
|
+
lease.release();
|
|
848
|
+
}
|
|
849
|
+
this.knownDownstreamTools.set(catalogName, { definition, tools });
|
|
850
|
+
await attach(tools);
|
|
851
|
+
return { ok: true };
|
|
852
|
+
} catch (err) {
|
|
853
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
854
|
+
// No connection because this CALLER has no usable sign-in for the manual
|
|
855
|
+
// (never signed in, or the grant was just found dead and wiped): the
|
|
856
|
+
// manual is not broken, the sign-in is. Keep its tools on the surface
|
|
857
|
+
// from what the downstream last advertised, so a call answers with the
|
|
858
|
+
// sign-in link — and never remember the failure, since re-authorizing is
|
|
859
|
+
// one visit to /connect away and the next attempt costs no network.
|
|
860
|
+
if (await this.signInMissing(userId, catalogName)) {
|
|
861
|
+
const known = this.knownDownstreamTools.get(catalogName);
|
|
862
|
+
if (known && known.definition === definition) {
|
|
863
|
+
await attach(known.tools);
|
|
864
|
+
return { ok: true };
|
|
865
|
+
}
|
|
866
|
+
return { ok: false, error, retryable: true, signInMissing: true };
|
|
493
867
|
}
|
|
868
|
+
// `retryable` keeps a transient credential failure out of the caller's
|
|
869
|
+
// five-minute failure memo: the refresh-and-retry policy for a rejected
|
|
870
|
+
// token is one minute, and a memo entry would silently outlast it.
|
|
871
|
+
return { ok: false, error, retryable: credentialTransient };
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/** Whether the caller's sign-in for the manual named `catalogName` is missing or broken — a check that must never throw here. */
|
|
876
|
+
private async signInMissing(userId: string, catalogName: string): Promise<boolean> {
|
|
877
|
+
try {
|
|
878
|
+
return (await this.missingUserSecrets(userId, catalogName)).length > 0;
|
|
879
|
+
} catch {
|
|
880
|
+
return false;
|
|
494
881
|
}
|
|
495
|
-
const utcpTools = await client.getTools();
|
|
496
|
-
return utcpTools.map((tool: UtcpTool) => flattenManualTool(tool, EXTERNAL_KB_MANUAL_NAME));
|
|
497
882
|
}
|
|
498
883
|
|
|
499
884
|
/**
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
* `
|
|
504
|
-
*
|
|
885
|
+
* Create one pooled downstream connection: a client registering just this
|
|
886
|
+
* manual.
|
|
887
|
+
*
|
|
888
|
+
* `@utcp/mcp` (1.2.0+) registers its protocol as a FACTORY, so this client
|
|
889
|
+
* gets an `mcp` protocol instance of its own: the session it opens lives in
|
|
890
|
+
* that instance, is reachable from no other client, and ends with
|
|
891
|
+
* `client.close()`. That is what makes a pool entry per (user, server) an
|
|
892
|
+
* actual isolation boundary — two users with byte-identical server
|
|
893
|
+
* definitions still never share a connection — with nothing reached into.
|
|
894
|
+
*
|
|
895
|
+
* SESSION RECOVERY is installed on the pooled client: a downstream server
|
|
896
|
+
* that restarts (or expires a session) answers our next call with the spec's
|
|
897
|
+
* 404/`-32001`, and recovery re-registers in place and retries once. It is
|
|
898
|
+
* deliberately clear of {@link ManualFailureMemo}: that memo is a circuit
|
|
899
|
+
* breaker for manuals whose REGISTRATION failed, so a recovery neither
|
|
900
|
+
* consults it nor records into it.
|
|
901
|
+
*
|
|
902
|
+
* A registration that fails closes the client it built (ending whatever the
|
|
903
|
+
* attempt opened) and throws, so the pool caches nothing and the caller's
|
|
904
|
+
* memo records the failure.
|
|
905
|
+
*/
|
|
906
|
+
private async connectDownstream(userId: string, template: CallTemplate): Promise<PooledDownstream> {
|
|
907
|
+
// Third-party manuals are never seeded loopback credentials (see
|
|
908
|
+
// `buildClient`), so a pooled client carries no request-scoped bearer.
|
|
909
|
+
const client = await CodeModeUtcpClient.create(process.cwd(), utcpClientConfig(userId, {}));
|
|
910
|
+
// Our own copy: registration renames the template in place, and recovery
|
|
911
|
+
// re-registers from exactly what discovery used.
|
|
912
|
+
const own = structuredClone(template);
|
|
913
|
+
const manualName = utcpManualName(own);
|
|
914
|
+
installSessionRecovery(client, { manualTemplate: (name) => (name === manualName ? own : undefined) });
|
|
915
|
+
const result = await registerManual(client, own);
|
|
916
|
+
if (!result.ok) {
|
|
917
|
+
await client.close().catch(() => {});
|
|
918
|
+
throw new Error(result.error);
|
|
919
|
+
}
|
|
920
|
+
return { client };
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* A pooled manual's operation failed: may it be retried with a refreshed
|
|
925
|
+
* token? Answers {@link RETRY_WITH_REFRESHED_TOKEN} when so, otherwise the
|
|
926
|
+
* error the caller should see.
|
|
927
|
+
*
|
|
928
|
+
* Only a token rejection (401 / `invalid_token`) qualifies. Then the
|
|
929
|
+
* caller's OAuth token for this manual is refreshed once, ignoring its
|
|
930
|
+
* stored expiry, and:
|
|
931
|
+
* - refreshed → the pooled connection (dialed with the dead token) is
|
|
932
|
+
* dropped and the operation retried once, on a fresh one;
|
|
933
|
+
* - rejected → the grant is gone and the vault has wiped it; the caller is
|
|
934
|
+
* told to re-authorize on /connect, where it now shows as not connected;
|
|
935
|
+
* - transient, or no refresh allowed / possible → the original error.
|
|
936
|
+
*/
|
|
937
|
+
private async afterDownstreamFailure(
|
|
938
|
+
userId: string,
|
|
939
|
+
manualName: string,
|
|
940
|
+
poolKey: string,
|
|
941
|
+
err: unknown,
|
|
942
|
+
): Promise<unknown> {
|
|
943
|
+
const verdict = await this.classifyDownstreamFailure(userId, manualName, poolKey, err);
|
|
944
|
+
return verdict.retry ? RETRY_WITH_REFRESHED_TOKEN : verdict.error;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/** {@link afterDownstreamFailure}, keeping the one distinction its symbol-or-error answer drops. */
|
|
948
|
+
private async classifyDownstreamFailure(
|
|
949
|
+
userId: string,
|
|
950
|
+
manualName: string,
|
|
951
|
+
poolKey: string,
|
|
952
|
+
err: unknown,
|
|
953
|
+
): Promise<DownstreamFailure> {
|
|
954
|
+
if (!isDownstreamTokenRejection(err)) return { retry: false, error: err, credentialTransient: false };
|
|
955
|
+
const outcome = await this.refreshDownstreamToken(userId, manualName);
|
|
956
|
+
if (outcome === 'refreshed') {
|
|
957
|
+
// The vault's mutation signal evicts this user's connections too; doing it
|
|
958
|
+
// here as well keeps the retry correct whether or not that is wired.
|
|
959
|
+
this.downstream.evictWhere((k) => k === poolKey);
|
|
960
|
+
return { retry: true };
|
|
961
|
+
}
|
|
962
|
+
if (outcome === 'rejected') {
|
|
963
|
+
// The connection was dialed with a grant that no longer exists; the
|
|
964
|
+
// vault's mutation signal drops it too, this keeps the next request
|
|
965
|
+
// honest whether or not that is wired.
|
|
966
|
+
this.downstream.evictWhere((k) => k === poolKey);
|
|
967
|
+
return {
|
|
968
|
+
retry: false,
|
|
969
|
+
credentialTransient: false,
|
|
970
|
+
error: new Error(
|
|
971
|
+
`Your sign-in for "${manualName}" was rejected and has been disconnected. ` +
|
|
972
|
+
`Re-authorize it on ${this.opts.publicFrontendUrl}/connect, then run the tool again.`,
|
|
973
|
+
),
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
// The token stands and so does the call's own error. Whether the NEXT
|
|
977
|
+
// call may try again depends on why: a refresh that failed transiently, or
|
|
978
|
+
// one the window refused, will be worth attempting shortly — while
|
|
979
|
+
// "nothing here to refresh" (no vault, no OAuth variable, no stored token)
|
|
980
|
+
// is a standing condition, and the failure memo should hold the manual
|
|
981
|
+
// down exactly as it does for any other broken credential.
|
|
982
|
+
const mayRecoverSoon = outcome === 'transient' || outcome === 'guarded';
|
|
983
|
+
return { retry: false, error: err, credentialTransient: mayRecoverSoon };
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Force-refresh the caller's OAuth token(s) for `manualName`, at most once per
|
|
988
|
+
* (user, manual) per minute. Logs ONE line per attempt — manual, user,
|
|
989
|
+
* outcome, never token material.
|
|
990
|
+
*
|
|
991
|
+
* `'guarded'` when the window refused this one (a refresh just ran, or is
|
|
992
|
+
* running, for this pair); `undefined` when there was nothing to refresh at
|
|
993
|
+
* all: no vault wired, or no OAuth variable on the manual. The two are not
|
|
994
|
+
* the same thing to the caller — see {@link classifyDownstreamFailure}.
|
|
505
995
|
*/
|
|
996
|
+
private async refreshDownstreamToken(
|
|
997
|
+
userId: string,
|
|
998
|
+
manualName: string,
|
|
999
|
+
): Promise<ForcedRefreshOutcome | 'guarded' | undefined> {
|
|
1000
|
+
const vault = this.secretsVault;
|
|
1001
|
+
if (!vault || !this.toolManuals || !manualName) return undefined;
|
|
1002
|
+
let keys: string[];
|
|
1003
|
+
try {
|
|
1004
|
+
keys = (await this.toolManuals.userScopedKeysForManual(manualName)).filter((v) => v.oauth).map((v) => v.key);
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
// The error's own message is caller-controlled as well (it quotes the
|
|
1007
|
+
// manual, and may quote a provider's reply), and a raw Error handed to
|
|
1008
|
+
// the logger renders its message and stack verbatim — so it goes through
|
|
1009
|
+
// the same escaper rather than travelling as an object.
|
|
1010
|
+
log.warn(
|
|
1011
|
+
`downstream token refresh: could not read the variables of manual=${printable(manualName)}: ` +
|
|
1012
|
+
printable(err instanceof Error ? err.message : String(err)),
|
|
1013
|
+
);
|
|
1014
|
+
return undefined;
|
|
1015
|
+
}
|
|
1016
|
+
if (keys.length === 0) return undefined;
|
|
1017
|
+
const attempt = this.tokenRefreshes.run(`${userId}${POOL_KEY_SEPARATOR}${manualName}`, async () => {
|
|
1018
|
+
const outcomes: ForcedRefreshOutcome[] = [];
|
|
1019
|
+
for (const key of keys) {
|
|
1020
|
+
// A vault fault is not a verdict on the grant: keep the token, try later.
|
|
1021
|
+
outcomes.push(await vault.forceRefresh(userId, key).catch((): ForcedRefreshOutcome => 'transient'));
|
|
1022
|
+
}
|
|
1023
|
+
const outcome: ForcedRefreshOutcome = outcomes.includes('rejected')
|
|
1024
|
+
? 'rejected'
|
|
1025
|
+
: outcomes.includes('transient')
|
|
1026
|
+
? 'transient'
|
|
1027
|
+
: outcomes.includes('refreshed')
|
|
1028
|
+
? 'refreshed'
|
|
1029
|
+
: 'skipped';
|
|
1030
|
+
if (outcome !== 'skipped') {
|
|
1031
|
+
// Both values reach the log from outside (a catalog name, an identity
|
|
1032
|
+
// provider's id), so both are escaped: one event stays one line.
|
|
1033
|
+
log.info(
|
|
1034
|
+
`downstream token refresh: manual=${printable(manualName)} user=${printable(userId)} outcome=${outcome}`,
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
return outcome;
|
|
1038
|
+
});
|
|
1039
|
+
return attempt ? await attempt : 'guarded';
|
|
1040
|
+
}
|
|
1041
|
+
|
|
506
1042
|
/**
|
|
507
1043
|
* If the tool declares per-user credentials the caller hasn't set, return a
|
|
508
1044
|
* needs-authorization result (naming the tool + a setup link); otherwise null
|
|
@@ -514,19 +1050,20 @@ export class McpService {
|
|
|
514
1050
|
*/
|
|
515
1051
|
private async checkUserSecrets(
|
|
516
1052
|
userId: string,
|
|
517
|
-
|
|
1053
|
+
toolName: string,
|
|
1054
|
+
manualName: string,
|
|
518
1055
|
): Promise<{ result: CallToolResult; brokenSignIn: boolean } | null> {
|
|
519
|
-
const missing = await this.missingUserSecrets(userId,
|
|
1056
|
+
const missing = await this.missingUserSecrets(userId, manualName);
|
|
520
1057
|
if (missing.length === 0) return null;
|
|
521
1058
|
return {
|
|
522
1059
|
result: needsAuthorizationResult(
|
|
523
|
-
|
|
1060
|
+
toolName,
|
|
524
1061
|
missing.map((v) => v.label ?? v.name),
|
|
525
1062
|
`${this.opts.publicFrontendUrl}/connect`,
|
|
526
1063
|
),
|
|
527
1064
|
// At least one missing item is a sign-in the caller already HAS a row
|
|
528
1065
|
// for (dead grant, abandoned consent, or missing scopes) — the state
|
|
529
|
-
// that warrants resetting an interactive
|
|
1066
|
+
// that warrants resetting an interactive client for re-auth, as
|
|
530
1067
|
// opposed to a tool the caller never set up at all.
|
|
531
1068
|
brokenSignIn: missing.some((v) => v.brokenSignIn),
|
|
532
1069
|
};
|
|
@@ -535,16 +1072,16 @@ export class McpService {
|
|
|
535
1072
|
/**
|
|
536
1073
|
* The per-user variables of `tool`'s manual the caller has NOT satisfied.
|
|
537
1074
|
* Shared by the call-time needs-authorization check above and the
|
|
538
|
-
* listing-time filter for connection-key
|
|
1075
|
+
* listing-time filter for connection-key callers (which registers only
|
|
539
1076
|
* ready tools). Empty when the vault/manual services aren't wired or the
|
|
540
1077
|
* manual declares no per-user credential.
|
|
541
1078
|
*/
|
|
542
1079
|
private async missingUserSecrets(
|
|
543
1080
|
userId: string,
|
|
544
|
-
|
|
1081
|
+
manualName: string,
|
|
545
1082
|
): Promise<{ name: string; label?: string | null; brokenSignIn: boolean }[]> {
|
|
546
|
-
if (!this.secretsVault || !this.toolManuals || !
|
|
547
|
-
const userVars = await this.toolManuals.userScopedKeysForManual(
|
|
1083
|
+
if (!this.secretsVault || !this.toolManuals || !manualName) return [];
|
|
1084
|
+
const userVars = await this.toolManuals.userScopedKeysForManual(manualName);
|
|
548
1085
|
if (userVars.length === 0) return [];
|
|
549
1086
|
const status = await this.secretsVault.statusFor(
|
|
550
1087
|
userId,
|
|
@@ -561,6 +1098,17 @@ export class McpService {
|
|
|
561
1098
|
missing.push({ name: v.name, label: v.label, brokenSignIn }); // no row at all → needs a value / sign-in
|
|
562
1099
|
continue;
|
|
563
1100
|
}
|
|
1101
|
+
// A row of the OTHER kind is not this credential: a `.tool` edited from
|
|
1102
|
+
// an OAuth sign-in to a plain key (or back) leaves the old row behind,
|
|
1103
|
+
// and serving a token where a key is declared — or a key where a
|
|
1104
|
+
// sign-in is — fails opaquely at the provider. Not a broken sign-in
|
|
1105
|
+
// either: nothing to reset, the user has to set the credential up as
|
|
1106
|
+
// the manual now declares it.
|
|
1107
|
+
const declaredKind = v.oauth ? 'oauth' : 'static';
|
|
1108
|
+
if (st.userKind && st.userKind !== declaredKind) {
|
|
1109
|
+
missing.push({ name: v.name, label: v.label, brokenSignIn: false });
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
564
1112
|
// An OAuth-backed var whose row exists but has no token yet is NOT ready —
|
|
565
1113
|
// the user has registered but not completed sign-in. Fail closed: anything
|
|
566
1114
|
// other than a confirmed `true` (including a non-oauth row → undefined)
|
|
@@ -581,6 +1129,14 @@ export class McpService {
|
|
|
581
1129
|
return missing;
|
|
582
1130
|
}
|
|
583
1131
|
|
|
1132
|
+
/**
|
|
1133
|
+
* Run one tool call through `callToolStreaming` with a one-chunk lookahead:
|
|
1134
|
+
* every chunk except the last becomes a progress notification — sent on THIS
|
|
1135
|
+
* request's own response stream — and the last is the result. Continuity is
|
|
1136
|
+
* the caller's: a tool that supports it (e.g. `ask`) returns its `sessionId`
|
|
1137
|
+
* in the result verbatim, and the caller echoes it back per the tool's own
|
|
1138
|
+
* schema — the proxy never rewrites args.
|
|
1139
|
+
*/
|
|
584
1140
|
private async dispatch(
|
|
585
1141
|
client: CodeModeUtcpClient,
|
|
586
1142
|
tool: ProxiedTool,
|
|
@@ -588,7 +1144,7 @@ export class McpService {
|
|
|
588
1144
|
// `sendNotification` typed loosely (`any`) so a `notifications/progress`
|
|
589
1145
|
// payload without a `progressToken` is accepted — same approach the prior
|
|
590
1146
|
// handler used; the strict ServerNotification type requires the token.
|
|
591
|
-
extra: {
|
|
1147
|
+
extra: { sendNotification: (n: any) => Promise<void> },
|
|
592
1148
|
): Promise<CallToolResult> {
|
|
593
1149
|
const progressToken = request.params._meta?.progressToken;
|
|
594
1150
|
return dispatchToolCall(client, tool, request.params.arguments ?? {}, (progress, message) =>
|
|
@@ -605,8 +1161,8 @@ export class McpService {
|
|
|
605
1161
|
|
|
606
1162
|
/**
|
|
607
1163
|
* Handle a code-mode meta-tool. The shared implementation reflects on this
|
|
608
|
-
*
|
|
609
|
-
* this
|
|
1164
|
+
* request's client, so `list_tools`/`tools_info` describe exactly the catalog
|
|
1165
|
+
* this request discovered and `call_tool_chain` runs in that client's
|
|
610
1166
|
* isolated-vm — resolving over loopback with the caller's key. The workspace
|
|
611
1167
|
* spill store is passed in so an oversized chain result comes back as a
|
|
612
1168
|
* `read_file`-able ref rather than a wall of JSON.
|
|
@@ -620,6 +1176,120 @@ export class McpService {
|
|
|
620
1176
|
}
|
|
621
1177
|
}
|
|
622
1178
|
|
|
1179
|
+
/**
|
|
1180
|
+
* The UTCP client config for `userId`: the given variables, plus the per-user
|
|
1181
|
+
* `bevel-secrets` loader that resolves every `${SECRET}` ref lazily. Shared by
|
|
1182
|
+
* request clients and pooled downstream clients, so both resolve a manual's
|
|
1183
|
+
* variables through exactly the same tiers.
|
|
1184
|
+
*/
|
|
1185
|
+
function utcpClientConfig(userId: string, variables: Record<string, string>) {
|
|
1186
|
+
return new UtcpClientConfigSerializer().validateDict({
|
|
1187
|
+
variables,
|
|
1188
|
+
load_variables_from: [bevelSecretsLoaderConfig(userId)],
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* The name a manual is registered under. `UtcpClient.registerManual` rewrites
|
|
1194
|
+
* every non-word character to `_`, and tool names are prefixed with the result,
|
|
1195
|
+
* so routing has to key on the same rewrite.
|
|
1196
|
+
*/
|
|
1197
|
+
function utcpManualName(template: CallTemplate): string {
|
|
1198
|
+
return String(template.name ?? '').replace(/[^\w]/g, '_');
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* The pool key for (user, manual). The template's fingerprint is part of it, so
|
|
1203
|
+
* an edited `mcp.json` (new URL, new headers) gets a new connection rather than
|
|
1204
|
+
* the one dialed for the old definition, which simply idles out. Hashed: the
|
|
1205
|
+
* template can carry credentials and the key must stay log-safe.
|
|
1206
|
+
*/
|
|
1207
|
+
function downstreamPoolKey(userId: string, template: CallTemplate): string {
|
|
1208
|
+
return [userId, utcpManualName(template), templateFingerprint(template)].join(POOL_KEY_SEPARATOR);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/** A short, log-safe hash of a manual's whole definition: changes whenever the definition does. */
|
|
1212
|
+
function templateFingerprint(template: CallTemplate): string {
|
|
1213
|
+
return createHash('sha256').update(JSON.stringify(template)).digest('hex').slice(0, 16);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* Route tool calls for pooled manuals to their pooled client, in place on the
|
|
1218
|
+
* request client. Both entry points are wrapped — `callToolStreaming` is the
|
|
1219
|
+
* MCP dispatch path, `callTool` is what `call_tool_chain` bridges every
|
|
1220
|
+
* in-isolate tool function to — so the two can never disagree. Every other
|
|
1221
|
+
* manual's calls go through the request client unchanged.
|
|
1222
|
+
*
|
|
1223
|
+
* Each routed call holds its pool lease until the call ends — for a stream,
|
|
1224
|
+
* until the consumer finishes or abandons it (`for await` returns the
|
|
1225
|
+
* generator, which runs the `finally`).
|
|
1226
|
+
*
|
|
1227
|
+
* A call the downstream refuses for its token (401 / `invalid_token`) goes to
|
|
1228
|
+
* the route's `afterFailure`, which may refresh the token and ask for ONE retry
|
|
1229
|
+
* on a fresh connection. Retrying is safe for the same reason session recovery
|
|
1230
|
+
* is: authentication is decided before the request reaches a tool, so the
|
|
1231
|
+
* refused attempt ran nothing.
|
|
1232
|
+
*/
|
|
1233
|
+
function routeToDownstream(client: CodeModeUtcpClient, routes: ReadonlyMap<string, DownstreamRoute>): void {
|
|
1234
|
+
const callTool = client.callTool.bind(client);
|
|
1235
|
+
const callToolStreaming = client.callToolStreaming.bind(client);
|
|
1236
|
+
const routeOf = (toolName: string) => routes.get(toolName.split('.')[0] ?? '');
|
|
1237
|
+
|
|
1238
|
+
client.callTool = async function routedCallTool(toolName: string, toolArgs: Record<string, unknown>) {
|
|
1239
|
+
const route = routeOf(toolName);
|
|
1240
|
+
if (!route) return callTool(toolName, toolArgs);
|
|
1241
|
+
const once = async () => {
|
|
1242
|
+
const lease = await route.acquire();
|
|
1243
|
+
try {
|
|
1244
|
+
return await lease.value.client.callTool(toolName, toolArgs);
|
|
1245
|
+
} finally {
|
|
1246
|
+
lease.release();
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
try {
|
|
1250
|
+
return await once();
|
|
1251
|
+
} catch (err) {
|
|
1252
|
+
const next = await route.afterFailure(err);
|
|
1253
|
+
if (next !== RETRY_WITH_REFRESHED_TOKEN) throw next;
|
|
1254
|
+
// Exactly one retry: whatever it produces is what the caller sees.
|
|
1255
|
+
return await once();
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
client.callToolStreaming = async function* routedCallToolStreaming(
|
|
1259
|
+
toolName: string,
|
|
1260
|
+
toolArgs: Record<string, unknown>,
|
|
1261
|
+
): AsyncGenerator<unknown, void, unknown> {
|
|
1262
|
+
const route = routeOf(toolName);
|
|
1263
|
+
if (!route) {
|
|
1264
|
+
yield* callToolStreaming(toolName, toolArgs);
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const once = async function* () {
|
|
1268
|
+
const lease = await route.acquire();
|
|
1269
|
+
try {
|
|
1270
|
+
yield* lease.value.client.callToolStreaming(toolName, toolArgs);
|
|
1271
|
+
} finally {
|
|
1272
|
+
lease.release();
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
let yielded = false;
|
|
1276
|
+
try {
|
|
1277
|
+
for await (const chunk of once()) {
|
|
1278
|
+
yielded = true;
|
|
1279
|
+
yield chunk;
|
|
1280
|
+
}
|
|
1281
|
+
return;
|
|
1282
|
+
} catch (err) {
|
|
1283
|
+
// A stream that already produced output was accepted — a rejection can
|
|
1284
|
+
// only come before the first chunk, and replaying would duplicate output.
|
|
1285
|
+
if (yielded) throw err;
|
|
1286
|
+
const next = await route.afterFailure(err);
|
|
1287
|
+
if (next !== RETRY_WITH_REFRESHED_TOKEN) throw next;
|
|
1288
|
+
}
|
|
1289
|
+
yield* once();
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
|
|
623
1293
|
/**
|
|
624
1294
|
* The pieces of the proxy that are shared with the local MCP server now live in
|
|
625
1295
|
* `@bevel-software/platform-mcp-core`. Re-exported here because this module is
|