@bevel-software/platform-core-backend 0.19.0 → 0.21.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/dist/core/catalog-cache-invalidation.d.ts +7 -2
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
- package/dist/core/catalog-cache-invalidation.js +8 -9
- package/dist/core/catalog-cache-invalidation.js.map +1 -1
- package/dist/core/core-ports.d.ts +19 -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 +2 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +45 -105
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +33 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +146 -82
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +74 -28
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +157 -20
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/public-config.d.ts +5 -1
- package/dist/core/public-config.d.ts.map +1 -1
- package/dist/core/public-config.js +7 -5
- package/dist/core/public-config.js.map +1 -1
- package/dist/core-config.d.ts +95 -1
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +24 -8
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +18 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +19 -8
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +2 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +10 -10
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/change-read-gate.d.ts +6 -3
- package/dist/modules/access/change-read-gate.d.ts.map +1 -1
- package/dist/modules/access/change-read-gate.js +9 -6
- package/dist/modules/access/change-read-gate.js.map +1 -1
- package/dist/modules/access/creator-access.d.ts +4 -2
- package/dist/modules/access/creator-access.d.ts.map +1 -1
- package/dist/modules/access/creator-access.js +7 -4
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/agent-instructions/read-preamble.d.ts +2 -1
- package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -1
- package/dist/modules/agent-instructions/read-preamble.js +6 -9
- package/dist/modules/agent-instructions/read-preamble.js.map +1 -1
- package/dist/modules/audit/agent-audit.service.d.ts +54 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -0
- package/dist/modules/audit/agent-audit.service.js +290 -0
- package/dist/modules/audit/agent-audit.service.js.map +1 -0
- package/dist/modules/audit/agent-rest-audit.middleware.d.ts +40 -0
- package/dist/modules/audit/agent-rest-audit.middleware.d.ts.map +1 -0
- package/dist/modules/audit/agent-rest-audit.middleware.js +75 -0
- package/dist/modules/audit/agent-rest-audit.middleware.js.map +1 -0
- package/dist/modules/audit/audit.contract.d.ts +145 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -0
- package/dist/modules/audit/audit.contract.js +35 -0
- package/dist/modules/audit/audit.contract.js.map +1 -0
- package/dist/modules/audit/audit.routes.d.ts +21 -0
- package/dist/modules/audit/audit.routes.d.ts.map +1 -0
- package/dist/modules/audit/audit.routes.js +157 -0
- package/dist/modules/audit/audit.routes.js.map +1 -0
- package/dist/modules/audit/event-classifier.d.ts +52 -0
- package/dist/modules/audit/event-classifier.d.ts.map +1 -0
- package/dist/modules/audit/event-classifier.js +123 -0
- package/dist/modules/audit/event-classifier.js.map +1 -0
- package/dist/modules/audit/index.d.ts +7 -0
- package/dist/modules/audit/index.d.ts.map +1 -0
- package/dist/modules/audit/index.js +7 -0
- package/dist/modules/audit/index.js.map +1 -0
- package/dist/modules/audit/request-audit.d.ts +57 -0
- package/dist/modules/audit/request-audit.d.ts.map +1 -0
- package/dist/modules/audit/request-audit.js +117 -0
- package/dist/modules/audit/request-audit.js.map +1 -0
- package/dist/modules/auth/account-admission.d.ts +40 -0
- package/dist/modules/auth/account-admission.d.ts.map +1 -0
- package/dist/modules/auth/account-admission.js +17 -0
- package/dist/modules/auth/account-admission.js.map +1 -0
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +4 -1
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +19 -1
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +33 -1
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +9 -0
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +10 -2
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/advisory-lock.d.ts +17 -0
- package/dist/modules/database/advisory-lock.d.ts.map +1 -1
- package/dist/modules/database/advisory-lock.js +24 -3
- package/dist/modules/database/advisory-lock.js.map +1 -1
- package/dist/modules/database/connection.d.ts +67 -2
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +118 -12
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +414 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +99 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts +1 -1
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +27 -3
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/diff/diff.service.d.ts.map +1 -1
- package/dist/modules/diff/diff.service.js +6 -1
- package/dist/modules/diff/diff.service.js.map +1 -1
- package/dist/modules/kb-fs/branch-name.d.ts +0 -1
- package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
- package/dist/modules/kb-fs/branch-name.js +3 -3
- package/dist/modules/kb-fs/branch-name.js.map +1 -1
- package/dist/modules/kb-fs/clone-config.d.ts +9 -8
- package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
- package/dist/modules/kb-fs/clone-config.js +14 -12
- package/dist/modules/kb-fs/clone-config.js.map +1 -1
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +9 -1
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/git-guarded-filesystem.js +16 -3
- package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +1 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +32 -0
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +8 -1
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +16 -1
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +52 -9
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +59 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +180 -8
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
- package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/native.source.js +4 -2
- package/dist/modules/plugins/discovery/native.source.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 +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.js +6 -6
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
- package/dist/modules/plugins/join-requests.service.d.ts +3 -1
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +9 -8
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +65 -10
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.js +136 -31
- package/dist/modules/plugins/plugin-links.js.map +1 -1
- package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.service.js +151 -21
- package/dist/modules/plugins/plugin-links.service.js.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +33 -26
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.js +10 -12
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +3 -0
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +45 -15
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +4 -4
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +7 -10
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/plugins/teams.routes.d.ts +4 -1
- package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
- package/dist/modules/plugins/teams.routes.js +11 -9
- package/dist/modules/plugins/teams.routes.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-variable-loader.d.ts +29 -11
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
- package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +22 -10
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +52 -28
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +9 -2
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +20 -14
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
- package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
- package/dist/modules/skills/allowed-tools-check.js +8 -8
- package/dist/modules/skills/allowed-tools-check.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +3 -1
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +9 -6
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +2 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.js +6 -6
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +4 -1
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +4 -4
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +4 -2
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +13 -11
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +21 -5
- 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 +22 -9
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +10 -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 +8 -0
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +9 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
- package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
- package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
- package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +9 -9
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +11 -5
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +23 -14
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +35 -5
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
- package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
- package/dist/modules/workflow/session-ontology.service.js +6 -2
- package/dist/modules/workflow/session-ontology.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +8 -3
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +33 -23
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.js +3 -3
- package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
- package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-git.js +15 -13
- package/dist/modules/workspace/startup/kb-git.js.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +5 -8
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +35 -38
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +10 -6
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts +2 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +8 -7
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +26 -12
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +41 -28
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +2 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +69 -31
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +3 -2
- package/dist/modules/workspace/write-denial.d.ts.map +1 -1
- package/dist/modules/workspace/write-denial.js +6 -5
- package/dist/modules/workspace/write-denial.js.map +1 -1
- package/dist/shared/git-internals.d.ts +7 -1
- package/dist/shared/git-internals.d.ts.map +1 -1
- package/dist/shared/git-internals.js +67 -9
- package/dist/shared/git-internals.js.map +1 -1
- package/dist/shared/git.contract.d.ts +52 -7
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +30 -8
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/kb-context.d.ts +76 -0
- package/dist/shared/kb-context.d.ts.map +1 -0
- package/dist/shared/kb-context.js +108 -0
- package/dist/shared/kb-context.js.map +1 -0
- package/dist/shared/kb-layout.d.ts +3 -2
- package/dist/shared/kb-layout.d.ts.map +1 -1
- package/dist/shared/kb-layout.js +4 -3
- package/dist/shared/kb-layout.js.map +1 -1
- package/dist/shared/kb-layout.test.js +35 -27
- package/dist/shared/kb-layout.test.js.map +1 -1
- package/dist/shared/pending-proposals.d.ts +4 -1
- package/dist/shared/pending-proposals.d.ts.map +1 -1
- package/dist/shared/pending-proposals.js +3 -4
- package/dist/shared/pending-proposals.js.map +1 -1
- package/dist/shared/redact-secret.d.ts +5 -5
- package/dist/shared/redact-secret.js +5 -5
- package/dist/tenancy/index.d.ts +13 -0
- package/dist/tenancy/index.d.ts.map +1 -0
- package/dist/tenancy/index.js +13 -0
- package/dist/tenancy/index.js.map +1 -0
- package/dist/tenancy/static-tenant-source.d.ts +98 -0
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
- package/dist/tenancy/static-tenant-source.js +220 -0
- package/dist/tenancy/static-tenant-source.js.map +1 -0
- package/dist/tenancy/tenant-host.d.ts +80 -0
- package/dist/tenancy/tenant-host.d.ts.map +1 -0
- package/dist/tenancy/tenant-host.js +217 -0
- package/dist/tenancy/tenant-host.js.map +1 -0
- package/dist/tenancy/tenant-runtime.d.ts +91 -0
- package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
- package/dist/tenancy/tenant-runtime.js +174 -0
- package/dist/tenancy/tenant-runtime.js.map +1 -0
- package/dist/tenancy/tenant-secrets.d.ts +24 -0
- package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
- package/dist/tenancy/tenant-secrets.js +30 -0
- package/dist/tenancy/tenant-secrets.js.map +1 -0
- package/dist/tenancy/tenant-source.contract.d.ts +45 -0
- package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
- package/dist/tenancy/tenant-source.contract.js +21 -0
- package/dist/tenancy/tenant-source.contract.js.map +1 -0
- package/migrations/0000_core_init.sql +14 -14
- package/migrations/0003_deployment_settings.sql +1 -1
- package/migrations/0005_github_facade.sql +1 -1
- package/migrations/0012_agent_audit.sql +41 -0
- package/migrations/0013_agent_identity_and_key_soft_delete.sql +29 -0
- package/migrations/meta/0012_snapshot.json +2247 -0
- package/migrations/meta/0013_snapshot.json +2259 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +4 -4
- package/src/__tests__/branch-model.test.ts +7 -6
- package/src/__tests__/kb-context.ts +30 -0
- package/src/__tests__/kb-layout-config.test.ts +35 -30
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
- package/src/core/__tests__/lifecycle.test.ts +78 -1
- package/src/core/__tests__/public-config.test.ts +5 -1
- package/src/core/catalog-cache-invalidation.ts +16 -11
- package/src/core/core-ports.ts +19 -0
- package/src/core/create-core-server.ts +55 -106
- package/src/core/create-core-services.ts +193 -95
- package/src/core/lifecycle.ts +214 -29
- package/src/core/public-config.ts +8 -5
- package/src/core-config.ts +107 -8
- package/src/index.ts +25 -2
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
- package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
- package/src/modules/access/__tests__/creator-access.test.ts +3 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
- package/src/modules/access/access-control.service.ts +20 -6
- package/src/modules/access/access.routes.ts +10 -13
- package/src/modules/access/change-read-gate.ts +10 -4
- package/src/modules/access/creator-access.ts +7 -2
- package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
- package/src/modules/agent-instructions/read-preamble.ts +10 -12
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +370 -0
- package/src/modules/audit/__tests__/agent-rest-audit.middleware.test.ts +113 -0
- package/src/modules/audit/__tests__/audit.routes.test.ts +201 -0
- package/src/modules/audit/__tests__/event-classifier.test.ts +121 -0
- package/src/modules/audit/agent-audit.service.ts +316 -0
- package/src/modules/audit/agent-rest-audit.middleware.ts +83 -0
- package/src/modules/audit/audit.contract.ts +164 -0
- package/src/modules/audit/audit.routes.ts +168 -0
- package/src/modules/audit/event-classifier.ts +155 -0
- package/src/modules/audit/index.ts +14 -0
- package/src/modules/audit/request-audit.ts +141 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +60 -0
- package/src/modules/auth/account-admission.ts +44 -0
- package/src/modules/auth/account.routes.ts +4 -1
- package/src/modules/auth/auth.service.ts +33 -1
- package/src/modules/auth/oidc-auth-provider.ts +9 -0
- package/src/modules/connection-probe/connection-probe.service.ts +7 -1
- package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
- package/src/modules/database/__tests__/connection.test.ts +98 -0
- package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
- package/src/modules/database/advisory-lock.ts +28 -4
- package/src/modules/database/connection.ts +143 -13
- package/src/modules/database/core-schema.ts +104 -0
- package/src/modules/database/migrate.ts +48 -14
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
- package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
- package/src/modules/diff/diff.service.ts +6 -1
- package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
- package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
- package/src/modules/kb-fs/branch-name.ts +3 -3
- package/src/modules/kb-fs/clone-config.ts +15 -12
- package/src/modules/kb-fs/git-guarded-filesystem.ts +16 -3
- package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +183 -13
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -2
- package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +24 -4
- package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +1 -1
- package/src/modules/mcp/__tests__/mcp.service.test.ts +120 -3
- package/src/modules/mcp/mcp-auth.middleware.ts +31 -0
- package/src/modules/mcp/mcp.routes.ts +11 -1
- package/src/modules/mcp/mcp.service.ts +73 -8
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +184 -6
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
- package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
- package/src/modules/plugins/__tests__/join-requests.service.test.ts +2 -1
- package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +11 -2
- package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
- package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
- package/src/modules/plugins/discovery/native.source.ts +4 -1
- package/src/modules/plugins/index.ts +1 -1
- package/src/modules/plugins/join-request-jobs.service.ts +8 -7
- package/src/modules/plugins/join-requests.service.ts +7 -7
- package/src/modules/plugins/plugin-links.service.ts +192 -19
- package/src/modules/plugins/plugin-links.ts +147 -27
- package/src/modules/plugins/plugin-provision.service.ts +54 -27
- package/src/modules/plugins/plugin-rename.service.ts +10 -10
- package/src/modules/plugins/plugins.routes.ts +45 -15
- package/src/modules/plugins/plugins.service.ts +7 -11
- package/src/modules/plugins/teams.routes.ts +16 -8
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
- package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
- package/src/modules/secrets-vault/index.ts +2 -0
- package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
- package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +59 -11
- package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
- package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
- package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
- package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
- package/src/modules/settings/deployment-settings.service.ts +68 -28
- package/src/modules/settings/setup.routes.ts +26 -16
- package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +2 -1
- package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
- package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
- package/src/modules/skills/allowed-tools-check.ts +7 -6
- package/src/modules/skills/pending-skills.service.ts +9 -8
- package/src/modules/skills/skill-access-requests.routes.ts +7 -7
- package/src/modules/skills/skills.routes.ts +4 -3
- package/src/modules/skills/skills.service.ts +13 -9
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +29 -8
- package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +6 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +22 -5
- package/src/modules/tool-auth/external-api-key.service.ts +25 -8
- package/src/modules/tool-auth/internal-token.service.ts +11 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +17 -1
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
- package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
- package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
- package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
- package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
- package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
- package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
- package/src/modules/workflow/__tests__/apply-failure.test.ts +2 -1
- package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
- package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
- package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +28 -27
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +2 -1
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +2 -1
- package/src/modules/workflow/agent-tools/workflow.tools.ts +11 -9
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
- package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
- package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
- package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
- package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
- package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
- package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
- package/src/modules/workflow/git/git.service.ts +24 -13
- package/src/modules/workflow/git/node-git-runner.ts +37 -4
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
- package/src/modules/workflow/session-ontology.service.ts +7 -2
- package/src/modules/workflow/workflow.service.ts +31 -24
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +144 -52
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
- package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
- package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +4 -3
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +9 -8
- package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
- package/src/modules/workspace/session-ontology.gate.ts +6 -4
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +29 -21
- package/src/modules/workspace/startup/kb-git.ts +15 -22
- package/src/modules/workspace/startup/kb-startup-runner.ts +40 -46
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
- package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
- package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
- package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
- package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
- package/src/modules/workspace/startup/steps/template-source.ts +8 -5
- package/src/modules/workspace/workspace.routes.ts +8 -7
- package/src/modules/workspace/workspace.service.ts +39 -27
- package/src/modules/workspace/workspace.tools.ts +68 -31
- package/src/modules/workspace/write-denial.ts +7 -5
- package/src/shared/__tests__/git-internals.test.ts +41 -0
- package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
- package/src/shared/git-internals.ts +61 -6
- package/src/shared/git.contract.ts +70 -8
- package/src/shared/kb-context.ts +130 -0
- package/src/shared/kb-layout.test.ts +37 -27
- package/src/shared/kb-layout.ts +4 -3
- package/src/shared/pending-proposals.ts +7 -4
- package/src/shared/redact-secret.ts +5 -5
- package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
- package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
- package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
- package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
- package/src/tenancy/index.ts +35 -0
- package/src/tenancy/static-tenant-source.ts +296 -0
- package/src/tenancy/tenant-host.ts +261 -0
- package/src/tenancy/tenant-runtime.ts +208 -0
- package/src/tenancy/tenant-secrets.ts +41 -0
- package/src/tenancy/tenant-source.contract.ts +59 -0
- package/dist/modules/access/access-errors.d.ts +0 -34
- package/dist/modules/access/access-errors.d.ts.map +0 -1
- package/dist/modules/access/access-errors.js +0 -40
- package/dist/modules/access/access-errors.js.map +0 -1
- package/dist/modules/access/access-splice.d.ts +0 -140
- package/dist/modules/access/access-splice.d.ts.map +0 -1
- package/dist/modules/access/access-splice.js +0 -389
- package/dist/modules/access/access-splice.js.map +0 -1
- package/dist/modules/access/group-files.d.ts +0 -83
- package/dist/modules/access/group-files.d.ts.map +0 -1
- package/dist/modules/access/group-files.js +0 -167
- package/dist/modules/access/group-files.js.map +0 -1
- package/dist/modules/access/kb-read-filter.d.ts +0 -41
- package/dist/modules/access/kb-read-filter.d.ts.map +0 -1
- package/dist/modules/access/kb-read-filter.js +0 -60
- package/dist/modules/access/kb-read-filter.js.map +0 -1
- package/dist/modules/access/render-roles-yaml.d.ts +0 -22
- package/dist/modules/access/render-roles-yaml.d.ts.map +0 -1
- package/dist/modules/access/render-roles-yaml.js +0 -56
- package/dist/modules/access/render-roles-yaml.js.map +0 -1
- package/dist/modules/access/roles-yaml-guard.d.ts +0 -56
- package/dist/modules/access/roles-yaml-guard.d.ts.map +0 -1
- package/dist/modules/access/roles-yaml-guard.js +0 -79
- package/dist/modules/access/roles-yaml-guard.js.map +0 -1
- package/dist/modules/diff/diff-paths.d.ts +0 -2
- package/dist/modules/diff/diff-paths.d.ts.map +0 -1
- package/dist/modules/diff/diff-paths.js +0 -9
- package/dist/modules/diff/diff-paths.js.map +0 -1
- package/dist/modules/groups/group-provision.service.d.ts +0 -130
- package/dist/modules/groups/group-provision.service.d.ts.map +0 -1
- package/dist/modules/groups/group-provision.service.js +0 -288
- package/dist/modules/groups/group-provision.service.js.map +0 -1
- package/dist/modules/groups/groups.contract.d.ts +0 -106
- package/dist/modules/groups/groups.contract.d.ts.map +0 -1
- package/dist/modules/groups/groups.contract.js +0 -36
- package/dist/modules/groups/groups.contract.js.map +0 -1
- package/dist/modules/groups/groups.routes.d.ts +0 -42
- package/dist/modules/groups/groups.routes.d.ts.map +0 -1
- package/dist/modules/groups/groups.routes.js +0 -379
- package/dist/modules/groups/groups.routes.js.map +0 -1
- package/dist/modules/groups/groups.service.d.ts +0 -60
- package/dist/modules/groups/groups.service.d.ts.map +0 -1
- package/dist/modules/groups/groups.service.js +0 -172
- package/dist/modules/groups/groups.service.js.map +0 -1
- package/dist/modules/groups/index.d.ts +0 -7
- package/dist/modules/groups/index.d.ts.map +0 -1
- package/dist/modules/groups/index.js +0 -6
- package/dist/modules/groups/index.js.map +0 -1
- package/dist/modules/groups/join-proposals.d.ts +0 -53
- package/dist/modules/groups/join-proposals.d.ts.map +0 -1
- package/dist/modules/groups/join-proposals.js +0 -67
- package/dist/modules/groups/join-proposals.js.map +0 -1
- package/dist/modules/groups/join-requests.service.d.ts +0 -81
- package/dist/modules/groups/join-requests.service.d.ts.map +0 -1
- package/dist/modules/groups/join-requests.service.js +0 -135
- package/dist/modules/groups/join-requests.service.js.map +0 -1
- package/dist/modules/mcp/mcp-session-store.d.ts +0 -74
- package/dist/modules/mcp/mcp-session-store.d.ts.map +0 -1
- package/dist/modules/mcp/mcp-session-store.js +0 -131
- package/dist/modules/mcp/mcp-session-store.js.map +0 -1
- package/dist/modules/workflow/file-change-notifier.d.ts +0 -38
- package/dist/modules/workflow/file-change-notifier.d.ts.map +0 -1
- package/dist/modules/workflow/file-change-notifier.js +0 -22
- package/dist/modules/workflow/file-change-notifier.js.map +0 -1
- package/dist/modules/workflow/git/branch-name.d.ts +0 -10
- package/dist/modules/workflow/git/branch-name.d.ts.map +0 -1
- package/dist/modules/workflow/git/branch-name.js +0 -76
- package/dist/modules/workflow/git/branch-name.js.map +0 -1
- package/dist/modules/workflow/git/clone-config.d.ts +0 -61
- package/dist/modules/workflow/git/clone-config.d.ts.map +0 -1
- package/dist/modules/workflow/git/clone-config.js +0 -69
- package/dist/modules/workflow/git/clone-config.js.map +0 -1
- package/dist/modules/workflow/git/mutex.d.ts +0 -11
- package/dist/modules/workflow/git/mutex.d.ts.map +0 -1
- package/dist/modules/workflow/git/mutex.js +0 -23
- package/dist/modules/workflow/git/mutex.js.map +0 -1
- package/dist/modules/workflow/locking-filesystem.d.ts +0 -137
- package/dist/modules/workflow/locking-filesystem.d.ts.map +0 -1
- package/dist/modules/workflow/locking-filesystem.js +0 -553
- package/dist/modules/workflow/locking-filesystem.js.map +0 -1
- package/dist/modules/workflow/read-only-filesystem.d.ts +0 -24
- package/dist/modules/workflow/read-only-filesystem.d.ts.map +0 -1
- package/dist/modules/workflow/read-only-filesystem.js +0 -39
- package/dist/modules/workflow/read-only-filesystem.js.map +0 -1
- package/dist/modules/workflow/workflow.errors.d.ts +0 -197
- package/dist/modules/workflow/workflow.errors.d.ts.map +0 -1
- package/dist/modules/workflow/workflow.errors.js +0 -298
- package/dist/modules/workflow/workflow.errors.js.map +0 -1
- package/dist/modules/workspace/bevel-ignore.d.ts +0 -30
- package/dist/modules/workspace/bevel-ignore.d.ts.map +0 -1
- package/dist/modules/workspace/bevel-ignore.js +0 -61
- package/dist/modules/workspace/bevel-ignore.js.map +0 -1
- package/dist/modules/workspace/kb-seed.interface.d.ts +0 -36
- package/dist/modules/workspace/kb-seed.interface.d.ts.map +0 -1
- package/dist/modules/workspace/kb-seed.interface.js +0 -2
- package/dist/modules/workspace/kb-seed.interface.js.map +0 -1
- package/dist/modules/workspace/kb-seed.service.d.ts +0 -125
- package/dist/modules/workspace/kb-seed.service.d.ts.map +0 -1
- package/dist/modules/workspace/kb-seed.service.js +0 -535
- package/dist/modules/workspace/kb-seed.service.js.map +0 -1
- package/dist/modules/workspace/plugins-migration.d.ts +0 -50
- package/dist/modules/workspace/plugins-migration.d.ts.map +0 -1
- package/dist/modules/workspace/plugins-migration.js +0 -379
- package/dist/modules/workspace/plugins-migration.js.map +0 -1
- package/dist/shared/fs-errors.d.ts +0 -8
- package/dist/shared/fs-errors.d.ts.map +0 -1
- package/dist/shared/fs-errors.js +0 -11
- package/dist/shared/fs-errors.js.map +0 -1
- package/dist/shared/fs-walk.d.ts +0 -23
- package/dist/shared/fs-walk.d.ts.map +0 -1
- package/dist/shared/fs-walk.js +0 -42
- package/dist/shared/fs-walk.js.map +0 -1
- package/dist/shared/hash-email.d.ts +0 -11
- package/dist/shared/hash-email.d.ts.map +0 -1
- package/dist/shared/hash-email.js +0 -14
- package/dist/shared/hash-email.js.map +0 -1
- package/dist/shared/kb-walk.d.ts +0 -52
- package/dist/shared/kb-walk.d.ts.map +0 -1
- package/dist/shared/kb-walk.js +0 -76
- package/dist/shared/kb-walk.js.map +0 -1
|
@@ -2,9 +2,12 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { InvalidGrantError, InvalidScopeError, InvalidTokenError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
4
4
|
import type { OAuthClientInformationFull } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
5
|
-
import {
|
|
5
|
+
import type { SQL } from 'drizzle-orm';
|
|
6
|
+
import { PgDialect } from 'drizzle-orm/pg-core';
|
|
7
|
+
import { BevelOAuthProvider, agentKeyFor } from '../oauth/bevel-oauth-provider.js';
|
|
6
8
|
import { signAuthRequest, verifyAuthRequest } from '../oauth/oauth-state.js';
|
|
7
9
|
import type { Database } from '../../database/connection.js';
|
|
10
|
+
import { agentConnections, oauthTokens } from '../../database/schema.js';
|
|
8
11
|
|
|
9
12
|
const STATE_SECRET = 'test-state-secret';
|
|
10
13
|
const PREFIX = 'bevel-mcp_';
|
|
@@ -12,9 +15,19 @@ const FRONTEND = 'https://app.example.com';
|
|
|
12
15
|
|
|
13
16
|
/** Same fake drizzle chain as external-api-key.service.test.ts: every
|
|
14
17
|
* chainable method returns the chain, the chain is thenable, and each
|
|
15
|
-
* db.insert/select/update pops the next queued result.
|
|
18
|
+
* db.insert/select/update pops the next queued result. What the provider
|
|
19
|
+
* WRITES and WHERE it writes it are captured too — `set`, `values`, the
|
|
20
|
+
* `update` target and every `where` predicate (rendered to SQL by
|
|
21
|
+
* {@link render}) — so a test can pin the row a statement lands on, not
|
|
22
|
+
* merely that a statement ran. */
|
|
16
23
|
function makeFakeDb(queue: any[]) {
|
|
17
|
-
const captured: {
|
|
24
|
+
const captured: {
|
|
25
|
+
values: any[];
|
|
26
|
+
set: any[];
|
|
27
|
+
where: SQL[];
|
|
28
|
+
updateTargets: unknown[];
|
|
29
|
+
onConflict: any[];
|
|
30
|
+
} = { values: [], set: [], where: [], updateTargets: [], onConflict: [] };
|
|
18
31
|
|
|
19
32
|
function nextChain() {
|
|
20
33
|
const result = queue.shift();
|
|
@@ -26,11 +39,13 @@ function makeFakeDb(queue: any[]) {
|
|
|
26
39
|
});
|
|
27
40
|
chain.values = passthrough((a) => captured.values.push(a[0]));
|
|
28
41
|
chain.set = passthrough((a) => captured.set.push(a[0]));
|
|
29
|
-
chain.where = passthrough();
|
|
42
|
+
chain.where = passthrough((a) => captured.where.push(a[0]));
|
|
30
43
|
chain.limit = passthrough();
|
|
31
44
|
chain.innerJoin = passthrough();
|
|
45
|
+
chain.leftJoin = passthrough();
|
|
32
46
|
chain.from = passthrough();
|
|
33
47
|
chain.returning = passthrough();
|
|
48
|
+
chain.onConflictDoNothing = passthrough((a) => captured.onConflict.push(a[0]));
|
|
34
49
|
chain.then = (onF: any, onR: any) => Promise.resolve(result).then(onF, onR);
|
|
35
50
|
return chain;
|
|
36
51
|
}
|
|
@@ -38,12 +53,23 @@ function makeFakeDb(queue: any[]) {
|
|
|
38
53
|
const db = {
|
|
39
54
|
insert: vi.fn(() => nextChain()),
|
|
40
55
|
select: vi.fn(() => nextChain()),
|
|
41
|
-
update: vi.fn(() =>
|
|
56
|
+
update: vi.fn((table: unknown) => {
|
|
57
|
+
captured.updateTargets.push(table);
|
|
58
|
+
return nextChain();
|
|
59
|
+
}),
|
|
60
|
+
// The mint-time prune of expired token rows; nothing here queues for it.
|
|
61
|
+
delete: vi.fn(() => nextChain()),
|
|
42
62
|
} as unknown as Database;
|
|
43
63
|
|
|
44
64
|
return { db, captured };
|
|
45
65
|
}
|
|
46
66
|
|
|
67
|
+
/** A captured predicate as the SQL it would send, with its parameters. */
|
|
68
|
+
function render(fragment: SQL): { sql: string; params: unknown[] } {
|
|
69
|
+
const q = new PgDialect().sqlToQuery(fragment);
|
|
70
|
+
return { sql: q.sql, params: q.params };
|
|
71
|
+
}
|
|
72
|
+
|
|
47
73
|
function makeProvider(queue: any[] = []) {
|
|
48
74
|
const { db, captured } = makeFakeDb(queue);
|
|
49
75
|
const provider = new BevelOAuthProvider({
|
|
@@ -183,7 +209,15 @@ describe('BevelOAuthProvider', () => {
|
|
|
183
209
|
clientId: 'client-1', userId: 'user-1', redirectUri: 'https://agent.example.com/callback',
|
|
184
210
|
scope: 'mcp', resource: null,
|
|
185
211
|
};
|
|
186
|
-
const { provider, captured } = makeProvider([
|
|
212
|
+
const { provider, captured } = makeProvider([
|
|
213
|
+
[consumed] /* update.returning */,
|
|
214
|
+
undefined /* prune: expired tokens */,
|
|
215
|
+
undefined /* prune: expired codes */,
|
|
216
|
+
[{ clientName: 'Claude' }] /* the client's registration */,
|
|
217
|
+
[] /* no live agent connection yet for (user, "claude") */,
|
|
218
|
+
[{ id: 'conn-1' }] /* connection insert.returning */,
|
|
219
|
+
undefined /* token insert */,
|
|
220
|
+
]);
|
|
187
221
|
|
|
188
222
|
const tokens = await provider.exchangeAuthorizationCode(
|
|
189
223
|
CLIENT, 'the-code', undefined, 'https://agent.example.com/callback',
|
|
@@ -194,15 +228,51 @@ describe('BevelOAuthProvider', () => {
|
|
|
194
228
|
expect(tokens.refresh_token!.startsWith(`${PREFIX}r_`)).toBe(true);
|
|
195
229
|
expect(tokens.expires_in).toBe(3600);
|
|
196
230
|
expect(tokens.scope).toBe('mcp');
|
|
197
|
-
//
|
|
198
|
-
|
|
231
|
+
// A first mint for (user, agent) makes the durable agent connection,
|
|
232
|
+
// keyed by the client's folded name and snapshotting the name shown…
|
|
233
|
+
expect(captured.values[0]).toEqual({ userId: 'user-1', clientId: 'client-1', clientName: 'Claude', agentKey: 'claude' });
|
|
234
|
+
// …looked up by that key, so a later registration of "Claude" by this
|
|
235
|
+
// person lands on the same row instead of making a sixth one.
|
|
236
|
+
// (where[0] consumed the code, [1]/[2] are the prunes, [3] read the client.)
|
|
237
|
+
expect(render(captured.where[4])).toMatchObject({ params: ['user-1', 'claude'] });
|
|
238
|
+
expect(render(captured.where[4]).sql).toMatch(/"agent_connections"."agent_key" = \$2/);
|
|
239
|
+
// …and the token row stores hashes of exactly what was returned, bound to it.
|
|
240
|
+
expect(captured.values[1]).toMatchObject({
|
|
199
241
|
accessTokenHash: sha256(tokens.access_token),
|
|
200
242
|
refreshTokenHash: sha256(tokens.refresh_token!),
|
|
201
243
|
clientId: 'client-1',
|
|
202
244
|
userId: 'user-1',
|
|
245
|
+
connectionId: 'conn-1',
|
|
203
246
|
});
|
|
204
247
|
});
|
|
205
248
|
|
|
249
|
+
it('a mint that loses the race for the first connection re-reads the winner instead of failing', async () => {
|
|
250
|
+
const consumed = { clientId: 'client-1', userId: 'user-1', redirectUri: 'https://agent.example.com/callback', scope: null, resource: null };
|
|
251
|
+
const { provider, captured } = makeProvider([
|
|
252
|
+
[consumed],
|
|
253
|
+
undefined, undefined, /* prunes */
|
|
254
|
+
[{ clientName: 'Claude' }],
|
|
255
|
+
[] /* no live connection at first look */,
|
|
256
|
+
[] /* insert: ON CONFLICT DO NOTHING returned no row */,
|
|
257
|
+
[{ id: 'conn-winner' }] /* the re-read finds the other mint's row */,
|
|
258
|
+
undefined /* token insert */,
|
|
259
|
+
]);
|
|
260
|
+
await provider.exchangeAuthorizationCode(CLIENT, 'the-code', undefined, 'https://agent.example.com/callback');
|
|
261
|
+
expect(captured.values[1]).toMatchObject({ connectionId: 'conn-winner' });
|
|
262
|
+
// The insert really did stand down on the conflict — on the live-agent
|
|
263
|
+
// index, not any other — rather than the fake merely answering "no row".
|
|
264
|
+
expect(captured.onConflict).toHaveLength(1);
|
|
265
|
+
expect(captured.onConflict[0].target).toEqual([agentConnections.userId, agentConnections.agentKey]);
|
|
266
|
+
expect(render(captured.onConflict[0].where).sql).toMatch(/"revoked_at" is null/);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('keys a connection by the folded client name, so re-registrations of one agent share a row; a nameless client keys by its id', () => {
|
|
270
|
+
expect(agentKeyFor('Claude', 'c-1')).toBe('claude');
|
|
271
|
+
expect(agentKeyFor(' Claude Code ', 'c-2')).toBe('claude code');
|
|
272
|
+
expect(agentKeyFor(null, 'c-3')).toBe('c-3');
|
|
273
|
+
expect(agentKeyFor(' ', 'c-4')).toBe('c-4');
|
|
274
|
+
});
|
|
275
|
+
|
|
206
276
|
it('exchangeAuthorizationCode rejects a spent/unknown code and a redirect_uri mismatch', async () => {
|
|
207
277
|
await expect(
|
|
208
278
|
makeProvider([[]]).provider.exchangeAuthorizationCode(CLIENT, 'spent'),
|
|
@@ -216,12 +286,30 @@ describe('BevelOAuthProvider', () => {
|
|
|
216
286
|
|
|
217
287
|
it('exchangeRefreshToken rotates (revokes old row, mints new pair) and only narrows scope', async () => {
|
|
218
288
|
const oldRow = {
|
|
219
|
-
clientId: 'client-1', userId: 'user-1', scope: 'mcp extra', resource: null,
|
|
289
|
+
clientId: 'client-1', userId: 'user-1', connectionId: 'conn-1', scope: 'mcp extra', resource: null,
|
|
220
290
|
refreshExpiresAt: new Date(Date.now() + 60_000),
|
|
221
291
|
};
|
|
222
|
-
const { provider } = makeProvider([
|
|
292
|
+
const { provider, captured, db } = makeProvider([
|
|
293
|
+
[oldRow] /* revoke.returning */,
|
|
294
|
+
[{ id: 'conn-1' }] /* the token's connection, still live */,
|
|
295
|
+
undefined /* the (throttled) use stamp */,
|
|
296
|
+
undefined, undefined, /* prunes */
|
|
297
|
+
undefined /* token insert */,
|
|
298
|
+
]);
|
|
223
299
|
const tokens = await provider.exchangeRefreshToken(CLIENT, 'bevel-mcp_r_old', ['mcp']);
|
|
224
300
|
expect(tokens.scope).toBe('mcp');
|
|
301
|
+
// A refresh STAYS on its token's connection: one connection spans every
|
|
302
|
+
// token the agent holds, nothing is looked up by (user, agent) and no
|
|
303
|
+
// second row is inserted for it.
|
|
304
|
+
expect(captured.values).toHaveLength(1);
|
|
305
|
+
expect(captured.values[0]).toMatchObject({ connectionId: 'conn-1' });
|
|
306
|
+
// The liveness check is a read of THAT connection, live rows only…
|
|
307
|
+
expect((db as any).select).toHaveBeenCalledTimes(1);
|
|
308
|
+
expect(render(captured.where[1])).toMatchObject({ params: ['conn-1'] });
|
|
309
|
+
expect(render(captured.where[1]).sql).toMatch(/"agent_connections"."revoked_at" is null/);
|
|
310
|
+
// …and the use is stamped separately, off the refresh's own path.
|
|
311
|
+
expect(captured.updateTargets).toEqual([oauthTokens, agentConnections]);
|
|
312
|
+
expect(Object.keys(captured.set[1])).toEqual(['lastUsedAt']);
|
|
225
313
|
|
|
226
314
|
// Widening is refused.
|
|
227
315
|
await expect(
|
|
@@ -234,18 +322,100 @@ describe('BevelOAuthProvider', () => {
|
|
|
234
322
|
).rejects.toBeInstanceOf(InvalidGrantError);
|
|
235
323
|
});
|
|
236
324
|
|
|
325
|
+
it('exchangeRefreshToken refuses a token whose agent connection was revoked, minting nothing', async () => {
|
|
326
|
+
// The race a revoke cannot otherwise win: the refresh consumed its old
|
|
327
|
+
// token a moment before the revoke landed. Looking the connection up
|
|
328
|
+
// afresh would find none live and make a new one; staying on the token's
|
|
329
|
+
// own connection finds it revoked and stops.
|
|
330
|
+
const oldRow = {
|
|
331
|
+
clientId: 'client-1', userId: 'user-1', connectionId: 'conn-1', scope: 'mcp', resource: null,
|
|
332
|
+
refreshExpiresAt: new Date(Date.now() + 60_000),
|
|
333
|
+
};
|
|
334
|
+
const { provider, captured } = makeProvider([[oldRow], [] /* connection revoked or gone */]);
|
|
335
|
+
await expect(provider.exchangeRefreshToken(CLIENT, 'bevel-mcp_r_old')).rejects.toBeInstanceOf(InvalidGrantError);
|
|
336
|
+
expect(captured.values).toHaveLength(0);
|
|
337
|
+
expect(captured.onConflict).toHaveLength(0);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('exchangeRefreshToken places a token from before connections existed under one, as a first mint would', async () => {
|
|
341
|
+
const oldRow = {
|
|
342
|
+
clientId: 'client-1', userId: 'user-1', connectionId: null, scope: 'mcp', resource: null,
|
|
343
|
+
refreshExpiresAt: new Date(Date.now() + 60_000),
|
|
344
|
+
};
|
|
345
|
+
const { provider, captured } = makeProvider([
|
|
346
|
+
[oldRow],
|
|
347
|
+
undefined, undefined, /* prunes */
|
|
348
|
+
[{ clientName: 'Claude' }],
|
|
349
|
+
[] /* no live connection */,
|
|
350
|
+
[{ id: 'conn-new' }] /* connection insert.returning */,
|
|
351
|
+
undefined /* token insert */,
|
|
352
|
+
]);
|
|
353
|
+
await provider.exchangeRefreshToken(CLIENT, 'bevel-mcp_r_old');
|
|
354
|
+
expect(captured.values[0]).toEqual({ userId: 'user-1', clientId: 'client-1', clientName: 'Claude', agentKey: 'claude' });
|
|
355
|
+
expect(captured.values[1]).toMatchObject({ connectionId: 'conn-new' });
|
|
356
|
+
});
|
|
357
|
+
|
|
237
358
|
it('verifyAccessToken resolves a live token to AuthInfo carrying the Bevel user', async () => {
|
|
238
359
|
const row = {
|
|
239
|
-
id: 'tok-row-1', clientId: 'client-1', scope: 'mcp', resource: null,
|
|
360
|
+
id: 'tok-row-1', clientId: 'client-1', connectionId: 'conn-1', scope: 'mcp', resource: null,
|
|
240
361
|
expiresAt: new Date(Date.now() + 60_000), userId: 'user-1', userEmail: 'alice@example.com',
|
|
241
362
|
};
|
|
242
|
-
const { provider } = makeProvider([
|
|
363
|
+
const { provider, captured } = makeProvider([
|
|
364
|
+
[row] /* select */,
|
|
365
|
+
undefined /* token lastUsedAt touch */,
|
|
366
|
+
undefined /* connection lastUsedAt touch */,
|
|
367
|
+
[row] /* a second verify a moment later */,
|
|
368
|
+
undefined /* its token touch — and NO second connection touch */,
|
|
369
|
+
]);
|
|
243
370
|
|
|
244
371
|
const info = await provider.verifyAccessToken('bevel-mcp_live');
|
|
245
372
|
|
|
246
373
|
expect(info.clientId).toBe('client-1');
|
|
247
374
|
expect(info.scopes).toEqual(['mcp']);
|
|
248
|
-
|
|
375
|
+
// The agent connection rides along, for the auth middleware to bind.
|
|
376
|
+
expect(info.extra).toMatchObject({ userId: 'user-1', userEmail: 'alice@example.com', connectionId: 'conn-1' });
|
|
377
|
+
// The lookup admits a token only while its connection is unrevoked — or
|
|
378
|
+
// has none to be revoked (a token from before connections existed).
|
|
379
|
+
const lookup = render(captured.where[0]).sql;
|
|
380
|
+
expect(lookup).toMatch(/"oauth_tokens"."connection_id" is null or "agent_connections"."revoked_at" is null/);
|
|
381
|
+
// Both "last used" marks are touched: the token's row, and THIS token's
|
|
382
|
+
// agent connection.
|
|
383
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
384
|
+
expect(captured.updateTargets).toEqual([oauthTokens, agentConnections]);
|
|
385
|
+
expect(captured.set.map((s) => Object.keys(s))).toEqual([['lastUsedAt'], ['lastUsedAt']]);
|
|
386
|
+
expect(render(captured.where[1])).toMatchObject({ params: ['tok-row-1'] });
|
|
387
|
+
expect(render(captured.where[2])).toMatchObject({ params: ['conn-1'] });
|
|
388
|
+
expect(render(captured.where[2]).sql).toMatch(/"agent_connections"."id" = \$1/);
|
|
389
|
+
// The connection's stamp is throttled: a verify a moment later touches
|
|
390
|
+
// the token row again but not the connection — a minute has not passed.
|
|
391
|
+
await provider.verifyAccessToken('bevel-mcp_live');
|
|
392
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
393
|
+
expect(captured.updateTargets).toEqual([oauthTokens, agentConnections, oauthTokens]);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('revokeToken (RFC 7009) ends the agent connection once no live token remains on it, as the owner\'s doing', async () => {
|
|
397
|
+
const { provider, captured } = makeProvider([
|
|
398
|
+
[{ connectionId: 'conn-1' }] /* the revoked pair's row */,
|
|
399
|
+
[{ live: 0 }] /* nothing else live on the connection */,
|
|
400
|
+
undefined /* connection close */,
|
|
401
|
+
]);
|
|
402
|
+
await provider.revokeToken(CLIENT, { token: 'bevel-mcp_r_old', token_type_hint: 'refresh_token' });
|
|
403
|
+
expect(captured.updateTargets).toEqual([oauthTokens, agentConnections]);
|
|
404
|
+
expect(captured.set[1]).toMatchObject({ revokedBy: 'owner' });
|
|
405
|
+
expect(captured.set[1].revokedAt).toBeInstanceOf(Date);
|
|
406
|
+
expect(render(captured.where[2])).toMatchObject({ params: ['conn-1'] });
|
|
407
|
+
expect(render(captured.where[2]).sql).toMatch(/"agent_connections"."revoked_at" is null/);
|
|
408
|
+
|
|
409
|
+
// Another live pair on the connection (the agent signed in twice): the
|
|
410
|
+
// connection stays.
|
|
411
|
+
const busy = makeProvider([[{ connectionId: 'conn-1' }], [{ live: 1 }]]);
|
|
412
|
+
await busy.provider.revokeToken(CLIENT, { token: 'bevel-mcp_r_other' });
|
|
413
|
+
expect(busy.captured.updateTargets).toEqual([oauthTokens]);
|
|
414
|
+
|
|
415
|
+
// An unknown or foreign token revokes nothing and touches no connection.
|
|
416
|
+
const unknown = makeProvider([[]]);
|
|
417
|
+
await unknown.provider.revokeToken(CLIENT, { token: 'bevel-mcp_r_gone' });
|
|
418
|
+
expect(unknown.captured.updateTargets).toEqual([oauthTokens]);
|
|
249
419
|
});
|
|
250
420
|
|
|
251
421
|
it('verifyAccessToken throws InvalidTokenError for unknown/revoked/expired tokens', async () => {
|
|
@@ -48,6 +48,8 @@ function makeExternalApiKeyService(
|
|
|
48
48
|
|
|
49
49
|
function makeOAuthProvider(
|
|
50
50
|
verify?: (t: string) => Promise<{ extra?: Record<string, unknown> }>,
|
|
51
|
+
/** Whether an agent connection still admits requests — true unless a test says otherwise. */
|
|
52
|
+
live?: (connectionId: string) => Promise<boolean>,
|
|
51
53
|
) {
|
|
52
54
|
return {
|
|
53
55
|
looksLikeAccessToken: (t: string) => typeof t === 'string' && t.startsWith('bevel-mcp_'),
|
|
@@ -57,6 +59,8 @@ function makeOAuthProvider(
|
|
|
57
59
|
throw new InvalidTokenError('unknown token');
|
|
58
60
|
}),
|
|
59
61
|
),
|
|
62
|
+
isConnectionLive: vi.fn(live ?? (async () => true)),
|
|
63
|
+
noteConnectionUse: vi.fn(),
|
|
60
64
|
} as unknown as BevelOAuthProvider;
|
|
61
65
|
}
|
|
62
66
|
|
|
@@ -179,7 +183,7 @@ describe('createMcpAuthMiddleware', () => {
|
|
|
179
183
|
const auth = makeAuthService();
|
|
180
184
|
const externalApiKeys = makeExternalApiKeyService();
|
|
181
185
|
const oauth = makeOAuthProvider(async () => ({
|
|
182
|
-
extra: { userId: 'user-5', userEmail: 'eve@example.com' },
|
|
186
|
+
extra: { userId: 'user-5', userEmail: 'eve@example.com', connectionId: 'conn-5' },
|
|
183
187
|
}));
|
|
184
188
|
const mw = makeMw({ auth, keys: externalApiKeys, oauth });
|
|
185
189
|
const { req, res, next } = makeReqRes('Bearer bevel-mcp_token123');
|
|
@@ -188,14 +192,28 @@ describe('createMcpAuthMiddleware', () => {
|
|
|
188
192
|
|
|
189
193
|
expect(req.userId).toBe('user-5');
|
|
190
194
|
expect(req.userEmail).toBe('eve@example.com');
|
|
191
|
-
// OAuth sessions are unmetered like JWT sessions — no connection key id
|
|
195
|
+
// OAuth sessions are unmetered like JWT sessions — no connection key id…
|
|
192
196
|
expect(req.externalApiKeyId).toBeUndefined();
|
|
197
|
+
// …but they ARE attributed to the agent behind the grant, for the Audit log.
|
|
198
|
+
expect(req.agentConnectionId).toBe('conn-5');
|
|
193
199
|
expect(next).toHaveBeenCalled();
|
|
194
200
|
// Neither the connection-key nor the JWT path may see this token shape.
|
|
195
201
|
expect((externalApiKeys as any).verifyAndLoadToken).not.toHaveBeenCalled();
|
|
196
202
|
expect((auth as any).verifyToken).not.toHaveBeenCalled();
|
|
197
203
|
});
|
|
198
204
|
|
|
205
|
+
it('binds no agent for an OAuth token minted before connections existed, and still admits it', async () => {
|
|
206
|
+
const oauth = makeOAuthProvider(async () => ({
|
|
207
|
+
extra: { userId: 'user-5', userEmail: 'eve@example.com', connectionId: null },
|
|
208
|
+
}));
|
|
209
|
+
const mw = makeMw({ oauth });
|
|
210
|
+
const { req, res, next } = makeReqRes('Bearer bevel-mcp_token123');
|
|
211
|
+
await mw(req, res, next);
|
|
212
|
+
expect(req.userId).toBe('user-5');
|
|
213
|
+
expect(req.agentConnectionId).toBeUndefined();
|
|
214
|
+
expect(next).toHaveBeenCalled();
|
|
215
|
+
});
|
|
216
|
+
|
|
199
217
|
it('401s with the discovery challenge when the OAuth token is invalid/expired/revoked', async () => {
|
|
200
218
|
const mw = makeMw();
|
|
201
219
|
const { req, res, next, setHeader, status } = makeReqRes('Bearer bevel-mcp_expired');
|
|
@@ -276,6 +294,46 @@ describe('createMcpAuthMiddleware', () => {
|
|
|
276
294
|
expect(next).toHaveBeenCalled();
|
|
277
295
|
expect(req.userId).toBe('user-7');
|
|
278
296
|
expect(req.userEmail).toBe('seven@example.com');
|
|
297
|
+
// A plain loopback identity names no agent.
|
|
298
|
+
expect(req.agentConnectionId).toBeUndefined();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('carries the agent connection of an exchanged grant, so the local server stays attributed', async () => {
|
|
302
|
+
const token = internal.mint({ userId: 'user-7', externalProxy: true, connectionId: 'conn-7' }, 60_000);
|
|
303
|
+
const auth = makeAuthService();
|
|
304
|
+
(auth.getUserById as ReturnType<typeof vi.fn>) = vi.fn(async () => ({
|
|
305
|
+
id: 'user-7',
|
|
306
|
+
email: 'seven@example.com',
|
|
307
|
+
}));
|
|
308
|
+
const oauth = makeOAuthProvider();
|
|
309
|
+
const mw = makeMw({ internal, auth, oauth });
|
|
310
|
+
const { req, res, next } = makeReqRes(`Bearer ${token}`);
|
|
311
|
+
await mw(req, res, next);
|
|
312
|
+
expect(next).toHaveBeenCalled();
|
|
313
|
+
expect(req.agentConnectionId).toBe('conn-7');
|
|
314
|
+
// Every request through the exchanged grant asks whether the
|
|
315
|
+
// connection still admits one, and counts as a use of it.
|
|
316
|
+
expect((oauth as any).isConnectionLive).toHaveBeenCalledWith('conn-7');
|
|
317
|
+
expect((oauth as any).noteConnectionUse).toHaveBeenCalledWith('conn-7');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('401s an exchanged grant whose agent connection was revoked — the token is stateless, the connection is not', async () => {
|
|
321
|
+
const token = internal.mint({ userId: 'user-7', externalProxy: true, connectionId: 'conn-7' }, 60_000);
|
|
322
|
+
const auth = makeAuthService();
|
|
323
|
+
(auth.getUserById as ReturnType<typeof vi.fn>) = vi.fn(async () => ({
|
|
324
|
+
id: 'user-7',
|
|
325
|
+
email: 'seven@example.com',
|
|
326
|
+
}));
|
|
327
|
+
const oauth = makeOAuthProvider(undefined, async () => false);
|
|
328
|
+
const mw = makeMw({ internal, auth, oauth });
|
|
329
|
+
const { req, res, next, status, setHeader } = makeReqRes(`Bearer ${token}`);
|
|
330
|
+
await mw(req, res, next);
|
|
331
|
+
expect(next).not.toHaveBeenCalled();
|
|
332
|
+
expect(status).toHaveBeenCalledWith(401);
|
|
333
|
+
expect(setHeader).toHaveBeenCalledWith('WWW-Authenticate', expect.stringContaining('resource_metadata'));
|
|
334
|
+
expect(req.agentConnectionId).toBeUndefined();
|
|
335
|
+
// A refused request is no use of the connection.
|
|
336
|
+
expect((oauth as any).noteConnectionUse).not.toHaveBeenCalled();
|
|
279
337
|
});
|
|
280
338
|
|
|
281
339
|
it('401s an expired internal token', async () => {
|
|
@@ -74,7 +74,7 @@ async function exchange(baseUrl: string, authorization?: string): Promise<Respon
|
|
|
74
74
|
describe('POST /mcp/local-token', () => {
|
|
75
75
|
it('exchanges a valid OAuth access token for an internal token with the loopback identity + TTL', async () => {
|
|
76
76
|
const oauth = makeOAuthProvider(async () => ({
|
|
77
|
-
extra: { userId: 'user-5', userEmail: 'eve@example.com' },
|
|
77
|
+
extra: { userId: 'user-5', userEmail: 'eve@example.com', connectionId: 'conn-9' },
|
|
78
78
|
// A grant with plenty of life left: the loopback constant is the binding cap.
|
|
79
79
|
expiresAt: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
|
|
80
80
|
}));
|
|
@@ -88,21 +88,41 @@ describe('POST /mcp/local-token', () => {
|
|
|
88
88
|
// above outlives it, so the constant is the cap that binds.
|
|
89
89
|
expect(body.expiresInMs).toBe(MCP_LOOPBACK_TOKEN_TTL_MS);
|
|
90
90
|
// The minted token verifies to the resolved user with the externalProxy
|
|
91
|
-
// flag — identical shape to the hosted session's loopback bearer
|
|
92
|
-
|
|
91
|
+
// flag — identical shape to the hosted session's loopback bearer — plus
|
|
92
|
+
// the grant's agent connection, so the local server's calls stay
|
|
93
|
+
// attributed to that agent in the Audit log.
|
|
94
|
+
expect(internalTokens.verify(body.token)).toEqual({
|
|
95
|
+
userId: 'user-5',
|
|
96
|
+
externalProxy: true,
|
|
97
|
+
connectionId: 'conn-9',
|
|
98
|
+
});
|
|
93
99
|
// …and the tool-auth verifier resolves it to source 'external', exactly
|
|
94
100
|
// how /api/agent/* will treat the local server.
|
|
95
101
|
const verify = createTokenVerifier(
|
|
96
102
|
{ looksLikeExternalApiKey: () => false } as unknown as IExternalApiKeyService,
|
|
97
103
|
internalTokens,
|
|
98
104
|
);
|
|
105
|
+
// …carrying the agent connection, which the REST recorder attributes
|
|
106
|
+
// the local server's direct tool calls to.
|
|
99
107
|
await expect(verify(body.token)).resolves.toEqual({
|
|
100
108
|
ok: true,
|
|
101
|
-
auth: { source: 'external', userId: 'user-5', scope: 'write' },
|
|
109
|
+
auth: { source: 'external', userId: 'user-5', connectionId: 'conn-9', scope: 'write' },
|
|
102
110
|
});
|
|
103
111
|
expect((oauth as any).verifyAccessToken).toHaveBeenCalledWith('bevel-mcp_valid123');
|
|
104
112
|
});
|
|
105
113
|
|
|
114
|
+
it('mints without a connection claim for a grant that carries none (a token from before connections existed)', async () => {
|
|
115
|
+
const oauth = makeOAuthProvider(async () => ({
|
|
116
|
+
extra: { userId: 'user-5', userEmail: 'eve@example.com' },
|
|
117
|
+
expiresAt: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
|
|
118
|
+
}));
|
|
119
|
+
const { baseUrl, internalTokens } = await mount(oauth);
|
|
120
|
+
const res = await exchange(baseUrl, 'Bearer bevel-mcp_valid123');
|
|
121
|
+
expect(res.status).toBe(200);
|
|
122
|
+
const body = (await res.json()) as { token: string };
|
|
123
|
+
expect(internalTokens.verify(body.token)).toEqual({ userId: 'user-5', externalProxy: true });
|
|
124
|
+
});
|
|
125
|
+
|
|
106
126
|
/**
|
|
107
127
|
* The binding that keeps the exchange from OUTLIVING its grant: an access
|
|
108
128
|
* token with less life left than the loopback constant caps the minted
|
|
@@ -108,7 +108,7 @@ describe('POST /mcp (stateless)', () => {
|
|
|
108
108
|
expect((await messagesOf(res))[0].result.serverInfo.name).toBe('stub');
|
|
109
109
|
// The messages reach the service, so it can tell an initialize apart.
|
|
110
110
|
expect(mcpService.createRequestServer).toHaveBeenCalledWith(
|
|
111
|
-
{ userId: 'user-A', tokenId: null, bearer: '' },
|
|
111
|
+
{ userId: 'user-A', tokenId: null, connectionId: null, bearer: '' },
|
|
112
112
|
INITIALIZE,
|
|
113
113
|
);
|
|
114
114
|
});
|
|
@@ -12,6 +12,7 @@ import { createManualRoutes } from '../../tool-registry/manual.routes.js';
|
|
|
12
12
|
import { ToolRegistry } from '../../tool-registry/tool-registry.js';
|
|
13
13
|
import { toolDef } from '../../tool-helpers/tool-def.js';
|
|
14
14
|
import { PLATFORM_HEADER, TOOL_PREFIX_LINE } from '../../agent-instructions/index.js';
|
|
15
|
+
import type { AgentEventInput, IAgentEventRecorder } from '../../audit/audit.contract.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* End-to-end proxy test: a real express app serving the registry-driven tool
|
|
@@ -43,6 +44,10 @@ async function setup(deps?: {
|
|
|
43
44
|
readAgentPreamble?: () => Promise<string | null>;
|
|
44
45
|
/** Extra echo tools to register under these names (the four KB tools, in the prefix tests). */
|
|
45
46
|
extraTools?: string[];
|
|
47
|
+
/** The Audit log's recorder, when a test watches what gets recorded. */
|
|
48
|
+
auditRecorder?: IAgentEventRecorder;
|
|
49
|
+
/** The agent connection an OAuth caller arrived through (with `tokenId: null`). */
|
|
50
|
+
connectionId?: string | null;
|
|
46
51
|
}) {
|
|
47
52
|
const registry = new ToolRegistry();
|
|
48
53
|
registry.registerExternalTool(
|
|
@@ -91,6 +96,16 @@ async function setup(deps?: {
|
|
|
91
96
|
res.json({ text: `echo: ${b.prompt}`, sessionId: typeof b.sessionId === 'string' ? b.sessionId : 'new-sess' });
|
|
92
97
|
});
|
|
93
98
|
app.post('/api/agent/tools/boom', (_req, res) => res.status(500).json({ error: 'kaboom' }));
|
|
99
|
+
// The two the Audit log's skill classification reads through: a catalog of
|
|
100
|
+
// one skill, and a file read that answers for any path (registered as
|
|
101
|
+
// tools only by the tests that need them, via `extraTools`).
|
|
102
|
+
app.post('/api/agent/tools/list_skills', (_req, res) =>
|
|
103
|
+
res.json({ skills: [{ name: 'rfi', description: 'RFI answers', path: 'Plugins/Sales/rfi' }] }),
|
|
104
|
+
);
|
|
105
|
+
app.post('/api/agent/tools/read_file', (req, res) => {
|
|
106
|
+
const b = (req.body ?? {}) as { path?: string };
|
|
107
|
+
res.json({ content: `contents of ${b.path}` });
|
|
108
|
+
});
|
|
94
109
|
// A live UTCP manual a catalog entry can point at — one tool, dispatched back
|
|
95
110
|
// to the echo above. Mounted always (it costs one route) so a test can add a
|
|
96
111
|
// MANUAL to the served catalog, not merely a tool to the registry: what a
|
|
@@ -149,10 +164,11 @@ async function setup(deps?: {
|
|
|
149
164
|
deps?.toolManuals as any,
|
|
150
165
|
undefined, // internalTokens — the fake loopback here accepts any bearer
|
|
151
166
|
deps?.revokeOAuthAccess,
|
|
167
|
+
deps?.auditRecorder,
|
|
152
168
|
);
|
|
153
169
|
lastService = mcp;
|
|
154
170
|
const tokenId = deps?.tokenId === undefined ? 'tok-1' : deps.tokenId;
|
|
155
|
-
return connectClient(mcp, tokenId);
|
|
171
|
+
return connectClient(mcp, tokenId, deps?.connectionId ?? null);
|
|
156
172
|
}
|
|
157
173
|
|
|
158
174
|
/** The service `setup` built last — for a test that needs a SECOND request against the same service. */
|
|
@@ -165,9 +181,9 @@ let lastService: McpService | undefined;
|
|
|
165
181
|
* The initialize message is passed so the server carries instructions,
|
|
166
182
|
* exactly as the route's initialize request would.
|
|
167
183
|
*/
|
|
168
|
-
async function connectClient(mcp: McpService, tokenId: string | null): Promise<Client> {
|
|
184
|
+
async function connectClient(mcp: McpService, tokenId: string | null, connectionId: string | null = null): Promise<Client> {
|
|
169
185
|
const server = await mcp.createRequestServer(
|
|
170
|
-
{ userId: 'user-A', tokenId, bearer: 'bevel_testkey' },
|
|
186
|
+
{ userId: 'user-A', tokenId, connectionId, bearer: 'bevel_testkey' },
|
|
171
187
|
{ jsonrpc: '2.0', id: 0, method: 'initialize', params: { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'test-client', version: '0.0.0' } } },
|
|
172
188
|
);
|
|
173
189
|
|
|
@@ -680,3 +696,104 @@ describe('McpService — a collision ends when its sibling is renamed away', ()
|
|
|
680
696
|
expect(survivor.some((m) => m.startsWith('[mcp] skipping manual "notion-eu": ') && !m.includes('rewrites to'))).toBe(true);
|
|
681
697
|
});
|
|
682
698
|
});
|
|
699
|
+
|
|
700
|
+
describe('McpService — the Audit log records what an agent calls', () => {
|
|
701
|
+
/** A recorder that keeps every event, and the same thing as the proxy's collaborator type. */
|
|
702
|
+
function spyRecorder(): IAgentEventRecorder & { events: AgentEventInput[] } {
|
|
703
|
+
const events: AgentEventInput[] = [];
|
|
704
|
+
return { events, record: (e) => void events.push(e) };
|
|
705
|
+
}
|
|
706
|
+
const text = (res: Awaited<ReturnType<Client['callTool']>>) => (res.content as Array<{ text: string }>)[0].text;
|
|
707
|
+
// Recording is fire-and-forget behind the call; give the microtasks a turn.
|
|
708
|
+
const settle = () => new Promise((r) => setTimeout(r, 10));
|
|
709
|
+
|
|
710
|
+
it('records a direct platform tool call as a hexis capability under the connection key, with its outcome', async () => {
|
|
711
|
+
const recorder = spyRecorder();
|
|
712
|
+
const client = await setup({ auditRecorder: recorder });
|
|
713
|
+
|
|
714
|
+
await client.callTool({ name: 'ask', arguments: { body: { prompt: 'hello' } } });
|
|
715
|
+
await client.callTool({ name: 'boom', arguments: {} });
|
|
716
|
+
await settle();
|
|
717
|
+
|
|
718
|
+
expect(recorder.events).toHaveLength(2);
|
|
719
|
+
expect(recorder.events[0]).toMatchObject({
|
|
720
|
+
userId: 'user-A',
|
|
721
|
+
principal: { kind: 'key', id: 'tok-1' },
|
|
722
|
+
kind: 'capability',
|
|
723
|
+
manual: null,
|
|
724
|
+
name: 'ask',
|
|
725
|
+
outcome: 'ok',
|
|
726
|
+
});
|
|
727
|
+
expect(typeof recorder.events[0].durationMs).toBe('number');
|
|
728
|
+
// The tool answered with an error result — recorded as such, not as ok.
|
|
729
|
+
expect(recorder.events[1]).toMatchObject({ kind: 'capability', name: 'boom', outcome: 'error' });
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it('records a chain as a capability AND each call made inside it', async () => {
|
|
733
|
+
const recorder = spyRecorder();
|
|
734
|
+
const client = await setup({ auditRecorder: recorder });
|
|
735
|
+
|
|
736
|
+
await client.callTool({
|
|
737
|
+
name: 'call_tool_chain',
|
|
738
|
+
arguments: { code: "KNOWLEDGE_BASE.ask({ body: { prompt: 'one' } }); return KNOWLEDGE_BASE.ask({ body: { prompt: 'two' } });" },
|
|
739
|
+
});
|
|
740
|
+
await settle();
|
|
741
|
+
|
|
742
|
+
const names = recorder.events.map((e) => `${e.kind}:${e.name}:${e.outcome}`);
|
|
743
|
+
// The two inner calls finish (and are recorded) before the chain itself.
|
|
744
|
+
expect(names).toEqual(['capability:ask:ok', 'capability:ask:ok', 'capability:call_tool_chain:ok']);
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
it('attributes an OAuth caller to its agent connection, and records nothing for a caller with neither', async () => {
|
|
748
|
+
const asAgent = spyRecorder();
|
|
749
|
+
const agent = await setup({ auditRecorder: asAgent, tokenId: null, connectionId: 'conn-1' });
|
|
750
|
+
await agent.callTool({ name: 'ask', arguments: { body: { prompt: 'hi' } } });
|
|
751
|
+
await settle();
|
|
752
|
+
expect(asAgent.events[0]).toMatchObject({ principal: { kind: 'agent', id: 'conn-1' }, name: 'ask' });
|
|
753
|
+
|
|
754
|
+
const nobody = spyRecorder();
|
|
755
|
+
const jwt = await setup({ auditRecorder: nobody, tokenId: null, connectionId: null });
|
|
756
|
+
await jwt.callTool({ name: 'ask', arguments: { body: { prompt: 'hi' } } });
|
|
757
|
+
await settle();
|
|
758
|
+
expect(nobody.events).toEqual([]);
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
it('records a read inside a skill folder as that skill, and a read elsewhere as the read', async () => {
|
|
762
|
+
const recorder = spyRecorder();
|
|
763
|
+
const client = await setup({ auditRecorder: recorder, extraTools: ['read_file', 'list_skills'] });
|
|
764
|
+
|
|
765
|
+
await client.callTool({ name: 'read_file', arguments: { body: { path: 'Plugins/Sales/rfi/SKILL.md' } } });
|
|
766
|
+
await client.callTool({ name: 'read_file', arguments: { body: { path: 'KnowledgeBase/Product/roadmap.md' } } });
|
|
767
|
+
await settle();
|
|
768
|
+
|
|
769
|
+
expect(recorder.events[0]).toMatchObject({ kind: 'skill', manual: 'Plugins/Sales/rfi', name: 'rfi', outcome: 'ok' });
|
|
770
|
+
expect(recorder.events[1]).toMatchObject({ kind: 'capability', manual: null, name: 'read_file', outcome: 'ok' });
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('records a prompt read as a skill, with an error outcome when the skill is unknown', async () => {
|
|
774
|
+
const recorder = spyRecorder();
|
|
775
|
+
const client = await setup({ auditRecorder: recorder });
|
|
776
|
+
// The harness serves no get_skill endpoint, so every prompt is unknown here.
|
|
777
|
+
await expect(client.getPrompt({ name: 'rfi' })).rejects.toThrow(/Unknown skill/);
|
|
778
|
+
await settle();
|
|
779
|
+
expect(recorder.events[0]).toMatchObject({ kind: 'skill', name: 'rfi', outcome: 'error' });
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
it('records a call refused for a missing sign-in as denied, without running the tool', async () => {
|
|
783
|
+
const recorder = spyRecorder();
|
|
784
|
+
const vault = { statusFor: async (_u: string, keys: string[]) => keys.map((key) => ({ key, adminConfigured: false, userConfigured: false })) };
|
|
785
|
+
const manuals = {
|
|
786
|
+
userScopedKeysForManual: async (manual: string) =>
|
|
787
|
+
manual === 'KNOWLEDGE_BASE' ? [{ key: 'KNOWLEDGE_BASE_API_KEY', name: 'API_KEY', label: 'Your API key', oauth: false }] : [],
|
|
788
|
+
};
|
|
789
|
+
const client = await setup({ auditRecorder: recorder, secretsVault: vault, toolManuals: manuals });
|
|
790
|
+
|
|
791
|
+
const res = await client.callTool({ name: 'ask', arguments: { body: { prompt: 'hello' } } });
|
|
792
|
+
await settle();
|
|
793
|
+
|
|
794
|
+
expect(text(res)).not.toMatch(/echo:/);
|
|
795
|
+
expect(recorder.events).toEqual([
|
|
796
|
+
expect.objectContaining({ kind: 'capability', name: 'ask', outcome: 'denied', durationMs: null }),
|
|
797
|
+
]);
|
|
798
|
+
});
|
|
799
|
+
});
|