@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
|
@@ -199,10 +199,16 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
199
199
|
* Called by the Finish endpoint once an authenticated user completes the
|
|
200
200
|
* /connect step: bind a one-time code to (user, client, redirect_uri, PKCE
|
|
201
201
|
* challenge) and hand back the client redirect the browser should follow.
|
|
202
|
+
*
|
|
203
|
+
* @param agentPath The "Connect as" choice — an agent configuration the consent
|
|
204
|
+
* route has already checked the person may choose — or null for
|
|
205
|
+
* Everything. Carried on the code to the exchange, which records it on
|
|
206
|
+
* the agent connection.
|
|
202
207
|
*/
|
|
203
208
|
async issueAuthCode(
|
|
204
209
|
userId: string,
|
|
205
210
|
st: McpAuthRequestState,
|
|
211
|
+
agentPath: string | null = null,
|
|
206
212
|
): Promise<{ redirectTo: string }> {
|
|
207
213
|
// A state without a PKCE challenge is a Claude-link request, which the
|
|
208
214
|
// marketplace bridge finishes; it must never reach the SDK's code store.
|
|
@@ -217,6 +223,7 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
217
223
|
scope: st.sc ?? null,
|
|
218
224
|
resource: st.rs ?? null,
|
|
219
225
|
expiresAt: new Date(Date.now() + CODE_TTL_MS),
|
|
226
|
+
...(agentPath ? { agentPath } : {}),
|
|
220
227
|
});
|
|
221
228
|
const redirect = new URL(st.r);
|
|
222
229
|
redirect.searchParams.set('code', code);
|
|
@@ -269,7 +276,7 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
269
276
|
if (redirectUri && redirectUri !== row.redirectUri) {
|
|
270
277
|
throw new InvalidGrantError('redirect_uri does not match the authorization request');
|
|
271
278
|
}
|
|
272
|
-
return this.mintTokens(row.userId, row.clientId, row.scope, row.resource);
|
|
279
|
+
return this.mintTokens(row.userId, row.clientId, row.scope, row.resource, undefined, undefined, row.agentPath ?? null);
|
|
273
280
|
}
|
|
274
281
|
|
|
275
282
|
async exchangeRefreshToken(
|
|
@@ -463,6 +470,7 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
463
470
|
id: oauthTokens.id,
|
|
464
471
|
clientId: oauthTokens.clientId,
|
|
465
472
|
connectionId: oauthTokens.connectionId,
|
|
473
|
+
agentPath: agentConnections.agentPath,
|
|
466
474
|
scope: oauthTokens.scope,
|
|
467
475
|
resource: oauthTokens.resource,
|
|
468
476
|
expiresAt: oauthTokens.expiresAt,
|
|
@@ -507,6 +515,10 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
507
515
|
// Null on a token minted before connections existed; the auth
|
|
508
516
|
// middleware then binds no agent and the call goes unrecorded.
|
|
509
517
|
connectionId: row.connectionId,
|
|
518
|
+
// The connection's "Connect as" choice, read off the row on every
|
|
519
|
+
// verification, so a re-consent with another choice reaches the
|
|
520
|
+
// agent's very next request. Null is Everything.
|
|
521
|
+
agentPath: row.agentPath ?? null,
|
|
510
522
|
},
|
|
511
523
|
};
|
|
512
524
|
}
|
|
@@ -575,6 +587,9 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
575
587
|
* first mint, which finds or makes the live connection for (user, client).
|
|
576
588
|
* @param tx The transaction the insert belongs to, when the mint must land
|
|
577
589
|
* with the statement that consumed the token it replaces (a refresh).
|
|
590
|
+
* @param agentPath The consent's "Connect as" choice, recorded on the
|
|
591
|
+
* connection the pair is bound to (null: Everything). Only a first mint —
|
|
592
|
+
* a consent — passes it; a refresh leaves the connection's choice alone.
|
|
578
593
|
* The prune is then the caller's, before the transaction opens: a
|
|
579
594
|
* best-effort statement that fails inside a transaction would abort the
|
|
580
595
|
* mint it is meant to spare, and one on the pool from inside it would
|
|
@@ -587,6 +602,7 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
587
602
|
resource: string | null,
|
|
588
603
|
connectionId?: string,
|
|
589
604
|
tx?: Tx,
|
|
605
|
+
agentPath?: string | null,
|
|
590
606
|
): Promise<OAuthTokens> {
|
|
591
607
|
const accessToken = this.deps.tokenPrefix + randomBytes(TOKEN_BYTES).toString('base64url');
|
|
592
608
|
const refreshToken = this.refreshPrefix + randomBytes(TOKEN_BYTES).toString('base64url');
|
|
@@ -594,7 +610,7 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
594
610
|
// A mint of its own (a sign-in) prunes here; a refresh pruned before its
|
|
595
611
|
// transaction opened.
|
|
596
612
|
if (!tx) await this.pruneTokenTables(now);
|
|
597
|
-
const boundTo = connectionId ?? (await this.ensureConnection(userId, clientId));
|
|
613
|
+
const boundTo = connectionId ?? (await this.ensureConnection(userId, clientId, agentPath));
|
|
598
614
|
const sink: Tx | Database = tx ?? this.deps.db;
|
|
599
615
|
await sink.insert(oauthTokens).values({
|
|
600
616
|
accessTokenHash: hashToken(accessToken),
|
|
@@ -665,12 +681,23 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
665
681
|
* revoke. A read, not a write: the use is stamped separately, throttled.
|
|
666
682
|
*/
|
|
667
683
|
async isConnectionLive(connectionId: string): Promise<boolean> {
|
|
684
|
+
return (await this.liveConnectionChoice(connectionId)) !== null;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* A live agent connection's current "Connect as" choice (`agentPath` null
|
|
689
|
+
* = Everything), or null when the connection is gone or revoked. Read on
|
|
690
|
+
* every request through the local server's exchanged grant: that token is
|
|
691
|
+
* stateless and outlives a re-consent, so the choice it was minted with is
|
|
692
|
+
* not the one it is served as — the row's is.
|
|
693
|
+
*/
|
|
694
|
+
async liveConnectionChoice(connectionId: string): Promise<{ agentPath: string | null } | null> {
|
|
668
695
|
const [row] = await this.deps.db
|
|
669
|
-
.select({
|
|
696
|
+
.select({ agentPath: agentConnections.agentPath })
|
|
670
697
|
.from(agentConnections)
|
|
671
698
|
.where(and(eq(agentConnections.id, connectionId), isNull(agentConnections.revokedAt)))
|
|
672
699
|
.limit(1);
|
|
673
|
-
return row
|
|
700
|
+
return row ? { agentPath: row.agentPath ?? null } : null;
|
|
674
701
|
}
|
|
675
702
|
|
|
676
703
|
/**
|
|
@@ -737,8 +764,13 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
737
764
|
* the other's `ON CONFLICT DO NOTHING` returns no row, and it re-reads the
|
|
738
765
|
* winner's. The client's display name is snapshotted from its registration
|
|
739
766
|
* at that moment.
|
|
767
|
+
*
|
|
768
|
+
* `agentPath` is a consent's "Connect as" choice: written on the row made,
|
|
769
|
+
* and on the live row found — the person's latest consent is what the agent
|
|
770
|
+
* connects as. `undefined` (a refresh of a token from before connections
|
|
771
|
+
* existed) leaves a found row's choice as it is.
|
|
740
772
|
*/
|
|
741
|
-
private async ensureConnection(userId: string, clientId: string): Promise<string> {
|
|
773
|
+
private async ensureConnection(userId: string, clientId: string, agentPath?: string | null): Promise<string> {
|
|
742
774
|
const [client] = await this.deps.db
|
|
743
775
|
.select({ clientName: oauthClients.clientName })
|
|
744
776
|
.from(oauthClients)
|
|
@@ -758,11 +790,17 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
758
790
|
),
|
|
759
791
|
)
|
|
760
792
|
.limit(1);
|
|
793
|
+
const choose = async (id: string): Promise<string> => {
|
|
794
|
+
if (agentPath !== undefined) {
|
|
795
|
+
await this.deps.db.update(agentConnections).set({ agentPath }).where(eq(agentConnections.id, id));
|
|
796
|
+
}
|
|
797
|
+
return id;
|
|
798
|
+
};
|
|
761
799
|
const [existing] = await live();
|
|
762
|
-
if (existing) return existing.id;
|
|
800
|
+
if (existing) return choose(existing.id);
|
|
763
801
|
const [inserted] = await this.deps.db
|
|
764
802
|
.insert(agentConnections)
|
|
765
|
-
.values({ userId, clientId, clientName, agentKey })
|
|
803
|
+
.values({ userId, clientId, clientName, agentKey, ...(agentPath ? { agentPath } : {}) })
|
|
766
804
|
.onConflictDoNothing({
|
|
767
805
|
target: [agentConnections.userId, agentConnections.agentKey],
|
|
768
806
|
where: sql`${agentConnections.revokedAt} is null`,
|
|
@@ -771,7 +809,7 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
771
809
|
if (inserted) return inserted.id;
|
|
772
810
|
const [winner] = await live();
|
|
773
811
|
if (!winner) throw new Error('agent connection vanished between insert and re-read');
|
|
774
|
-
return winner.id;
|
|
812
|
+
return choose(winner.id);
|
|
775
813
|
}
|
|
776
814
|
}
|
|
777
815
|
|
|
@@ -4,6 +4,7 @@ import { logger } from '../../../shared/logging.js';
|
|
|
4
4
|
const log = logger('mcp-oauth');
|
|
5
5
|
import type { BevelOAuthProvider } from './bevel-oauth-provider.js';
|
|
6
6
|
import { verifyAuthRequest, type McpAuthRequestState } from './oauth-state.js';
|
|
7
|
+
import type { IAgentScopeResolver } from '../../agent-scope/index.js';
|
|
7
8
|
import '../../auth/index.js'; // Express Request augmentation (req.userId / req.userEmail)
|
|
8
9
|
|
|
9
10
|
export interface OAuthConsentRoutesDeps {
|
|
@@ -21,8 +22,17 @@ export interface OAuthConsentRoutesDeps {
|
|
|
21
22
|
clientNameFor(st: McpAuthRequestState): string;
|
|
22
23
|
completeConsent(userId: string, st: McpAuthRequestState): Promise<{ redirectTo: string }>;
|
|
23
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* The "Connect as" choices: the agent configurations the person may connect an
|
|
27
|
+
* MCP client as, offered below Everything. Absent, the consent offers
|
|
28
|
+
* Everything alone and Finish refuses any other choice.
|
|
29
|
+
*/
|
|
30
|
+
agents?: Pick<IAgentScopeResolver, 'listChoices' | 'choosable'>;
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
/** The refusal of a choice that is not one of the person's — one answer for missing and unreadable. */
|
|
34
|
+
export const NOT_AN_AGENT_CHOICE = 'That agent is not one you can connect as. Choose Everything or one of the agents listed.';
|
|
35
|
+
|
|
26
36
|
/**
|
|
27
37
|
* The authenticated tail of the MCP OAuth flow. `/authorize` (SDK-owned)
|
|
28
38
|
* validated the client's request, packed it into a signed state, and sent the
|
|
@@ -49,10 +59,25 @@ export function createOAuthConsentRoutes(deps: OAuthConsentRoutesDeps): express.
|
|
|
49
59
|
return;
|
|
50
60
|
}
|
|
51
61
|
const client = await deps.provider.clientsStore.getClient(st.c);
|
|
62
|
+
// Everything is not in the list: it is the choice every consent has, and
|
|
63
|
+
// the one the page selects. A failure to read the agents costs the choice
|
|
64
|
+
// of an agent, never the consent itself — but it is SAID
|
|
65
|
+
// (`agentsUnavailable`), so the page does not pass an empty list off as
|
|
66
|
+
// "you have no agents" and approve Everything unasked.
|
|
67
|
+
let agentsUnavailable = false;
|
|
68
|
+
const agents = deps.agents && req.userEmail
|
|
69
|
+
? await deps.agents.listChoices(req.userEmail).catch((err: unknown) => {
|
|
70
|
+
log.warn('listing the agents to connect as failed:', { detail: err instanceof Error ? err.message : String(err) });
|
|
71
|
+
agentsUnavailable = true;
|
|
72
|
+
return [];
|
|
73
|
+
})
|
|
74
|
+
: [];
|
|
52
75
|
res.json({
|
|
53
76
|
clientName: client?.client_name ?? null,
|
|
54
77
|
scope: st.sc ?? null,
|
|
55
78
|
resource: st.rs ?? null,
|
|
79
|
+
agents,
|
|
80
|
+
...(agentsUnavailable ? { agentsUnavailable: true } : {}),
|
|
56
81
|
});
|
|
57
82
|
});
|
|
58
83
|
|
|
@@ -65,10 +90,25 @@ export function createOAuthConsentRoutes(deps: OAuthConsentRoutesDeps): express.
|
|
|
65
90
|
const st = raw ? verifyAuthRequest(deps.stateSecret, raw) : null;
|
|
66
91
|
if (!st) return void res.status(400).json({ error: 'Invalid or expired authorization request. Restart the connection from your agent.' });
|
|
67
92
|
if (st.gh && !deps.facade) return void res.status(400).json({ error: NO_FACADE });
|
|
93
|
+
// "Connect as": absent, null or empty is Everything. Anything else must be
|
|
94
|
+
// an agent the person may choose right now — checked here, at the only
|
|
95
|
+
// place a choice is made, so the token exchange records it as given.
|
|
96
|
+
const asked: unknown = (req.body ?? {}).agentPath;
|
|
97
|
+
if (asked !== undefined && asked !== null && typeof asked !== 'string') {
|
|
98
|
+
return void res.status(400).json({ error: NOT_AN_AGENT_CHOICE });
|
|
99
|
+
}
|
|
100
|
+
const choosing = typeof asked === 'string' && asked.trim() !== '';
|
|
101
|
+
// An account linked through the GitHub facade has no agent connection to
|
|
102
|
+
// record a choice on.
|
|
103
|
+
if (choosing && (st.gh || !deps.agents || !req.userEmail)) {
|
|
104
|
+
return void res.status(400).json({ error: NOT_AN_AGENT_CHOICE });
|
|
105
|
+
}
|
|
68
106
|
try {
|
|
107
|
+
const agentPath = choosing && deps.agents && req.userEmail ? await deps.agents.choosable(req.userEmail, asked as string) : null;
|
|
108
|
+
if (choosing && agentPath === null) return void res.status(400).json({ error: NOT_AN_AGENT_CHOICE });
|
|
69
109
|
const { redirectTo } = st.gh && deps.facade
|
|
70
110
|
? await deps.facade.completeConsent(userId, st)
|
|
71
|
-
: await deps.provider.issueAuthCode(userId, st);
|
|
111
|
+
: await deps.provider.issueAuthCode(userId, st, agentPath);
|
|
72
112
|
res.json({ redirectTo });
|
|
73
113
|
} catch (err) {
|
|
74
114
|
// Message only — the raw error object can carry sensitive context
|
|
@@ -13,6 +13,8 @@ import { GUIDE_FIRST_SENTENCE } from '../../tool-registry/guide-first.js';
|
|
|
13
13
|
import { registerPluginsTools, CREATE_PLUGIN, myPluginDef, myPluginDescription } from '../plugins.tools.js';
|
|
14
14
|
import { createPluginCreationRoutes } from '../plugins.routes.js';
|
|
15
15
|
import { PluginProvisionError } from '../plugin-provision.service.js';
|
|
16
|
+
import type { IAgentScopeResolver } from '../../agent-scope/index.js';
|
|
17
|
+
import { agentFile, agentScopeOver, inMemoryKb } from '../../agent-scope/__tests__/in-memory-kb.js';
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* The plugin tools are DESCRIPTIONS of the app's own creation endpoints.
|
|
@@ -200,7 +202,12 @@ describe('the creation endpoints behind the key-or-session gate', () => {
|
|
|
200
202
|
|
|
201
203
|
const externalApiKeyService = {
|
|
202
204
|
looksLikeExternalApiKey: (t: string) => typeof t === 'string' && t.startsWith('bevel_'),
|
|
203
|
-
verifyAndLoadToken: async (t: string) =>
|
|
205
|
+
verifyAndLoadToken: async (t: string) =>
|
|
206
|
+
t === 'bevel_alice'
|
|
207
|
+
? { user: ALICE, tokenId: 'tok-a' }
|
|
208
|
+
: t === 'bevel_alice_agent'
|
|
209
|
+
? { user: ALICE, tokenId: 'tok-b', agentPath: 'Agents/plugin-maker.agent' }
|
|
210
|
+
: null,
|
|
204
211
|
} as never;
|
|
205
212
|
const authService = {
|
|
206
213
|
getUserById: async (id: string) => (id === ALICE.id ? ALICE : null),
|
|
@@ -211,7 +218,7 @@ describe('the creation endpoints behind the key-or-session gate', () => {
|
|
|
211
218
|
},
|
|
212
219
|
} as never;
|
|
213
220
|
|
|
214
|
-
async function start(): Promise<string> {
|
|
221
|
+
async function start(agentScope?: Pick<IAgentScopeResolver, 'resolve'>): Promise<string> {
|
|
215
222
|
const internalToken = new InternalTokenService({ secret: 'test-secret' });
|
|
216
223
|
const app = express();
|
|
217
224
|
app.use(express.json());
|
|
@@ -224,6 +231,7 @@ describe('the creation endpoints behind the key-or-session gate', () => {
|
|
|
224
231
|
}),
|
|
225
232
|
createPluginCreationRoutes({ ensurePersonalPlugin, createPlugin } as never, async (req) =>
|
|
226
233
|
req.userId ? ((await (authService as { getUserById(id: string): Promise<unknown> }).getUserById(req.userId)) as never) : null,
|
|
234
|
+
agentScope,
|
|
227
235
|
),
|
|
228
236
|
);
|
|
229
237
|
httpServer = await new Promise<HttpServer>((r) => {
|
|
@@ -302,4 +310,32 @@ describe('the creation endpoints behind the key-or-session gate', () => {
|
|
|
302
310
|
);
|
|
303
311
|
expect(createPlugin).not.toHaveBeenCalled();
|
|
304
312
|
});
|
|
313
|
+
|
|
314
|
+
it("refuses a connection made as an .agent that does not list the tool, by name, before anything is provisioned", async () => {
|
|
315
|
+
// The agent lists `my_plugin` only. These routes are not hosted by
|
|
316
|
+
// `toolHandler`, so they make its connection-scope check themselves.
|
|
317
|
+
const kb = inMemoryKb(
|
|
318
|
+
{ 'Agents/plugin-maker.agent': agentFile('name: Plugin maker\nplatformTools: [my_plugin]') },
|
|
319
|
+
{ reader: ALICE.email },
|
|
320
|
+
);
|
|
321
|
+
const base = await start(agentScopeOver(kb));
|
|
322
|
+
const refused = await post(base, '/api/plugins', { name: 'GTM' }, 'bevel_alice_agent');
|
|
323
|
+
expect(refused.status).toBe(403);
|
|
324
|
+
expect(await refused.json()).toEqual({
|
|
325
|
+
error: expect.stringContaining('The tool "create_plugin" is not available to this connection'),
|
|
326
|
+
code: 'not_available_to_connection',
|
|
327
|
+
});
|
|
328
|
+
expect(createPlugin).not.toHaveBeenCalled();
|
|
329
|
+
|
|
330
|
+
// The tool it lists is served; a key made as Everything and a session are untouched.
|
|
331
|
+
expect((await post(base, '/api/plugins/personal', {}, 'bevel_alice_agent')).status).toBe(200);
|
|
332
|
+
expect((await post(base, '/api/plugins', { name: 'Ops' }, 'bevel_alice')).status).toBe(201);
|
|
333
|
+
expect((await post(base, '/api/plugins', { name: 'Fin' }, 'session-alice')).status).toBe(201);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('a narrowed connection with no scope resolver wired loads nothing, never Everything', async () => {
|
|
337
|
+
const base = await start();
|
|
338
|
+
expect((await post(base, '/api/plugins/personal', {}, 'bevel_alice_agent')).status).toBe(403);
|
|
339
|
+
expect(ensurePersonalPlugin).not.toHaveBeenCalled();
|
|
340
|
+
});
|
|
305
341
|
});
|
|
@@ -19,7 +19,7 @@ export {
|
|
|
19
19
|
type JoinRequestStatus,
|
|
20
20
|
type JoinRequestStore,
|
|
21
21
|
} from './join-request-records.store.js';
|
|
22
|
-
export { PluginLinkIndex, type LinkMembership, type PluginLinks } from './plugin-links.js';
|
|
22
|
+
export { PluginLinkIndex, type IPluginLinkIndex, type LinkMembership, type PluginLinks } from './plugin-links.js';
|
|
23
23
|
export { PluginLinksService, PluginLinkError } from './plugin-links.service.js';
|
|
24
24
|
export { PluginRenameService, PluginRenameError, renamePluginPrincipalInText } from './plugin-rename.service.js';
|
|
25
25
|
export { compileMarketplace, type VirtualTree, type CompileInput, type MarketplaceOptions } from './compile/compile-marketplace.js';
|
|
@@ -58,10 +58,27 @@ export const LINK_GRANT_VERBS = ['read', 'write'] as const;
|
|
|
58
58
|
export interface LinkMembership {
|
|
59
59
|
/** skill path → every plugin that holds it, inline or by link. */
|
|
60
60
|
bySkill: Map<string, PluginMembership[]>;
|
|
61
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* plugin identity (`PluginLinks.name`) → its links. Every plugin appears,
|
|
63
|
+
* linked or not. A name and a folder may differ, so a repository path is
|
|
64
|
+
* read from the value's `folder`, never from the key.
|
|
65
|
+
*/
|
|
62
66
|
byPlugin: Map<string, PluginLinks>;
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
/**
|
|
70
|
+
* What a reader of the link index asks it — the contract a consumer outside
|
|
71
|
+
* this module (a distribution's agent-scope resolver, through
|
|
72
|
+
* `CorePorts.agentScope`) is written against. Dropping the cache is the
|
|
73
|
+
* index owner's affair, not a reader's, so it is not here.
|
|
74
|
+
*/
|
|
75
|
+
export interface IPluginLinkIndex {
|
|
76
|
+
/** Every plugin's links and every skill's memberships, from the released catalog. */
|
|
77
|
+
membership(): Promise<LinkMembership>;
|
|
78
|
+
/** The memberships of one skill (empty when it is in no plugin). */
|
|
79
|
+
pluginsOf(skillPath: string): Promise<PluginMembership[]>;
|
|
80
|
+
}
|
|
81
|
+
|
|
65
82
|
/**
|
|
66
83
|
* The link index: which shared skills each plugin links, and — the other way
|
|
67
84
|
* round — which plugins each skill belongs to, whether by sitting inside the
|
|
@@ -88,7 +105,7 @@ export interface LinkMembership {
|
|
|
88
105
|
* subscriber that drops the plugin index, so a link committed on the default
|
|
89
106
|
* branch shows within one round-trip.
|
|
90
107
|
*/
|
|
91
|
-
export class PluginLinkIndex {
|
|
108
|
+
export class PluginLinkIndex implements IPluginLinkIndex {
|
|
92
109
|
private readonly cache: TtlCache<LinkMembership>;
|
|
93
110
|
|
|
94
111
|
constructor(
|
|
@@ -17,6 +17,8 @@ import { pluginFolderBelowRoot } from './plugins.service.js';
|
|
|
17
17
|
import type { KbContext } from '../../shared/kb-context.js';
|
|
18
18
|
import { PluginProvisionError, type PluginProvisionService } from './plugin-provision.service.js';
|
|
19
19
|
import { argumentsRefusal } from '../tool-helpers/index.js';
|
|
20
|
+
import { resolveConnectionScope, type IAgentScopeResolver } from '../agent-scope/index.js';
|
|
21
|
+
import '../tool-auth/index.js'; // Express Request.toolAuth augmentation
|
|
20
22
|
import { PluginLinkError, type PluginLinksService } from './plugin-links.service.js';
|
|
21
23
|
import { PluginRenameError, type PluginRenameService } from './plugin-rename.service.js';
|
|
22
24
|
import type { JoinRequestsService } from './join-requests.service.js';
|
|
@@ -83,6 +85,29 @@ function refuseMismatchedArguments(req: express.Request, res: express.Response):
|
|
|
83
85
|
return true;
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Answer the connection-scope refusal when the caller's connection was made
|
|
90
|
+
* as an agent configuration that does not list `toolName`; `true` when it answered.
|
|
91
|
+
* These routes ARE the `create_plugin` and `my_plugin` tools but are not
|
|
92
|
+
* hosted by `toolHandler`, so they ask the one policy it asks —
|
|
93
|
+
* `ConnectionScope.platformToolRefusal` — and answer what it answers. A
|
|
94
|
+
* session, or a key made as Everything, carries no `agentPath` and is never
|
|
95
|
+
* refused.
|
|
96
|
+
*/
|
|
97
|
+
async function refuseOutsideConnectionScope(
|
|
98
|
+
req: express.Request,
|
|
99
|
+
res: express.Response,
|
|
100
|
+
user: AuthUser,
|
|
101
|
+
toolName: string,
|
|
102
|
+
agentScope: Pick<IAgentScopeResolver, 'resolve'> | undefined,
|
|
103
|
+
): Promise<boolean> {
|
|
104
|
+
const scope = await resolveConnectionScope(agentScope, user.email, req.toolAuth?.agentPath);
|
|
105
|
+
const refusal = scope.platformToolRefusal(toolName);
|
|
106
|
+
if (!refusal) return false;
|
|
107
|
+
res.status(403).json(refusal);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
86
111
|
/**
|
|
87
112
|
* The two doors through which plugin folders come to exist — ONE
|
|
88
113
|
* implementation each, for the app and for agents alike:
|
|
@@ -106,6 +131,8 @@ function refuseMismatchedArguments(req: express.Request, res: express.Response):
|
|
|
106
131
|
export function createPluginCreationRoutes(
|
|
107
132
|
provision: Pick<PluginProvisionService, 'createPlugin' | 'ensurePersonalPlugin'>,
|
|
108
133
|
resolveUser: (req: express.Request) => Promise<AuthUser | null>,
|
|
134
|
+
/** Resolves a connection's agent choice to its scope; absent, a narrowed connection loads nothing. */
|
|
135
|
+
agentScope?: Pick<IAgentScopeResolver, 'resolve'>,
|
|
109
136
|
): express.Router {
|
|
110
137
|
const router = express.Router();
|
|
111
138
|
|
|
@@ -117,8 +144,10 @@ export function createPluginCreationRoutes(
|
|
|
117
144
|
}
|
|
118
145
|
// These two routes ARE the `create_plugin` and `my_plugin` tools, so an
|
|
119
146
|
// agent's call is checked against what those tools declare, exactly as a
|
|
120
|
-
// route-hosted tool's is in `toolHandler`: a call that does not match
|
|
121
|
-
//
|
|
147
|
+
// route-hosted tool's is in `toolHandler`: a call that does not match, or
|
|
148
|
+
// a connection whose agent does not list the tool, is refused before
|
|
149
|
+
// anything is provisioned.
|
|
150
|
+
if (await refuseOutsideConnectionScope(req, res, user, 'create_plugin', agentScope)) return;
|
|
122
151
|
if (refuseMismatchedArguments(req, res)) return;
|
|
123
152
|
// `req.body` is undefined when no JSON body was sent at all — that is a
|
|
124
153
|
// 400, not a destructuring crash.
|
|
@@ -155,8 +184,10 @@ export function createPluginCreationRoutes(
|
|
|
155
184
|
}
|
|
156
185
|
// These two routes ARE the `create_plugin` and `my_plugin` tools, so an
|
|
157
186
|
// agent's call is checked against what those tools declare, exactly as a
|
|
158
|
-
// route-hosted tool's is in `toolHandler`: a call that does not match
|
|
159
|
-
//
|
|
187
|
+
// route-hosted tool's is in `toolHandler`: a call that does not match, or
|
|
188
|
+
// a connection whose agent does not list the tool, is refused before
|
|
189
|
+
// anything is provisioned.
|
|
190
|
+
if (await refuseOutsideConnectionScope(req, res, user, 'my_plugin', agentScope)) return;
|
|
160
191
|
if (refuseMismatchedArguments(req, res)) return;
|
|
161
192
|
try {
|
|
162
193
|
res.json(await provision.ensurePersonalPlugin(user));
|
|
@@ -3,6 +3,7 @@ import express from 'express';
|
|
|
3
3
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
4
4
|
import { createSecretsVaultRoutes } from '../secrets-vault.routes.js';
|
|
5
5
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
6
|
+
import { ConnectionScope } from '../../agent-scope/index.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* The aggregated `/connect/pending` surface: everything standing between the
|
|
@@ -336,6 +337,118 @@ describe('GET /api/connect/pending — readability', () => {
|
|
|
336
337
|
});
|
|
337
338
|
});
|
|
338
339
|
|
|
340
|
+
describe('GET /api/connect/pending — narrowed to the agent the connection is made as', () => {
|
|
341
|
+
/**
|
|
342
|
+
* The consent step's "Connect as" choice: a connection made as an `.agent`
|
|
343
|
+
* file loads only the connectors the file lists, so the page that asks for
|
|
344
|
+
* sign-ins and keys lists only those. Two readable tools, one of which the
|
|
345
|
+
* agent loads; the agent is answered by a stub resolver over the REAL
|
|
346
|
+
* `ConnectionScope`, the one answer every agent surface asks.
|
|
347
|
+
*/
|
|
348
|
+
const AGENT = { path: 'Agents/ops.agent', name: 'ops' };
|
|
349
|
+
const resolver = {
|
|
350
|
+
resolve: async (_email: string | undefined, agentPath: string | null | undefined) => {
|
|
351
|
+
if (!agentPath) return ConnectionScope.everything();
|
|
352
|
+
if (agentPath === AGENT.path) {
|
|
353
|
+
return ConnectionScope.narrowed(AGENT, { skills: [], connectors: ['Tools/weather.tool'], platformTools: null }, 'fp');
|
|
354
|
+
}
|
|
355
|
+
return ConnectionScope.gone(agentPath);
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
const twoTools = {
|
|
359
|
+
listAccessible: async () => [
|
|
360
|
+
{
|
|
361
|
+
slug: 'weather',
|
|
362
|
+
name: 'weather',
|
|
363
|
+
path: 'Tools/weather.tool',
|
|
364
|
+
type: 'http' as const,
|
|
365
|
+
variables: [{ name: 'API_KEY', scope: 'user' as const, label: null }],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
slug: 'notion',
|
|
369
|
+
name: 'notion',
|
|
370
|
+
path: 'Tools/notion.tool',
|
|
371
|
+
type: 'mcp' as const,
|
|
372
|
+
variables: [
|
|
373
|
+
{
|
|
374
|
+
name: 'MCP_OAUTH',
|
|
375
|
+
scope: 'user' as const,
|
|
376
|
+
label: 'notion sign-in',
|
|
377
|
+
oauth: { authorizationUrl: 'https://a.example/auth', tokenUrl: 'https://a.example/token', clientId: 'cid' },
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
} as unknown as Parameters<typeof createSecretsVaultRoutes>[0]['toolManualService'];
|
|
383
|
+
const vault = {
|
|
384
|
+
statusFor: async (_u: string, keys: string[]) =>
|
|
385
|
+
keys.map((key) => ({
|
|
386
|
+
key,
|
|
387
|
+
adminConfigured: key === 'notion_MCP_OAUTH',
|
|
388
|
+
adminKind: key === 'notion_MCP_OAUTH' ? ('oauth' as const) : null,
|
|
389
|
+
userConfigured: false,
|
|
390
|
+
userKind: null,
|
|
391
|
+
})),
|
|
392
|
+
// A sign-in registered directly on the Secrets page: nobody's connector.
|
|
393
|
+
list: async () => [
|
|
394
|
+
{ id: 's-own', key: 'MY_TOKEN', kind: 'oauth' as const, label: 'Mine', authorized: false, createdAt: new Date(), updatedAt: new Date() },
|
|
395
|
+
],
|
|
396
|
+
} as unknown as Parameters<typeof createSecretsVaultRoutes>[0]['secretsVault'];
|
|
397
|
+
|
|
398
|
+
async function pendingAs(agent: string | null, withResolver = true) {
|
|
399
|
+
const app = express();
|
|
400
|
+
app.use(express.json());
|
|
401
|
+
app.use((req, _res, next) => {
|
|
402
|
+
req.userId = 'u1';
|
|
403
|
+
req.userEmail = 'a@x.com';
|
|
404
|
+
next();
|
|
405
|
+
});
|
|
406
|
+
app.use(
|
|
407
|
+
'/api',
|
|
408
|
+
createSecretsVaultRoutes({
|
|
409
|
+
kb: testKbContext(),
|
|
410
|
+
secretsVault: vault,
|
|
411
|
+
toolManualService: twoTools,
|
|
412
|
+
accessControl,
|
|
413
|
+
...(withResolver ? { agents: resolver } : {}),
|
|
414
|
+
connectionProbe: {
|
|
415
|
+
probe: async () => ({ status: 'unverifiable' as const, detail: null, checkedAt: new Date() }),
|
|
416
|
+
},
|
|
417
|
+
stateSecret: 'test-secret',
|
|
418
|
+
publicBackendUrl: 'http://localhost:3000',
|
|
419
|
+
publicFrontendUrl: 'http://localhost:5173',
|
|
420
|
+
}),
|
|
421
|
+
);
|
|
422
|
+
httpServer = await new Promise<HttpServer>((resolve) => {
|
|
423
|
+
const s = app.listen(0, () => resolve(s));
|
|
424
|
+
});
|
|
425
|
+
const port = (httpServer.address() as { port: number }).port;
|
|
426
|
+
const query = agent === null ? '' : `?agent=${encodeURIComponent(agent)}`;
|
|
427
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/connect/pending${query}`);
|
|
428
|
+
expect(res.status).toBe(200);
|
|
429
|
+
const body = (await res.json()) as PendingBody & { toolOAuth: { slug: string }[] };
|
|
430
|
+
return { tools: body.tools.map((t) => t.slug), signIns: body.toolOAuth.map((o) => o.slug), standalone: body.oauth.map((o) => o.id) };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
it('lists everything without a choice, as before', async () => {
|
|
434
|
+
expect(await pendingAs(null)).toEqual({ tools: ['weather'], signIns: ['notion'], standalone: ['s-own'] });
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('lists only the connectors the agent loads, and no standalone sign-in', async () => {
|
|
438
|
+
expect(await pendingAs(AGENT.path)).toEqual({ tools: ['weather'], signIns: [], standalone: [] });
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('lists nothing for an agent the person cannot use: a narrowing that cannot be read is never Everything', async () => {
|
|
442
|
+
expect(await pendingAs('Agents/gone.agent')).toEqual({ tools: [], signIns: [], standalone: [] });
|
|
443
|
+
// …and the same with no resolver wired at all.
|
|
444
|
+
expect(await pendingAs(AGENT.path, false)).toEqual({ tools: [], signIns: [], standalone: [] });
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('reads an empty or blank choice as Everything', async () => {
|
|
448
|
+
expect(await pendingAs(' ')).toEqual({ tools: ['weather'], signIns: ['notion'], standalone: ['s-own'] });
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
339
452
|
describe('GET /api/connect/pending — OAuth scope coverage', () => {
|
|
340
453
|
// A tool with an OAuth-backed var that now requires two scopes.
|
|
341
454
|
const oauthTool = {
|