@bevel-software/platform-core-backend 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-guide/access-control.md +17 -0
- package/agent-guide/conventions.md +8 -0
- package/agent-guide/html-views.md +83 -0
- package/agent-guide/new-knowledge-base.md +24 -0
- package/agent-guide/tool-manuals.md +10 -0
- package/dist/assets.d.ts +19 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +26 -2
- package/dist/assets.js.map +1 -1
- package/dist/core/catalog-revision.d.ts +19 -1
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +14 -3
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/create-core-server.d.ts +5 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +56 -5
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +33 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +105 -8
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +16 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +18 -1
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +21 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +22 -1
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +17 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +2 -0
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +31 -0
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +4 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +66 -7
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +1 -1
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +35 -6
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +36 -9
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access/user-access-removal.service.js +1 -1
- package/dist/modules/access/user-access-removal.service.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +6 -0
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.js +8 -0
- package/dist/modules/agent-guide/agent-guide.js.map +1 -1
- package/dist/modules/agent-guide/index.d.ts +1 -1
- package/dist/modules/agent-guide/index.d.ts.map +1 -1
- package/dist/modules/agent-guide/index.js +1 -1
- package/dist/modules/agent-guide/index.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +5 -0
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +19 -2
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +19 -19
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/audit/agent-audit.service.d.ts +21 -2
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +43 -2
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/agent-connection.routes.d.ts +34 -0
- package/dist/modules/audit/agent-connection.routes.d.ts.map +1 -0
- package/dist/modules/audit/agent-connection.routes.js +38 -0
- package/dist/modules/audit/agent-connection.routes.js.map +1 -0
- package/dist/modules/audit/audit.contract.d.ts +24 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/audit/index.d.ts +1 -0
- package/dist/modules/audit/index.d.ts.map +1 -1
- package/dist/modules/audit/index.js +1 -0
- package/dist/modules/audit/index.js.map +1 -1
- package/dist/modules/auth/account-hooks.d.ts +46 -0
- package/dist/modules/auth/account-hooks.d.ts.map +1 -0
- package/dist/modules/auth/account-hooks.js +25 -0
- package/dist/modules/auth/account-hooks.js.map +1 -0
- package/dist/modules/auth/account-switch-guard.d.ts +15 -0
- package/dist/modules/auth/account-switch-guard.d.ts.map +1 -0
- package/dist/modules/auth/account-switch-guard.js +2 -0
- package/dist/modules/auth/account-switch-guard.js.map +1 -0
- package/dist/modules/auth/account-switch-lock.d.ts +34 -0
- package/dist/modules/auth/account-switch-lock.d.ts.map +1 -0
- package/dist/modules/auth/account-switch-lock.js +48 -0
- package/dist/modules/auth/account-switch-lock.js.map +1 -0
- package/dist/modules/auth/account.routes.d.ts +4 -1
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +110 -13
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +7 -0
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +1 -1
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +101 -6
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +246 -51
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/password-hash.d.ts +5 -2
- package/dist/modules/auth/password-hash.d.ts.map +1 -1
- package/dist/modules/auth/password-hash.js +5 -2
- package/dist/modules/auth/password-hash.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +11 -3
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +96 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +32 -3
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/pg-error.d.ts +9 -0
- package/dist/modules/database/pg-error.d.ts.map +1 -0
- package/dist/modules/database/pg-error.js +17 -0
- package/dist/modules/database/pg-error.js.map +1 -0
- package/dist/modules/embed/account-link.service.d.ts +42 -0
- package/dist/modules/embed/account-link.service.d.ts.map +1 -0
- package/dist/modules/embed/account-link.service.js +50 -0
- package/dist/modules/embed/account-link.service.js.map +1 -0
- package/dist/modules/embed/embed-link.d.ts +58 -0
- package/dist/modules/embed/embed-link.d.ts.map +1 -0
- package/dist/modules/embed/embed-link.js +160 -0
- package/dist/modules/embed/embed-link.js.map +1 -0
- package/dist/modules/embed/embed.errors.d.ts +30 -0
- package/dist/modules/embed/embed.errors.d.ts.map +1 -0
- package/dist/modules/embed/embed.errors.js +42 -0
- package/dist/modules/embed/embed.errors.js.map +1 -0
- package/dist/modules/embed/embed.interface.d.ts +204 -0
- package/dist/modules/embed/embed.interface.d.ts.map +1 -0
- package/dist/modules/embed/embed.interface.js +2 -0
- package/dist/modules/embed/embed.interface.js.map +1 -0
- package/dist/modules/embed/embed.routes.d.ts +59 -0
- package/dist/modules/embed/embed.routes.d.ts.map +1 -0
- package/dist/modules/embed/embed.routes.js +343 -0
- package/dist/modules/embed/embed.routes.js.map +1 -0
- package/dist/modules/embed/embed.service.d.ts +169 -0
- package/dist/modules/embed/embed.service.d.ts.map +1 -0
- package/dist/modules/embed/embed.service.js +627 -0
- package/dist/modules/embed/embed.service.js.map +1 -0
- package/dist/modules/embed/embed.tools.d.ts +51 -0
- package/dist/modules/embed/embed.tools.d.ts.map +1 -0
- package/dist/modules/embed/embed.tools.js +170 -0
- package/dist/modules/embed/embed.tools.js.map +1 -0
- package/dist/modules/embed/mcp-app.d.ts +64 -0
- package/dist/modules/embed/mcp-app.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.js +147 -0
- package/dist/modules/embed/mcp-app.js.map +1 -0
- package/dist/modules/embed/mcp-app.routes.d.ts +21 -0
- package/dist/modules/embed/mcp-app.routes.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.routes.js +36 -0
- package/dist/modules/embed/mcp-app.routes.js.map +1 -0
- package/dist/modules/github-app/github-app.connection.d.ts +15 -0
- package/dist/modules/github-app/github-app.connection.d.ts.map +1 -1
- package/dist/modules/github-app/github-app.connection.js +34 -3
- package/dist/modules/github-app/github-app.connection.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts +65 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +300 -0
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +5 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.js +6 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +2 -1
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +28 -1
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +64 -8
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +32 -4
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/onboarding/onboarding.contract.d.ts +80 -0
- package/dist/modules/onboarding/onboarding.contract.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.contract.js +7 -0
- package/dist/modules/onboarding/onboarding.contract.js.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts +18 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.js +81 -0
- package/dist/modules/onboarding/onboarding.routes.js.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts +213 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.js +492 -0
- package/dist/modules/onboarding/starter-pack.service.js.map +1 -0
- package/dist/modules/onboarding/starter-packs.d.ts +78 -0
- package/dist/modules/onboarding/starter-packs.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-packs.js +156 -0
- package/dist/modules/onboarding/starter-packs.js.map +1 -0
- 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/plugin-provision.service.d.ts +19 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +18 -1
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +0 -36
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +31 -0
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +34 -0
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +134 -1
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/repository-source.d.ts +1 -0
- package/dist/modules/settings/repository-source.d.ts.map +1 -1
- package/dist/modules/settings/repository-source.js +3 -3
- package/dist/modules/settings/repository-source.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +50 -32
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts +10 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.js +11 -3
- package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +8 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +24 -2
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +59 -2
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +9 -4
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/index.d.ts +28 -1
- package/dist/modules/tool-helpers/index.d.ts.map +1 -1
- package/dist/modules/tool-helpers/index.js +28 -1
- package/dist/modules/tool-helpers/index.js.map +1 -1
- package/dist/modules/tool-helpers/route-argument-check.d.ts +10 -0
- package/dist/modules/tool-helpers/route-argument-check.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-argument-check.js +132 -0
- package/dist/modules/tool-helpers/route-argument-check.js.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts +89 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js +113 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js.map +1 -0
- package/dist/modules/tool-helpers/tool-def.d.ts +71 -0
- package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-def.js +70 -6
- package/dist/modules/tool-helpers/tool-def.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +46 -6
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +157 -8
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +9 -0
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +14 -5
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +6 -2
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
- package/dist/modules/tool-registry/description-length.js +27 -5
- package/dist/modules/tool-registry/description-length.js.map +1 -1
- package/dist/modules/tool-registry/guide-first.d.ts +4 -0
- package/dist/modules/tool-registry/guide-first.d.ts.map +1 -1
- package/dist/modules/tool-registry/guide-first.js +9 -1
- package/dist/modules/tool-registry/guide-first.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +53 -0
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js +97 -68
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +8 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +124 -37
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +29 -5
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +62 -5
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +72 -1
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +172 -36
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +15 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +78 -6
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +15 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +25 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +6 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +23 -6
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +1 -1
- 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 +2 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/sanitize-error.d.ts +16 -0
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
- package/dist/modules/workflow/sanitize-error.js +45 -0
- package/dist/modules/workflow/sanitize-error.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +183 -15
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +807 -107
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-download.builder.d.ts +105 -0
- package/dist/modules/workspace/agent-download.builder.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.builder.js +238 -0
- package/dist/modules/workspace/agent-download.builder.js.map +1 -0
- package/dist/modules/workspace/agent-download.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-download.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.routes.js +156 -0
- package/dist/modules/workspace/agent-download.routes.js.map +1 -0
- package/dist/modules/workspace/agent-download.store.d.ts +225 -0
- package/dist/modules/workspace/agent-download.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.store.js +423 -0
- package/dist/modules/workspace/agent-download.store.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +10 -1
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -1
- package/dist/modules/workspace/agent-upload.store.js +16 -6
- package/dist/modules/workspace/agent-upload.store.js.map +1 -1
- package/dist/modules/workspace/file-readers/raw-mime.d.ts +6 -0
- package/dist/modules/workspace/file-readers/raw-mime.d.ts.map +1 -0
- package/dist/modules/workspace/file-readers/raw-mime.js +38 -0
- package/dist/modules/workspace/file-readers/raw-mime.js.map +1 -0
- package/dist/modules/workspace/file-readers/text-reader.d.ts +5 -5
- package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.js +5 -8
- package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
- package/dist/modules/workspace/first-run.d.ts +111 -0
- package/dist/modules/workspace/first-run.d.ts.map +1 -0
- package/dist/modules/workspace/first-run.js +181 -0
- package/dist/modules/workspace/first-run.js.map +1 -0
- package/dist/modules/workspace/move-links.d.ts +92 -0
- package/dist/modules/workspace/move-links.d.ts.map +1 -0
- package/dist/modules/workspace/move-links.js +173 -0
- package/dist/modules/workspace/move-links.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +3 -13
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +63 -22
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +75 -2
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +197 -24
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +59 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +443 -65
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/write-access/write-access.d.ts.map +1 -1
- package/dist/modules/write-access/write-access.js +9 -0
- package/dist/modules/write-access/write-access.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +73 -1
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +86 -12
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +10 -1
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/utf8-text.d.ts +15 -0
- package/dist/shared/utf8-text.d.ts.map +1 -0
- package/dist/shared/utf8-text.js +20 -0
- package/dist/shared/utf8-text.js.map +1 -0
- package/dist/tenancy/static-tenant-source.d.ts +2 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +9 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/mcp-app/page.html +349 -0
- package/migrations/0017_atlassian_account_links.sql +37 -0
- package/migrations/meta/0017_snapshot.json +2396 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +5 -3
- package/src/__tests__/packaged-assets-ship.test.ts +16 -4
- package/src/__tests__/pg-scratch.ts +48 -0
- package/src/assets.ts +28 -2
- package/src/core/__tests__/catalog-revision.test.ts +40 -0
- package/src/core/__tests__/gated-boot.test.ts +1 -0
- package/src/core/__tests__/lifecycle.test.ts +14 -0
- package/src/core/catalog-revision.ts +15 -2
- package/src/core/create-core-server.ts +69 -3
- package/src/core/create-core-services.ts +151 -6
- package/src/core/lifecycle.ts +28 -2
- package/src/core-config.ts +24 -1
- package/src/index.ts +2 -2
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +1 -0
- package/src/modules/access/__tests__/access-control.service.test.ts +19 -0
- package/src/modules/access/__tests__/access.routes.admins.test.ts +203 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +25 -5
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +1 -1
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +1 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +1 -1
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +1 -0
- package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
- package/src/modules/access/__tests__/roles-admin.service.test.ts +15 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +132 -7
- package/src/modules/access/__tests__/user-access-removal.service.test.ts +2 -2
- package/src/modules/access/__tests__/users-db-double.ts +12 -3
- package/src/modules/access/access-control.interface.ts +23 -0
- package/src/modules/access/access-control.service.ts +35 -0
- package/src/modules/access/access.routes.ts +67 -6
- package/src/modules/access/directory-sync-bot.ts +1 -1
- package/src/modules/access/roles-admin.service.ts +51 -11
- package/src/modules/access/user-access-removal.service.ts +1 -1
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +58 -0
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +19 -1
- package/src/modules/agent-guide/agent-guide.ts +9 -0
- package/src/modules/agent-guide/index.ts +1 -0
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +14 -0
- package/src/modules/agent-instructions/compose.ts +17 -2
- package/src/modules/agent-instructions/shared-file-rules.ts +19 -19
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +64 -4
- package/src/modules/audit/__tests__/agent-connection.routes.test.ts +102 -0
- package/src/modules/audit/agent-audit.service.ts +58 -3
- package/src/modules/audit/agent-connection.routes.ts +56 -0
- package/src/modules/audit/audit.contract.ts +26 -0
- package/src/modules/audit/index.ts +1 -0
- package/src/modules/auth/__tests__/account-deactivation.test.ts +145 -16
- package/src/modules/auth/__tests__/account-hooks.test.ts +57 -0
- package/src/modules/auth/__tests__/account-switch-lock.pg.test.ts +78 -0
- package/src/modules/auth/__tests__/account-switch-lock.test.ts +43 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +387 -9
- package/src/modules/auth/__tests__/auth.service.created.pg.test.ts +69 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +159 -1
- package/src/modules/auth/account-hooks.ts +69 -0
- package/src/modules/auth/account-switch-guard.ts +14 -0
- package/src/modules/auth/account-switch-lock.ts +58 -0
- package/src/modules/auth/account.routes.ts +122 -13
- package/src/modules/auth/auth.middleware.ts +1 -1
- package/src/modules/auth/auth.service.ts +268 -52
- package/src/modules/auth/password-hash.ts +5 -2
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +18 -2
- package/src/modules/code-mode/code-mode.tool.ts +13 -2
- package/src/modules/database/__tests__/pg-error.test.ts +21 -0
- package/src/modules/database/__tests__/pii-backfill.pg.test.ts +6 -27
- package/src/modules/database/core-schema.ts +33 -3
- package/src/modules/database/pg-error.ts +15 -0
- package/src/modules/embed/__tests__/account-links.migration.test.ts +98 -0
- package/src/modules/embed/__tests__/embed-link.test.ts +122 -0
- package/src/modules/embed/__tests__/embed.routes.test.ts +362 -0
- package/src/modules/embed/__tests__/embed.service.lock.test.ts +193 -0
- package/src/modules/embed/__tests__/embed.service.test.ts +805 -0
- package/src/modules/embed/__tests__/embed.tools.test.ts +304 -0
- package/src/modules/embed/__tests__/mcp-app.test.ts +207 -0
- package/src/modules/embed/account-link.service.ts +76 -0
- package/src/modules/embed/embed-link.ts +180 -0
- package/src/modules/embed/embed.errors.ts +43 -0
- package/src/modules/embed/embed.interface.ts +203 -0
- package/src/modules/embed/embed.routes.ts +363 -0
- package/src/modules/embed/embed.service.ts +705 -0
- package/src/modules/embed/embed.tools.ts +206 -0
- package/src/modules/embed/mcp-app.routes.ts +41 -0
- package/src/modules/embed/mcp-app.ts +173 -0
- package/src/modules/github-app/__tests__/github-app.test.ts +76 -0
- package/src/modules/github-app/github-app.connection.ts +38 -4
- package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +304 -0
- package/src/modules/kb-fs/locking-filesystem.ts +287 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +35 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +2 -0
- package/src/modules/kb-sync/kb-sync.service.ts +6 -0
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +68 -11
- package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +37 -2
- package/src/modules/mcp/__tests__/mcp.app.e2e.test.ts +200 -0
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +3 -1
- package/src/modules/mcp/__tests__/mcp.service.test.ts +23 -13
- package/src/modules/mcp/__tests__/tool-call-interface.e2e.test.ts +499 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +2 -1
- package/src/modules/mcp/mcp.service.ts +81 -12
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +42 -4
- package/src/modules/onboarding/__tests__/onboarding.routes.test.ts +163 -0
- package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +635 -0
- package/src/modules/onboarding/__tests__/starter-packs.test.ts +169 -0
- package/src/modules/onboarding/onboarding.contract.ts +85 -0
- package/src/modules/onboarding/onboarding.routes.ts +85 -0
- package/src/modules/onboarding/starter-pack.service.ts +580 -0
- package/src/modules/onboarding/starter-packs.ts +182 -0
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +25 -2
- package/src/modules/plugins/index.ts +1 -0
- package/src/modules/plugins/plugin-provision.service.ts +22 -1
- package/src/modules/plugins/plugins.routes.ts +29 -0
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +150 -1
- package/src/modules/settings/__tests__/repository-source.test.ts +9 -0
- package/src/modules/settings/deployment-settings.service.ts +131 -1
- package/src/modules/settings/repository-source.ts +4 -4
- package/src/modules/skills/__tests__/skills.tools.description.test.ts +5 -1
- package/src/modules/skills/skills.tools.ts +55 -35
- package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +32 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +65 -0
- package/src/modules/tool-auth/connection-key-rejection.ts +13 -3
- package/src/modules/tool-auth/external-api-key.interface.ts +9 -0
- package/src/modules/tool-auth/external-api-key.service.ts +61 -4
- package/src/modules/tool-auth/tool-auth.middleware.ts +13 -4
- package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +502 -0
- package/src/modules/tool-helpers/__tests__/route-argument-check.test.ts +337 -0
- package/src/modules/tool-helpers/index.ts +34 -1
- package/src/modules/tool-helpers/route-argument-check.ts +149 -0
- package/src/modules/tool-helpers/route-tool-schemas.ts +171 -0
- package/src/modules/tool-helpers/tool-def.ts +132 -7
- package/src/modules/tool-helpers/tool-handler.ts +196 -8
- package/src/modules/tool-helpers/tool.contract.ts +9 -0
- package/src/modules/tool-manuals/tool-manuals.tools.ts +15 -5
- package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +14 -1
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +23 -10
- package/src/modules/tool-registry/description-length.ts +34 -7
- package/src/modules/tool-registry/guide-first.ts +8 -1
- package/src/modules/workflow/__tests__/file-lock.service.deletion-gate.test.ts +104 -0
- package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +66 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +31 -2
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +110 -6
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +89 -1
- package/src/modules/workflow/__tests__/workflow.routes.push-refused.test.ts +202 -0
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +6 -6
- package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +1026 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +468 -10
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +4 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +5 -4
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +54 -2
- package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +41 -5
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +127 -2
- package/src/modules/workflow/agent-tools/change-request-read.tools.ts +157 -122
- package/src/modules/workflow/agent-tools/workflow.tools.ts +129 -35
- package/src/modules/workflow/file-lock.service.ts +67 -5
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +164 -0
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +48 -1
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +231 -2
- package/src/modules/workflow/git/git.service.ts +176 -38
- package/src/modules/workflow/git/node-git-runner.ts +82 -7
- package/src/modules/workflow/pending-commits.service.ts +29 -1
- package/src/modules/workflow/pending-commits.worker.ts +24 -6
- package/src/modules/workflow/recovery-bot.ts +1 -1
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +2 -1
- package/src/modules/workflow/sanitize-error.ts +50 -0
- package/src/modules/workflow/workflow.service.ts +881 -113
- package/src/modules/workspace/__tests__/agent-downloads.test.ts +823 -0
- package/src/modules/workspace/__tests__/first-run.test.ts +234 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +5 -2
- package/src/modules/workspace/__tests__/md-links.test.ts +353 -0
- package/src/modules/workspace/__tests__/move-links.test.ts +261 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +219 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -0
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +102 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +52 -5
- package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +190 -0
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +8 -4
- package/src/modules/workspace/__tests__/workspace.service.test.ts +50 -8
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +44 -1
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +409 -12
- package/src/modules/workspace/agent-download.builder.ts +308 -0
- package/src/modules/workspace/agent-download.routes.ts +183 -0
- package/src/modules/workspace/agent-download.store.ts +533 -0
- package/src/modules/workspace/agent-upload.store.ts +23 -7
- package/src/modules/workspace/file-readers/raw-mime.ts +40 -0
- package/src/modules/workspace/file-readers/text-reader.ts +5 -8
- package/src/modules/workspace/first-run.ts +222 -0
- package/src/modules/workspace/move-links.ts +237 -0
- package/src/modules/workspace/startup/steps/seed-tree.ts +3 -12
- package/src/modules/workspace/workspace.routes.ts +64 -22
- package/src/modules/workspace/workspace.service.ts +202 -25
- package/src/modules/workspace/workspace.tools.ts +492 -63
- package/src/modules/write-access/__tests__/write-access.test.ts +13 -0
- package/src/modules/write-access/write-access.ts +9 -0
- package/src/shared/domain-errors.ts +108 -11
- package/src/shared/git.contract.ts +8 -1
- package/src/shared/utf8-text.ts +21 -0
- package/src/tenancy/__tests__/static-tenant-source.test.ts +1 -0
- package/src/tenancy/static-tenant-source.ts +10 -2
- package/starter-packs/README.md +103 -0
- package/starter-packs/engineering/KnowledgeBase/About us.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Architecture.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/How we ship.md +9 -0
- package/starter-packs/engineering/Plugins/engineering-starter/LICENSE +21 -0
- package/starter-packs/engineering/Plugins/engineering-starter/SOURCE.md +20 -0
- package/starter-packs/engineering/Plugins/engineering-starter/access.md +19 -0
- package/starter-packs/engineering/Plugins/engineering-starter/plugin.json +10 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/SKILL.md +171 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2a-startup-diagnostic.md +157 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2b-builder-brainstorm.md +39 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-ceo-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-eng-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/SKILL.md +80 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/checklist.md +184 -0
- package/starter-packs/engineering/pack.yaml +11 -0
- package/starter-packs/general/KnowledgeBase/About us.md +9 -0
- package/starter-packs/general/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/general/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/general/Plugins/general-starter/LICENSE +202 -0
- package/starter-packs/general/Plugins/general-starter/SOURCE.md +10 -0
- package/starter-packs/general/Plugins/general-starter/access.md +19 -0
- package/starter-packs/general/Plugins/general-starter/plugin.json +10 -0
- package/starter-packs/general/Plugins/general-starter/skills/memory-management/SKILL.md +332 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-start/SKILL.md +163 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-update/SKILL.md +167 -0
- package/starter-packs/general/Plugins/general-starter/skills/task-management/SKILL.md +83 -0
- package/starter-packs/general/pack.yaml +10 -0
- package/starter-packs/operations/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/operations/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Processes.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Tools we use.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Vendors.md +9 -0
- package/starter-packs/operations/Plugins/operations-starter/LICENSE +202 -0
- package/starter-packs/operations/Plugins/operations-starter/SOURCE.md +10 -0
- package/starter-packs/operations/Plugins/operations-starter/access.md +19 -0
- package/starter-packs/operations/Plugins/operations-starter/plugin.json +10 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/capacity-plan/SKILL.md +111 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/change-request/SKILL.md +119 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/compliance-tracking/SKILL.md +44 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-doc/SKILL.md +80 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-optimization/SKILL.md +39 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/risk-assessment/SKILL.md +40 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/runbook/SKILL.md +85 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/status-report/SKILL.md +75 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/vendor-review/SKILL.md +102 -0
- package/starter-packs/operations/pack.yaml +12 -0
- package/starter-packs/sales/KnowledgeBase/About us.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Customers.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/sales/KnowledgeBase/How we sell.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Objections.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Pricing.md +9 -0
- package/starter-packs/sales/Plugins/sales-starter/LICENSE +202 -0
- package/starter-packs/sales/Plugins/sales-starter/SOURCE.md +10 -0
- package/starter-packs/sales/Plugins/sales-starter/access.md +19 -0
- package/starter-packs/sales/Plugins/sales-starter/plugin.json +10 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-context/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-plan/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-research/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-tiering/SKILL.md +101 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-prep/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-summary/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/close-plan/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/competitive-intelligence/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/create-an-asset/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/crm-hygiene-check/SKILL.md +103 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-health/SKILL.md +108 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-voice/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/daily-briefing/SKILL.md +61 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-advance-gap/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-review/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-signals/SKILL.md +96 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-slip-scenario/SKILL.md +104 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/draft-outreach/SKILL.md +116 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/end-of-day/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/expansion-whitespace/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/forecast/SKILL.md +100 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/handle-objection/SKILL.md +93 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/inbox-sweep/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/lead-triage/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/log-activity/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/pipeline-review/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/renewal-radar/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/rep-context/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/route-lead/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/sales-setup/SKILL.md +200 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/schedule-meeting/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/stakeholder-map/SKILL.md +107 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/team-pipeline/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/update-opportunity/SKILL.md +112 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/weekly-wrap/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/win-loss-review/SKILL.md +88 -0
- package/starter-packs/sales/pack.yaml +13 -0
- package/starter-packs/support/KnowledgeBase/Escalations.md +9 -0
- package/starter-packs/support/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/support/KnowledgeBase/Known issues.md +9 -0
- package/starter-packs/support/KnowledgeBase/Products.md +9 -0
- package/starter-packs/support/KnowledgeBase/Tone of voice.md +9 -0
- package/starter-packs/support/Plugins/support-starter/LICENSE +202 -0
- package/starter-packs/support/Plugins/support-starter/SOURCE.md +10 -0
- package/starter-packs/support/Plugins/support-starter/access.md +19 -0
- package/starter-packs/support/Plugins/support-starter/plugin.json +10 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-escalation/SKILL.md +246 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-research/SKILL.md +250 -0
- package/starter-packs/support/Plugins/support-starter/skills/draft-response/SKILL.md +416 -0
- package/starter-packs/support/Plugins/support-starter/skills/kb-article/SKILL.md +350 -0
- package/starter-packs/support/Plugins/support-starter/skills/ticket-triage/SKILL.md +273 -0
- package/starter-packs/support/pack.yaml +12 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import jwt from 'jsonwebtoken';
|
|
2
|
-
import { and, eq, isNotNull, isNull } from 'drizzle-orm';
|
|
2
|
+
import { and, eq, getTableColumns, isNotNull, isNull, sql } from 'drizzle-orm';
|
|
3
3
|
import type { Database } from '../database/connection.js';
|
|
4
4
|
import { users } from '../database/schema.js';
|
|
5
5
|
import type { AuthUser } from '@bevel-software/platform-shared';
|
|
@@ -21,6 +21,9 @@ import {
|
|
|
21
21
|
} from './password-hash.js';
|
|
22
22
|
import { RECOVERY_BOT_EMAIL } from '../workflow/recovery-bot.js';
|
|
23
23
|
import { DIRECTORY_SYNC_BOT_EMAIL } from '../access/directory-sync-bot.js';
|
|
24
|
+
import { takeAccountSwitchLock } from './account-switch-lock.js';
|
|
25
|
+
import type { IAccountSwitchGuard } from './account-switch-guard.js';
|
|
26
|
+
import { WorkflowDomainError } from '../../shared/domain-errors.js';
|
|
24
27
|
|
|
25
28
|
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
26
29
|
|
|
@@ -35,6 +38,22 @@ const ACTIVE_CACHE_MS = 30_000;
|
|
|
35
38
|
/** The accounts core runs its own work as. Nobody signs in with them, and nobody switches them off. */
|
|
36
39
|
const SYSTEM_ACCOUNT_EMAILS: readonly string[] = [RECOVERY_BOT_EMAIL, DIRECTORY_SYNC_BOT_EMAIL];
|
|
37
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Whether an upsert's returned row was INSERTED rather than updated, from the
|
|
43
|
+
* one statement that did it: Postgres leaves `xmax` at zero on a row the
|
|
44
|
+
* statement inserted and sets it on one its `ON CONFLICT DO UPDATE` touched.
|
|
45
|
+
* So "is this account new" is answered by the write itself, never by a read
|
|
46
|
+
* before it that a concurrent create could make a lie of.
|
|
47
|
+
*/
|
|
48
|
+
const CREATED = sql<boolean>`(xmax = 0)`;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* What every password write refuses for the accounts the platform runs its own
|
|
52
|
+
* work as: a stored hash would make them able to sign in. One guard,
|
|
53
|
+
* {@link AuthService.assertNotSystemAccount}, on every path that writes one.
|
|
54
|
+
*/
|
|
55
|
+
const SYSTEM_ACCOUNT_PASSWORD_REFUSAL = 'System accounts cannot be given a password';
|
|
56
|
+
|
|
38
57
|
/**
|
|
39
58
|
* What every caller is told when it tries to give the deployment admin a
|
|
40
59
|
* stored password — the Account page's own change and an admin's "Set
|
|
@@ -44,6 +63,12 @@ const SYSTEM_ACCOUNT_EMAILS: readonly string[] = [RECOVERY_BOT_EMAIL, DIRECTORY_
|
|
|
44
63
|
const ENV_ADMIN_PASSWORD_REFUSAL =
|
|
45
64
|
"This account's password is set in the deployment environment and cannot be changed here";
|
|
46
65
|
|
|
66
|
+
/** What an admin is told when they try to switch an owner off. */
|
|
67
|
+
export const OWNER_SWITCH_OFF_REFUSAL = "The owner can't be switched off.";
|
|
68
|
+
|
|
69
|
+
/** What an admin is told when deleting an owner's account would leave them no way to sign in. */
|
|
70
|
+
export const OWNER_DELETE_REFUSAL = "The owner's account can't be deleted: they would have no way to sign in.";
|
|
71
|
+
|
|
47
72
|
/**
|
|
48
73
|
* Decoy hash verified when the email is unknown or has no password set, so
|
|
49
74
|
* those paths cost the same scrypt work as a real wrong-password attempt —
|
|
@@ -119,7 +144,7 @@ export interface SsoLoginOptions {
|
|
|
119
144
|
admittedByProvider?: boolean;
|
|
120
145
|
}
|
|
121
146
|
|
|
122
|
-
export class AuthService {
|
|
147
|
+
export class AuthService implements IAccountSwitchGuard {
|
|
123
148
|
constructor(
|
|
124
149
|
private readonly db: Database,
|
|
125
150
|
private readonly config: AuthConfig,
|
|
@@ -150,16 +175,20 @@ export class AuthService {
|
|
|
150
175
|
// is put on file, switched off, for an admin to switch on — and is still
|
|
151
176
|
// refused now, with the port's words. Only for single sign-on, the one
|
|
152
177
|
// provisioning nobody asked for (see `AccountAdmissionVerdict`).
|
|
178
|
+
// Created switched off, so under the switch lock (see account-switch-lock).
|
|
153
179
|
if (verdict.waitForAdmin && reason === 'sso') {
|
|
154
|
-
await this.db
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
180
|
+
await this.db.transaction(async (tx) => {
|
|
181
|
+
await takeAccountSwitchLock(tx, normalizedEmail);
|
|
182
|
+
await tx
|
|
183
|
+
.insert(users)
|
|
184
|
+
.values({
|
|
185
|
+
email: normalizedEmail,
|
|
186
|
+
emailBidx: normalizedEmail,
|
|
187
|
+
name: normalizedEmail.split('@')[0] || normalizedEmail,
|
|
188
|
+
deactivatedAt: new Date(),
|
|
189
|
+
})
|
|
190
|
+
.onConflictDoNothing({ target: users.emailBidx });
|
|
191
|
+
});
|
|
163
192
|
throw new AccountAdmissionRefusedError(verdict.message, { waitingForAdmin: true });
|
|
164
193
|
}
|
|
165
194
|
throw new AccountAdmissionRefusedError(verdict.message);
|
|
@@ -236,10 +265,12 @@ export class AuthService {
|
|
|
236
265
|
}
|
|
237
266
|
// Never refused for being switched off: the environment password is
|
|
238
267
|
// the deployment's way back in, and {@link deactivate} refuses this
|
|
239
|
-
// account for that reason.
|
|
268
|
+
// account for that reason. A switch-off left on the row by an earlier
|
|
269
|
+
// version is cleared here too, so the credentials that read the column
|
|
270
|
+
// directly come back with this sign-in ({@link clearOwnerSwitchOff}).
|
|
240
271
|
const defaultName = normalizedEmail.split('@')[0] || normalizedEmail;
|
|
241
272
|
await this.assertAdmitted(normalizedEmail, 'bootstrap');
|
|
242
|
-
const admin = await this.upsertUserByEmail(normalizedEmail, defaultName);
|
|
273
|
+
const admin = await this.clearOwnerSwitchOff(await this.upsertUserByEmail(normalizedEmail, defaultName));
|
|
243
274
|
return { token: this.signToken(admin.id, admin.email), user: this.toClientUser(admin) };
|
|
244
275
|
}
|
|
245
276
|
|
|
@@ -254,8 +285,9 @@ export class AuthService {
|
|
|
254
285
|
}
|
|
255
286
|
// Only now, with the password proven: telling a stranger that an address
|
|
256
287
|
// is switched off would tell them it has an account.
|
|
257
|
-
|
|
258
|
-
|
|
288
|
+
const signedIn = await this.clearOwnerSwitchOff(user);
|
|
289
|
+
if (signedIn.deactivatedAt) throw new AccountDeactivatedError();
|
|
290
|
+
return { token: this.signToken(signedIn.id, signedIn.email), user: this.toClientUser(signedIn) };
|
|
259
291
|
}
|
|
260
292
|
|
|
261
293
|
/**
|
|
@@ -285,7 +317,7 @@ export class AuthService {
|
|
|
285
317
|
if (!opts.admittedByProvider) this.assertAllowedDomain(normalizedEmail);
|
|
286
318
|
await this.assertAdmitted(normalizedEmail, 'sso');
|
|
287
319
|
const displayName = (name ?? '').trim() || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
288
|
-
const user = await this.upsertUserByEmail(normalizedEmail, displayName);
|
|
320
|
+
const user = await this.clearOwnerSwitchOff(await this.upsertUserByEmail(normalizedEmail, displayName));
|
|
289
321
|
if (user.deactivatedAt) throw new AccountDeactivatedError();
|
|
290
322
|
return { token: this.signToken(user.id, user.email), user: this.toClientUser(user) };
|
|
291
323
|
}
|
|
@@ -302,7 +334,10 @@ export class AuthService {
|
|
|
302
334
|
* Password sign-in refuses it until a password is set. For an address that
|
|
303
335
|
* already has an account it changes nothing but an explicitly given name.
|
|
304
336
|
*
|
|
305
|
-
* WITH a password,
|
|
337
|
+
* WITH a password, this refuses two kinds of target. The accounts the
|
|
338
|
+
* platform runs its own work as are refused first
|
|
339
|
+
* ({@link assertNotSystemAccount}): a password would make them able to sign
|
|
340
|
+
* in. The deployment admin is refused for the reason
|
|
306
341
|
* {@link changePassword} refuses it: that account's password is the
|
|
307
342
|
* environment's, so a stored hash would not replace it but ADD a second
|
|
308
343
|
* credential — one that keeps signing in after `ADMIN_PASSWORD` is rotated,
|
|
@@ -321,20 +356,10 @@ export class AuthService {
|
|
|
321
356
|
}
|
|
322
357
|
const suppliedName = (name ?? '').trim();
|
|
323
358
|
const displayName = suppliedName || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
324
|
-
if (!password)
|
|
325
|
-
await this.assertAdmitted(normalizedEmail, 'admin-create');
|
|
326
|
-
const [created] = await this.db
|
|
327
|
-
.insert(users)
|
|
328
|
-
.values({ email: normalizedEmail, emailBidx: normalizedEmail, name: displayName })
|
|
329
|
-
.onConflictDoUpdate({
|
|
330
|
-
target: users.emailBidx,
|
|
331
|
-
set: suppliedName ? { name: suppliedName, updatedAt: new Date() } : { updatedAt: new Date() },
|
|
332
|
-
})
|
|
333
|
-
.returning();
|
|
334
|
-
return this.toClientUser(created);
|
|
335
|
-
}
|
|
359
|
+
if (!password) return (await this.createAccountWithoutPassword(email, name)).user;
|
|
336
360
|
// Before the policy check, so the refusal names the real reason rather
|
|
337
361
|
// than sending the admin off to pick a longer password first.
|
|
362
|
+
this.assertNotSystemAccount(normalizedEmail);
|
|
338
363
|
if (this.isEnvAdminEmail(normalizedEmail)) {
|
|
339
364
|
throw new Error(ENV_ADMIN_PASSWORD_REFUSAL);
|
|
340
365
|
}
|
|
@@ -358,6 +383,88 @@ export class AuthService {
|
|
|
358
383
|
return this.toClientUser(row);
|
|
359
384
|
}
|
|
360
385
|
|
|
386
|
+
/**
|
|
387
|
+
* An account for single sign-on, or for a password somebody sets later:
|
|
388
|
+
* created without one, and for an address that already has an account,
|
|
389
|
+
* nothing changes but an explicitly given name. `created` says which
|
|
390
|
+
* happened, from the one statement that did it (see {@link CREATED}): an
|
|
391
|
+
* invite is only an invite when the account is new.
|
|
392
|
+
*/
|
|
393
|
+
async createAccountWithoutPassword(
|
|
394
|
+
email: string,
|
|
395
|
+
name: string | undefined,
|
|
396
|
+
): Promise<{ user: AuthUser; created: boolean }> {
|
|
397
|
+
const normalizedEmail = canonicalEmail(email ?? '');
|
|
398
|
+
if (!EMAIL_REGEX.test(normalizedEmail)) {
|
|
399
|
+
throw new Error('Invalid email');
|
|
400
|
+
}
|
|
401
|
+
const suppliedName = (name ?? '').trim();
|
|
402
|
+
const displayName = suppliedName || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
403
|
+
await this.assertAdmitted(normalizedEmail, 'admin-create');
|
|
404
|
+
const [row] = await this.db
|
|
405
|
+
.insert(users)
|
|
406
|
+
.values({ email: normalizedEmail, emailBidx: normalizedEmail, name: displayName })
|
|
407
|
+
.onConflictDoUpdate({
|
|
408
|
+
target: users.emailBidx,
|
|
409
|
+
set: suppliedName ? { name: suppliedName, updatedAt: new Date() } : { updatedAt: new Date() },
|
|
410
|
+
})
|
|
411
|
+
.returning({ ...getTableColumns(users), created: CREATED });
|
|
412
|
+
return { user: this.toClientUser(row), created: row.created === true };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* An invite's starting password: create the account with it, or give it to
|
|
417
|
+
* an existing account that is switched on and has no password yet — never
|
|
418
|
+
* to one that has a password of its own or is switched off. The test and
|
|
419
|
+
* the write are ONE statement (the upsert's `setWhere`), so a password the
|
|
420
|
+
* person sets between the admin reading the account list and sending the
|
|
421
|
+
* invite is never overwritten; `passwordSet` says which happened, and
|
|
422
|
+
* `created` whether the account is new (see {@link CREATED}).
|
|
423
|
+
*
|
|
424
|
+
* The deployment admin has its own password (the environment's): it is
|
|
425
|
+
* left as it is, as {@link createAccount} would refuse to store one. The
|
|
426
|
+
* accounts the platform runs its own work as are refused outright: a
|
|
427
|
+
* password would make them able to sign in.
|
|
428
|
+
*
|
|
429
|
+
* When nothing was written, `deactivated` tells a switched-off account
|
|
430
|
+
* apart from one that has a password of its own.
|
|
431
|
+
*/
|
|
432
|
+
async createAccountWithStartingPassword(
|
|
433
|
+
email: string,
|
|
434
|
+
password: string,
|
|
435
|
+
): Promise<{ user: AuthUser; created: boolean; passwordSet: boolean; deactivated: boolean }> {
|
|
436
|
+
const normalizedEmail = canonicalEmail(email ?? '');
|
|
437
|
+
if (!EMAIL_REGEX.test(normalizedEmail)) {
|
|
438
|
+
throw new Error('Invalid email');
|
|
439
|
+
}
|
|
440
|
+
this.assertNotSystemAccount(normalizedEmail);
|
|
441
|
+
if (this.isEnvAdminEmail(normalizedEmail)) {
|
|
442
|
+
const [existing] = await this.db.select().from(users).where(eq(users.emailBidx, normalizedEmail)).limit(1);
|
|
443
|
+
if (existing) {
|
|
444
|
+
return { user: this.toClientUser(existing), created: false, passwordSet: false, deactivated: existing.deactivatedAt != null };
|
|
445
|
+
}
|
|
446
|
+
throw new Error(ENV_ADMIN_PASSWORD_REFUSAL);
|
|
447
|
+
}
|
|
448
|
+
this.assertPasswordPolicy(password);
|
|
449
|
+
await this.assertAdmitted(normalizedEmail, 'admin-create');
|
|
450
|
+
const passwordHash = await hashPassword(password);
|
|
451
|
+
const displayName = normalizedEmail.split('@')[0] || normalizedEmail;
|
|
452
|
+
const [row] = await this.db
|
|
453
|
+
.insert(users)
|
|
454
|
+
.values({ email: normalizedEmail, emailBidx: normalizedEmail, name: displayName, passwordHash })
|
|
455
|
+
.onConflictDoUpdate({
|
|
456
|
+
target: users.emailBidx,
|
|
457
|
+
set: { passwordHash, updatedAt: new Date() },
|
|
458
|
+
setWhere: and(isNull(users.passwordHash), isNull(users.deactivatedAt)),
|
|
459
|
+
})
|
|
460
|
+
.returning({ ...getTableColumns(users), created: CREATED });
|
|
461
|
+
if (row) return { user: this.toClientUser(row), created: row.created === true, passwordSet: true, deactivated: false };
|
|
462
|
+
// The conflicting account has a password or is switched off: untouched.
|
|
463
|
+
const [existing] = await this.db.select().from(users).where(eq(users.emailBidx, normalizedEmail)).limit(1);
|
|
464
|
+
if (!existing) throw new Error('Could not set the password');
|
|
465
|
+
return { user: this.toClientUser(existing), created: false, passwordSet: false, deactivated: existing.deactivatedAt != null };
|
|
466
|
+
}
|
|
467
|
+
|
|
361
468
|
/**
|
|
362
469
|
* Is `email` the deployment admin — the account whose password is set in
|
|
363
470
|
* the deployment environment (`ADMIN_EMAIL` while `ADMIN_PASSWORD` is set)
|
|
@@ -380,6 +487,64 @@ export class AuthService {
|
|
|
380
487
|
);
|
|
381
488
|
}
|
|
382
489
|
|
|
490
|
+
/**
|
|
491
|
+
* Is `email` an owner of this deployment — an address it is configured
|
|
492
|
+
* with as its owner (`ADMIN_EMAIL`; on Hexis by Bevel cloud, whoever
|
|
493
|
+
* created the workspace)? Unlike {@link isEnvAdminEmail} this asks nothing
|
|
494
|
+
* of `ADMIN_PASSWORD`: the cloud never sets one, and its owner must be
|
|
495
|
+
* protected all the same. Behind {@link deactivate} (which always refuses
|
|
496
|
+
* an owner) and {@link assertDeletable} (which refuses one who could not
|
|
497
|
+
* sign back in). Canonicalises both sides, so `email` may be raw.
|
|
498
|
+
*/
|
|
499
|
+
isOwnerEmail(email: string): boolean {
|
|
500
|
+
// Not trusted to be text: the accounts API passes a request body's value
|
|
501
|
+
// in before validating it, and a non-string is simply not an owner.
|
|
502
|
+
const candidate = typeof email === 'string' ? canonicalEmail(email) : '';
|
|
503
|
+
return candidate.length > 0 && this.ownerEmails().includes(candidate);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* An owner is never switched off, but an earlier version did switch off an
|
|
508
|
+
* owner without a server-held password, and no admin can undo that for a
|
|
509
|
+
* workspace's only owner. So wherever an owner's row is read to let them
|
|
510
|
+
* in, a switch-off left on it is cleared — on the row itself, so that the
|
|
511
|
+
* credentials that read the column directly (API keys, MCP tokens) are
|
|
512
|
+
* honoured again too. Returns `row` as it now stands; anyone else's row
|
|
513
|
+
* unchanged.
|
|
514
|
+
*/
|
|
515
|
+
private async clearOwnerSwitchOff<T extends { id: string; email: string; deactivatedAt: Date | null }>(
|
|
516
|
+
row: T,
|
|
517
|
+
): Promise<T> {
|
|
518
|
+
if (!row.deactivatedAt || !this.isOwnerEmail(row.email)) return row;
|
|
519
|
+
await this.db
|
|
520
|
+
.update(users)
|
|
521
|
+
.set({ deactivatedAt: null, updatedAt: new Date() })
|
|
522
|
+
.where(and(eq(users.id, row.id), isNotNull(users.deactivatedAt)));
|
|
523
|
+
this.activeCache.delete(row.id);
|
|
524
|
+
return { ...row, deactivatedAt: null };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* The configured owner addresses, canonical, blanks dropped. One today
|
|
529
|
+
* (`ADMIN_EMAIL` is a single string); a list so that every owner is
|
|
530
|
+
* treated alike should the configuration ever carry more.
|
|
531
|
+
*/
|
|
532
|
+
private ownerEmails(): string[] {
|
|
533
|
+
return [this.config.adminEmail].map((e) => canonicalEmail(e ?? '')).filter((e) => e.length > 0);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Refuse to delete `email`'s account when it is an owner who could not
|
|
538
|
+
* sign back in afterwards: only an owner whose password the server holds,
|
|
539
|
+
* with password sign-in on (exactly {@link reportsAsEnvAdmin}), still has a
|
|
540
|
+
* way in once the row is gone. Anyone else's account passes.
|
|
541
|
+
*/
|
|
542
|
+
assertDeletable(email: string): void {
|
|
543
|
+
if (this.isOwnerEmail(email) && !this.reportsAsEnvAdmin(canonicalEmail(email ?? ''))) {
|
|
544
|
+
throw new AccountChangeRefusedError(OWNER_DELETE_REFUSAL);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
383
548
|
/**
|
|
384
549
|
* Whether a client is told `email` is the deployment admin — behind
|
|
385
550
|
* {@link toClientUser} and {@link listAccounts}. The identity is
|
|
@@ -431,6 +596,7 @@ export class AuthService {
|
|
|
431
596
|
if (!user) throw new Error('User not found');
|
|
432
597
|
// Before the policy check, so the deployment admin is told the real reason
|
|
433
598
|
// rather than being sent to fix a password that would be refused anyway.
|
|
599
|
+
this.assertNotSystemAccount(user.email);
|
|
434
600
|
if (this.isEnvAdminEmail(user.email)) {
|
|
435
601
|
throw new Error(ENV_ADMIN_PASSWORD_REFUSAL);
|
|
436
602
|
}
|
|
@@ -461,6 +627,10 @@ export class AuthService {
|
|
|
461
627
|
name: string;
|
|
462
628
|
hasPassword: boolean;
|
|
463
629
|
isEnvAdmin: boolean;
|
|
630
|
+
/** One of the deployment's owners (`ADMIN_EMAIL`), with or without a server-held password. */
|
|
631
|
+
isOwner: boolean;
|
|
632
|
+
/** An owner whose account may be deleted, since they can sign back in (see {@link assertDeletable}); false for everyone else. */
|
|
633
|
+
ownerCanBeDeleted: boolean;
|
|
464
634
|
/** When an admin switched the account off; null while it is on. */
|
|
465
635
|
deactivatedAt: Date | null;
|
|
466
636
|
/** One of the accounts the platform runs its own work as: never switched off, nobody signs in with it. */
|
|
@@ -472,19 +642,32 @@ export class AuthService {
|
|
|
472
642
|
// would sort by IV noise. The table is one row per team member.
|
|
473
643
|
const rows = await this.db.select().from(users);
|
|
474
644
|
return rows
|
|
475
|
-
.map((row) =>
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
645
|
+
.map((row) => {
|
|
646
|
+
const isEnvAdmin = this.reportsAsEnvAdmin(row.email);
|
|
647
|
+
const isOwner = this.isOwnerEmail(row.email);
|
|
648
|
+
return {
|
|
649
|
+
id: row.id,
|
|
650
|
+
email: row.email,
|
|
651
|
+
name: row.name,
|
|
652
|
+
hasPassword: row.passwordHash != null,
|
|
653
|
+
isEnvAdmin,
|
|
654
|
+
isOwner,
|
|
655
|
+
ownerCanBeDeleted: isOwner && isEnvAdmin,
|
|
656
|
+
deactivatedAt: row.deactivatedAt,
|
|
657
|
+
isSystem: SYSTEM_ACCOUNT_EMAILS.includes(row.email),
|
|
658
|
+
createdAt: row.createdAt,
|
|
659
|
+
};
|
|
660
|
+
})
|
|
485
661
|
.sort((a, b) => a.email.localeCompare(b.email));
|
|
486
662
|
}
|
|
487
663
|
|
|
664
|
+
/** Every password write calls this first: the system accounts never get one. */
|
|
665
|
+
private assertNotSystemAccount(normalizedEmail: string): void {
|
|
666
|
+
if (SYSTEM_ACCOUNT_EMAILS.includes(normalizedEmail)) {
|
|
667
|
+
throw new Error(SYSTEM_ACCOUNT_PASSWORD_REFUSAL);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
488
671
|
private assertPasswordPolicy(password: string): void {
|
|
489
672
|
if ((password ?? '').length < MIN_PASSWORD_LENGTH) {
|
|
490
673
|
throw new Error(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
|
|
@@ -511,7 +694,7 @@ export class AuthService {
|
|
|
511
694
|
if (!EMAIL_REGEX.test(normalizedEmail)) return null;
|
|
512
695
|
await this.assertAdmitted(normalizedEmail, 'embed');
|
|
513
696
|
const displayName = (name ?? '').trim() || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
514
|
-
const user = await this.upsertUserByEmail(normalizedEmail, displayName);
|
|
697
|
+
const user = await this.clearOwnerSwitchOff(await this.upsertUserByEmail(normalizedEmail, displayName));
|
|
515
698
|
if (user.deactivatedAt) throw new AccountDeactivatedError();
|
|
516
699
|
return { id: user.id, email: user.email, name: user.name };
|
|
517
700
|
}
|
|
@@ -574,31 +757,59 @@ export class AuthService {
|
|
|
574
757
|
* environment password signs it in regardless (see `loginWithPassword`),
|
|
575
758
|
* and a session that sign-in mints must then be usable too, or the way
|
|
576
759
|
* back into a deployment would lead nowhere. {@link deactivate} refuses
|
|
577
|
-
* the account, but a host may write the column itself.
|
|
760
|
+
* the account, but a host may write the column itself. Any owner's
|
|
761
|
+
* leftover switch-off is cleared here, as at sign-in
|
|
762
|
+
* ({@link clearOwnerSwitchOff}).
|
|
578
763
|
*/
|
|
579
764
|
async isActive(userId: string): Promise<boolean> {
|
|
580
765
|
const now = Date.now();
|
|
581
766
|
const hit = this.activeCache.get(userId);
|
|
582
767
|
if (hit && now - hit.at < ACTIVE_CACHE_MS) return hit.active;
|
|
583
|
-
const [
|
|
584
|
-
.select({ email: users.email, deactivatedAt: users.deactivatedAt })
|
|
768
|
+
const [found] = await this.db
|
|
769
|
+
.select({ id: users.id, email: users.email, deactivatedAt: users.deactivatedAt })
|
|
585
770
|
.from(users)
|
|
586
771
|
.where(eq(users.id, userId))
|
|
587
772
|
.limit(1);
|
|
773
|
+
const row = found && (await this.clearOwnerSwitchOff(found));
|
|
588
774
|
const active = row !== undefined && (!row.deactivatedAt || this.isEnvAdminEmail(row.email ?? ''));
|
|
589
775
|
this.activeCache.set(userId, { active, at: now });
|
|
590
776
|
return active;
|
|
591
777
|
}
|
|
592
778
|
|
|
779
|
+
/**
|
|
780
|
+
* Run `write` only while `email` is not switched off, and keep it so until
|
|
781
|
+
* `write` has finished: a transaction takes the address's switch lock (see
|
|
782
|
+
* account-switch-lock), reads the account and stays open across `write`.
|
|
783
|
+
* Every write that switches an account off, or creates one switched off,
|
|
784
|
+
* takes the same lock first, so it either committed before the read (and
|
|
785
|
+
* `write` is refused) or waits until `write` is done. An address with no
|
|
786
|
+
* account yet counts as switched on. A switched-off account throws a 409
|
|
787
|
+
* `deactivated` and `write` never runs.
|
|
788
|
+
*/
|
|
789
|
+
async whileSwitchedOn(email: string, write: () => Promise<void>): Promise<void> {
|
|
790
|
+
await this.db.transaction(async (tx) => {
|
|
791
|
+
await takeAccountSwitchLock(tx, email);
|
|
792
|
+
const [account] = await tx
|
|
793
|
+
.select({ deactivatedAt: users.deactivatedAt })
|
|
794
|
+
.from(users)
|
|
795
|
+
.where(eq(users.emailBidx, canonicalEmail(email)));
|
|
796
|
+
if (account?.deactivatedAt) {
|
|
797
|
+
throw new WorkflowDomainError('This account is switched off', 409, { kind: 'deactivated' });
|
|
798
|
+
}
|
|
799
|
+
await write();
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
|
|
593
803
|
/**
|
|
594
804
|
* Switch an account off. Its row, history and place in roles and groups
|
|
595
805
|
* stay; every credential it holds stops being honoured (see
|
|
596
806
|
* {@link isActive}). Idempotent: an account already off keeps the moment
|
|
597
807
|
* it was first switched off.
|
|
598
808
|
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
809
|
+
* An owner is refused, on every deployment, whether or not the server
|
|
810
|
+
* holds a password for them: switching the owner off locks them out of
|
|
811
|
+
* their own workspace (and, where `ADMIN_PASSWORD` is set, closes the
|
|
812
|
+
* way back into a deployment whose every other admin is gone). So are the machine accounts
|
|
602
813
|
* core runs its own work as. Refusing an admin's own account is the
|
|
603
814
|
* route's concern, since it knows who is asking.
|
|
604
815
|
*
|
|
@@ -607,16 +818,21 @@ export class AuthService {
|
|
|
607
818
|
async deactivate(userId: string): Promise<boolean> {
|
|
608
819
|
const [row] = await this.db.select().from(users).where(eq(users.id, userId)).limit(1);
|
|
609
820
|
if (!row) return false;
|
|
610
|
-
if (this.
|
|
611
|
-
throw new AccountChangeRefusedError(
|
|
821
|
+
if (this.isOwnerEmail(row.email)) {
|
|
822
|
+
throw new AccountChangeRefusedError(OWNER_SWITCH_OFF_REFUSAL);
|
|
612
823
|
}
|
|
613
824
|
if (SYSTEM_ACCOUNT_EMAILS.includes(row.email)) {
|
|
614
825
|
throw new AccountChangeRefusedError('This account belongs to the platform itself and cannot be switched off.');
|
|
615
826
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
827
|
+
// Under the switch lock, so a promotion that checked this account was
|
|
828
|
+
// on is written before this lands (see account-switch-lock).
|
|
829
|
+
await this.db.transaction(async (tx) => {
|
|
830
|
+
await takeAccountSwitchLock(tx, row.email);
|
|
831
|
+
await tx
|
|
832
|
+
.update(users)
|
|
833
|
+
.set({ deactivatedAt: new Date(), updatedAt: new Date() })
|
|
834
|
+
.where(and(eq(users.id, userId), isNull(users.deactivatedAt)));
|
|
835
|
+
});
|
|
620
836
|
this.activeCache.delete(userId);
|
|
621
837
|
return true;
|
|
622
838
|
}
|
|
@@ -16,8 +16,11 @@ const SCRYPT_P = 1;
|
|
|
16
16
|
const KEY_LEN = 32;
|
|
17
17
|
const SALT_LEN = 16;
|
|
18
18
|
|
|
19
|
-
/** Guard rail surfaced by the routes as a 400 — not a silent truncation.
|
|
20
|
-
|
|
19
|
+
/** Guard rail surfaced by the routes as a 400 — not a silent truncation. The
|
|
20
|
+
* policy itself is platform-shared so the Invite dialog's starting-password
|
|
21
|
+
* field can hold Invite on the same minimum; the other password forms (Account
|
|
22
|
+
* page, User accounts) don't check it and show the server's 400 instead. */
|
|
23
|
+
export { MIN_PASSWORD_LENGTH } from '@bevel-software/platform-shared';
|
|
21
24
|
|
|
22
25
|
function scryptAsync(
|
|
23
26
|
password: string,
|
|
@@ -27,6 +27,16 @@ vi.mock('../code-mode-names.js', async (importOriginal) => {
|
|
|
27
27
|
}
|
|
28
28
|
if (name === 'down.run') throw new Error('repository unavailable: ECONNREFUSED');
|
|
29
29
|
if (name === 'solo.run') return { tool: { name: 'solo.run' }, utcpName: 'solo.run' };
|
|
30
|
+
if (name === 'kb.read_file') {
|
|
31
|
+
return {
|
|
32
|
+
tool: {
|
|
33
|
+
name: 'kb.read_file',
|
|
34
|
+
description: 'Read a file.',
|
|
35
|
+
inputs: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] },
|
|
36
|
+
},
|
|
37
|
+
utcpName: 'kb.read_file',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
30
40
|
return null;
|
|
31
41
|
}),
|
|
32
42
|
};
|
|
@@ -35,7 +45,8 @@ vi.mock('../code-mode-names.js', async (importOriginal) => {
|
|
|
35
45
|
const { createToolsInfoTool } = await import('../code-mode.tool.js');
|
|
36
46
|
|
|
37
47
|
const client = {
|
|
38
|
-
toolToTypeScriptInterface: (tool: { name: string }) =>
|
|
48
|
+
toolToTypeScriptInterface: (tool: { name: string; description?: string }) =>
|
|
49
|
+
tool.description ? `/** ${tool.description} */ interface ${tool.name}` : `interface ${tool.name}`,
|
|
39
50
|
} as unknown as CodeModeUtcpClient;
|
|
40
51
|
|
|
41
52
|
type ToolsInfoResult = { interfaces: string; not_found: string[]; errors?: string[] };
|
|
@@ -58,11 +69,16 @@ describe('tools_info', () => {
|
|
|
58
69
|
|
|
59
70
|
it('omits the errors field entirely when every name resolves or misses cleanly', async () => {
|
|
60
71
|
const result = await run(['solo.run', 'nope']);
|
|
61
|
-
expect(result.interfaces).toBe('interface solo.run');
|
|
72
|
+
expect(result.interfaces).toBe('/** Call: solo.run({}) */ interface solo.run');
|
|
62
73
|
expect(result.not_found).toEqual(['nope']);
|
|
63
74
|
expect(result.errors).toBeUndefined();
|
|
64
75
|
});
|
|
65
76
|
|
|
77
|
+
it('shows each tool with its Call: line, as the chain description promises', async () => {
|
|
78
|
+
const result = await run(['kb.read_file']);
|
|
79
|
+
expect(result.interfaces).toBe('/** Call: kb.read_file({ query: "..." })\n\nRead a file. */ interface kb.read_file');
|
|
80
|
+
});
|
|
81
|
+
|
|
66
82
|
it('contains ONLY ambiguity — an outage rethrows instead of posing as partial success', async () => {
|
|
67
83
|
await expect(run(['solo.run', 'down.run'])).rejects.toThrow(/repository unavailable/);
|
|
68
84
|
});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
chainExample,
|
|
12
12
|
type ChainExampleTool,
|
|
13
13
|
runToolChain,
|
|
14
|
+
withCallExample,
|
|
14
15
|
} from '@bevel-software/platform-mcp-core';
|
|
15
16
|
import type { SpillStore } from '../workspace/spill-store.js';
|
|
16
17
|
import { utcpNameToTsInterfaceName, findToolByName, AmbiguousToolNameError } from './code-mode-names.js';
|
|
@@ -46,7 +47,8 @@ export function createCallToolChainTool(
|
|
|
46
47
|
id: 'call_tool_chain',
|
|
47
48
|
description: [
|
|
48
49
|
CodeModeUtcpClient.AGENT_PROMPT_TEMPLATE,
|
|
49
|
-
`Execute JavaScript code with direct access to all registered UTCP tools as hierarchical functions
|
|
50
|
+
`Execute JavaScript code with direct access to all registered UTCP tools as hierarchical functions, synchronous, no await.${worked} The runtime is plain JavaScript — no type annotations or other TypeScript-only syntax — plus \`atob\`, \`btoa\`, \`TextEncoder\` and \`TextDecoder\` for base64 and UTF-8 bytes, as in a browser; there is no \`Buffer\`, no \`fetch\` and no \`require\`. Return the final value with \`return\`. Use \`list_tools\` and \`tools_info\` first to discover available tools and their interfaces.`,
|
|
51
|
+
`There is NO single calling shape: some tools take their arguments under a \`body\` object, others take them flat. Call each tool as the \`Call:\` line at the top of its own description shows — it is generated from that tool's input schema, and every tool in \`${ns}\` has one. Every argument a tool declares REQUIRED must be present; for the knowledge-base tools that includes \`branch\`. Arguments that do not match the schema are refused before anything is sent or run, and the refusal carries the tool's interface.`,
|
|
50
52
|
'Error handling inside the chain: a failing tool call THROWS, and the thrown error\'s `.message` holds the server\'s actual reason (e.g. a 403 with the explanation, not just a status code). If you catch it, surface `err.message` (and `err.status` / `err.data` when present) — NEVER `return { error: err }` or otherwise return the raw Error object, because an Error serializes to `{}` (its `message` is non-enumerable) and the reason is lost. If you don\'t need to handle it, just let it throw — the runtime already reports `err.message` back to you.',
|
|
51
53
|
`Failures are answered, never dropped: a chain that throws comes back with \`success: false\` and the reason, and one that outlives \`timeout\` (default ${CHAIN_TIMEOUT_DEFAULT_MS} ms, maximum ${CHAIN_TIMEOUT_MAX_MS} ms) comes back saying it timed out — raise \`timeout\` or split the work and run it again. Either way your next tool call works as usual.`,
|
|
52
54
|
'Large return values: if the returned value exceeds `max_output_size`, the full JSON is auto-spilled to a shared spill store (outside any workspace, never committed) and the response contains only a `__tool_chain_spill__/…` ref + a truncated marker. You can read the spill back with the regular `read_file` tool — pass that ref as `path` (its `branch` is ignored) plus `offset` / `limit` to slice it, never read a multi-MB file in full. Order of preference: (1) re-run `call_tool_chain` with a follow-up code chain that filters/maps the data inline and returns just what you need; (2) narrow the API call — shorter `fields`, tighter date window, lower `limit`; (3) last resort — `read_file` against the spill ref with `offset` / `limit`. The spill is read-only context only; do NOT use it as a way to persist KB content — for KB writes use the regular `write_file` / `edit_file` tools, which go through the lock/commit pipeline.',
|
|
@@ -176,7 +178,16 @@ export function createToolsInfoTool(client: CodeModeUtcpClient) {
|
|
|
176
178
|
try {
|
|
177
179
|
const found = await findToolByName(client, name);
|
|
178
180
|
if (found) {
|
|
179
|
-
|
|
181
|
+
// With its `Call:` line, as on the MCP surface (`meta-tools.ts`):
|
|
182
|
+
// the chain's description tells the agent to call each tool as
|
|
183
|
+
// that line shows, and this is where it reads the tool. The
|
|
184
|
+
// repository's tool is not ours to edit, so a copy carries it.
|
|
185
|
+
interfaces.push(
|
|
186
|
+
client.toolToTypeScriptInterface({
|
|
187
|
+
...found.tool,
|
|
188
|
+
description: withCallExample(found.tool.description, found.utcpName, found.tool.inputs),
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
180
191
|
} else {
|
|
181
192
|
notFound.push(name);
|
|
182
193
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { DrizzleQueryError } from 'drizzle-orm/errors';
|
|
3
|
+
import { pgErrorCode } from '../pg-error.js';
|
|
4
|
+
|
|
5
|
+
describe('pgErrorCode', () => {
|
|
6
|
+
const driverError = () => Object.assign(new Error('canceling statement due to lock timeout'), { code: '55P03' });
|
|
7
|
+
|
|
8
|
+
it('reads the code off the driver error', () => {
|
|
9
|
+
expect(pgErrorCode(driverError())).toBe('55P03');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("reads it off a Drizzle query error's cause, where Drizzle puts it", () => {
|
|
13
|
+
expect(pgErrorCode(new DrizzleQueryError('select 1', [], driverError()))).toBe('55P03');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('is undefined for anything Postgres did not raise', () => {
|
|
17
|
+
expect(pgErrorCode(new Error('boom'))).toBeUndefined();
|
|
18
|
+
expect(pgErrorCode(null)).toBeUndefined();
|
|
19
|
+
expect(pgErrorCode('55P03')).toBeUndefined();
|
|
20
|
+
});
|
|
21
|
+
});
|