@bevel-software/platform-core-backend 0.26.0 → 0.27.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 +54 -3
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +26 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +100 -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 +8 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +1 -0
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +11 -0
- package/dist/modules/access/access-control.service.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/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.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +10 -3
- 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/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/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 +805 -106
- 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/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 +67 -3
- package/src/core/create-core-services.ts +140 -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.service.test.ts +19 -0
- package/src/modules/access/access-control.interface.ts +13 -0
- package/src/modules/access/access-control.service.ts +16 -0
- package/src/modules/access/directory-sync-bot.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.routes.test.ts +5 -3
- package/src/modules/auth/account.routes.ts +10 -3
- package/src/modules/auth/auth.middleware.ts +1 -1
- 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/core-schema.ts +33 -3
- 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 +879 -112
- 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
|
@@ -15,9 +15,11 @@ import { ToolError, type ToolContext } from '../../tool-helpers/tool.contract.js
|
|
|
15
15
|
import type { ToolAuth } from '../../tool-auth/tool-auth.middleware.js';
|
|
16
16
|
import { registerWorkspaceTools } from '../workspace.tools.js';
|
|
17
17
|
import { sharedFileRules, sharedFileRulesSection } from '../../agent-instructions/shared-file-rules.js';
|
|
18
|
+
import { composeAgentGuide } from '../../agent-guide/agent-guide.js';
|
|
18
19
|
import { GUIDE_FIRST_SENTENCE } from '../../tool-registry/guide-first.js';
|
|
19
20
|
import { RoutineWritePolicyService } from '../routine-write-policy.js';
|
|
20
21
|
import { UuidSessionSink, type ISessionSink } from '../session-sink.js';
|
|
22
|
+
import { FIRST_RUN_SECTION_ID, STARTER_GUIDE_FILE, firstRunNote, type FirstRunStarterSource } from '../first-run.js';
|
|
21
23
|
import { WorkflowHooks, type AgentOperationContext } from '../../workflow/workflow-hooks.js';
|
|
22
24
|
import { SESSION_ID_DESCRIPTION, ToolDescriptionNotes } from '../agent-access.gate.js';
|
|
23
25
|
import { SpillStore } from '../spill-store.js';
|
|
@@ -27,13 +29,16 @@ import type { IAccessControl } from '../../access/access-control.interface.js';
|
|
|
27
29
|
import { AccessControlService } from '../../access/access-control.service.js';
|
|
28
30
|
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
29
31
|
import type { WorkspaceService } from '../workspace.service.js';
|
|
30
|
-
import { isBranchAuthoredBy, isOwnSuggestionsBranch } from '@bevel-software/platform-shared';
|
|
32
|
+
import { DEFAULT_KB_LAYOUT, isBranchAuthoredBy, isOwnSuggestionsBranch } from '@bevel-software/platform-shared';
|
|
31
33
|
import { assertValidBranchName } from '../../kb-fs/branch-name.js';
|
|
32
34
|
import { normalizeWorkspacePath } from '../../kb-fs/repo-path.js';
|
|
33
35
|
import { GIT_INTERNALS_MESSAGE, PathNotFoundError } from '../../../shared/domain-errors.js';
|
|
34
36
|
import { AccessDeniedError } from '../../access-model/access-errors.js';
|
|
35
37
|
import { proposalTitleFor } from '../write-denial.js';
|
|
36
38
|
import { NOT_FOUND_NEXT_STEP } from '../not-found.js';
|
|
39
|
+
import { compileCheck, exampleArguments } from '@bevel-software/platform-mcp-core';
|
|
40
|
+
import { routeToolSchemas } from '../../tool-helpers/route-tool-schemas.js';
|
|
41
|
+
import { TOOL_DESCRIPTION_CAP, clientVisibleLength } from '../../tool-registry/description-length.js';
|
|
37
42
|
|
|
38
43
|
const KB_DIR = 'knowledge-base';
|
|
39
44
|
|
|
@@ -316,7 +321,9 @@ const postRaw = (url: string, body: unknown = {}) =>
|
|
|
316
321
|
* POST a well-formed call. Every KB tool requires `branch` — a call that names
|
|
317
322
|
* none is refused at the mount with 400 `branch-required` — so this names one
|
|
318
323
|
* unless the test already did. A test ABOUT the missing input uses `postRaw`,
|
|
319
|
-
* so the thing under test is never papered over by the helper.
|
|
324
|
+
* so the thing under test is never papered over by the helper. So does
|
|
325
|
+
* `start_session`, which declares no arguments at all and forbids extras: its
|
|
326
|
+
* route refuses a `branch` as an argument it does not have.
|
|
320
327
|
*/
|
|
321
328
|
const post = (url: string, body: unknown = {}) =>
|
|
322
329
|
postRaw(
|
|
@@ -328,6 +335,7 @@ const post = (url: string, body: unknown = {}) =>
|
|
|
328
335
|
|
|
329
336
|
beforeEach(() => {
|
|
330
337
|
/* fresh per test via start() */
|
|
338
|
+
guideText = 'THE PLATFORM GUIDE\n';
|
|
331
339
|
});
|
|
332
340
|
afterEach(async () => {
|
|
333
341
|
if (httpServer) await new Promise<void>((r) => httpServer!.close(() => r()));
|
|
@@ -966,6 +974,8 @@ describe('write modes and per-path outcomes', () => {
|
|
|
966
974
|
for (const res of [
|
|
967
975
|
await writeFile(base, { path: `${KB_DIR}/a.md`, content: 'x', mode: 'replace' }),
|
|
968
976
|
await writeFiles(base, { files: [{ path: `${KB_DIR}/a.md`, content: 'x' }], mode: 'replace' }),
|
|
977
|
+
// An empty batch is no way round it: the mode is judged before the batch is.
|
|
978
|
+
await writeFiles(base, { files: [], mode: 'bogus' }),
|
|
969
979
|
]) {
|
|
970
980
|
expect(res.status).toBe(400);
|
|
971
981
|
const body = (await res.json()) as { error: string; code: string };
|
|
@@ -1122,6 +1132,15 @@ describe("the agent guide at the guide's name", () => {
|
|
|
1122
1132
|
expect((await read(base, GUIDE, { offset: 4, limit: 8 })).content).toBe('PLATFORM');
|
|
1123
1133
|
});
|
|
1124
1134
|
|
|
1135
|
+
it('answers with the platform\'s composed guide, HTML views section and all', async () => {
|
|
1136
|
+
guideText = await composeAgentGuide(DEFAULT_KB_LAYOUT);
|
|
1137
|
+
const base = await start();
|
|
1138
|
+
const { content } = await read(base, 'AGENTS.md');
|
|
1139
|
+
expect(content).toBe(guideText);
|
|
1140
|
+
expect(content).toContain('## HTML views');
|
|
1141
|
+
expect(content).toContain('**A bare fragment scrolls the page.**');
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1125
1144
|
it("puts the knowledge base's own AGENTS.md first, then the separator, then the guide", async () => {
|
|
1126
1145
|
guideText = 'THE PLATFORM GUIDE\n';
|
|
1127
1146
|
const base = await start();
|
|
@@ -1936,6 +1955,52 @@ describe('office documents and PDFs', () => {
|
|
|
1936
1955
|
expect(external.find((t) => t.name === 'start_session')!.description!.startsWith(`${GUIDE_FIRST_SENTENCE} `)).toBe(true);
|
|
1937
1956
|
});
|
|
1938
1957
|
|
|
1958
|
+
/**
|
|
1959
|
+
* The call example at the top of every description is generated from the
|
|
1960
|
+
* tool's input schema, and the same schema is what the argument check reads.
|
|
1961
|
+
* If the two could disagree, the platform would publish an example its own
|
|
1962
|
+
* check refuses — so every declared tool is called with its own example here.
|
|
1963
|
+
*/
|
|
1964
|
+
it('every declared tool can be called with its own generated example', async () => {
|
|
1965
|
+
await start();
|
|
1966
|
+
const tools = await toolRegistry.listInternal();
|
|
1967
|
+
// The whole family this harness declares, the four the scenarios name included.
|
|
1968
|
+
for (const name of ['read_file', 'write_file', 'list_files', 'grep']) {
|
|
1969
|
+
expect(tools.some((t) => t.name === name), name).toBe(true);
|
|
1970
|
+
}
|
|
1971
|
+
expect(tools.length).toBeGreaterThan(12);
|
|
1972
|
+
for (const def of tools) {
|
|
1973
|
+
const compiled = compileCheck(def.inputs);
|
|
1974
|
+
expect(compiled.checkable, `${def.name}: ${compiled.checkable ? '' : compiled.reason}`).toBe(true);
|
|
1975
|
+
// Narrowed by hand: the assertion above already failed the test if not.
|
|
1976
|
+
if (!compiled.checkable) throw new Error(compiled.reason);
|
|
1977
|
+
expect(compiled.check(exampleArguments(def.inputs)), def.name).toEqual([]);
|
|
1978
|
+
// And the FLAT schema, which is what the tool's own route checks the
|
|
1979
|
+
// call against: the two must agree, or a call the example produced would
|
|
1980
|
+
// be refused one layer in.
|
|
1981
|
+
const flat = routeToolSchemas(def.name)?.flat;
|
|
1982
|
+
expect(flat, def.name).toBeDefined();
|
|
1983
|
+
const flatCheck = compileCheck(flat);
|
|
1984
|
+
expect(flatCheck.checkable, `${def.name} (flat): ${flatCheck.checkable ? '' : flatCheck.reason}`).toBe(true);
|
|
1985
|
+
if (!flatCheck.checkable) throw new Error(flatCheck.reason);
|
|
1986
|
+
expect(flatCheck.check(exampleArguments(flat)), `${def.name} (flat)`).toEqual([]);
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
|
|
1990
|
+
it('every description, call example and purpose prefix included, stays inside the cap a client shows', async () => {
|
|
1991
|
+
await start();
|
|
1992
|
+
const tools = await toolRegistry.listInternal();
|
|
1993
|
+
for (const def of tools) {
|
|
1994
|
+
// Measured as a CLIENT receives it: the call line, the purpose prefix at
|
|
1995
|
+
// its own cap (the four knowledge-base tools carry one), and the
|
|
1996
|
+
// description — the three things that ride one tool's entry.
|
|
1997
|
+
const received = clientVisibleLength(def);
|
|
1998
|
+
expect(received, `${def.name} is ${received} characters (cap ${TOOL_DESCRIPTION_CAP})`).toBeLessThanOrEqual(
|
|
1999
|
+
TOOL_DESCRIPTION_CAP,
|
|
2000
|
+
);
|
|
2001
|
+
}
|
|
2002
|
+
});
|
|
2003
|
+
|
|
1939
2004
|
describe('binary capability contract: a text file, a document, an image and a zip', () => {
|
|
1940
2005
|
const PNG = Buffer.from(
|
|
1941
2006
|
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
|
|
@@ -2201,6 +2266,15 @@ describe('start_session', () => {
|
|
|
2201
2266
|
// `UuidSessionSink`, because "did fifty first calls collide?" is a question
|
|
2202
2267
|
// only the real minting can answer.
|
|
2203
2268
|
sink?: ISessionSink,
|
|
2269
|
+
// The default branch's workspace directory, for the tests about the
|
|
2270
|
+
// `firstRun` note. Without one the workspace service is a bare stand-in
|
|
2271
|
+
// the note's check cannot use, which is what every other test here wants:
|
|
2272
|
+
// the call answers with the id alone.
|
|
2273
|
+
defaultWorkspaceDir?: string,
|
|
2274
|
+
// The starter pack the knowledge base was filled from, for the note.
|
|
2275
|
+
starterPacks?: FirstRunStarterSource,
|
|
2276
|
+
// Who may read what: everything, unless a test about the note's gate says otherwise.
|
|
2277
|
+
access: IAccessControl = allowAll,
|
|
2204
2278
|
): Promise<string> {
|
|
2205
2279
|
created = [];
|
|
2206
2280
|
const registry = new ToolRegistry();
|
|
@@ -2209,7 +2283,12 @@ describe('start_session', () => {
|
|
|
2209
2283
|
scope: auth.scope,
|
|
2210
2284
|
source: auth.source,
|
|
2211
2285
|
abortSignal: signal,
|
|
2212
|
-
workspaceService:
|
|
2286
|
+
workspaceService: (defaultWorkspaceDir
|
|
2287
|
+
? {
|
|
2288
|
+
hasBootstrappedWorkspace: async () => true,
|
|
2289
|
+
getWorkspacePath: async () => defaultWorkspaceDir,
|
|
2290
|
+
}
|
|
2291
|
+
: {}) as never,
|
|
2213
2292
|
workflowService: {} as never,
|
|
2214
2293
|
events: {} as never,
|
|
2215
2294
|
getFilesystem: async () => ({}) as never,
|
|
@@ -2232,10 +2311,17 @@ describe('start_session', () => {
|
|
|
2232
2311
|
const router = express.Router();
|
|
2233
2312
|
registerWorkspaceTools(
|
|
2234
2313
|
registry, router, auth, toolHandler,
|
|
2235
|
-
new SpillStore(join(tmpdir(), 'bevel-test-spills')), new DocExtractService(join(tmpdir(), 'bevel-test-doc-extract')),
|
|
2314
|
+
new SpillStore(join(tmpdir(), 'bevel-test-spills')), new DocExtractService(join(tmpdir(), 'bevel-test-doc-extract')), access, testKbContext({ kbDirName: KB_DIR }),
|
|
2236
2315
|
{ recoveryBotEmail: RECOVERY_BOT, hooks: new WorkflowHooks(), notes: new ToolDescriptionNotes() },
|
|
2237
2316
|
new RoutineWritePolicyService(),
|
|
2238
2317
|
sink ?? fakeSessionSink,
|
|
2318
|
+
undefined,
|
|
2319
|
+
undefined,
|
|
2320
|
+
undefined,
|
|
2321
|
+
undefined,
|
|
2322
|
+
undefined /* downloads */,
|
|
2323
|
+
starterPacks,
|
|
2324
|
+
new NodeFs(),
|
|
2239
2325
|
);
|
|
2240
2326
|
app.use('/api', router);
|
|
2241
2327
|
server = await new Promise<HttpServer>((r) => {
|
|
@@ -2251,13 +2337,13 @@ describe('start_session', () => {
|
|
|
2251
2337
|
|
|
2252
2338
|
it('returns the sink-minted id as sessionId', async () => {
|
|
2253
2339
|
const base = await startSessionApp();
|
|
2254
|
-
const res = (await (await
|
|
2340
|
+
const res = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { sessionId: string };
|
|
2255
2341
|
expect(res.sessionId).toBe('thread-xyz');
|
|
2256
2342
|
});
|
|
2257
2343
|
|
|
2258
2344
|
it('mints the session for the authenticated user', async () => {
|
|
2259
2345
|
const base = await startSessionApp();
|
|
2260
|
-
await
|
|
2346
|
+
await postRaw(`${base}/api/agent/tools/start_session`);
|
|
2261
2347
|
expect(created).toHaveLength(1);
|
|
2262
2348
|
expect(created[0].userId).toBe('user-42');
|
|
2263
2349
|
expect(created[0].startedAt).toBeInstanceOf(Date);
|
|
@@ -2268,7 +2354,7 @@ describe('start_session', () => {
|
|
|
2268
2354
|
// loopback token resolves to source 'external' at the verifier (see
|
|
2269
2355
|
// tool-auth), so it is admitted here like any external agent.
|
|
2270
2356
|
const base = await startSessionApp('internal');
|
|
2271
|
-
const res = await
|
|
2357
|
+
const res = await postRaw(`${base}/api/agent/tools/start_session`);
|
|
2272
2358
|
expect(res.status).toBe(403);
|
|
2273
2359
|
expect(created).toHaveLength(0);
|
|
2274
2360
|
});
|
|
@@ -2292,7 +2378,7 @@ describe('start_session', () => {
|
|
|
2292
2378
|
it('answers every one of them with a session id of its own', async () => {
|
|
2293
2379
|
const base = await startSessionApp('external', new UuidSessionSink());
|
|
2294
2380
|
|
|
2295
|
-
const responses = await Promise.all(Array.from({ length: 50 }, () =>
|
|
2381
|
+
const responses = await Promise.all(Array.from({ length: 50 }, () => postRaw(`${base}/api/agent/tools/start_session`)));
|
|
2296
2382
|
const bodies = (await Promise.all(responses.map((r) => r.json()))) as Array<{ sessionId?: string }>;
|
|
2297
2383
|
|
|
2298
2384
|
expect(responses.map((r) => r.status)).toEqual(Array.from({ length: 50 }, () => 200));
|
|
@@ -2325,13 +2411,13 @@ describe('start_session', () => {
|
|
|
2325
2411
|
const errorLog = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
2326
2412
|
let failed: Awaited<ReturnType<typeof post>>;
|
|
2327
2413
|
try {
|
|
2328
|
-
failed = await
|
|
2414
|
+
failed = await postRaw(`${base}/api/agent/tools/start_session`);
|
|
2329
2415
|
} finally {
|
|
2330
2416
|
errorLog.mockRestore();
|
|
2331
2417
|
}
|
|
2332
2418
|
expect(failed.status).toBeGreaterThanOrEqual(500);
|
|
2333
2419
|
|
|
2334
|
-
const retried = (await (await
|
|
2420
|
+
const retried = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { sessionId: string };
|
|
2335
2421
|
expect(retried.sessionId).toBe('session-2');
|
|
2336
2422
|
expect(calls).toBe(2);
|
|
2337
2423
|
});
|
|
@@ -2343,8 +2429,8 @@ describe('start_session', () => {
|
|
|
2343
2429
|
// to other tools, and the spare is simply never mentioned again.
|
|
2344
2430
|
const base = await startSessionApp('external', new UuidSessionSink());
|
|
2345
2431
|
|
|
2346
|
-
const first = (await (await
|
|
2347
|
-
const retry = (await (await
|
|
2432
|
+
const first = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { sessionId: string };
|
|
2433
|
+
const retry = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { sessionId: string };
|
|
2348
2434
|
|
|
2349
2435
|
expect(retry.sessionId).not.toBe(first.sessionId);
|
|
2350
2436
|
expect(first.sessionId).toBeTruthy();
|
|
@@ -2372,6 +2458,141 @@ describe('start_session', () => {
|
|
|
2372
2458
|
expect(description).toMatch(/retry/i);
|
|
2373
2459
|
expect(description).toMatch(/created nothing/i);
|
|
2374
2460
|
expect(description).toMatch(/harmless/i);
|
|
2461
|
+
// And that the answer may carry a note to act on, so an agent reading
|
|
2462
|
+
// only the catalog knows the field is not noise.
|
|
2463
|
+
expect(description).toContain('`firstRun`');
|
|
2464
|
+
});
|
|
2465
|
+
|
|
2466
|
+
/**
|
|
2467
|
+
* "The agent is the onboarding guide": on a knowledge base nobody has
|
|
2468
|
+
* written in yet, the first call of a conversation says so, and the note
|
|
2469
|
+
* stops on its own once a page exists.
|
|
2470
|
+
*/
|
|
2471
|
+
describe('the firstRun note', () => {
|
|
2472
|
+
let wsDir = '';
|
|
2473
|
+
const knowledge = () => join(wsDir, KB_DIR, 'KnowledgeBase');
|
|
2474
|
+
|
|
2475
|
+
beforeEach(async () => {
|
|
2476
|
+
wsDir = await mkdtemp(join(tmpdir(), 'bevel-first-run-'));
|
|
2477
|
+
await mkdir(knowledge(), { recursive: true });
|
|
2478
|
+
await writeFile(join(knowledge(), STARTER_GUIDE_FILE), '# How to get started\n');
|
|
2479
|
+
});
|
|
2480
|
+
|
|
2481
|
+
afterEach(async () => {
|
|
2482
|
+
await rm(wsDir, { recursive: true, force: true });
|
|
2483
|
+
});
|
|
2484
|
+
|
|
2485
|
+
const startSession = async () => {
|
|
2486
|
+
const base = await startSessionApp('external', undefined, wsDir);
|
|
2487
|
+
// `postRaw`: the tool takes no arguments, and `post` adds a branch.
|
|
2488
|
+
return (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { sessionId: string; firstRun?: string };
|
|
2489
|
+
};
|
|
2490
|
+
|
|
2491
|
+
it('is there while the knowledge folder holds only the starter guide', async () => {
|
|
2492
|
+
// Folder placeholders and access rules are not pages either.
|
|
2493
|
+
await mkdir(join(knowledge(), 'Empty'), { recursive: true });
|
|
2494
|
+
await writeFile(join(knowledge(), 'Empty', '.gitkeep'), '');
|
|
2495
|
+
await writeFile(join(knowledge(), 'access.md'), '# Access\n');
|
|
2496
|
+
|
|
2497
|
+
const res = await startSession();
|
|
2498
|
+
|
|
2499
|
+
expect(res.sessionId).toBe('thread-xyz');
|
|
2500
|
+
expect(res.firstRun).toBe(firstRunNote(`${KB_DIR}/KnowledgeBase`));
|
|
2501
|
+
expect(res.firstRun).toContain(`\`${FIRST_RUN_SECTION_ID}\``);
|
|
2502
|
+
});
|
|
2503
|
+
|
|
2504
|
+
it('is gone once another page exists beside the starter guide', async () => {
|
|
2505
|
+
await writeFile(join(knowledge(), 'Glossary.md'), '# Glossary\n');
|
|
2506
|
+
|
|
2507
|
+
const res = await startSession();
|
|
2508
|
+
|
|
2509
|
+
expect(res.sessionId).toBe('thread-xyz');
|
|
2510
|
+
expect(res).not.toHaveProperty('firstRun');
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
it('is gone once another page exists in a folder', async () => {
|
|
2514
|
+
await mkdir(join(knowledge(), 'Company'), { recursive: true });
|
|
2515
|
+
await writeFile(join(knowledge(), 'Company', 'About.md'), '# About us\n');
|
|
2516
|
+
|
|
2517
|
+
const res = await startSession();
|
|
2518
|
+
|
|
2519
|
+
expect(res.sessionId).toBe('thread-xyz');
|
|
2520
|
+
expect(res).not.toHaveProperty('firstRun');
|
|
2521
|
+
});
|
|
2522
|
+
|
|
2523
|
+
it("after a starter pack, stays while the pack's pages are untouched and names its suggestions", async () => {
|
|
2524
|
+
await writeFile(join(knowledge(), 'Customers.md'), '# Customers\n');
|
|
2525
|
+
const starter: FirstRunStarterSource = {
|
|
2526
|
+
firstRunStarter: async () => ({
|
|
2527
|
+
name: 'Sales',
|
|
2528
|
+
suggestedPages: ['Customers', 'Pricing'],
|
|
2529
|
+
pages: new Map([['Customers.md', '# Customers\n']]),
|
|
2530
|
+
}),
|
|
2531
|
+
};
|
|
2532
|
+
const base = await startSessionApp('external', undefined, wsDir, starter);
|
|
2533
|
+
const first = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { firstRun?: string };
|
|
2534
|
+
expect(first.firstRun).toBe(
|
|
2535
|
+
firstRunNote(`${KB_DIR}/KnowledgeBase`, { name: 'Sales', suggestedPages: ['Customers', 'Pricing'] }),
|
|
2536
|
+
);
|
|
2537
|
+
|
|
2538
|
+
await writeFile(join(knowledge(), 'Customers.md'), '# Customers\n\nAcme.\n');
|
|
2539
|
+
const second = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { firstRun?: string };
|
|
2540
|
+
expect(second).not.toHaveProperty('firstRun');
|
|
2541
|
+
});
|
|
2542
|
+
|
|
2543
|
+
/**
|
|
2544
|
+
* The note is a read: it says what the knowledge folder holds. A caller
|
|
2545
|
+
* who may not read the folder gets the id alone, and a pack page the
|
|
2546
|
+
* caller may not read is judged as anybody's page, so the note never
|
|
2547
|
+
* tells them it is still a placeholder.
|
|
2548
|
+
*/
|
|
2549
|
+
it('is withheld from a caller who may not read the knowledge folder', async () => {
|
|
2550
|
+
const closed = { ...allowAll, canRead: async () => false } as unknown as IAccessControl;
|
|
2551
|
+
const base = await startSessionApp('external', undefined, wsDir, undefined, closed);
|
|
2552
|
+
const res = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { firstRun?: string };
|
|
2553
|
+
expect(res).toEqual({ sessionId: 'thread-xyz' });
|
|
2554
|
+
});
|
|
2555
|
+
|
|
2556
|
+
it('judges the folder as the caller may see it: a page they may not read leaves the note standing', async () => {
|
|
2557
|
+
await mkdir(join(knowledge(), 'Leadership'), { recursive: true });
|
|
2558
|
+
await writeFile(join(knowledge(), 'Leadership', 'Plan.md'), '# Plan\n');
|
|
2559
|
+
const planClosed = {
|
|
2560
|
+
...allowAll,
|
|
2561
|
+
canReadBatch: async (_w: string, _u: string, paths: string[]) =>
|
|
2562
|
+
new Map(paths.map((p) => [p, !p.endsWith('Plan.md')])),
|
|
2563
|
+
} as unknown as IAccessControl;
|
|
2564
|
+
const base = await startSessionApp('external', undefined, wsDir, undefined, planClosed);
|
|
2565
|
+
const res = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { firstRun?: string };
|
|
2566
|
+
expect(res.firstRun).toBe(firstRunNote(`${KB_DIR}/KnowledgeBase`));
|
|
2567
|
+
});
|
|
2568
|
+
|
|
2569
|
+
it('says nothing of a pack whose page the caller may not read, in the checkout or gone from it', async () => {
|
|
2570
|
+
// Absent from the checkout, so the folder reads as new — and the note
|
|
2571
|
+
// would still name the pack and its suggestions.
|
|
2572
|
+
const starter: FirstRunStarterSource = {
|
|
2573
|
+
firstRunStarter: async () => ({
|
|
2574
|
+
name: 'Sales',
|
|
2575
|
+
suggestedPages: ['Customers'],
|
|
2576
|
+
pages: new Map([['Customers.md', '# Customers\n']]),
|
|
2577
|
+
}),
|
|
2578
|
+
};
|
|
2579
|
+
const pageClosed = {
|
|
2580
|
+
...allowAll,
|
|
2581
|
+
canReadBatch: async (_w: string, _u: string, paths: string[]) =>
|
|
2582
|
+
new Map(paths.map((p) => [p, !p.endsWith('Customers.md')])),
|
|
2583
|
+
} as unknown as IAccessControl;
|
|
2584
|
+
const base = await startSessionApp('external', undefined, wsDir, starter, pageClosed);
|
|
2585
|
+
const res = (await (await postRaw(`${base}/api/agent/tools/start_session`)).json()) as { firstRun?: string };
|
|
2586
|
+
expect(res).toEqual({ sessionId: 'thread-xyz' });
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
it('never costs the session id: a workspace it cannot read answers with the id alone', async () => {
|
|
2590
|
+
await rm(wsDir, { recursive: true, force: true });
|
|
2591
|
+
|
|
2592
|
+
const res = await startSession();
|
|
2593
|
+
|
|
2594
|
+
expect(res).toEqual({ sessionId: 'thread-xyz' });
|
|
2595
|
+
});
|
|
2375
2596
|
});
|
|
2376
2597
|
});
|
|
2377
2598
|
|
|
@@ -2945,6 +3166,108 @@ describe('preflight for moves and deletes', () => {
|
|
|
2945
3166
|
});
|
|
2946
3167
|
|
|
2947
3168
|
describe('move_file', () => {
|
|
3169
|
+
describe('rewrites links', () => {
|
|
3170
|
+
/** A node at Sales/deal.md, linked from a page beside it, a read-only page, and a page the caller cannot read. */
|
|
3171
|
+
async function linked(): Promise<string> {
|
|
3172
|
+
const base = await seeded();
|
|
3173
|
+
await fs.writeFile(KB('NodeTypes/Deal.md'), '# Deal\n');
|
|
3174
|
+
await fs.writeFile(KB('Sales/deal.md'), '---\nnodeType: "[Deal](../NodeTypes/Deal.md)"\nid: deal-a\n---\n\n# Deal\nSee [policy](../HR/policy.md).\n');
|
|
3175
|
+
await fs.writeFile(KB('Sales/index.md'), `[d](deal.md "Deal") [r](/${KB_DIR}/Sales/deal.md#terms) [id](deal-a)\n\n\`deal.md\`\n`);
|
|
3176
|
+
await fs.writeFile(KB('Locked/refs.md'), '[d](../Sales/deal.md)\n');
|
|
3177
|
+
await fs.writeFile(KB('Secret/refs.md'), '[d](../Sales/deal.md)\n');
|
|
3178
|
+
await fs.writeFile(KB('Sales/transcripts/01.md'), '[d](../deal.md)\n');
|
|
3179
|
+
return base;
|
|
3180
|
+
}
|
|
3181
|
+
const args = { src: KB('Sales/deal.md'), dest: KB('Sales/2026/deal.md') };
|
|
3182
|
+
|
|
3183
|
+
it('by default: links in and out are rewritten, the dry run answering the plan the move carries out', async () => {
|
|
3184
|
+
const base = await linked();
|
|
3185
|
+
const dry = await call(base, 'move_file', { ...args, dryRun: true });
|
|
3186
|
+
expect(dry.status).toBe(200);
|
|
3187
|
+
expect(dry.body.links).toEqual({
|
|
3188
|
+
filesEdited: 2,
|
|
3189
|
+
linksRewritten: 4,
|
|
3190
|
+
edits: [
|
|
3191
|
+
{ path: KB('Sales/2026/deal.md'), from: '../NodeTypes/Deal.md', to: '../../NodeTypes/Deal.md' },
|
|
3192
|
+
{ path: KB('Sales/2026/deal.md'), from: '../HR/policy.md', to: '../../HR/policy.md' },
|
|
3193
|
+
{ path: KB('Sales/index.md'), from: 'deal.md', to: '2026/deal.md' },
|
|
3194
|
+
{ path: KB('Sales/index.md'), from: `/${KB_DIR}/Sales/deal.md#terms`, to: `/${KB_DIR}/Sales/2026/deal.md#terms` },
|
|
3195
|
+
],
|
|
3196
|
+
notRewritten: [{ path: KB('Locked/refs.md'), reason: 'no write access', links: ['../Sales/deal.md'] }],
|
|
3197
|
+
unsearched: 'Links in files you cannot read were not searched, and may still point at the old path.',
|
|
3198
|
+
});
|
|
3199
|
+
// Nothing about the unreadable page leaks.
|
|
3200
|
+
expect(JSON.stringify(dry.body)).not.toContain('Secret');
|
|
3201
|
+
expect(await fs.readFile(KB('Sales/index.md'), { encoding: 'utf8' })).toContain('[d](deal.md "Deal")');
|
|
3202
|
+
|
|
3203
|
+
const run = await call(base, 'move_file', args);
|
|
3204
|
+
expect(run.status).toBe(200);
|
|
3205
|
+
expect(run.body).toMatchObject({ moved: true });
|
|
3206
|
+
expect(run.body.links).toEqual(dry.body.links);
|
|
3207
|
+
expect(await fs.readFile(KB('Sales/2026/deal.md'), { encoding: 'utf8' })).toBe(
|
|
3208
|
+
'---\nnodeType: "[Deal](../../NodeTypes/Deal.md)"\nid: deal-a\n---\n\n# Deal\nSee [policy](../../HR/policy.md).\n',
|
|
3209
|
+
);
|
|
3210
|
+
expect(await fs.readFile(KB('Sales/index.md'), { encoding: 'utf8' })).toBe(
|
|
3211
|
+
`[d](2026/deal.md "Deal") [r](/${KB_DIR}/Sales/2026/deal.md#terms) [id](deal-a)\n\n\`deal.md\`\n`,
|
|
3212
|
+
);
|
|
3213
|
+
// The read-only page, the unreadable page and the transcript keep the old path.
|
|
3214
|
+
expect(await fs.readFile(KB('Locked/refs.md'), { encoding: 'utf8' })).toBe('[d](../Sales/deal.md)\n');
|
|
3215
|
+
expect(await fs.readFile(KB('Secret/refs.md'), { encoding: 'utf8' })).toBe('[d](../Sales/deal.md)\n');
|
|
3216
|
+
expect(await fs.readFile(KB('Sales/transcripts/01.md'), { encoding: 'utf8' })).toBe('[d](../deal.md)\n');
|
|
3217
|
+
});
|
|
3218
|
+
|
|
3219
|
+
it('`rewriteLinks: false` moves exactly as before: no link touched, no `links` block', async () => {
|
|
3220
|
+
const base = await linked();
|
|
3221
|
+
const dry = await call(base, 'move_file', { ...args, dryRun: true, rewriteLinks: false });
|
|
3222
|
+
expect(dry.body.links).toBeUndefined();
|
|
3223
|
+
const run = await call(base, 'move_file', { ...args, rewriteLinks: false });
|
|
3224
|
+
expect(run.body).toMatchObject({ moved: true });
|
|
3225
|
+
expect(run.body.links).toBeUndefined();
|
|
3226
|
+
expect(await fs.readFile(KB('Sales/index.md'), { encoding: 'utf8' })).toContain('[d](deal.md "Deal")');
|
|
3227
|
+
expect(await fs.readFile(KB('Sales/2026/deal.md'), { encoding: 'utf8' })).toContain('../NodeTypes/Deal.md');
|
|
3228
|
+
});
|
|
3229
|
+
|
|
3230
|
+
it('the pages the answer names reach the read hook, the edited ones the write hook, and a page merely searched neither', async () => {
|
|
3231
|
+
const base = await linked();
|
|
3232
|
+
const reads: string[] = [];
|
|
3233
|
+
const writes: string[] = [];
|
|
3234
|
+
hooks.onAgentRead(async (op) => {
|
|
3235
|
+
reads.push(op.wsPath ?? '');
|
|
3236
|
+
});
|
|
3237
|
+
hooks.onPreWrite(async (op) => {
|
|
3238
|
+
writes.push(op.wsPath ?? '');
|
|
3239
|
+
});
|
|
3240
|
+
expect((await call(base, 'move_file', args)).body).toMatchObject({ moved: true });
|
|
3241
|
+
// `Locked/refs.md` is named (left for want of write access, its links
|
|
3242
|
+
// listed), so the read hook hears of it; the unreadable page and the
|
|
3243
|
+
// transcript are never named, so it never hears of them.
|
|
3244
|
+
expect(reads.sort()).toEqual([KB('Locked/refs.md'), KB('Sales/deal.md'), KB('Sales/index.md')]);
|
|
3245
|
+
expect([...new Set(writes)].sort()).toEqual([KB('Sales/2026/deal.md'), KB('Sales/deal.md'), KB('Sales/index.md')]);
|
|
3246
|
+
});
|
|
3247
|
+
|
|
3248
|
+
it('a move that would edit more than 200 files is refused, and nothing changes', async () => {
|
|
3249
|
+
const base = await seeded();
|
|
3250
|
+
for (let i = 0; i < 201; i++) await fs.writeFile(KB(`Sales/pages/p${i}.md`), '[d](../deal.md)\n');
|
|
3251
|
+
const dry = await call(base, 'move_file', { ...args, dryRun: true });
|
|
3252
|
+
expect(dry.body).toMatchObject({ allowed: false });
|
|
3253
|
+
expect(dry.body.reason).toMatch(/201 files.*rewriteLinks: false/s);
|
|
3254
|
+
const run = await call(base, 'move_file', args);
|
|
3255
|
+
expect(run.status).toBe(400);
|
|
3256
|
+
expect(JSON.stringify(run.body)).toContain('rewriteLinks: false');
|
|
3257
|
+
expect(await exists(args.src)).toBe(true);
|
|
3258
|
+
expect(await fs.readFile(KB('Sales/pages/p0.md'), { encoding: 'utf8' })).toBe('[d](../deal.md)\n');
|
|
3259
|
+
});
|
|
3260
|
+
|
|
3261
|
+
it('the description states the default, the opt-out, and that prose and code are not changed', async () => {
|
|
3262
|
+
await start();
|
|
3263
|
+
const def = (await toolRegistry.listInternal()).find((t) => t.name === 'move_file');
|
|
3264
|
+
const description = typeof def?.description === 'string' ? def.description : '';
|
|
3265
|
+
expect(description).toMatch(/rewritten by default/);
|
|
3266
|
+
expect(description).toContain('`rewriteLinks: false`');
|
|
3267
|
+
expect(description).toMatch(/plain prose or code are never changed/);
|
|
3268
|
+
});
|
|
3269
|
+
});
|
|
3270
|
+
|
|
2948
3271
|
it('a same-access move: the dry run changes nothing, the real call runs without confirm', async () => {
|
|
2949
3272
|
const base = await seeded();
|
|
2950
3273
|
const args = { src: KB('Sales/deal.md'), dest: KB('Sales/2026/deal.md') };
|
|
@@ -3303,6 +3626,80 @@ describe('preflight for moves and deletes', () => {
|
|
|
3303
3626
|
expect(run.status).toBe(403);
|
|
3304
3627
|
expect(run.body).toMatchObject({ kind: 'write-denied', path: KB('Sales/deal.md'), canPropose: true });
|
|
3305
3628
|
});
|
|
3629
|
+
|
|
3630
|
+
// A nested access.md is deleted the way a person deletes it in the app:
|
|
3631
|
+
// by whoever may write it, through the same one-file delete (the locking
|
|
3632
|
+
// filesystem's, which commits and pushes as the caller) as any file.
|
|
3633
|
+
it('a nested access.md is deleted by a caller who may write it, like any file', async () => {
|
|
3634
|
+
const base = await seeded();
|
|
3635
|
+
await fs.writeFile(KB('Sales/access.md'), '---\nread: Admin\n---\n');
|
|
3636
|
+
expect((await call(base, 'file_stat', { path: KB('Sales/access.md') })).body).toMatchObject({
|
|
3637
|
+
managed: true, movable: false, deletable: true,
|
|
3638
|
+
});
|
|
3639
|
+
const deleted: string[] = [];
|
|
3640
|
+
const deleteFile = fs.deleteFile.bind(fs);
|
|
3641
|
+
fs.deleteFile = async (p: string) => { deleted.push(p); return deleteFile(p); };
|
|
3642
|
+
|
|
3643
|
+
const run = await call(base, 'delete_file', { path: KB('Sales/access.md') });
|
|
3644
|
+
|
|
3645
|
+
expect(run.status).toBe(200);
|
|
3646
|
+
expect(run.body).toEqual({ path: KB('Sales/access.md'), deleted: true });
|
|
3647
|
+
expect(deleted).toEqual([KB('Sales/access.md')]);
|
|
3648
|
+
expect(await exists(KB('Sales/access.md'))).toBe(false);
|
|
3649
|
+
expect(await exists(KB('Sales/deal.md'))).toBe(true);
|
|
3650
|
+
});
|
|
3651
|
+
|
|
3652
|
+
it('a nested access.md the caller may not write is the ordinary write refusal, and stays', async () => {
|
|
3653
|
+
const base = await seeded();
|
|
3654
|
+
await fs.writeFile(KB('Locked/access.md'), '---\nread: everyone\n---\n');
|
|
3655
|
+
expect((await call(base, 'file_stat', { path: KB('Locked/access.md') })).body).toMatchObject({
|
|
3656
|
+
managed: true, deletable: false,
|
|
3657
|
+
});
|
|
3658
|
+
|
|
3659
|
+
const run = await call(base, 'delete_file', { path: KB('Locked/access.md') });
|
|
3660
|
+
|
|
3661
|
+
expect(run.status).toBe(403);
|
|
3662
|
+
expect(run.body).toMatchObject({ kind: 'write-denied', path: KB('Locked/access.md'), canPropose: true });
|
|
3663
|
+
expect(JSON.stringify(run.body)).not.toContain('platform file');
|
|
3664
|
+
expect(await exists(KB('Locked/access.md'))).toBe(true);
|
|
3665
|
+
});
|
|
3666
|
+
|
|
3667
|
+
it('the root access.md and roles.yaml are the repository\'s own: refused for everyone, and they stay', async () => {
|
|
3668
|
+
const base = await seeded();
|
|
3669
|
+
await fs.writeFile(KB('roles.yaml'), 'roles: {}\n');
|
|
3670
|
+
for (const name of ['access.md', 'roles.yaml']) {
|
|
3671
|
+
// The caller here may write and own everything: the refusal is not about who asks.
|
|
3672
|
+
expect((await call(base, 'file_stat', { path: KB(name) })).body).toMatchObject({
|
|
3673
|
+
managed: true, movable: false, deletable: false,
|
|
3674
|
+
access: { read: true, write: true, download: true, owner: true },
|
|
3675
|
+
});
|
|
3676
|
+
const run = await call(base, 'delete_file', { path: KB(name) });
|
|
3677
|
+
expect(run.status).toBe(400);
|
|
3678
|
+
expect(run.body.error).toBe(`${name} is the repository's own file and cannot be deleted.`);
|
|
3679
|
+
expect(await exists(KB(name))).toBe(true);
|
|
3680
|
+
}
|
|
3681
|
+
});
|
|
3682
|
+
|
|
3683
|
+
it('a nested roles.yaml is content, and .bevelignore stays refused as before', async () => {
|
|
3684
|
+
const base = await seeded();
|
|
3685
|
+
await fs.writeFile(KB('Sales/roles.yaml'), 'content');
|
|
3686
|
+
await fs.writeFile(KB('Sales/.bevelignore'), '*.tmp\n');
|
|
3687
|
+
expect((await call(base, 'delete_file', { path: KB('Sales/roles.yaml') })).status).toBe(200);
|
|
3688
|
+
const ignore = await call(base, 'delete_file', { path: KB('Sales/.bevelignore') });
|
|
3689
|
+
expect(ignore.status).toBe(400);
|
|
3690
|
+
expect(ignore.body.error).toBe('.bevelignore is a platform file and cannot be deleted through the agent tools.');
|
|
3691
|
+
expect((await call(base, 'file_stat', { path: KB('Sales/.bevelignore') })).body).toMatchObject({ deletable: false });
|
|
3692
|
+
});
|
|
3693
|
+
|
|
3694
|
+
it('delete_folder still refuses the repository root, which keeps its access.md', async () => {
|
|
3695
|
+
const base = await seeded();
|
|
3696
|
+
for (const args of [{ dryRun: true }, { confirm: true }]) {
|
|
3697
|
+
const run = await call(base, 'delete_folder', { path: KB_DIR, ...args });
|
|
3698
|
+
expect(run.body.allowed === false || run.status === 400).toBe(true);
|
|
3699
|
+
expect(JSON.stringify(run.body)).toContain('The repository root is a platform folder and cannot be moved or deleted.');
|
|
3700
|
+
}
|
|
3701
|
+
expect(await exists(KB('access.md'))).toBe(true);
|
|
3702
|
+
});
|
|
3306
3703
|
});
|
|
3307
3704
|
|
|
3308
3705
|
describe('what counts as the platform\'s own, and what a move or delete may reach', () => {
|