@bevel-software/platform-core-backend 0.33.0 → 0.35.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/agent-guide/access-control.md +146 -289
- package/agent-guide/conventions.md +23 -25
- package/agent-guide/directory-structure.md +72 -120
- package/agent-guide/drafts-and-change-requests.md +92 -0
- package/agent-guide/for-git-users.md +47 -0
- package/agent-guide/glossary.md +36 -0
- package/agent-guide/how-agents-find-things.md +49 -0
- package/agent-guide/html-views.md +239 -8
- package/agent-guide/new-knowledge-base.md +9 -8
- package/agent-guide/skills.md +67 -42
- package/agent-guide/start-here.md +65 -0
- package/agent-guide/tool-manuals.md +133 -155
- package/agent-guide/tools.md +67 -0
- package/agent-guide/where-a-new-file-goes.md +45 -28
- package/dist/core/catalog-revision.d.ts +12 -1
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +23 -3
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/core-ports.d.ts +41 -0
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +15 -8
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +22 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +44 -16
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +16 -0
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +12 -2
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +39 -9
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +15 -3
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +239 -75
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +22 -24
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +48 -51
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-view.d.ts.map +1 -1
- package/dist/modules/access/access-view.js +12 -11
- package/dist/modules/access/access-view.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +3 -0
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +58 -33
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/groups-admin.routes.d.ts.map +1 -1
- package/dist/modules/access/groups-admin.routes.js +4 -2
- package/dist/modules/access/groups-admin.routes.js.map +1 -1
- package/dist/modules/access/groups-admin.service.d.ts +44 -13
- package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
- package/dist/modules/access/groups-admin.service.js +80 -38
- package/dist/modules/access/groups-admin.service.js.map +1 -1
- package/dist/modules/access/groups-edit.d.ts +1 -1
- package/dist/modules/access/groups-edit.d.ts.map +1 -1
- package/dist/modules/access/groups-edit.js +1 -1
- package/dist/modules/access/groups-edit.js.map +1 -1
- package/dist/modules/access/index.d.ts +1 -1
- package/dist/modules/access/index.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.d.ts +116 -24
- package/dist/modules/access/reference-scan.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.js +305 -44
- package/dist/modules/access/reference-scan.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +17 -8
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +32 -23
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +35 -10
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +60 -20
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/access-splice.d.ts +22 -18
- package/dist/modules/access-model/access-splice.d.ts.map +1 -1
- package/dist/modules/access-model/access-splice.js +20 -14
- package/dist/modules/access-model/access-splice.js.map +1 -1
- package/dist/modules/access-model/group-files.d.ts +1 -0
- package/dist/modules/access-model/group-files.d.ts.map +1 -1
- package/dist/modules/access-model/group-files.js +5 -1
- package/dist/modules/access-model/group-files.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +47 -7
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.js +103 -13
- package/dist/modules/agent-guide/agent-guide.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
- package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.js +46 -22
- package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
- package/dist/modules/agent-guide/guide-budget.d.ts +48 -0
- package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
- package/dist/modules/agent-guide/guide-budget.js +67 -0
- package/dist/modules/agent-guide/guide-budget.js.map +1 -0
- package/dist/modules/agent-guide/index.d.ts +2 -1
- package/dist/modules/agent-guide/index.d.ts.map +1 -1
- package/dist/modules/agent-guide/index.js +2 -1
- package/dist/modules/agent-guide/index.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +3 -1
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +7 -1
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +5 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +26 -9
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/agent-scope/agent-scope.contract.d.ts +67 -0
- package/dist/modules/agent-scope/agent-scope.contract.d.ts.map +1 -0
- package/dist/modules/agent-scope/agent-scope.contract.js +27 -0
- package/dist/modules/agent-scope/agent-scope.contract.js.map +1 -0
- package/dist/modules/agent-scope/connection-scope.d.ts +111 -0
- package/dist/modules/agent-scope/connection-scope.d.ts.map +1 -0
- package/dist/modules/agent-scope/connection-scope.js +165 -0
- package/dist/modules/agent-scope/connection-scope.js.map +1 -0
- package/dist/modules/agent-scope/index.d.ts +3 -0
- package/dist/modules/agent-scope/index.d.ts.map +1 -0
- package/dist/modules/agent-scope/index.js +3 -0
- package/dist/modules/agent-scope/index.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +2 -0
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/audit.contract.d.ts +6 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/auth/account-admission.d.ts +12 -0
- package/dist/modules/auth/account-admission.d.ts.map +1 -1
- package/dist/modules/auth/account-admission.js +15 -0
- package/dist/modules/auth/account-admission.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +27 -1
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +42 -4
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts +13 -9
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +35 -29
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +55 -6
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +128 -34
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/index.d.ts +1 -1
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/auth/index.js +1 -1
- package/dist/modules/auth/index.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +2 -9
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +2 -2
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +1 -38
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +68 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +33 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts +8 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.js +13 -2
- package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
- package/dist/modules/embed/embed.tools.d.ts.map +1 -1
- package/dist/modules/embed/embed.tools.js +5 -0
- package/dist/modules/embed/embed.tools.js.map +1 -1
- package/dist/modules/kb-fs/index.d.ts +2 -0
- package/dist/modules/kb-fs/index.d.ts.map +1 -1
- package/dist/modules/kb-fs/index.js +1 -0
- package/dist/modules/kb-fs/index.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +7 -1
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +26 -6
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +22 -7
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-fs/tmp-ref.d.ts +36 -0
- package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -0
- package/dist/modules/kb-fs/tmp-ref.js +76 -0
- package/dist/modules/kb-fs/tmp-ref.js.map +1 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +7 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +14 -1
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +7 -1
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +39 -4
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +11 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +74 -48
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +45 -10
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +9 -0
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +34 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- package/dist/modules/plugins/index.d.ts +1 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +18 -2
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +5 -1
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +32 -5
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +11 -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 +159 -24
- 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 +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +53 -11
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +9 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +100 -42
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/oidc-check.d.ts.map +1 -1
- package/dist/modules/settings/oidc-check.js +5 -17
- package/dist/modules/settings/oidc-check.js.map +1 -1
- package/dist/modules/skills/index.d.ts +3 -3
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +0 -2
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +16 -0
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +6 -1
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +25 -13
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +11 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -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 +1 -0
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +4 -1
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/index.d.ts +1 -1
- package/dist/modules/tool-auth/index.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +9 -0
- package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.js +1 -0
- package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +27 -3
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +32 -6
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts +7 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +3 -0
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-def.js +1 -1
- package/dist/modules/tool-helpers/tool-def.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +15 -0
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +19 -0
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +10 -0
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-helpers/validate-token.d.ts +3 -1
- package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -1
- package/dist/modules/tool-helpers/validate-token.js +1 -1
- package/dist/modules/tool-helpers/validate-token.js.map +1 -1
- package/dist/modules/tool-manuals/index.d.ts +3 -3
- package/dist/modules/tool-manuals/index.d.ts.map +1 -1
- package/dist/modules/tool-manuals/index.js +0 -2
- package/dist/modules/tool-manuals/index.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +46 -6
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +7 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +41 -4
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +24 -13
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +138 -59
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +8 -2
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +56 -20
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/manual.routes.d.ts +8 -1
- package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -1
- package/dist/modules/tool-registry/manual.routes.js +27 -7
- package/dist/modules/tool-registry/manual.routes.js.map +1 -1
- package/dist/modules/tool-registry/tool.contract.d.ts +7 -0
- package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-registry/tool.contract.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 +36 -27
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +2 -2
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +20 -35
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +2 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +3 -0
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
- package/dist/modules/workspace/agent-access.gate.js +1 -1
- package/dist/modules/workspace/agent-access.gate.js.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +28 -4
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js +61 -40
- package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
- package/dist/modules/workspace/index.d.ts +8 -0
- package/dist/modules/workspace/index.d.ts.map +1 -1
- package/dist/modules/workspace/index.js +8 -0
- package/dist/modules/workspace/index.js.map +1 -1
- package/dist/modules/workspace/nul-scan.d.ts +19 -0
- package/dist/modules/workspace/nul-scan.d.ts.map +1 -0
- package/dist/modules/workspace/nul-scan.js +55 -0
- package/dist/modules/workspace/nul-scan.js.map +1 -0
- package/dist/modules/workspace/pattern-matcher.contract.d.ts +110 -27
- package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -1
- package/dist/modules/workspace/pattern-matcher.contract.js +30 -13
- package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -1
- package/dist/modules/workspace/ripgrep-search.d.ts +54 -0
- package/dist/modules/workspace/ripgrep-search.d.ts.map +1 -0
- package/dist/modules/workspace/ripgrep-search.js +657 -0
- package/dist/modules/workspace/ripgrep-search.js.map +1 -0
- package/dist/modules/workspace/ripgrep-version.d.ts +15 -0
- package/dist/modules/workspace/ripgrep-version.d.ts.map +1 -0
- package/dist/modules/workspace/ripgrep-version.js +31 -0
- package/dist/modules/workspace/ripgrep-version.js.map +1 -0
- package/dist/modules/workspace/startup/beside-checkout.d.ts +8 -5
- package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -1
- package/dist/modules/workspace/startup/beside-checkout.js +14 -5
- package/dist/modules/workspace/startup/beside-checkout.js.map +1 -1
- package/dist/modules/workspace/worker-pool.d.ts +3 -4
- package/dist/modules/workspace/worker-pool.d.ts.map +1 -1
- package/dist/modules/workspace/worker-pool.js.map +1 -1
- package/dist/modules/workspace/workspace-tmp.d.ts +146 -0
- package/dist/modules/workspace/workspace-tmp.d.ts.map +1 -0
- package/dist/modules/workspace/workspace-tmp.js +435 -0
- package/dist/modules/workspace/workspace-tmp.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +5 -26
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +47 -6
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +86 -6
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +3 -2
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +531 -137
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/oauth-token-error.d.ts +23 -0
- package/dist/shared/oauth-token-error.d.ts.map +1 -0
- package/dist/shared/oauth-token-error.js +47 -0
- package/dist/shared/oauth-token-error.js.map +1 -0
- package/dist/shared/path-order.d.ts +28 -8
- package/dist/shared/path-order.d.ts.map +1 -1
- package/dist/shared/path-order.js +37 -8
- package/dist/shared/path-order.js.map +1 -1
- package/dist/shared/read-capped.d.ts +19 -0
- package/dist/shared/read-capped.d.ts.map +1 -0
- package/dist/shared/read-capped.js +45 -0
- package/dist/shared/read-capped.js.map +1 -0
- package/migrations/0022_session_version.sql +6 -0
- package/migrations/0023_agent_path.sql +3 -0
- package/migrations/meta/0022_snapshot.json +2360 -0
- package/migrations/meta/0023_snapshot.json +2378 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +4 -3
- package/src/__tests__/hexis-catalog.ts +144 -0
- package/src/__tests__/pg-scratch.test.ts +69 -0
- package/src/__tests__/pg-scratch.ts +31 -0
- package/src/__tests__/real-disk.ts +21 -0
- package/src/__tests__/slow-ripgrep.ts +65 -0
- package/src/__tests__/tokens.ts +19 -0
- package/src/__tests__/tool-manuals.ts +32 -0
- package/src/__tests__/tool-wiring.ts +25 -0
- package/src/core/__tests__/boot-without-ripgrep.test.ts +34 -0
- package/src/core/__tests__/catalog-revision.test.ts +89 -0
- package/src/core/__tests__/damaged-document.tools.test.ts +2 -2
- package/src/core/__tests__/gated-boot.test.ts +8 -1
- package/src/core/__tests__/lifecycle.test.ts +46 -13
- package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
- package/src/core/catalog-revision.ts +24 -2
- package/src/core/core-ports.ts +42 -0
- package/src/core/create-core-server.ts +14 -1
- package/src/core/create-core-services.ts +62 -15
- package/src/core/lifecycle.ts +26 -2
- package/src/index.ts +1 -1
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +12 -0
- package/src/modules/access/__tests__/access-control.colliding-name-cases.test.ts +113 -0
- package/src/modules/access/__tests__/access-control.frontmatter-head.test.ts +117 -0
- package/src/modules/access/__tests__/access-control.group-named-admin.test.ts +122 -0
- package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +50 -0
- package/src/modules/access/__tests__/access-declarations.test.ts +19 -0
- package/src/modules/access/__tests__/access-groups.test.ts +55 -22
- package/src/modules/access/__tests__/access-mutation.service.test.ts +85 -59
- package/src/modules/access/__tests__/access-restrict-here.test.ts +7 -5
- package/src/modules/access/__tests__/access.routes.admins.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -0
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +50 -14
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +52 -10
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +18 -13
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
- package/src/modules/access/__tests__/grant-sources.test.ts +20 -15
- package/src/modules/access/__tests__/groups-admin.routes.test.ts +61 -0
- package/src/modules/access/__tests__/groups-admin.service.test.ts +211 -18
- package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
- package/src/modules/access/__tests__/reference-scan.test.ts +366 -14
- package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
- package/src/modules/access/__tests__/roles-admin.service.test.ts +43 -22
- package/src/modules/access/__tests__/roles-capabilities.test.ts +6 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +1 -1
- package/src/modules/access/access-control.interface.ts +43 -10
- package/src/modules/access/access-control.service.ts +250 -72
- package/src/modules/access/access-mutation.service.ts +48 -53
- package/src/modules/access/access-view.ts +12 -10
- package/src/modules/access/access.routes.ts +66 -37
- package/src/modules/access/groups-admin.routes.ts +4 -2
- package/src/modules/access/groups-admin.service.ts +114 -44
- package/src/modules/access/groups-edit.ts +1 -1
- package/src/modules/access/index.ts +1 -1
- package/src/modules/access/reference-scan.ts +319 -46
- package/src/modules/access/roles-admin.service.ts +43 -26
- package/src/modules/access/synced-groups-writer.ts +1 -1
- package/src/modules/access-model/__tests__/access-grammar.test.ts +38 -1
- package/src/modules/access-model/__tests__/access-splice.test.ts +50 -8
- package/src/modules/access-model/access-grammar.ts +65 -20
- package/src/modules/access-model/access-splice.ts +31 -23
- package/src/modules/access-model/group-files.ts +5 -0
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +110 -25
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +38 -11
- package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
- package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
- package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
- package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
- package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
- package/src/modules/agent-guide/agent-guide.tools.ts +51 -22
- package/src/modules/agent-guide/agent-guide.ts +120 -13
- package/src/modules/agent-guide/guide-budget.ts +73 -0
- package/src/modules/agent-guide/index.ts +12 -0
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +5 -4
- package/src/modules/agent-instructions/compose.ts +7 -1
- package/src/modules/agent-instructions/shared-file-rules.ts +27 -9
- package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +421 -0
- package/src/modules/agent-scope/__tests__/connection-scope.test.ts +46 -0
- package/src/modules/agent-scope/__tests__/in-memory-kb.ts +148 -0
- package/src/modules/agent-scope/agent-scope.contract.ts +83 -0
- package/src/modules/agent-scope/connection-scope.ts +199 -0
- package/src/modules/agent-scope/index.ts +15 -0
- package/src/modules/audit/agent-audit.service.ts +2 -0
- package/src/modules/audit/audit.contract.ts +6 -0
- package/src/modules/auth/__tests__/auth.middleware.test.ts +30 -2
- package/src/modules/auth/__tests__/auth.routes.test.ts +71 -5
- package/src/modules/auth/__tests__/auth.service.test.ts +90 -9
- package/src/modules/auth/__tests__/password-change-sessions.pg.test.ts +269 -0
- package/src/modules/auth/account-admission.ts +19 -0
- package/src/modules/auth/auth.middleware.ts +45 -4
- package/src/modules/auth/auth.routes.ts +46 -29
- package/src/modules/auth/auth.service.ts +135 -34
- package/src/modules/auth/index.ts +2 -0
- package/src/modules/auth/oidc-auth-provider.ts +2 -9
- package/src/modules/code-mode/code-mode.tool.ts +2 -2
- package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
- package/src/modules/connection-probe/connection-probe.service.ts +1 -34
- package/src/modules/database/__tests__/session-version-migration.pg.test.ts +133 -0
- package/src/modules/database/core-schema.ts +33 -0
- package/src/modules/declared-variables/__tests__/declared-variables.route.test.ts +44 -1
- package/src/modules/declared-variables/declared-variables.routes.ts +12 -1
- package/src/modules/embed/__tests__/embed.tools.test.ts +12 -0
- package/src/modules/embed/embed.tools.ts +5 -0
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +25 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +53 -8
- package/src/modules/kb-fs/index.ts +2 -0
- package/src/modules/kb-fs/locking-filesystem.ts +7 -1
- package/src/modules/kb-fs/repo-path.ts +33 -7
- package/src/modules/kb-fs/tmp-ref.ts +81 -0
- package/src/modules/mcp/__tests__/connect-as.pg.test.ts +137 -0
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +29 -5
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +8 -0
- package/src/modules/mcp/__tests__/mcp.connect-as.test.ts +195 -0
- package/src/modules/mcp/__tests__/mcp.routes.connect-as.test.ts +192 -0
- package/src/modules/mcp/__tests__/oauth-consent.connect-as.test.ts +146 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +19 -2
- package/src/modules/mcp/mcp.routes.ts +39 -3
- package/src/modules/mcp/mcp.service.ts +112 -52
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +46 -8
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +41 -1
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +38 -2
- package/src/modules/plugins/index.ts +1 -1
- package/src/modules/plugins/plugin-links.ts +19 -2
- package/src/modules/plugins/plugins.routes.ts +35 -4
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +113 -0
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +430 -3
- package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
- package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +170 -27
- package/src/modules/secrets-vault/index.ts +1 -1
- package/src/modules/secrets-vault/secrets-vault.contract.ts +61 -11
- package/src/modules/secrets-vault/secrets-vault.routes.ts +114 -42
- package/src/modules/settings/oidc-check.ts +5 -18
- package/src/modules/skills/__tests__/skills.service.test.ts +34 -0
- package/src/modules/skills/index.ts +7 -2
- package/src/modules/skills/skills.contract.ts +17 -0
- package/src/modules/skills/skills.service.ts +5 -1
- package/src/modules/skills/skills.tools.ts +45 -14
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +4 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +11 -1
- package/src/modules/tool-auth/external-api-key.service.ts +5 -2
- package/src/modules/tool-auth/index.ts +1 -1
- package/src/modules/tool-auth/internal-token.service.ts +10 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +45 -3
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +19 -11
- package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -1
- package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
- package/src/modules/tool-helpers/tool-context.ts +10 -0
- package/src/modules/tool-helpers/tool-def.ts +1 -1
- package/src/modules/tool-helpers/tool-handler.ts +34 -0
- package/src/modules/tool-helpers/tool.contract.ts +10 -0
- package/src/modules/tool-helpers/validate-token.ts +4 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +80 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +42 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +99 -8
- package/src/modules/tool-manuals/index.ts +6 -2
- package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
- package/src/modules/tool-manuals/tool-manuals.contract.ts +40 -6
- package/src/modules/tool-manuals/tool-manuals.routes.ts +40 -3
- package/src/modules/tool-manuals/tool-manuals.service.ts +169 -61
- package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -25
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -113
- package/src/modules/tool-registry/manual.routes.ts +26 -5
- package/src/modules/tool-registry/tool.contract.ts +7 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +1 -1
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +54 -3
- package/src/modules/workflow/agent-tools/workflow.tools.ts +36 -27
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +130 -3
- package/src/modules/workflow/git/git.service.ts +29 -48
- package/src/modules/workflow/workflow.service.ts +5 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -2
- package/src/modules/workspace/__tests__/holds-nul.test.ts +66 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
- package/src/modules/workspace/__tests__/ripgrep-search.test.ts +570 -0
- package/src/modules/workspace/__tests__/ripgrep-version.test.ts +29 -0
- package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +928 -0
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +17 -37
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +20 -8
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -12
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +22 -10
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +549 -162
- package/src/modules/workspace/agent-access.gate.ts +1 -1
- package/src/modules/workspace/file-readers/__tests__/doc-extract.service.test.ts +10 -0
- package/src/modules/workspace/file-readers/doc-extract.service.ts +71 -38
- package/src/modules/workspace/index.ts +20 -0
- package/src/modules/workspace/nul-scan.ts +50 -0
- package/src/modules/workspace/pattern-matcher.contract.ts +123 -32
- package/src/modules/workspace/ripgrep-search.ts +715 -0
- package/src/modules/workspace/ripgrep-version.ts +35 -0
- package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +11 -0
- package/src/modules/workspace/startup/beside-checkout.ts +13 -5
- package/src/modules/workspace/worker-pool.ts +3 -4
- package/src/modules/workspace/workspace-tmp.ts +457 -0
- package/src/modules/workspace/workspace.routes.ts +6 -27
- package/src/modules/workspace/workspace.service.ts +92 -15
- package/src/modules/workspace/workspace.tools.ts +574 -176
- package/src/shared/__tests__/read-capped.test.ts +68 -0
- package/src/shared/oauth-token-error.ts +46 -0
- package/src/shared/path-order.ts +37 -8
- package/src/shared/read-capped.ts +40 -0
- package/starter-packs/engineering/Plugins/engineering-starter/access.md +1 -1
- package/starter-packs/general/Plugins/general-starter/access.md +1 -1
- package/starter-packs/operations/Plugins/operations-starter/access.md +1 -1
- package/starter-packs/sales/Plugins/sales-starter/access.md +1 -1
- package/starter-packs/support/Plugins/support-starter/access.md +1 -1
- package/agent-guide/finding-things.md +0 -7
- package/agent-guide/introduction.md +0 -27
- package/dist/modules/workspace/bounded-matcher.d.ts +0 -73
- package/dist/modules/workspace/bounded-matcher.d.ts.map +0 -1
- package/dist/modules/workspace/bounded-matcher.js +0 -200
- package/dist/modules/workspace/bounded-matcher.js.map +0 -1
- package/src/modules/workspace/__tests__/bounded-matcher.test.ts +0 -213
- package/src/modules/workspace/bounded-matcher.ts +0 -271
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import type { AddressInfo } from 'node:net';
|
|
3
|
+
import type { Server } from 'node:http';
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { createOAuthConsentRoutes, NOT_AN_AGENT_CHOICE } from '../oauth/oauth-consent.routes.js';
|
|
6
|
+
import { signAuthRequest } from '../oauth/oauth-state.js';
|
|
7
|
+
import { agentFile, agentScopeOver, inMemoryKb } from '../../agent-scope/__tests__/in-memory-kb.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The consent step's "Connect as" choice, through the two routes the
|
|
11
|
+
* /connect page calls: the request description offers the agents the person
|
|
12
|
+
* may read, and Finish hands the provider exactly the choice made — null for
|
|
13
|
+
* Everything — or refuses one that is not on offer, before any code is minted.
|
|
14
|
+
* The choice offered and checked is the REAL agent-scope service's, over a
|
|
15
|
+
* knowledge base held in memory.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const SECRET = 'state-secret';
|
|
19
|
+
const ADA = 'ada@example.com';
|
|
20
|
+
|
|
21
|
+
let http: Server | null = null;
|
|
22
|
+
afterEach(async () => {
|
|
23
|
+
if (http) await new Promise<void>((r) => http!.close(() => r()));
|
|
24
|
+
http = null;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const kb = inMemoryKb({
|
|
28
|
+
'Agents/mechanic.agent': agentFile('name: delivery-mechanic\ndescription: Moves code around.'),
|
|
29
|
+
'Agents/secret.agent': agentFile('name: secret'),
|
|
30
|
+
});
|
|
31
|
+
kb.hidden.add('Agents/secret.agent');
|
|
32
|
+
|
|
33
|
+
async function mount(opts: { email?: string; agents?: Parameters<typeof createOAuthConsentRoutes>[0]['agents'] } = {}) {
|
|
34
|
+
const issueAuthCode = vi.fn(async () => ({ redirectTo: 'https://agent.example.com/cb?code=c1' }));
|
|
35
|
+
const completeConsent = vi.fn(async () => ({ redirectTo: 'https://claude.ai/cb?code=gh' }));
|
|
36
|
+
const app = express();
|
|
37
|
+
app.use(express.json());
|
|
38
|
+
app.use((req, _res, next) => {
|
|
39
|
+
req.userId = 'u1';
|
|
40
|
+
req.userEmail = opts.email ?? ADA;
|
|
41
|
+
next();
|
|
42
|
+
});
|
|
43
|
+
app.use(
|
|
44
|
+
createOAuthConsentRoutes({
|
|
45
|
+
provider: {
|
|
46
|
+
issueAuthCode,
|
|
47
|
+
clientsStore: { getClient: async () => ({ client_name: 'Claude' }) },
|
|
48
|
+
} as never,
|
|
49
|
+
stateSecret: SECRET,
|
|
50
|
+
facade: {
|
|
51
|
+
isFacadeRequest: () => true,
|
|
52
|
+
clientNameFor: () => 'Claude',
|
|
53
|
+
completeConsent,
|
|
54
|
+
},
|
|
55
|
+
agents: opts.agents ?? agentScopeOver(kb),
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
http = app.listen(0);
|
|
59
|
+
await new Promise<void>((r) => http!.once('listening', r));
|
|
60
|
+
const base = `http://127.0.0.1:${(http.address() as AddressInfo).port}`;
|
|
61
|
+
return { base, issueAuthCode, completeConsent };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const state = (extra: Record<string, unknown> = {}) =>
|
|
65
|
+
signAuthRequest(SECRET, { c: 'client-1', r: 'https://agent.example.com/cb', cc: 'challenge', ...extra });
|
|
66
|
+
|
|
67
|
+
async function finish(base: string, body: Record<string, unknown>) {
|
|
68
|
+
return fetch(`${base}/mcp/oauth/complete`, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers: { 'content-type': 'application/json' },
|
|
71
|
+
body: JSON.stringify(body),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe('the consent step offers "Connect as"', () => {
|
|
76
|
+
it('lists the agents the person may read, by name with description and path — Everything is the page’s own', async () => {
|
|
77
|
+
const { base } = await mount();
|
|
78
|
+
const res = await fetch(`${base}/mcp/oauth/request?state=${encodeURIComponent(state())}`);
|
|
79
|
+
expect(await res.json()).toEqual({
|
|
80
|
+
clientName: 'Claude',
|
|
81
|
+
scope: null,
|
|
82
|
+
resource: null,
|
|
83
|
+
agents: [{ path: 'Agents/mechanic.agent', name: 'delivery-mechanic', description: 'Moves code around.' }],
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('says so when the agents cannot be listed, rather than passing it off as having none', async () => {
|
|
88
|
+
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
89
|
+
const failing = {
|
|
90
|
+
listChoices: async () => {
|
|
91
|
+
throw new Error('checkout unavailable');
|
|
92
|
+
},
|
|
93
|
+
choosable: async () => null,
|
|
94
|
+
};
|
|
95
|
+
const { base } = await mount({ agents: failing });
|
|
96
|
+
const res = await fetch(`${base}/mcp/oauth/request?state=${encodeURIComponent(state())}`);
|
|
97
|
+
expect(await res.json()).toEqual({
|
|
98
|
+
clientName: 'Claude',
|
|
99
|
+
scope: null,
|
|
100
|
+
resource: null,
|
|
101
|
+
agents: [],
|
|
102
|
+
agentsUnavailable: true,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Finish records the choice', () => {
|
|
108
|
+
it('Everything — no choice, null or empty — reaches the provider as null, exactly as before', async () => {
|
|
109
|
+
const { base, issueAuthCode } = await mount();
|
|
110
|
+
for (const body of [{}, { agentPath: null }, { agentPath: '' }]) {
|
|
111
|
+
const st = state();
|
|
112
|
+
const res = await finish(base, { state: st, ...body });
|
|
113
|
+
expect(res.status).toBe(200);
|
|
114
|
+
expect(await res.json()).toEqual({ redirectTo: 'https://agent.example.com/cb?code=c1' });
|
|
115
|
+
}
|
|
116
|
+
expect(issueAuthCode).toHaveBeenCalledTimes(3);
|
|
117
|
+
for (const call of issueAuthCode.mock.calls as unknown as unknown[][]) expect(call[2]).toBeNull();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('an agent on offer reaches the provider as its normalized path', async () => {
|
|
121
|
+
const { base, issueAuthCode } = await mount();
|
|
122
|
+
const res = await finish(base, { state: state(), agentPath: '/Agents/mechanic.agent' });
|
|
123
|
+
expect(res.status).toBe(200);
|
|
124
|
+
expect(issueAuthCode).toHaveBeenCalledWith('u1', expect.objectContaining({ c: 'client-1' }), 'Agents/mechanic.agent');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it.each([
|
|
128
|
+
['a file the person may not read', 'Agents/secret.agent'],
|
|
129
|
+
['a file that does not exist', 'Agents/none.agent'],
|
|
130
|
+
['a path that is not an agent', 'notes.md'],
|
|
131
|
+
['a value that is not a path', 42],
|
|
132
|
+
])('refuses %s with one answer, minting nothing', async (_what, agentPath) => {
|
|
133
|
+
const { base, issueAuthCode } = await mount();
|
|
134
|
+
const res = await finish(base, { state: state(), agentPath });
|
|
135
|
+
expect(res.status).toBe(400);
|
|
136
|
+
expect(await res.json()).toEqual({ error: NOT_AN_AGENT_CHOICE });
|
|
137
|
+
expect(issueAuthCode).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('refuses a choice on an account linked through the GitHub facade, which has no connection to record it on', async () => {
|
|
141
|
+
const { base, completeConsent } = await mount();
|
|
142
|
+
const res = await finish(base, { state: state({ gh: true, cc: undefined }), agentPath: 'Agents/mechanic.agent' });
|
|
143
|
+
expect(res.status).toBe(400);
|
|
144
|
+
expect(completeConsent).not.toHaveBeenCalled();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -24,6 +24,13 @@ declare global {
|
|
|
24
24
|
* the page its call comes from (`X-Bevel-Page`).
|
|
25
25
|
*/
|
|
26
26
|
mcpCallerKind?: McpCallerKind;
|
|
27
|
+
/**
|
|
28
|
+
* The agent configuration the MCP caller's credential connects as — a key's
|
|
29
|
+
* `api_tokens.agent_path`, an agent connection's
|
|
30
|
+
* `agent_connections.agent_path`, or the one an exchanged grant
|
|
31
|
+
* carries. Unset is Everything.
|
|
32
|
+
*/
|
|
33
|
+
mcpAgentPath?: string;
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
}
|
|
@@ -110,6 +117,7 @@ export function createMcpAuthMiddleware(
|
|
|
110
117
|
req.userId = resolved.user.id;
|
|
111
118
|
req.userEmail = resolved.user.email;
|
|
112
119
|
req.externalApiKeyId = resolved.tokenId;
|
|
120
|
+
if (resolved.agentPath) req.mcpAgentPath = resolved.agentPath;
|
|
113
121
|
req.mcpCallerKind = 'key';
|
|
114
122
|
next();
|
|
115
123
|
return;
|
|
@@ -137,6 +145,9 @@ export function createMcpAuthMiddleware(
|
|
|
137
145
|
if (typeof info.extra?.connectionId === 'string') {
|
|
138
146
|
req.agentConnectionId = info.extra.connectionId;
|
|
139
147
|
}
|
|
148
|
+
if (typeof info.extra?.agentPath === 'string' && info.extra.agentPath) {
|
|
149
|
+
req.mcpAgentPath = info.extra.agentPath;
|
|
150
|
+
}
|
|
140
151
|
if (!req.userId) {
|
|
141
152
|
unauthorized(res, 'Invalid access token');
|
|
142
153
|
return;
|
|
@@ -217,10 +228,13 @@ export function createMcpAuthMiddleware(
|
|
|
217
228
|
// stamped too (throttled, off the request's path), so the Audit log's
|
|
218
229
|
// "last used" follows the local server's calls, not only its sign-ins.
|
|
219
230
|
// Carried on the request so those calls are logged under the agent.
|
|
231
|
+
// The same read answers what the connection is made as NOW: a
|
|
232
|
+
// re-consent changes the row, not the token, so the row's choice is
|
|
233
|
+
// the one served, never the one the token was minted with.
|
|
220
234
|
if (claim.connectionId) {
|
|
221
|
-
let live:
|
|
235
|
+
let live: { agentPath: string | null } | null;
|
|
222
236
|
try {
|
|
223
|
-
live = await oauthProvider.
|
|
237
|
+
live = await oauthProvider.liveConnectionChoice(claim.connectionId);
|
|
224
238
|
} catch (err) {
|
|
225
239
|
log.error('agent-connection check failed:', { err });
|
|
226
240
|
res.status(500).json({ error: 'Authentication backend unavailable' });
|
|
@@ -232,6 +246,9 @@ export function createMcpAuthMiddleware(
|
|
|
232
246
|
}
|
|
233
247
|
oauthProvider.noteConnectionUse(claim.connectionId);
|
|
234
248
|
req.agentConnectionId = claim.connectionId;
|
|
249
|
+
if (live.agentPath) req.mcpAgentPath = live.agentPath;
|
|
250
|
+
} else if (claim.agentPath) {
|
|
251
|
+
req.mcpAgentPath = claim.agentPath;
|
|
235
252
|
}
|
|
236
253
|
req.mcpCallerKind = 'internal';
|
|
237
254
|
next();
|
|
@@ -16,6 +16,8 @@ import type { BevelOAuthProvider } from './oauth/bevel-oauth-provider.js';
|
|
|
16
16
|
import '../tool-auth/index.js'; // req.externalApiKeyId augmentation
|
|
17
17
|
import './mcp-auth.middleware.js'; // req.mcpCallerKind augmentation
|
|
18
18
|
import { isPagePath } from '../audit/index.js';
|
|
19
|
+
import type { IAgentScopeResolver } from '../agent-scope/index.js';
|
|
20
|
+
import { NOT_AN_AGENT_CHOICE } from './oauth/oauth-consent.routes.js';
|
|
19
21
|
|
|
20
22
|
/** Pull the raw bearer token off an already-authenticated request. */
|
|
21
23
|
function extractBearer(req: Request): string {
|
|
@@ -104,6 +106,11 @@ export function createMcpRoutes(
|
|
|
104
106
|
// RFC 9728 pointer carried on this router's own 401 challenges (the
|
|
105
107
|
// local-token exchange), same value McpAuthMiddleware advertises.
|
|
106
108
|
resourceMetadataUrl: string,
|
|
109
|
+
/**
|
|
110
|
+
* The "Connect as" choices a key may be minted as (`modules/agent-scope`).
|
|
111
|
+
* Absent: keys are minted as Everything only, and no choices are listed.
|
|
112
|
+
*/
|
|
113
|
+
agents?: Pick<IAgentScopeResolver, 'listChoices' | 'choosable'>,
|
|
107
114
|
): express.Router {
|
|
108
115
|
const router = express.Router();
|
|
109
116
|
|
|
@@ -153,6 +160,10 @@ export function createMcpRoutes(
|
|
|
153
160
|
// exchanged grant; undefined for keys/JWTs) — the Audit log's other
|
|
154
161
|
// principal, resolved per request like the key id.
|
|
155
162
|
connectionId: req.agentConnectionId ?? null,
|
|
163
|
+
// The agent configuration the credential connects as (a key's, an agent
|
|
164
|
+
// connection's, or the one an exchanged grant carries); null is
|
|
165
|
+
// Everything. Resolved per request like the ids above.
|
|
166
|
+
...(req.mcpAgentPath ? { agentPath: req.mcpAgentPath } : {}),
|
|
156
167
|
// The proxy authenticates its loopback calls with the SAME bearer the
|
|
157
168
|
// client used here, so it acts on the request exactly as the caller would.
|
|
158
169
|
bearer: extractBearer(req),
|
|
@@ -233,6 +244,17 @@ export function createMcpRoutes(
|
|
|
233
244
|
|
|
234
245
|
// ── Connection-key management (JWT-only) ───────────────────────────────
|
|
235
246
|
|
|
247
|
+
// The agents a key may be minted as — the same choices the OAuth consent
|
|
248
|
+
// step offers below Everything, for the key-mint dialog.
|
|
249
|
+
router.get('/mcp/agent-choices', jwtAuthMiddleware, async (req, res) => {
|
|
250
|
+
try {
|
|
251
|
+
res.json({ agents: agents && req.userEmail ? await agents.listChoices(req.userEmail) : [] });
|
|
252
|
+
} catch (err) {
|
|
253
|
+
log.error('listing the agents to connect as failed:', { err });
|
|
254
|
+
res.status(500).json({ error: 'Failed to list the agents' });
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
236
258
|
router.get('/mcp/external-api-keys', jwtAuthMiddleware, async (req, res) => {
|
|
237
259
|
try {
|
|
238
260
|
const tokens = await externalApiKeyService.listForUser(req.userId!);
|
|
@@ -264,12 +286,23 @@ export function createMcpRoutes(
|
|
|
264
286
|
res.status(400).json({ error: 'JSON body required' });
|
|
265
287
|
return;
|
|
266
288
|
}
|
|
267
|
-
const { label } = req.body as { label?: string };
|
|
289
|
+
const { label, agentPath: asked } = req.body as { label?: string; agentPath?: unknown };
|
|
268
290
|
if (!label) {
|
|
269
291
|
res.status(400).json({ error: 'label is required' });
|
|
270
292
|
return;
|
|
271
293
|
}
|
|
272
|
-
|
|
294
|
+
// "Connect as", exactly as on the consent step: absent, null or empty is
|
|
295
|
+
// Everything; anything else must be an agent the owner may choose now.
|
|
296
|
+
let agentPath: string | null = null;
|
|
297
|
+
if (asked !== undefined && asked !== null && asked !== '') {
|
|
298
|
+
agentPath =
|
|
299
|
+
typeof asked === 'string' && agents && req.userEmail ? await agents.choosable(req.userEmail, asked) : null;
|
|
300
|
+
if (agentPath === null) {
|
|
301
|
+
res.status(400).json({ error: NOT_AN_AGENT_CHOICE });
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const minted = await externalApiKeyService.mint(req.userId!, label, agentPath ? { agentPath } : {});
|
|
273
306
|
// The plaintext field is the *only* read path for the raw key. The
|
|
274
307
|
// frontend must store it nowhere — it shows the dialog once and then
|
|
275
308
|
// discards. Subsequent fetches return only `summary`-shaped rows.
|
|
@@ -428,8 +461,11 @@ export function createMcpRoutes(
|
|
|
428
461
|
// The grant's agent connection rides along, so the local server's
|
|
429
462
|
// calls with this token stay attributed to the agent in the Audit log.
|
|
430
463
|
const connectionId = typeof info.extra?.connectionId === 'string' ? info.extra.connectionId : undefined;
|
|
464
|
+
// So does the agent the connection was made as: the local server's
|
|
465
|
+
// calls see the same narrowed surface as the hosted endpoint's.
|
|
466
|
+
const agentPath = typeof info.extra?.agentPath === 'string' && info.extra.agentPath ? info.extra.agentPath : undefined;
|
|
431
467
|
const minted = internalTokens.mint(
|
|
432
|
-
{ userId, externalProxy: true, ...(connectionId ? { connectionId } : {}) },
|
|
468
|
+
{ userId, externalProxy: true, ...(connectionId ? { connectionId } : {}), ...(agentPath ? { agentPath } : {}) },
|
|
433
469
|
ttlMs,
|
|
434
470
|
);
|
|
435
471
|
// The ACTUAL lifetime, not the constant — the caller schedules its
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
type LoadedSkill,
|
|
51
51
|
} from '@bevel-software/platform-mcp-core';
|
|
52
52
|
import type { KbLayout } from '@bevel-software/platform-shared';
|
|
53
|
-
import { bevelSecretsLoaderConfig, type ForcedRefreshOutcome, type ISecretsVaultService,
|
|
53
|
+
import { bevelSecretsLoaderConfig, type ForcedRefreshOutcome, type ISecretsVaultService, userSecretGap } from '../secrets-vault/index.js';
|
|
54
54
|
import { EXTERNAL_KB_MANUAL_NAME } from '../tool-manuals/index.js';
|
|
55
55
|
import type { IToolManualService } from '../tool-manuals/index.js';
|
|
56
56
|
import type { SpillStore } from '../workspace/index.js';
|
|
@@ -75,6 +75,16 @@ import {
|
|
|
75
75
|
type ComposedAgentInstructions,
|
|
76
76
|
} from '../agent-instructions/index.js';
|
|
77
77
|
import { guideFirstDescription } from '../tool-registry/index.js';
|
|
78
|
+
import {
|
|
79
|
+
CONNECTION_META_KEY,
|
|
80
|
+
connectionRefusal,
|
|
81
|
+
connectionWireOf,
|
|
82
|
+
describeConnection,
|
|
83
|
+
type ConnectionScopeWire,
|
|
84
|
+
} from '@bevel-software/platform-mcp-core';
|
|
85
|
+
|
|
86
|
+
// Still offered from here, as this module's tests and callers learnt it.
|
|
87
|
+
export { CONNECTION_META_KEY };
|
|
78
88
|
|
|
79
89
|
/**
|
|
80
90
|
* Configuration for the loopback proxy. `loopbackBaseUrl` is the backend's own
|
|
@@ -142,6 +152,14 @@ export interface McpCaller {
|
|
|
142
152
|
* attributed in the Audit log; a request with neither records nothing.
|
|
143
153
|
*/
|
|
144
154
|
connectionId?: string | null;
|
|
155
|
+
/**
|
|
156
|
+
* The agent configuration the caller's credential connects as (a connection
|
|
157
|
+
* key's, or an agent connection's), or null for Everything. It rides the
|
|
158
|
+
* request's loopback identity, so every agent surface behind the loopback
|
|
159
|
+
* narrows to that agent's scope (`modules/agent-scope`); here it only keys
|
|
160
|
+
* the surface and words the handshake.
|
|
161
|
+
*/
|
|
162
|
+
agentPath?: string | null;
|
|
145
163
|
/** The raw bearer the request carried. */
|
|
146
164
|
bearer: string;
|
|
147
165
|
/**
|
|
@@ -222,6 +240,24 @@ interface RequestSurface {
|
|
|
222
240
|
* build, at the latest after an hour unused.
|
|
223
241
|
*/
|
|
224
242
|
degraded: boolean;
|
|
243
|
+
/**
|
|
244
|
+
* The agent the catalog was narrowed to, as `all-tools` reported it —
|
|
245
|
+
* absent for Everything. A call to a tool not on a narrowed surface is
|
|
246
|
+
* refused by name and agent rather than as unknown, and a connection whose
|
|
247
|
+
* agent configuration cannot be used is served nothing, with the sentence that says
|
|
248
|
+
* why.
|
|
249
|
+
*/
|
|
250
|
+
connection?: ConnectionScopeWire;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The `connection` a loopback answer (`all-tools`, `catalog-revision`)
|
|
255
|
+
* carries, as a field to spread: validated at the boundary by the MCP
|
|
256
|
+
* core's reader (HTTP loses the type), and absent for Everything.
|
|
257
|
+
*/
|
|
258
|
+
function connectionOf(body: unknown): { connection?: ConnectionScopeWire } {
|
|
259
|
+
const connection = connectionWireOf(body);
|
|
260
|
+
return connection ? { connection } : {};
|
|
225
261
|
}
|
|
226
262
|
|
|
227
263
|
/**
|
|
@@ -490,13 +526,26 @@ export class McpService {
|
|
|
490
526
|
// verifier resolves it to `source: 'external'`: the caller IS an external
|
|
491
527
|
// agent and must be treated like one (admitted to `start_session`/`ask`,
|
|
492
528
|
// refused from internal-only tools).
|
|
529
|
+
// The agent the connection was made as rides that token too, so the
|
|
530
|
+
// surfaces behind the loopback narrow to its scope; a connection key
|
|
531
|
+
// carries its own.
|
|
532
|
+
const agentPath = caller.agentPath || null;
|
|
493
533
|
const loopbackBearer =
|
|
494
534
|
tokenId == null && this.internalTokens
|
|
495
|
-
? this.internalTokens.mint(
|
|
535
|
+
? this.internalTokens.mint(
|
|
536
|
+
{ userId, externalProxy: true, ...(agentPath ? { agentPath } : {}) },
|
|
537
|
+
MCP_LOOPBACK_TOKEN_TTL_MS,
|
|
538
|
+
)
|
|
496
539
|
: bearer;
|
|
497
540
|
|
|
498
541
|
let instructions: Promise<ComposedAgentInstructions> | undefined;
|
|
499
542
|
const agentInstructions = () => (instructions ??= this.composeAgentInstructions());
|
|
543
|
+
// What a narrowed connection is told about itself at the handshake: the
|
|
544
|
+
// agent it is connected as, or why it loads nothing. Read off the catalog
|
|
545
|
+
// revision (cheap), not the surface (a full discovery). Everything is
|
|
546
|
+
// told nothing new, so its handshake is exactly what it was.
|
|
547
|
+
const connectionLine = async (): Promise<string | null> =>
|
|
548
|
+
agentPath ? describeConnection((await this.fetchCatalogRevision(loopbackBearer)).connection) : null;
|
|
500
549
|
// The Audit log's view of this request — only when the request can be
|
|
501
550
|
// attributed to a key, an agent connection or a page. The catalog names
|
|
502
551
|
// it classifies pooled tools by are the surface's, which is looked up
|
|
@@ -534,6 +583,10 @@ export class McpService {
|
|
|
534
583
|
}));
|
|
535
584
|
|
|
536
585
|
const initializing = (Array.isArray(messages) ? messages : [messages]).some((m) => isInitializeRequest(m));
|
|
586
|
+
const handshakeInstructions = async (): Promise<string> => {
|
|
587
|
+
const [{ instructions: text }, line] = await Promise.all([agentInstructions(), connectionLine()]);
|
|
588
|
+
return line ? `${text}\n\n${line}` : text;
|
|
589
|
+
};
|
|
537
590
|
// The deployment's MCP Apps, read once per request. Declaring the
|
|
538
591
|
// `resources` capability is what permits the two handlers below, and a
|
|
539
592
|
// deployment with no view must not declare it — a client would then list
|
|
@@ -554,12 +607,12 @@ export class McpService {
|
|
|
554
607
|
},
|
|
555
608
|
// `instructions` rides the initialize result; clients that honour it
|
|
556
609
|
// place the text in the model's system prompt without the model acting.
|
|
557
|
-
...(initializing ? { instructions:
|
|
610
|
+
...(initializing ? { instructions: await handshakeInstructions() } : {}),
|
|
558
611
|
},
|
|
559
612
|
);
|
|
560
613
|
|
|
561
614
|
server.setRequestHandler(ListToolsRequestSchema, async () => inRequest(async () => {
|
|
562
|
-
const [{ tools, catalogNames }, { toolPrefix }, readOnly] = await Promise.all([
|
|
615
|
+
const [{ tools, catalogNames, connection }, { toolPrefix }, readOnly] = await Promise.all([
|
|
563
616
|
requestSurface(),
|
|
564
617
|
agentInstructions(),
|
|
565
618
|
// A failed read lists the built-in hints rather than no listing at all.
|
|
@@ -568,6 +621,12 @@ export class McpService {
|
|
|
568
621
|
return undefined;
|
|
569
622
|
}),
|
|
570
623
|
]);
|
|
624
|
+
// A connection whose agent configuration cannot be used is served NOTHING — not
|
|
625
|
+
// Everything — and the one sentence saying why rides the listing's
|
|
626
|
+
// `_meta` (the handshake carries it too).
|
|
627
|
+
if (connection?.unavailable) {
|
|
628
|
+
return { tools: [], _meta: { [CONNECTION_META_KEY]: connection.unavailable } };
|
|
629
|
+
}
|
|
571
630
|
// A discovered tool whose name collides with a meta-tool would be
|
|
572
631
|
// listed but never callable (the dispatcher routes the name to the
|
|
573
632
|
// meta-tool first), so drop it from the listing entirely.
|
|
@@ -694,7 +753,10 @@ export class McpService {
|
|
|
694
753
|
}
|
|
695
754
|
|
|
696
755
|
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => inRequest(async () => {
|
|
697
|
-
const { client: shared, tools, unavailable, catalogNames, hidden: hiddenTools } = await requestSurface();
|
|
756
|
+
const { client: shared, tools, unavailable, catalogNames, hidden: hiddenTools, connection } = await requestSurface();
|
|
757
|
+
// Nothing is callable on a connection that loads nothing, meta-tools
|
|
758
|
+
// included: every call is answered with the reason.
|
|
759
|
+
if (connection?.unavailable) return toolError(connection.unavailable);
|
|
698
760
|
// The person's client, as this request uses it: every call it makes —
|
|
699
761
|
// a chain's too, which comes back from the isolate outside any async
|
|
700
762
|
// context — carries this request's bearer and audit.
|
|
@@ -769,7 +831,16 @@ export class McpService {
|
|
|
769
831
|
if (answer && owner && audit) {
|
|
770
832
|
await audit.denied(`${owner.utcpName}.${toolName.slice(owner.utcpName.length + 1)}`, args);
|
|
771
833
|
}
|
|
772
|
-
|
|
834
|
+
// On a narrowed connection, a tool off the surface is one its agent
|
|
835
|
+
// does not load: refused by name and agent, never "unknown" — the tool
|
|
836
|
+
// may well exist, for this person, on another connection.
|
|
837
|
+
return (
|
|
838
|
+
answer ??
|
|
839
|
+
toolError(
|
|
840
|
+
retiredToolMessage(toolName) ??
|
|
841
|
+
(connection ? connectionRefusal(toolName, connection) : `Unknown tool "${toolName}".`),
|
|
842
|
+
)
|
|
843
|
+
);
|
|
773
844
|
}
|
|
774
845
|
const needsAuth = await needsAuthorization(proxied.manualName);
|
|
775
846
|
if (needsAuth) {
|
|
@@ -879,9 +950,20 @@ export class McpService {
|
|
|
879
950
|
*/
|
|
880
951
|
private async surfaceFor(caller: McpCaller, loopbackBearer: string): Promise<RequestSurface> {
|
|
881
952
|
const { userId, tokenId } = caller;
|
|
882
|
-
const revision = await this.fetchCatalogRevision(loopbackBearer);
|
|
953
|
+
const { revision } = await this.fetchCatalogRevision(loopbackBearer);
|
|
883
954
|
if (revision === null) return this.buildSurface(userId, tokenId, loopbackBearer, 'uncached');
|
|
884
|
-
|
|
955
|
+
// The agent the connection was made as is part of the key: two
|
|
956
|
+
// connections of one person made as different agents are two surfaces.
|
|
957
|
+
// (The revision already moves when the agent's FILE changes — it carries
|
|
958
|
+
// the file's digest — so an edit is a new build at the next request.)
|
|
959
|
+
// Everything adds nothing, and keeps the key it always had.
|
|
960
|
+
const key = [
|
|
961
|
+
userId,
|
|
962
|
+
tokenId != null ? 'key' : 'person',
|
|
963
|
+
caller.scope ?? 'write',
|
|
964
|
+
revision,
|
|
965
|
+
...(caller.agentPath ? [caller.agentPath] : []),
|
|
966
|
+
].join(POOL_KEY_SEPARATOR);
|
|
885
967
|
const { value } = await this.surfaces.get(
|
|
886
968
|
key,
|
|
887
969
|
() => this.buildSurface(userId, tokenId, loopbackBearer, 'miss'),
|
|
@@ -893,10 +975,13 @@ export class McpService {
|
|
|
893
975
|
/**
|
|
894
976
|
* The caller's catalog revision (`GET /api/agent/catalog-revision`, the one
|
|
895
977
|
* `hexis-mcp` polls), read with their bearer; null when it cannot be read.
|
|
978
|
+
* With it, the agent the caller's connection is narrowed to, when it is.
|
|
896
979
|
* A deployment or a test that does not serve the route answers 404, which
|
|
897
980
|
* is not worth a line in the log; anything else is.
|
|
898
981
|
*/
|
|
899
|
-
private async fetchCatalogRevision(
|
|
982
|
+
private async fetchCatalogRevision(
|
|
983
|
+
bearer: string,
|
|
984
|
+
): Promise<{ revision: string | null; connection?: ConnectionScopeWire }> {
|
|
900
985
|
try {
|
|
901
986
|
const res = await fetch(`${this.opts.loopbackBaseUrl}/api/agent/catalog-revision`, {
|
|
902
987
|
headers: { Authorization: `Bearer ${bearer}` },
|
|
@@ -904,13 +989,16 @@ export class McpService {
|
|
|
904
989
|
});
|
|
905
990
|
if (!res.ok) {
|
|
906
991
|
if (res.status !== 404) log.warn(`catalog-revision loopback failed: HTTP ${res.status}; building this request's surface uncached`);
|
|
907
|
-
return null;
|
|
992
|
+
return { revision: null };
|
|
908
993
|
}
|
|
909
|
-
const body = (await res.json()) as { revision?: unknown };
|
|
910
|
-
return
|
|
994
|
+
const body = (await res.json()) as { revision?: unknown; connection?: unknown };
|
|
995
|
+
return {
|
|
996
|
+
revision: typeof body.revision === 'string' && body.revision ? body.revision : null,
|
|
997
|
+
...connectionOf(body),
|
|
998
|
+
};
|
|
911
999
|
} catch (err) {
|
|
912
1000
|
log.warn("catalog-revision loopback threw; building this request's surface uncached:", { err });
|
|
913
|
-
return null;
|
|
1001
|
+
return { revision: null };
|
|
914
1002
|
}
|
|
915
1003
|
}
|
|
916
1004
|
|
|
@@ -935,7 +1023,7 @@ export class McpService {
|
|
|
935
1023
|
// The list of manuals this caller gets: the KB manual + each `.tool` they
|
|
936
1024
|
// can read. Fetched over loopback with the caller's key, so ACL filtering
|
|
937
1025
|
// lives ONCE behind the REST surface.
|
|
938
|
-
const manuals = await this.fetchManualTemplates(loopbackBearer);
|
|
1026
|
+
const { manuals, connection } = await this.fetchManualTemplates(loopbackBearer);
|
|
939
1027
|
const catalogMs = performance.now() - started;
|
|
940
1028
|
const client = await this.buildClient(userId, manuals);
|
|
941
1029
|
const discovered = await this.discoverTools(client, manuals, userId);
|
|
@@ -960,7 +1048,7 @@ export class McpService {
|
|
|
960
1048
|
(decision.suppressed > 0 ? ` [+${decision.suppressed} identical rebuild(s) since last line]` : ''),
|
|
961
1049
|
);
|
|
962
1050
|
}
|
|
963
|
-
return { ...discovered, client, tools };
|
|
1051
|
+
return { ...discovered, client, tools, ...(connection ? { connection } : {}) };
|
|
964
1052
|
}
|
|
965
1053
|
|
|
966
1054
|
/**
|
|
@@ -1039,11 +1127,13 @@ export class McpService {
|
|
|
1039
1127
|
* request. HTTP transport loses the `CallTemplate` type, so we re-validate the
|
|
1040
1128
|
* received JSON even though the producing endpoint already validated it.
|
|
1041
1129
|
*/
|
|
1042
|
-
private async fetchManualTemplates(
|
|
1130
|
+
private async fetchManualTemplates(
|
|
1131
|
+
bearer: string,
|
|
1132
|
+
): Promise<{ manuals: CallTemplate[]; connection?: ConnectionScopeWire }> {
|
|
1043
1133
|
// This is the REMOTE proxy, so ask for remote-capable manuals only — local-only
|
|
1044
1134
|
// `.tool`s are surfaced instead via the `list_local_tools` tool in the KB manual.
|
|
1045
1135
|
const body = (await this.loopbackJson(bearer, '/api/agent/all-tools?remote=true', 'all-tools')) as
|
|
1046
|
-
| { manuals?: unknown }
|
|
1136
|
+
| { manuals?: unknown; connection?: unknown }
|
|
1047
1137
|
| null;
|
|
1048
1138
|
const rawManuals: unknown[] = Array.isArray(body?.manuals) ? body.manuals : [];
|
|
1049
1139
|
|
|
@@ -1062,7 +1152,7 @@ export class McpService {
|
|
|
1062
1152
|
}
|
|
1063
1153
|
// Always include the KB manual, even if `all-tools` was unavailable/regressed.
|
|
1064
1154
|
if (!hasKb) out.unshift(this.kbManualTemplate());
|
|
1065
|
-
return out;
|
|
1155
|
+
return { manuals: out, ...connectionOf(body) };
|
|
1066
1156
|
}
|
|
1067
1157
|
|
|
1068
1158
|
/** The KB manual's discovery template — the fallback if `all-tools` is unavailable. */
|
|
@@ -1600,41 +1690,11 @@ export class McpService {
|
|
|
1600
1690
|
const statusMap = new Map(status.map((s) => [s.key, s]));
|
|
1601
1691
|
const missing: { name: string; label?: string | null; brokenSignIn: boolean }[] = [];
|
|
1602
1692
|
for (const v of userVars) {
|
|
1603
|
-
|
|
1693
|
+
// The one readiness rule `list_tool_setup` reports from (`userSecretGap`).
|
|
1604
1694
|
// A sign-in the caller already HAS a row for but that isn't (or is no
|
|
1605
|
-
// longer) usable
|
|
1606
|
-
const
|
|
1607
|
-
if (
|
|
1608
|
-
missing.push({ name: v.name, label: v.label, brokenSignIn }); // no row at all → needs a value / sign-in
|
|
1609
|
-
continue;
|
|
1610
|
-
}
|
|
1611
|
-
// A row of the OTHER kind is not this credential: a `.tool` edited from
|
|
1612
|
-
// an OAuth sign-in to a plain key (or back) leaves the old row behind,
|
|
1613
|
-
// and serving a token where a key is declared — or a key where a
|
|
1614
|
-
// sign-in is — fails opaquely at the provider. Not a broken sign-in
|
|
1615
|
-
// either: nothing to reset, the user has to set the credential up as
|
|
1616
|
-
// the manual now declares it.
|
|
1617
|
-
const declaredKind = v.oauth ? 'oauth' : 'static';
|
|
1618
|
-
if (st.userKind && st.userKind !== declaredKind) {
|
|
1619
|
-
missing.push({ name: v.name, label: v.label, brokenSignIn: false });
|
|
1620
|
-
continue;
|
|
1621
|
-
}
|
|
1622
|
-
// An OAuth-backed var whose row exists but has no token yet is NOT ready —
|
|
1623
|
-
// the user has registered but not completed sign-in. Fail closed: anything
|
|
1624
|
-
// other than a confirmed `true` (including a non-oauth row → undefined)
|
|
1625
|
-
// counts as not-yet-authorized.
|
|
1626
|
-
if (v.oauth && st.userAuthorized !== true) {
|
|
1627
|
-
missing.push({ name: v.name, label: v.label, brokenSignIn });
|
|
1628
|
-
continue;
|
|
1629
|
-
}
|
|
1630
|
-
// An OAuth-backed var the user signed in for, but whose token was granted
|
|
1631
|
-
// fewer scopes than the tool now declares, is ALSO not ready — the call would
|
|
1632
|
-
// otherwise fail opaquely at the provider. Compare the live required scopes
|
|
1633
|
-
// against the token's recorded granted scopes; an under-scoped (or unknown)
|
|
1634
|
-
// token needs re-authorization.
|
|
1635
|
-
if (v.oauth && !scopesCovered(v.oauthScopes, st.grantedScopes)) {
|
|
1636
|
-
missing.push({ name: v.name, label: v.label, brokenSignIn });
|
|
1637
|
-
}
|
|
1695
|
+
// longer) usable is a broken sign-in: abandoned consent, missing scopes.
|
|
1696
|
+
const gap = userSecretGap(v, statusMap.get(v.key));
|
|
1697
|
+
if (gap) missing.push({ name: v.name, label: v.label, brokenSignIn: gap === 'unauthorized' || gap === 'under-scoped' });
|
|
1638
1698
|
}
|
|
1639
1699
|
return missing;
|
|
1640
1700
|
}
|