@bevel-software/platform-core-backend 0.22.0 → 0.24.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/core-ports.d.ts +11 -1
- 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 +3 -3
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +46 -13
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +12 -2
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +137 -24
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +46 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +99 -13
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +16 -12
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +28 -13
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +42 -12
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +67 -10
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +214 -35
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +17 -16
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +7 -3
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +8 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.js +8 -2
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +38 -6
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +39 -6
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/index.d.ts +2 -1
- package/dist/modules/agent-instructions/index.d.ts.map +1 -1
- package/dist/modules/agent-instructions/index.js +2 -1
- package/dist/modules/agent-instructions/index.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +115 -0
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -0
- package/dist/modules/agent-instructions/shared-file-rules.js +272 -0
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +4 -2
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/auth/account-admission.d.ts +58 -7
- package/dist/modules/auth/account-admission.d.ts.map +1 -1
- package/dist/modules/auth/account-admission.js +44 -1
- package/dist/modules/auth/account-admission.js.map +1 -1
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +57 -16
- package/dist/modules/auth/account-erasure.service.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts +1 -1
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +61 -5
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +1 -1
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +18 -7
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +8 -0
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +70 -2
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +197 -18
- 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 +7 -2
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts +20 -2
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +66 -35
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/connection.d.ts +16 -0
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +117 -0
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +337 -120
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +116 -58
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts +8 -0
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +271 -1
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
- package/dist/modules/kb-fs/branch-name.js +12 -2
- package/dist/modules/kb-fs/branch-name.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +11 -16
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +79 -0
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.d.ts +2 -2
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.js +14 -3
- package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
- package/dist/modules/kb-sync/sync-auth.d.ts +3 -1
- package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -1
- package/dist/modules/kb-sync/sync-auth.js +1 -1
- package/dist/modules/kb-sync/sync-auth.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +21 -6
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +8 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +38 -8
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +3 -2
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-records.store.js +7 -4
- package/dist/modules/plugins/join-request-records.store.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +8 -3
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +52 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +207 -19
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +90 -18
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.contract.js +4 -1
- package/dist/modules/skills/skills.contract.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +98 -9
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +239 -37
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts +7 -0
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +71 -7
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +12 -4
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +3 -3
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +16 -7
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +34 -12
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +2 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +25 -4
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +3 -2
- 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.js +1 -1
- package/dist/modules/tool-helpers/validate-token.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +42 -31
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +80 -0
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -0
- package/dist/modules/tool-registry/description-length.js +108 -0
- package/dist/modules/tool-registry/description-length.js.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +98 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.js +81 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +75 -35
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +24 -0
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +30 -0
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +94 -0
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +193 -3
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +38 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +60 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +7 -3
- package/dist/modules/workflow/recovery-bot.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 +12 -3
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/workflow-hooks.d.ts +54 -32
- package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -1
- package/dist/modules/workflow/workflow-hooks.js +16 -1
- package/dist/modules/workflow/workflow-hooks.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +60 -6
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +115 -5
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts +94 -0
- package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -0
- package/dist/modules/workspace/agent-access.gate.js +123 -0
- package/dist/modules/workspace/agent-access.gate.js.map +1 -0
- package/dist/modules/workspace/agent-upload.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-upload.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.routes.js +210 -0
- package/dist/modules/workspace/agent-upload.routes.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +284 -0
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.store.js +553 -0
- package/dist/modules/workspace/agent-upload.store.js.map +1 -0
- package/dist/modules/workspace/routine-write-policy.d.ts +5 -6
- package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
- package/dist/modules/workspace/routine-write-policy.js +5 -6
- package/dist/modules/workspace/routine-write-policy.js.map +1 -1
- package/dist/modules/workspace/session-sink.d.ts +5 -5
- package/dist/modules/workspace/set-aside-clone.d.ts +46 -0
- package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -0
- package/dist/modules/workspace/set-aside-clone.js +92 -0
- package/dist/modules/workspace/set-aside-clone.js.map +1 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +59 -9
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +65 -24
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +3 -3
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +40 -0
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +46 -4
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/upload-limits.d.ts +13 -0
- package/dist/modules/workspace/upload-limits.d.ts.map +1 -0
- package/dist/modules/workspace/upload-limits.js +13 -0
- package/dist/modules/workspace/upload-limits.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +93 -4
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +128 -5
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +316 -58
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +13 -11
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +791 -198
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +0 -6
- package/dist/modules/workspace/write-denial.d.ts.map +1 -1
- package/dist/modules/workspace/write-denial.js +0 -6
- package/dist/modules/workspace/write-denial.js.map +1 -1
- package/dist/modules/workspace/zip-entry-rules.d.ts +114 -0
- package/dist/modules/workspace/zip-entry-rules.d.ts.map +1 -0
- package/dist/modules/workspace/zip-entry-rules.js +154 -0
- package/dist/modules/workspace/zip-entry-rules.js.map +1 -0
- package/dist/modules/write-access/write-access.d.ts +60 -0
- package/dist/modules/write-access/write-access.d.ts.map +1 -0
- package/dist/modules/write-access/write-access.js +129 -0
- package/dist/modules/write-access/write-access.js.map +1 -0
- package/dist/shared/column-crypto.d.ts +194 -0
- package/dist/shared/column-crypto.d.ts.map +1 -0
- package/dist/shared/column-crypto.js +144 -0
- package/dist/shared/column-crypto.js.map +1 -0
- package/dist/shared/domain-errors.d.ts +25 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +28 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +20 -0
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +26 -0
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/token-crypto.d.ts.map +1 -1
- package/dist/shared/token-crypto.js +25 -1
- package/dist/shared/token-crypto.js.map +1 -1
- package/dist/tenancy/static-tenant-source.d.ts +0 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +1 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/dist/tenancy/tenant-secrets.d.ts +5 -1
- package/dist/tenancy/tenant-secrets.d.ts.map +1 -1
- package/dist/tenancy/tenant-secrets.js +4 -0
- package/dist/tenancy/tenant-secrets.js.map +1 -1
- package/kb-template/AGENTS.md +2 -0
- package/migrations/0014_change_request_closed_reason.sql +1 -0
- package/migrations/0015_account_deactivation.sql +3 -0
- package/migrations/0016_pii_encryption.sql +20 -0
- package/migrations/meta/0014_snapshot.json +2265 -0
- package/migrations/meta/0015_snapshot.json +2271 -0
- package/migrations/meta/0016_snapshot.json +2327 -0
- package/migrations/meta/_journal.json +21 -0
- package/package.json +3 -3
- package/src/__tests__/kb-layout-config.test.ts +0 -2
- package/src/__tests__/retired-settings.test.ts +96 -0
- package/src/core/__tests__/gated-boot.test.ts +132 -0
- package/src/core/__tests__/lifecycle.test.ts +241 -5
- package/src/core/__tests__/set-aside-root-is-one-place.test.ts +63 -0
- package/src/core/core-ports.ts +11 -1
- package/src/core/create-core-server.ts +52 -16
- package/src/core/create-core-services.ts +156 -24
- package/src/core/lifecycle.ts +117 -13
- package/src/core-config.ts +31 -14
- package/src/index.ts +30 -1
- package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +385 -0
- package/src/modules/access/__tests__/access-control.prospective.test.ts +94 -18
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +6 -7
- package/src/modules/access/__tests__/users-db-double.ts +21 -12
- package/src/modules/access/access-control.interface.ts +43 -12
- package/src/modules/access/access-control.service.ts +234 -36
- package/src/modules/access/access.routes.ts +17 -16
- package/src/modules/access/directory-sync-bot.ts +7 -3
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +11 -5
- package/src/modules/agent-instructions/__tests__/compose.test.ts +19 -10
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +238 -0
- package/src/modules/agent-instructions/agent-instructions.routes.ts +12 -2
- package/src/modules/agent-instructions/compose.ts +50 -7
- package/src/modules/agent-instructions/index.ts +12 -0
- package/src/modules/agent-instructions/shared-file-rules.ts +314 -0
- package/src/modules/audit/agent-audit.service.ts +4 -2
- package/src/modules/auth/__tests__/account-deactivation.test.ts +253 -0
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +6 -2
- package/src/modules/auth/__tests__/account.routes.test.ts +87 -6
- package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
- package/src/modules/auth/__tests__/auth.service.test.ts +4 -2
- package/src/modules/auth/account-admission.ts +78 -9
- package/src/modules/auth/account-erasure.service.ts +69 -18
- package/src/modules/auth/account.routes.ts +63 -7
- package/src/modules/auth/auth.middleware.ts +19 -8
- package/src/modules/auth/auth.routes.ts +8 -0
- package/src/modules/auth/auth.service.ts +206 -23
- package/src/modules/auth/oidc-auth-provider.ts +7 -2
- package/src/modules/code-mode/__tests__/chain-runtime.e2e.test.ts +335 -0
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +46 -2
- package/src/modules/code-mode/code-mode.tool.ts +80 -34
- package/src/modules/database/__tests__/connection.test.ts +12 -0
- package/src/modules/database/__tests__/pii-backfill.pg.test.ts +561 -0
- package/src/modules/database/connection.ts +117 -0
- package/src/modules/database/core-schema.ts +116 -58
- package/src/modules/database/migrate.ts +353 -1
- package/src/modules/kb-fs/__tests__/branch-name.test.ts +10 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +123 -0
- package/src/modules/kb-fs/branch-name.ts +14 -1
- package/src/modules/kb-fs/repo-path.ts +85 -0
- package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +26 -1
- package/src/modules/kb-sync/kb-sync.routes.ts +14 -4
- package/src/modules/kb-sync/sync-auth.ts +2 -2
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -3
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +4 -3
- package/src/modules/mcp/__tests__/mcp.service.test.ts +115 -13
- package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
- package/src/modules/mcp/mcp.service.ts +46 -7
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
- package/src/modules/plugins/join-request-records.store.ts +7 -4
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
- package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +91 -58
- package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +25 -8
- package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +14 -11
- package/src/modules/settings/__tests__/setup.routes.repository-change.test.ts +390 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +3 -0
- package/src/modules/settings/deployment-settings.service.ts +8 -3
- package/src/modules/settings/setup.routes.ts +263 -19
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +2 -1
- package/src/modules/skills/__tests__/branch-skills.tools.test.ts +218 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +229 -5
- package/src/modules/skills/skills.contract.ts +91 -18
- package/src/modules/skills/skills.service.ts +278 -41
- package/src/modules/skills/skills.tools.ts +80 -8
- package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +14 -1
- package/src/modules/tool-auth/external-api-key.service.ts +12 -4
- package/src/modules/tool-auth/internal-token.service.ts +3 -3
- package/src/modules/tool-auth/tool-auth.middleware.ts +34 -11
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +5 -3
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -3
- package/src/modules/tool-helpers/__tests__/validate-token.test.ts +11 -1
- package/src/modules/tool-helpers/tool-handler.ts +24 -4
- package/src/modules/tool-helpers/tool.contract.ts +3 -2
- package/src/modules/tool-helpers/validate-token.ts +1 -1
- package/src/modules/tool-manuals/tool-manuals.tools.ts +44 -31
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +378 -0
- package/src/modules/tool-registry/description-length.ts +111 -0
- package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +111 -0
- package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +25 -0
- package/src/modules/workflow/__tests__/workflow.service.repository-replaced.test.ts +249 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +264 -6
- package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
- package/src/modules/workflow/agent-tools/workflow.tools.ts +86 -35
- package/src/modules/workflow/file-lock.service.ts +31 -0
- package/src/modules/workflow/git/__tests__/git.service.fileBytesAtCommit.test.ts +260 -0
- package/src/modules/workflow/git/__tests__/git.service.prFetchFailure.test.ts +170 -0
- package/src/modules/workflow/git/git.service.ts +215 -1
- package/src/modules/workflow/pending-commits.service.ts +64 -2
- package/src/modules/workflow/recovery-bot.ts +7 -3
- package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +4 -0
- package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +25 -3
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
- package/src/modules/workflow/workflow-hooks.ts +64 -26
- package/src/modules/workflow/workflow.service.ts +125 -5
- package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
- package/src/modules/workspace/__tests__/agent-uploads.test.ts +1604 -0
- package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +394 -0
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +2 -1
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -1
- package/src/modules/workspace/__tests__/set-aside-clone.test.ts +52 -0
- package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +305 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -0
- package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +156 -0
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +147 -0
- package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +402 -0
- package/src/modules/workspace/__tests__/workspace.service.test.ts +77 -9
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +41 -39
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +2 -3
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +766 -56
- package/src/modules/workspace/agent-access.gate.ts +164 -0
- package/src/modules/workspace/agent-upload.routes.ts +214 -0
- package/src/modules/workspace/agent-upload.store.ts +668 -0
- package/src/modules/workspace/routine-write-policy.ts +5 -6
- package/src/modules/workspace/session-sink.ts +5 -5
- package/src/modules/workspace/set-aside-clone.ts +96 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +86 -0
- package/src/modules/workspace/startup/kb-startup-runner.ts +115 -34
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +9 -9
- package/src/modules/workspace/startup/steps/seed-tree.ts +3 -6
- package/src/modules/workspace/startup/steps/template-source.ts +53 -5
- package/src/modules/workspace/upload-limits.ts +12 -0
- package/src/modules/workspace/workspace.routes.ts +96 -5
- package/src/modules/workspace/workspace.service.ts +319 -61
- package/src/modules/workspace/workspace.tools.ts +894 -214
- package/src/modules/workspace/write-denial.ts +0 -8
- package/src/modules/workspace/zip-entry-rules.ts +173 -0
- package/src/modules/write-access/__tests__/write-access.test.ts +248 -0
- package/src/modules/write-access/write-access.ts +153 -0
- package/src/shared/__tests__/column-crypto.test.ts +217 -0
- package/src/shared/column-crypto.ts +218 -0
- package/src/shared/domain-errors.ts +31 -0
- package/src/shared/git.contract.ts +27 -0
- package/src/shared/token-crypto.ts +28 -1
- package/src/tenancy/__tests__/static-tenant-source.test.ts +4 -1
- package/src/tenancy/static-tenant-source.ts +1 -3
- package/src/tenancy/tenant-secrets.ts +5 -1
- package/dist/modules/workflow/session-ontology.policy.d.ts +0 -52
- package/dist/modules/workflow/session-ontology.policy.d.ts.map +0 -1
- package/dist/modules/workflow/session-ontology.policy.js +0 -62
- package/dist/modules/workflow/session-ontology.policy.js.map +0 -1
- package/dist/modules/workflow/session-ontology.service.d.ts +0 -105
- package/dist/modules/workflow/session-ontology.service.d.ts.map +0 -1
- package/dist/modules/workflow/session-ontology.service.js +0 -147
- package/dist/modules/workflow/session-ontology.service.js.map +0 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts +0 -114
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +0 -1
- package/dist/modules/workspace/session-ontology.gate.js +0 -161
- package/dist/modules/workspace/session-ontology.gate.js.map +0 -1
- package/dist/shared/kb-layout.d.ts +0 -39
- package/dist/shared/kb-layout.d.ts.map +0 -1
- package/dist/shared/kb-layout.js +0 -103
- package/dist/shared/kb-layout.js.map +0 -1
- package/dist/shared/kb-layout.test.d.ts +0 -2
- package/dist/shared/kb-layout.test.d.ts.map +0 -1
- package/dist/shared/kb-layout.test.js +0 -75
- package/dist/shared/kb-layout.test.js.map +0 -1
- package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +0 -62
- package/src/modules/workflow/__tests__/session-ontology.service.test.ts +0 -201
- package/src/modules/workflow/session-ontology.policy.ts +0 -70
- package/src/modules/workflow/session-ontology.service.ts +0 -183
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +0 -239
- package/src/modules/workspace/session-ontology.gate.ts +0 -191
- package/src/shared/kb-layout.test.ts +0 -98
- package/src/shared/kb-layout.ts +0 -102
|
@@ -2,7 +2,7 @@ import { createHash, randomBytes } from 'node:crypto';
|
|
|
2
2
|
import { logger } from '../../shared/logging.js';
|
|
3
3
|
|
|
4
4
|
const log = logger('external-api-key');
|
|
5
|
-
import { and,
|
|
5
|
+
import { and, desc, eq, isNotNull, isNull, type SQL } from 'drizzle-orm';
|
|
6
6
|
import type { AuthUser } from '@bevel-software/platform-shared';
|
|
7
7
|
import type { Database } from '../database/connection.js';
|
|
8
8
|
import { externalApiKeys, users } from '../database/schema.js';
|
|
@@ -108,7 +108,9 @@ export class ExternalApiKeyService implements IExternalApiKeyService {
|
|
|
108
108
|
// Join users so a single round-trip resolves both "token is valid" and
|
|
109
109
|
// "load the user it belongs to". The unique index on token_hash makes
|
|
110
110
|
// this a point-lookup. `isNull(revokedAt)` is what enforces revocation
|
|
111
|
-
// — the row remains for audit
|
|
111
|
+
// — the row remains for audit — and `isNull(deactivatedAt)` is what
|
|
112
|
+
// stops a switched-off account's keys without revoking them, so
|
|
113
|
+
// switching it back on restores them.
|
|
112
114
|
const [row] = await this.db
|
|
113
115
|
.select({
|
|
114
116
|
tokenId: externalApiKeys.id,
|
|
@@ -119,7 +121,7 @@ export class ExternalApiKeyService implements IExternalApiKeyService {
|
|
|
119
121
|
})
|
|
120
122
|
.from(externalApiKeys)
|
|
121
123
|
.innerJoin(users, eq(externalApiKeys.userId, users.id))
|
|
122
|
-
.where(and(eq(externalApiKeys.tokenHash, tokenHash), isNull(externalApiKeys.revokedAt)))
|
|
124
|
+
.where(and(eq(externalApiKeys.tokenHash, tokenHash), isNull(externalApiKeys.revokedAt), isNull(users.deactivatedAt)))
|
|
123
125
|
.limit(1);
|
|
124
126
|
|
|
125
127
|
if (!row) return null;
|
|
@@ -167,6 +169,11 @@ export class ExternalApiKeyService implements IExternalApiKeyService {
|
|
|
167
169
|
// Owner joined in so the admin overview is one round-trip; ordered by
|
|
168
170
|
// owner email so per-account grouping is a linear pass, newest key
|
|
169
171
|
// first within an account (same order the owner sees on their own page).
|
|
172
|
+
// The email half of that order is applied in-process: the column is
|
|
173
|
+
// ciphertext in the database, where ORDER BY would sort by IV noise. The
|
|
174
|
+
// sort is stable, so the database's newest-first survives within an owner.
|
|
175
|
+
// By code unit, not by locale: a collation can call two different
|
|
176
|
+
// addresses equal, and equal here means "the same owner's group".
|
|
170
177
|
const rows = await this.db
|
|
171
178
|
.select({
|
|
172
179
|
key: externalApiKeys,
|
|
@@ -176,7 +183,8 @@ export class ExternalApiKeyService implements IExternalApiKeyService {
|
|
|
176
183
|
})
|
|
177
184
|
.from(externalApiKeys)
|
|
178
185
|
.innerJoin(users, eq(externalApiKeys.userId, users.id))
|
|
179
|
-
.orderBy(
|
|
186
|
+
.orderBy(desc(externalApiKeys.createdAt));
|
|
187
|
+
rows.sort((a, b) => (a.email < b.email ? -1 : a.email > b.email ? 1 : 0));
|
|
180
188
|
return rows.map((row) => ({
|
|
181
189
|
...toSummary(row.key),
|
|
182
190
|
user: { id: row.userId, email: row.email, name: row.name },
|
|
@@ -12,9 +12,9 @@ export interface InternalTokenClaim {
|
|
|
12
12
|
/**
|
|
13
13
|
* The agent run / conversation id (the Mastra thread id) this token is scoped
|
|
14
14
|
* to, when minted per-run. Surfaced as `ToolContext.sessionId` so the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* agent-access hooks can tell the in-process agent's run apart (the
|
|
16
|
+
* external/MCP path carries `sessionId` on the tool body instead). Absent
|
|
17
|
+
* for identity-only tokens.
|
|
18
18
|
*/
|
|
19
19
|
sessionId?: string;
|
|
20
20
|
/**
|
|
@@ -4,6 +4,7 @@ import { logger } from '../../shared/logging.js';
|
|
|
4
4
|
const log = logger('tools');
|
|
5
5
|
import type { IExternalApiKeyService } from './external-api-key.interface.js';
|
|
6
6
|
import type { AuthService } from '../auth/auth.service.js';
|
|
7
|
+
import { ACCOUNT_DEACTIVATED_MESSAGE, AuthBackendError } from '../auth/account-admission.js';
|
|
7
8
|
import { InternalTokenService } from './internal-token.service.js';
|
|
8
9
|
import { INVALID_CONNECTION_KEY_CHALLENGE, INVALID_CONNECTION_KEY_MESSAGE } from './connection-key-rejection.js';
|
|
9
10
|
|
|
@@ -79,10 +80,10 @@ export const requireInternalSource: RequestHandler = (req, res, next) => {
|
|
|
79
80
|
|
|
80
81
|
/**
|
|
81
82
|
* Mirror of `requireInternalSource` for surfaces that must admit ONLY external
|
|
82
|
-
* callers. `start_session` mints a fresh chat thread as the caller's
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
83
|
+
* callers. `start_session` mints a fresh chat thread as the caller's session
|
|
84
|
+
* id; an internal (in-process agent) token already carries its run's
|
|
85
|
+
* `sessionId`, so letting it mint a new thread mid-run would silently split
|
|
86
|
+
* one conversation in two.
|
|
86
87
|
*
|
|
87
88
|
* "External" is decided by the VERIFIER, not by which credential shape
|
|
88
89
|
* authenticated: a `bevel_…` connection key resolves external, and so does an
|
|
@@ -122,6 +123,15 @@ export type VerifyResult =
|
|
|
122
123
|
export function createTokenVerifier(
|
|
123
124
|
externalApiKeyService: IExternalApiKeyService,
|
|
124
125
|
internalTokenService: InternalTokenService,
|
|
126
|
+
/**
|
|
127
|
+
* Whether the account an internal token was minted for is still on. An
|
|
128
|
+
* internal token carries only a user id and lives up to an hour (longer
|
|
129
|
+
* for the MCP loopback), so without this a switched-off account's running
|
|
130
|
+
* agent would keep working until it expired. Connection keys need no
|
|
131
|
+
* second look: their lookup reads the account. Every deployment passes
|
|
132
|
+
* it; absent, internal tokens are judged on their signature alone.
|
|
133
|
+
*/
|
|
134
|
+
accounts?: Pick<AuthService, 'isActive'>,
|
|
125
135
|
): (token: string | undefined) => Promise<VerifyResult> {
|
|
126
136
|
const missing = { ok: false as const, status: 401, message: 'A connection key or internal token is required' };
|
|
127
137
|
return async (token) => {
|
|
@@ -136,6 +146,9 @@ export function createTokenVerifier(
|
|
|
136
146
|
if (internalTokenService.looksLikeInternalToken(token)) {
|
|
137
147
|
const claim = internalTokenService.verify(token);
|
|
138
148
|
if (!claim) return { ok: false, status: 401, message: 'Invalid or expired internal token' };
|
|
149
|
+
if (accounts && !(await accounts.isActive(claim.userId))) {
|
|
150
|
+
return { ok: false, status: 401, message: ACCOUNT_DEACTIVATED_MESSAGE };
|
|
151
|
+
}
|
|
139
152
|
return claim.externalProxy
|
|
140
153
|
? {
|
|
141
154
|
ok: true,
|
|
@@ -170,8 +183,9 @@ export function createTokenVerifier(
|
|
|
170
183
|
export function createToolAuthMiddleware(
|
|
171
184
|
externalApiKeyService: IExternalApiKeyService,
|
|
172
185
|
internalTokenService: InternalTokenService,
|
|
186
|
+
accounts?: Pick<AuthService, 'isActive'>,
|
|
173
187
|
): RequestHandler {
|
|
174
|
-
const verify = createTokenVerifier(externalApiKeyService, internalTokenService);
|
|
188
|
+
const verify = createTokenVerifier(externalApiKeyService, internalTokenService, accounts);
|
|
175
189
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
176
190
|
const header = req.headers.authorization;
|
|
177
191
|
if (!header || !header.toLowerCase().startsWith('bearer ')) {
|
|
@@ -210,9 +224,9 @@ export function createToolAuthMiddleware(
|
|
|
210
224
|
export function createManualAuthMiddleware(
|
|
211
225
|
externalApiKeyService: IExternalApiKeyService,
|
|
212
226
|
internalTokenService: InternalTokenService,
|
|
213
|
-
authService: AuthService,
|
|
227
|
+
authService: Pick<AuthService, 'isActive' | 'resolveSession'>,
|
|
214
228
|
): RequestHandler {
|
|
215
|
-
const verify = createTokenVerifier(externalApiKeyService, internalTokenService);
|
|
229
|
+
const verify = createTokenVerifier(externalApiKeyService, internalTokenService, authService);
|
|
216
230
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
217
231
|
const header = req.headers.authorization;
|
|
218
232
|
if (!header || !header.toLowerCase().startsWith('bearer ')) {
|
|
@@ -241,13 +255,22 @@ export function createManualAuthMiddleware(
|
|
|
241
255
|
}
|
|
242
256
|
|
|
243
257
|
// Otherwise treat the bearer as a browser session JWT.
|
|
258
|
+
let userId: string;
|
|
244
259
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
260
|
+
({ userId } = await authService.resolveSession(token));
|
|
261
|
+
} catch (err) {
|
|
262
|
+
// The account could not be looked up: an outage, not a verdict on the
|
|
263
|
+
// session, and a 401 would sign a valid caller out over it.
|
|
264
|
+
if (err instanceof AuthBackendError) {
|
|
265
|
+
log.error('session account lookup failed:', { err: err.cause });
|
|
266
|
+
res.status(500).json({ error: 'Authentication backend unavailable' });
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
249
269
|
res.setHeader('WWW-Authenticate', WWW_AUTH);
|
|
250
270
|
res.status(401).json({ error: 'Invalid or expired token' });
|
|
271
|
+
return;
|
|
251
272
|
}
|
|
273
|
+
req.toolAuth = { source: 'session', userId, scope: 'write' };
|
|
274
|
+
next();
|
|
252
275
|
};
|
|
253
276
|
}
|
|
@@ -12,6 +12,7 @@ import { createToolHandlerFactory } from '../tool-handler.js';
|
|
|
12
12
|
import { registerWorkspaceTools } from '../../workspace/workspace.tools.js';
|
|
13
13
|
import { RoutineWritePolicyService } from '../../workspace/routine-write-policy.js';
|
|
14
14
|
import { WorkflowHooks } from '../../workflow/workflow-hooks.js';
|
|
15
|
+
import { ToolDescriptionNotes } from '../../workspace/agent-access.gate.js';
|
|
15
16
|
import { SpillStore } from '../../workspace/spill-store.js';
|
|
16
17
|
import { DocExtractService } from '../../workspace/file-readers/doc-extract.service.js';
|
|
17
18
|
import { NEW_ROLE_GUIDANCE } from '../../access-model/roles-yaml-guard.js';
|
|
@@ -68,13 +69,14 @@ async function start(): Promise<string> {
|
|
|
68
69
|
canOwner: async () => true,
|
|
69
70
|
canWriteBatchAtRef: async () => null,
|
|
70
71
|
canReadBatch: async (_w: string, _u: string, paths: string[]) => new Map(paths.map((p) => [p, true])),
|
|
72
|
+
// The `after` half of a move's or copy's preview — allowing everything, like the gates above.
|
|
73
|
+
previewAccessAfterRelocation: async () => ({ read: true, write: true, download: true, owner: true }),
|
|
71
74
|
} as never;
|
|
72
75
|
registerWorkspaceTools(registry, router, toolAuth, createToolHandlerFactory(resolve), new SpillStore(path.join(os.tmpdir(), 'bevel-test-spills')), new DocExtractService(docCache), allowAll, testKbContext({ kbDirName: KB }), {
|
|
73
|
-
|
|
74
|
-
enabled: false, // ontology boundary not under test here
|
|
75
|
-
kb: testKbContext({ kbDirName: KB }),
|
|
76
|
+
// No hook registered: the agent-access gate refuses nothing here.
|
|
76
77
|
recoveryBotEmail: 'recovery-bot@bevel.local',
|
|
77
78
|
hooks: new WorkflowHooks(),
|
|
79
|
+
notes: new ToolDescriptionNotes(),
|
|
78
80
|
}, new RoutineWritePolicyService(), {} as never);
|
|
79
81
|
const app = express();
|
|
80
82
|
app.use(express.json());
|
|
@@ -10,6 +10,7 @@ import { createManualRoutes } from '../../tool-registry/manual.routes.js';
|
|
|
10
10
|
import { registerWorkspaceTools } from '../../workspace/workspace.tools.js';
|
|
11
11
|
import { RoutineWritePolicyService } from '../../workspace/routine-write-policy.js';
|
|
12
12
|
import { WorkflowHooks } from '../../workflow/workflow-hooks.js';
|
|
13
|
+
import { ToolDescriptionNotes } from '../../workspace/agent-access.gate.js';
|
|
13
14
|
import { SpillStore } from '../../workspace/spill-store.js';
|
|
14
15
|
import { DocExtractService } from '../../workspace/file-readers/doc-extract.service.js';
|
|
15
16
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
@@ -65,11 +66,10 @@ async function start(): Promise<string> {
|
|
|
65
66
|
// and their web_search / submit_feedback / cite_kb_node cases — moved to
|
|
66
67
|
// the enterprise repo with their modules; core registers only core tools.
|
|
67
68
|
registerWorkspaceTools(registry, router, toolAuth, th, new SpillStore('/tmp/bevel-test-spills'), new DocExtractService('/tmp/bevel-test-doc-extract'), accessControl, testKbContext(), {
|
|
68
|
-
|
|
69
|
-
enabled: false, // ontology boundary not under test here
|
|
70
|
-
kb: testKbContext(),
|
|
69
|
+
// No hook registered: the agent-access gate refuses nothing here.
|
|
71
70
|
recoveryBotEmail: 'recovery-bot@bevel.local',
|
|
72
71
|
hooks: new WorkflowHooks(),
|
|
72
|
+
notes: new ToolDescriptionNotes(),
|
|
73
73
|
}, new RoutineWritePolicyService(), {} as never /* sessionSink — start_session not exercised here */);
|
|
74
74
|
router.use(createManualRoutes(registry, toolAuth));
|
|
75
75
|
|
|
@@ -18,7 +18,11 @@ const externalApiKeyService = {
|
|
|
18
18
|
verifyAndLoadToken: async (t: string) =>
|
|
19
19
|
t === 'bevel_ok' ? { user: { id: 'u-ext', email: 'e@x', name: 'Ext' }, tokenId: 'tk-1' } : null,
|
|
20
20
|
} as never;
|
|
21
|
-
|
|
21
|
+
// Every account is on except `u-off`, which an admin switched off.
|
|
22
|
+
const authService = {
|
|
23
|
+
getUserById: async (id: string) => ({ id, email: 'e@x', name: 'N' }),
|
|
24
|
+
isActive: async (id: string) => id !== 'u-off',
|
|
25
|
+
} as never;
|
|
22
26
|
|
|
23
27
|
let tempDir = '';
|
|
24
28
|
const workspaceService = {
|
|
@@ -66,6 +70,12 @@ describe('validateToken (tool-author SDK entry)', () => {
|
|
|
66
70
|
expect((await fs.readFile('a.md')).toString()).toBe('hi');
|
|
67
71
|
});
|
|
68
72
|
|
|
73
|
+
it('throws a 401 ToolError for a valid internal token whose account was switched off', async () => {
|
|
74
|
+
const err = await validateToken(internalToken.mint({ userId: 'u-off' })).catch((e) => e);
|
|
75
|
+
expect(hasHttpStatus(err) && err.status).toBe(401);
|
|
76
|
+
expect((err as Error).message).toContain('switched off');
|
|
77
|
+
});
|
|
78
|
+
|
|
69
79
|
it('throws a 401 ToolError on a bad token and an empty token', async () => {
|
|
70
80
|
for (const bad of ['garbage', 'bevel_nope', '', undefined]) {
|
|
71
81
|
const err = await validateToken(bad as string).catch((e) => e);
|
|
@@ -6,6 +6,7 @@ import { hasHttpStatus, ToolError, type ToolHandler } from './tool.contract.js';
|
|
|
6
6
|
import { WorkflowDomainError } from '../../shared/domain-errors.js';
|
|
7
7
|
import { domainErrorBody } from '../../shared/http-errors.js';
|
|
8
8
|
import type { ResolveToolContext } from './tool-context.js';
|
|
9
|
+
import { alwaysWritable, READ_ONLY_CODE, refuseWriteTool, type IWriteAccess } from '../write-access/write-access.js';
|
|
9
10
|
import '../tool-auth/tool-auth.middleware.js'; // Express Request.toolAuth augmentation
|
|
10
11
|
|
|
11
12
|
function isAsyncIterable(v: unknown): v is AsyncIterable<unknown> {
|
|
@@ -29,7 +30,7 @@ export interface ToolHandlerOptions {
|
|
|
29
30
|
* The handler receives `req.body` as the flat args (UTCP's `body_field` already
|
|
30
31
|
* delivered the inner body as the request body).
|
|
31
32
|
*/
|
|
32
|
-
export function createToolHandlerFactory(resolve: ResolveToolContext) {
|
|
33
|
+
export function createToolHandlerFactory(resolve: ResolveToolContext, writeAccess: IWriteAccess = alwaysWritable) {
|
|
33
34
|
return function toolHandler(handler: ToolHandler, opts: ToolHandlerOptions = {}) {
|
|
34
35
|
return async (req: Request, res: Response): Promise<void> => {
|
|
35
36
|
const auth = req.toolAuth;
|
|
@@ -41,10 +42,29 @@ export function createToolHandlerFactory(resolve: ResolveToolContext) {
|
|
|
41
42
|
res.status(403).json({ error: 'This tool requires write access.' });
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
45
|
+
// Before anything is awaited: a client that goes away during the
|
|
46
|
+
// write-access check below must still abort the call.
|
|
47
|
+
//
|
|
48
|
+
// Asked of the RESPONSE. The request closes as soon as its body has
|
|
49
|
+
// been read, with the client still there and waiting, so its `close`
|
|
50
|
+
// says nothing about the client; the response closes when it has been
|
|
51
|
+
// sent or the connection is gone, and `writableEnded` tells the two
|
|
52
|
+
// apart.
|
|
44
53
|
const abort = new AbortController();
|
|
45
|
-
|
|
54
|
+
res.on('close', () => {
|
|
46
55
|
if (!res.writableEnded) abort.abort();
|
|
47
56
|
});
|
|
57
|
+
// The tool layer's half of the read-only gate: the HTTP gate lets every
|
|
58
|
+
// tool call through, since only here is a write tool told from a read.
|
|
59
|
+
if (opts.write) {
|
|
60
|
+
const refusal = await refuseWriteTool(writeAccess);
|
|
61
|
+
if (refusal !== null) {
|
|
62
|
+
res.status(403).json({ error: refusal, code: READ_ONLY_CODE });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// The client left while the verdict was awaited: nobody to answer.
|
|
66
|
+
if (abort.signal.aborted) return;
|
|
67
|
+
}
|
|
48
68
|
const body: unknown = req.body;
|
|
49
69
|
if (body !== undefined && body !== null && (typeof body !== 'object' || Array.isArray(body))) {
|
|
50
70
|
res.status(400).json({ error: 'Request body must be a JSON object.' });
|
|
@@ -53,8 +73,8 @@ export function createToolHandlerFactory(resolve: ResolveToolContext) {
|
|
|
53
73
|
const args = (body ?? {}) as Record<string, unknown>;
|
|
54
74
|
// `sessionId` rides the tool body like `branch` does: the external MCP
|
|
55
75
|
// proxy injects it (ask-tool continuity convention) and the in-process
|
|
56
|
-
// agent passes its thread id. Surfaced on the context so the
|
|
57
|
-
// gate can
|
|
76
|
+
// agent passes its thread id. Surfaced on the context so the
|
|
77
|
+
// agent-access gate can name the run without each handler re-reading args.
|
|
58
78
|
const sessionId = typeof args.sessionId === 'string' && args.sessionId.length > 0
|
|
59
79
|
? args.sessionId
|
|
60
80
|
: undefined;
|
|
@@ -34,8 +34,9 @@ export interface ToolContext {
|
|
|
34
34
|
* The agent run / conversation id this call belongs to (the agent thread id).
|
|
35
35
|
* Supplied as a `sessionId` field on the tool's input body — the external MCP
|
|
36
36
|
* proxy injects it via the `ask`-tool continuity convention, and the
|
|
37
|
-
* in-process agent passes its thread id.
|
|
38
|
-
*
|
|
37
|
+
* in-process agent passes its thread id. Handed to the agent-access hooks,
|
|
38
|
+
* which is how a deployment scopes a rule to one conversation. Absent for
|
|
39
|
+
* non-agent or unthreaded calls.
|
|
39
40
|
*/
|
|
40
41
|
sessionId?: string;
|
|
41
42
|
/**
|
|
@@ -35,7 +35,7 @@ export type ValidateToken = (
|
|
|
35
35
|
) => Promise<ToolContext>;
|
|
36
36
|
|
|
37
37
|
export function createToolValidator(deps: ToolValidatorDeps): ValidateToken {
|
|
38
|
-
const verify = createTokenVerifier(deps.externalApiKeyService, deps.internalTokenService);
|
|
38
|
+
const verify = createTokenVerifier(deps.externalApiKeyService, deps.internalTokenService, deps.authService);
|
|
39
39
|
const resolve = createToolContextResolver(deps);
|
|
40
40
|
return async (token, signal) => {
|
|
41
41
|
let result;
|
|
@@ -65,32 +65,24 @@ export function registerToolManualsTools(
|
|
|
65
65
|
|
|
66
66
|
const listSetupDef = toolDef({
|
|
67
67
|
name: 'list_tool_setup',
|
|
68
|
+
// What each FIELD means is documented on the field, in `outputs` below:
|
|
69
|
+
// `setup.kind`, `variables` and `invalid` each carried a paragraph here,
|
|
70
|
+
// which made this description two thousand characters and so the first
|
|
71
|
+
// thing a client cut. The description says what the tool answers and the
|
|
72
|
+
// three things an agent cannot read off a field.
|
|
68
73
|
description:
|
|
69
|
-
'Configuration status of every `.tool` the current user can access: what each tool needs set up and ' +
|
|
70
|
-
'
|
|
71
|
-
'absent entirely, and
|
|
72
|
-
'
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'from its frontmatter `write:`/`owner:` verbs and the access.md chain — NOT a platform role), which ' +
|
|
82
|
-
'is exactly what gates setting its shared secrets: the people who manage the file configure the tool. ' +
|
|
83
|
-
'Secret VALUES are never returned and can never be set through a tool — an admin enters them in the ' +
|
|
84
|
-
'tool editor; users sign in on /connect. ' +
|
|
85
|
-
'`invalid` names any `.tool` file the scan REFUSED, with the reason and its location: those files ' +
|
|
86
|
-
'are the only ones missing — every other tool is listed and callable, and a refused file is listed ' +
|
|
87
|
-
'again as a normal tool on the next call once it is fixed (or removed), with nothing to restart or ' +
|
|
88
|
-
'reconnect. ' +
|
|
89
|
-
'The listing is the RELEASED catalog, built from the default branch only: a server or `.tool` you ' +
|
|
90
|
-
'declared on a draft is not listed, not callable and not signed-in-able until that draft is merged. ' +
|
|
91
|
-
'Pass `branch` (the draft you wrote the declaration on) and `onBranchOnly` names every tool declared ' +
|
|
92
|
-
'there that the default branch does not serve yet — open a change request, then ask the user to ' +
|
|
93
|
-
'review and merge it in the app to activate it.',
|
|
74
|
+
'Configuration status of every `.tool` the current user can access: what each tool needs set up and what is ' +
|
|
75
|
+
'already configured, as `{ tools, invalid, onBranchOnly, note? }`. Scoped to the CALLER — a `.tool` it cannot ' +
|
|
76
|
+
'READ is absent entirely, and every flag is the caller\'s own state. ' +
|
|
77
|
+
'Secret VALUES are never returned and can never be set through a tool: an admin enters them in the tool editor, ' +
|
|
78
|
+
'and users sign in on /connect rather than typing a value. ' +
|
|
79
|
+
'What gates setting a tool\'s shared secrets is `canWrite` on the `.tool` FILE — per-file access from its ' +
|
|
80
|
+
'frontmatter `write:`/`owner:` verbs and the access.md chain, NOT a platform role: the people who manage the ' +
|
|
81
|
+
'file configure the tool. ' +
|
|
82
|
+
'The listing is the RELEASED catalog, built from the default branch only: a server or `.tool` you declared on a ' +
|
|
83
|
+
'draft is not listed, not callable and not signed-in-able until that draft is merged. Pass `branch` (the draft ' +
|
|
84
|
+
'you wrote the declaration on) and `onBranchOnly` names every tool declared there that the default branch does ' +
|
|
85
|
+
'not serve yet — open a change request, then ask the user to review and merge it in the app to activate it.',
|
|
94
86
|
path: '/api/agent/tools/list_tool_setup',
|
|
95
87
|
inputs: {
|
|
96
88
|
type: 'object',
|
|
@@ -119,12 +111,32 @@ export function registerToolManualsTools(
|
|
|
119
111
|
type: { type: 'string' },
|
|
120
112
|
setup: {
|
|
121
113
|
type: ['object', 'null'],
|
|
122
|
-
description:
|
|
123
|
-
properties: {
|
|
114
|
+
description: "An MCP server's sign-in requirement; null for non-mcp tools.",
|
|
115
|
+
properties: {
|
|
116
|
+
kind: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description:
|
|
119
|
+
'`open` = no sign-in; `oauth-auto` = sign-in was configured automatically; `oauth-manual` = the ' +
|
|
120
|
+
'sign-in needs an OAuth app the owner registers with the provider: a writer declares its client ' +
|
|
121
|
+
'id on a `user`-scoped variable with an `oauth` block — in the plugin.json extensions entry for ' +
|
|
122
|
+
'an mcp.json server (endpoints are discovered from the server; PKCE is on by default), or in the ' +
|
|
123
|
+
'`.tool` file with explicit URLs — and pastes the client secret on the tool\'s page.',
|
|
124
|
+
},
|
|
125
|
+
reason: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
description: 'Present only while something still blocks the sign-in, and says what.',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
canWrite: {
|
|
132
|
+
type: 'boolean',
|
|
133
|
+
description: 'You may write this `.tool` FILE, which is what gates setting its shared secrets.',
|
|
124
134
|
},
|
|
125
|
-
canWrite: { type: 'boolean' },
|
|
126
135
|
variables: {
|
|
127
136
|
type: 'array',
|
|
137
|
+
description:
|
|
138
|
+
'What this tool needs configured, and by whom: per variable, whether the shared (admin) value is ' +
|
|
139
|
+
'set and whether the CURRENT user has set or authorized their own.',
|
|
128
140
|
items: {
|
|
129
141
|
type: 'object',
|
|
130
142
|
properties: {
|
|
@@ -150,9 +162,10 @@ export function registerToolManualsTools(
|
|
|
150
162
|
invalid: {
|
|
151
163
|
type: 'array',
|
|
152
164
|
description:
|
|
153
|
-
'`.tool` files the scan refused — the ONLY tools missing from `tools
|
|
154
|
-
'why it was refused, with the line/column or field where the validation
|
|
155
|
-
'delete it) and the next call lists it as a normal tool
|
|
165
|
+
'`.tool` files the scan refused — the ONLY tools missing from `tools`; every other tool is listed and ' +
|
|
166
|
+
'callable. Each names the file and why it was refused, with the line/column or field where the validation ' +
|
|
167
|
+
'failed. Fix the file (or delete it) and the next call lists it as a normal tool, with nothing to restart ' +
|
|
168
|
+
'or reconnect. Never contains a secret value.',
|
|
156
169
|
items: {
|
|
157
170
|
type: 'object',
|
|
158
171
|
properties: {
|