@bevel-software/platform-core-backend 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-guide/access-control.md +146 -289
- package/agent-guide/conventions.md +23 -25
- package/agent-guide/directory-structure.md +72 -120
- package/agent-guide/drafts-and-change-requests.md +92 -0
- package/agent-guide/for-git-users.md +47 -0
- package/agent-guide/glossary.md +36 -0
- package/agent-guide/how-agents-find-things.md +49 -0
- package/agent-guide/html-views.md +239 -8
- package/agent-guide/new-knowledge-base.md +9 -8
- package/agent-guide/skills.md +67 -42
- package/agent-guide/start-here.md +65 -0
- package/agent-guide/tool-manuals.md +133 -155
- package/agent-guide/tools.md +67 -0
- package/agent-guide/where-a-new-file-goes.md +45 -28
- package/dist/core/catalog-revision.d.ts +12 -1
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +23 -3
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/core-ports.d.ts +41 -0
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +15 -8
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +22 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +44 -16
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +16 -0
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +12 -2
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +39 -9
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +15 -3
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +239 -75
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +22 -24
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +48 -51
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-view.d.ts.map +1 -1
- package/dist/modules/access/access-view.js +12 -11
- package/dist/modules/access/access-view.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +3 -0
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +58 -33
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/groups-admin.routes.d.ts.map +1 -1
- package/dist/modules/access/groups-admin.routes.js +4 -2
- package/dist/modules/access/groups-admin.routes.js.map +1 -1
- package/dist/modules/access/groups-admin.service.d.ts +44 -13
- package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
- package/dist/modules/access/groups-admin.service.js +80 -38
- package/dist/modules/access/groups-admin.service.js.map +1 -1
- package/dist/modules/access/groups-edit.d.ts +1 -1
- package/dist/modules/access/groups-edit.d.ts.map +1 -1
- package/dist/modules/access/groups-edit.js +1 -1
- package/dist/modules/access/groups-edit.js.map +1 -1
- package/dist/modules/access/index.d.ts +1 -1
- package/dist/modules/access/index.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.d.ts +116 -24
- package/dist/modules/access/reference-scan.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.js +305 -44
- package/dist/modules/access/reference-scan.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +17 -8
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +32 -23
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +35 -10
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +60 -20
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/access-splice.d.ts +22 -18
- package/dist/modules/access-model/access-splice.d.ts.map +1 -1
- package/dist/modules/access-model/access-splice.js +20 -14
- package/dist/modules/access-model/access-splice.js.map +1 -1
- package/dist/modules/access-model/group-files.d.ts +1 -0
- package/dist/modules/access-model/group-files.d.ts.map +1 -1
- package/dist/modules/access-model/group-files.js +5 -1
- package/dist/modules/access-model/group-files.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +47 -7
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.js +103 -13
- package/dist/modules/agent-guide/agent-guide.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
- package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.js +46 -22
- package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
- package/dist/modules/agent-guide/guide-budget.d.ts +48 -0
- package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
- package/dist/modules/agent-guide/guide-budget.js +67 -0
- package/dist/modules/agent-guide/guide-budget.js.map +1 -0
- package/dist/modules/agent-guide/index.d.ts +2 -1
- package/dist/modules/agent-guide/index.d.ts.map +1 -1
- package/dist/modules/agent-guide/index.js +2 -1
- package/dist/modules/agent-guide/index.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +3 -1
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +7 -1
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +5 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +26 -9
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/agent-scope/agent-scope.contract.d.ts +67 -0
- package/dist/modules/agent-scope/agent-scope.contract.d.ts.map +1 -0
- package/dist/modules/agent-scope/agent-scope.contract.js +27 -0
- package/dist/modules/agent-scope/agent-scope.contract.js.map +1 -0
- package/dist/modules/agent-scope/connection-scope.d.ts +111 -0
- package/dist/modules/agent-scope/connection-scope.d.ts.map +1 -0
- package/dist/modules/agent-scope/connection-scope.js +165 -0
- package/dist/modules/agent-scope/connection-scope.js.map +1 -0
- package/dist/modules/agent-scope/index.d.ts +3 -0
- package/dist/modules/agent-scope/index.d.ts.map +1 -0
- package/dist/modules/agent-scope/index.js +3 -0
- package/dist/modules/agent-scope/index.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +2 -0
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/audit.contract.d.ts +6 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/auth/account-admission.d.ts +12 -0
- package/dist/modules/auth/account-admission.d.ts.map +1 -1
- package/dist/modules/auth/account-admission.js +15 -0
- package/dist/modules/auth/account-admission.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +27 -1
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +42 -4
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts +13 -9
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +35 -29
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +55 -6
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +128 -34
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/index.d.ts +1 -1
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/auth/index.js +1 -1
- package/dist/modules/auth/index.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +2 -9
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +2 -2
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +1 -38
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +68 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +33 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts +8 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.js +13 -2
- package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
- package/dist/modules/embed/embed.tools.d.ts.map +1 -1
- package/dist/modules/embed/embed.tools.js +5 -0
- package/dist/modules/embed/embed.tools.js.map +1 -1
- package/dist/modules/kb-fs/index.d.ts +2 -0
- package/dist/modules/kb-fs/index.d.ts.map +1 -1
- package/dist/modules/kb-fs/index.js +1 -0
- package/dist/modules/kb-fs/index.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +7 -1
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +26 -6
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +22 -7
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-fs/tmp-ref.d.ts +36 -0
- package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -0
- package/dist/modules/kb-fs/tmp-ref.js +76 -0
- package/dist/modules/kb-fs/tmp-ref.js.map +1 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +7 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +14 -1
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +7 -1
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +39 -4
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +11 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +74 -48
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +45 -10
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +9 -0
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +34 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- package/dist/modules/plugins/index.d.ts +1 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +18 -2
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +5 -1
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +32 -5
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +11 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +159 -24
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +1 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +53 -11
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +9 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +100 -42
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/oidc-check.d.ts.map +1 -1
- package/dist/modules/settings/oidc-check.js +5 -17
- package/dist/modules/settings/oidc-check.js.map +1 -1
- package/dist/modules/skills/index.d.ts +3 -3
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +0 -2
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +16 -0
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +6 -1
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +25 -13
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +11 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +1 -0
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +4 -1
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/index.d.ts +1 -1
- package/dist/modules/tool-auth/index.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +9 -0
- package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.js +1 -0
- package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +27 -3
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +32 -6
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts +7 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +3 -0
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-def.js +1 -1
- package/dist/modules/tool-helpers/tool-def.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +15 -0
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +19 -0
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +10 -0
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-helpers/validate-token.d.ts +3 -1
- package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -1
- package/dist/modules/tool-helpers/validate-token.js +1 -1
- package/dist/modules/tool-helpers/validate-token.js.map +1 -1
- package/dist/modules/tool-manuals/index.d.ts +3 -3
- package/dist/modules/tool-manuals/index.d.ts.map +1 -1
- package/dist/modules/tool-manuals/index.js +0 -2
- package/dist/modules/tool-manuals/index.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +46 -6
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +7 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +41 -4
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +24 -13
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +138 -59
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +8 -2
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +56 -20
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/manual.routes.d.ts +8 -1
- package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -1
- package/dist/modules/tool-registry/manual.routes.js +27 -7
- package/dist/modules/tool-registry/manual.routes.js.map +1 -1
- package/dist/modules/tool-registry/tool.contract.d.ts +7 -0
- package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-registry/tool.contract.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +36 -27
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +2 -2
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +20 -35
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +2 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +3 -0
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
- package/dist/modules/workspace/agent-access.gate.js +1 -1
- package/dist/modules/workspace/agent-access.gate.js.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +28 -4
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js +61 -40
- package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
- package/dist/modules/workspace/index.d.ts +8 -0
- package/dist/modules/workspace/index.d.ts.map +1 -1
- package/dist/modules/workspace/index.js +8 -0
- package/dist/modules/workspace/index.js.map +1 -1
- package/dist/modules/workspace/nul-scan.d.ts +19 -0
- package/dist/modules/workspace/nul-scan.d.ts.map +1 -0
- package/dist/modules/workspace/nul-scan.js +55 -0
- package/dist/modules/workspace/nul-scan.js.map +1 -0
- package/dist/modules/workspace/pattern-matcher.contract.d.ts +110 -27
- package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -1
- package/dist/modules/workspace/pattern-matcher.contract.js +30 -13
- package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -1
- package/dist/modules/workspace/ripgrep-search.d.ts +54 -0
- package/dist/modules/workspace/ripgrep-search.d.ts.map +1 -0
- package/dist/modules/workspace/ripgrep-search.js +657 -0
- package/dist/modules/workspace/ripgrep-search.js.map +1 -0
- package/dist/modules/workspace/ripgrep-version.d.ts +15 -0
- package/dist/modules/workspace/ripgrep-version.d.ts.map +1 -0
- package/dist/modules/workspace/ripgrep-version.js +31 -0
- package/dist/modules/workspace/ripgrep-version.js.map +1 -0
- package/dist/modules/workspace/startup/beside-checkout.d.ts +8 -5
- package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -1
- package/dist/modules/workspace/startup/beside-checkout.js +14 -5
- package/dist/modules/workspace/startup/beside-checkout.js.map +1 -1
- package/dist/modules/workspace/worker-pool.d.ts +3 -4
- package/dist/modules/workspace/worker-pool.d.ts.map +1 -1
- package/dist/modules/workspace/worker-pool.js.map +1 -1
- package/dist/modules/workspace/workspace-tmp.d.ts +146 -0
- package/dist/modules/workspace/workspace-tmp.d.ts.map +1 -0
- package/dist/modules/workspace/workspace-tmp.js +435 -0
- package/dist/modules/workspace/workspace-tmp.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +5 -26
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +47 -6
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +86 -6
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +3 -2
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +531 -137
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/oauth-token-error.d.ts +23 -0
- package/dist/shared/oauth-token-error.d.ts.map +1 -0
- package/dist/shared/oauth-token-error.js +47 -0
- package/dist/shared/oauth-token-error.js.map +1 -0
- package/dist/shared/path-order.d.ts +28 -8
- package/dist/shared/path-order.d.ts.map +1 -1
- package/dist/shared/path-order.js +37 -8
- package/dist/shared/path-order.js.map +1 -1
- package/dist/shared/read-capped.d.ts +19 -0
- package/dist/shared/read-capped.d.ts.map +1 -0
- package/dist/shared/read-capped.js +45 -0
- package/dist/shared/read-capped.js.map +1 -0
- package/migrations/0022_session_version.sql +6 -0
- package/migrations/0023_agent_path.sql +3 -0
- package/migrations/meta/0022_snapshot.json +2360 -0
- package/migrations/meta/0023_snapshot.json +2378 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +4 -3
- package/src/__tests__/hexis-catalog.ts +144 -0
- package/src/__tests__/pg-scratch.test.ts +69 -0
- package/src/__tests__/pg-scratch.ts +31 -0
- package/src/__tests__/real-disk.ts +21 -0
- package/src/__tests__/slow-ripgrep.ts +65 -0
- package/src/__tests__/tokens.ts +19 -0
- package/src/__tests__/tool-manuals.ts +32 -0
- package/src/__tests__/tool-wiring.ts +25 -0
- package/src/core/__tests__/boot-without-ripgrep.test.ts +34 -0
- package/src/core/__tests__/catalog-revision.test.ts +89 -0
- package/src/core/__tests__/damaged-document.tools.test.ts +2 -2
- package/src/core/__tests__/gated-boot.test.ts +8 -1
- package/src/core/__tests__/lifecycle.test.ts +46 -13
- package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
- package/src/core/catalog-revision.ts +24 -2
- package/src/core/core-ports.ts +42 -0
- package/src/core/create-core-server.ts +14 -1
- package/src/core/create-core-services.ts +62 -15
- package/src/core/lifecycle.ts +26 -2
- package/src/index.ts +1 -1
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +12 -0
- package/src/modules/access/__tests__/access-control.colliding-name-cases.test.ts +113 -0
- package/src/modules/access/__tests__/access-control.frontmatter-head.test.ts +117 -0
- package/src/modules/access/__tests__/access-control.group-named-admin.test.ts +122 -0
- package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +50 -0
- package/src/modules/access/__tests__/access-declarations.test.ts +19 -0
- package/src/modules/access/__tests__/access-groups.test.ts +55 -22
- package/src/modules/access/__tests__/access-mutation.service.test.ts +85 -59
- package/src/modules/access/__tests__/access-restrict-here.test.ts +7 -5
- package/src/modules/access/__tests__/access.routes.admins.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -0
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +50 -14
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +52 -10
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +18 -13
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
- package/src/modules/access/__tests__/grant-sources.test.ts +20 -15
- package/src/modules/access/__tests__/groups-admin.routes.test.ts +61 -0
- package/src/modules/access/__tests__/groups-admin.service.test.ts +211 -18
- package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
- package/src/modules/access/__tests__/reference-scan.test.ts +366 -14
- package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
- package/src/modules/access/__tests__/roles-admin.service.test.ts +43 -22
- package/src/modules/access/__tests__/roles-capabilities.test.ts +6 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +1 -1
- package/src/modules/access/access-control.interface.ts +43 -10
- package/src/modules/access/access-control.service.ts +250 -72
- package/src/modules/access/access-mutation.service.ts +48 -53
- package/src/modules/access/access-view.ts +12 -10
- package/src/modules/access/access.routes.ts +66 -37
- package/src/modules/access/groups-admin.routes.ts +4 -2
- package/src/modules/access/groups-admin.service.ts +114 -44
- package/src/modules/access/groups-edit.ts +1 -1
- package/src/modules/access/index.ts +1 -1
- package/src/modules/access/reference-scan.ts +319 -46
- package/src/modules/access/roles-admin.service.ts +43 -26
- package/src/modules/access/synced-groups-writer.ts +1 -1
- package/src/modules/access-model/__tests__/access-grammar.test.ts +38 -1
- package/src/modules/access-model/__tests__/access-splice.test.ts +50 -8
- package/src/modules/access-model/access-grammar.ts +65 -20
- package/src/modules/access-model/access-splice.ts +31 -23
- package/src/modules/access-model/group-files.ts +5 -0
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +110 -25
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +38 -11
- package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
- package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
- package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
- package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
- package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
- package/src/modules/agent-guide/agent-guide.tools.ts +51 -22
- package/src/modules/agent-guide/agent-guide.ts +120 -13
- package/src/modules/agent-guide/guide-budget.ts +73 -0
- package/src/modules/agent-guide/index.ts +12 -0
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +5 -4
- package/src/modules/agent-instructions/compose.ts +7 -1
- package/src/modules/agent-instructions/shared-file-rules.ts +27 -9
- package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +421 -0
- package/src/modules/agent-scope/__tests__/connection-scope.test.ts +46 -0
- package/src/modules/agent-scope/__tests__/in-memory-kb.ts +148 -0
- package/src/modules/agent-scope/agent-scope.contract.ts +83 -0
- package/src/modules/agent-scope/connection-scope.ts +199 -0
- package/src/modules/agent-scope/index.ts +15 -0
- package/src/modules/audit/agent-audit.service.ts +2 -0
- package/src/modules/audit/audit.contract.ts +6 -0
- package/src/modules/auth/__tests__/auth.middleware.test.ts +30 -2
- package/src/modules/auth/__tests__/auth.routes.test.ts +71 -5
- package/src/modules/auth/__tests__/auth.service.test.ts +90 -9
- package/src/modules/auth/__tests__/password-change-sessions.pg.test.ts +269 -0
- package/src/modules/auth/account-admission.ts +19 -0
- package/src/modules/auth/auth.middleware.ts +45 -4
- package/src/modules/auth/auth.routes.ts +46 -29
- package/src/modules/auth/auth.service.ts +135 -34
- package/src/modules/auth/index.ts +2 -0
- package/src/modules/auth/oidc-auth-provider.ts +2 -9
- package/src/modules/code-mode/code-mode.tool.ts +2 -2
- package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
- package/src/modules/connection-probe/connection-probe.service.ts +1 -34
- package/src/modules/database/__tests__/session-version-migration.pg.test.ts +133 -0
- package/src/modules/database/core-schema.ts +33 -0
- package/src/modules/declared-variables/__tests__/declared-variables.route.test.ts +44 -1
- package/src/modules/declared-variables/declared-variables.routes.ts +12 -1
- package/src/modules/embed/__tests__/embed.tools.test.ts +12 -0
- package/src/modules/embed/embed.tools.ts +5 -0
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +25 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +53 -8
- package/src/modules/kb-fs/index.ts +2 -0
- package/src/modules/kb-fs/locking-filesystem.ts +7 -1
- package/src/modules/kb-fs/repo-path.ts +33 -7
- package/src/modules/kb-fs/tmp-ref.ts +81 -0
- package/src/modules/mcp/__tests__/connect-as.pg.test.ts +137 -0
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +29 -5
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +8 -0
- package/src/modules/mcp/__tests__/mcp.connect-as.test.ts +195 -0
- package/src/modules/mcp/__tests__/mcp.routes.connect-as.test.ts +192 -0
- package/src/modules/mcp/__tests__/oauth-consent.connect-as.test.ts +146 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +19 -2
- package/src/modules/mcp/mcp.routes.ts +39 -3
- package/src/modules/mcp/mcp.service.ts +112 -52
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +46 -8
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +41 -1
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +38 -2
- package/src/modules/plugins/index.ts +1 -1
- package/src/modules/plugins/plugin-links.ts +19 -2
- package/src/modules/plugins/plugins.routes.ts +35 -4
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +113 -0
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +430 -3
- package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
- package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +170 -27
- package/src/modules/secrets-vault/index.ts +1 -1
- package/src/modules/secrets-vault/secrets-vault.contract.ts +61 -11
- package/src/modules/secrets-vault/secrets-vault.routes.ts +114 -42
- package/src/modules/settings/oidc-check.ts +5 -18
- package/src/modules/skills/__tests__/skills.service.test.ts +34 -0
- package/src/modules/skills/index.ts +7 -2
- package/src/modules/skills/skills.contract.ts +17 -0
- package/src/modules/skills/skills.service.ts +5 -1
- package/src/modules/skills/skills.tools.ts +45 -14
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +4 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +11 -1
- package/src/modules/tool-auth/external-api-key.service.ts +5 -2
- package/src/modules/tool-auth/index.ts +1 -1
- package/src/modules/tool-auth/internal-token.service.ts +10 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +45 -3
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +19 -11
- package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -1
- package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
- package/src/modules/tool-helpers/tool-context.ts +10 -0
- package/src/modules/tool-helpers/tool-def.ts +1 -1
- package/src/modules/tool-helpers/tool-handler.ts +34 -0
- package/src/modules/tool-helpers/tool.contract.ts +10 -0
- package/src/modules/tool-helpers/validate-token.ts +4 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +80 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +42 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +99 -8
- package/src/modules/tool-manuals/index.ts +6 -2
- package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
- package/src/modules/tool-manuals/tool-manuals.contract.ts +40 -6
- package/src/modules/tool-manuals/tool-manuals.routes.ts +40 -3
- package/src/modules/tool-manuals/tool-manuals.service.ts +169 -61
- package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -25
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -113
- package/src/modules/tool-registry/manual.routes.ts +26 -5
- package/src/modules/tool-registry/tool.contract.ts +7 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +1 -1
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +54 -3
- package/src/modules/workflow/agent-tools/workflow.tools.ts +36 -27
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +130 -3
- package/src/modules/workflow/git/git.service.ts +29 -48
- package/src/modules/workflow/workflow.service.ts +5 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -2
- package/src/modules/workspace/__tests__/holds-nul.test.ts +66 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
- package/src/modules/workspace/__tests__/ripgrep-search.test.ts +570 -0
- package/src/modules/workspace/__tests__/ripgrep-version.test.ts +29 -0
- package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +928 -0
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +17 -37
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +20 -8
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -12
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +22 -10
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +549 -162
- package/src/modules/workspace/agent-access.gate.ts +1 -1
- package/src/modules/workspace/file-readers/__tests__/doc-extract.service.test.ts +10 -0
- package/src/modules/workspace/file-readers/doc-extract.service.ts +71 -38
- package/src/modules/workspace/index.ts +20 -0
- package/src/modules/workspace/nul-scan.ts +50 -0
- package/src/modules/workspace/pattern-matcher.contract.ts +123 -32
- package/src/modules/workspace/ripgrep-search.ts +715 -0
- package/src/modules/workspace/ripgrep-version.ts +35 -0
- package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +11 -0
- package/src/modules/workspace/startup/beside-checkout.ts +13 -5
- package/src/modules/workspace/worker-pool.ts +3 -4
- package/src/modules/workspace/workspace-tmp.ts +457 -0
- package/src/modules/workspace/workspace.routes.ts +6 -27
- package/src/modules/workspace/workspace.service.ts +92 -15
- package/src/modules/workspace/workspace.tools.ts +574 -176
- package/src/shared/__tests__/read-capped.test.ts +68 -0
- package/src/shared/oauth-token-error.ts +46 -0
- package/src/shared/path-order.ts +37 -8
- package/src/shared/read-capped.ts +40 -0
- package/starter-packs/engineering/Plugins/engineering-starter/access.md +1 -1
- package/starter-packs/general/Plugins/general-starter/access.md +1 -1
- package/starter-packs/operations/Plugins/operations-starter/access.md +1 -1
- package/starter-packs/sales/Plugins/sales-starter/access.md +1 -1
- package/starter-packs/support/Plugins/support-starter/access.md +1 -1
- package/agent-guide/finding-things.md +0 -7
- package/agent-guide/introduction.md +0 -27
- package/dist/modules/workspace/bounded-matcher.d.ts +0 -73
- package/dist/modules/workspace/bounded-matcher.d.ts.map +0 -1
- package/dist/modules/workspace/bounded-matcher.js +0 -200
- package/dist/modules/workspace/bounded-matcher.js.map +0 -1
- package/src/modules/workspace/__tests__/bounded-matcher.test.ts +0 -213
- package/src/modules/workspace/bounded-matcher.ts +0 -271
|
@@ -41,6 +41,16 @@ export const users = pgTable('users', {
|
|
|
41
41
|
* until an admin (or the user, from their Account page) sets one.
|
|
42
42
|
*/
|
|
43
43
|
passwordHash: text('password_hash'),
|
|
44
|
+
/**
|
|
45
|
+
* Which generation of this account's sessions is honoured. Every session
|
|
46
|
+
* token carries the value it was issued at (`sv`; a token from before the
|
|
47
|
+
* column existed carries none and counts as 0), and one whose value is not
|
|
48
|
+
* the row's is refused. Changing the password moves it on, so every session
|
|
49
|
+
* opened with the old password ends at its next request — the one that
|
|
50
|
+
* made the change is handed a fresh token. Connection keys are not
|
|
51
|
+
* sessions and do not look at it.
|
|
52
|
+
*/
|
|
53
|
+
sessionVersion: integer('session_version').default(0).notNull(),
|
|
44
54
|
/**
|
|
45
55
|
* The one onboarding fact the server keeps: has this person concluded the
|
|
46
56
|
* connect-your-agent setup (the welcome page's Done, or the reminder
|
|
@@ -277,6 +287,15 @@ export const externalApiKeys = pgTable('api_tokens', {
|
|
|
277
287
|
* deleted in one step.
|
|
278
288
|
*/
|
|
279
289
|
deletedAt: timestamp('deleted_at'),
|
|
290
|
+
/**
|
|
291
|
+
* The agent configuration the key connects as — a repo-relative knowledge-base
|
|
292
|
+
* path, chosen when the key is minted. Null is Everything: the key sees
|
|
293
|
+
* every tool, skill and plugin its owner may read, exactly as before the
|
|
294
|
+
* column existed. The path is the whole record: the file is re-read on
|
|
295
|
+
* every surface build, so editing it changes what the key sees without a
|
|
296
|
+
* new key (see `modules/agent-scope`).
|
|
297
|
+
*/
|
|
298
|
+
agentPath: text('agent_path'),
|
|
280
299
|
}, (t) => ({
|
|
281
300
|
byUser: index('api_tokens_by_user').on(t.userId),
|
|
282
301
|
}));
|
|
@@ -486,6 +505,12 @@ export const oauthAuthCodes = pgTable('oauth_auth_codes', {
|
|
|
486
505
|
expiresAt: timestamp('expires_at').notNull(),
|
|
487
506
|
consumedAt: timestamp('consumed_at'),
|
|
488
507
|
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
508
|
+
/**
|
|
509
|
+
* The "Connect as" choice made on the consent step, carried from Finish to
|
|
510
|
+
* the token exchange that makes (or finds) the agent connection — the code
|
|
511
|
+
* is the only thing that travels between the two. Null is Everything.
|
|
512
|
+
*/
|
|
513
|
+
agentPath: text('agent_path'),
|
|
489
514
|
}, (t) => ({
|
|
490
515
|
// Sweep expired/spent rows without a full scan.
|
|
491
516
|
byExpiry: index('oauth_auth_codes_by_expiry').on(t.expiresAt),
|
|
@@ -525,6 +550,14 @@ export const agentConnections = pgTable('agent_connections', {
|
|
|
525
550
|
revokedAt: timestamp('revoked_at'),
|
|
526
551
|
/** `owner` | `admin` — who ended it; null while live. Same vocabulary as `api_tokens.revoked_by`. */
|
|
527
552
|
revokedBy: text('revoked_by'),
|
|
553
|
+
/**
|
|
554
|
+
* The agent configuration the person chose to connect as on the consent step — a
|
|
555
|
+
* repo-relative knowledge-base path — or null for Everything. Set by every
|
|
556
|
+
* consent that lands on the row, so re-authorising with another choice
|
|
557
|
+
* changes what the agent sees from its next request. Same meaning as
|
|
558
|
+
* `api_tokens.agent_path`.
|
|
559
|
+
*/
|
|
560
|
+
agentPath: text('agent_path'),
|
|
528
561
|
}, (t) => ({
|
|
529
562
|
byUser: index('agent_connections_by_user').on(t.userId),
|
|
530
563
|
liveUnq: uniqueIndex('agent_connections_live_unq')
|
|
@@ -4,6 +4,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
4
4
|
import { createDeclaredVariableRoutes } from '../declared-variables.routes.js';
|
|
5
5
|
import type { IToolManualService, ToolManualSummary } from '../../tool-manuals/tool-manuals.contract.js';
|
|
6
6
|
import type { ISecretsVaultService } from '../../secrets-vault/secrets-vault.contract.js';
|
|
7
|
+
import { ConnectionScope, type IAgentScopeResolver } from '../../agent-scope/index.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* The one core route that releases secret VALUES. What is worth pinning here is
|
|
@@ -46,6 +47,9 @@ async function mount(opts: {
|
|
|
46
47
|
source?: 'session' | 'external' | 'internal';
|
|
47
48
|
manuals?: ToolManualSummary[];
|
|
48
49
|
secrets?: Record<string, string>;
|
|
50
|
+
/** The `.agent` file the caller's connection was made as; absent is Everything. */
|
|
51
|
+
agentPath?: string;
|
|
52
|
+
agentScope?: Pick<IAgentScopeResolver, 'resolve'>;
|
|
49
53
|
}): Promise<Harness> {
|
|
50
54
|
const userId = opts.userId ?? 'user-1';
|
|
51
55
|
const source = opts.source ?? 'external';
|
|
@@ -64,10 +68,18 @@ async function mount(opts: {
|
|
|
64
68
|
toolManualService,
|
|
65
69
|
secretsVault,
|
|
66
70
|
(req, _res, next) => {
|
|
67
|
-
if (userId)
|
|
71
|
+
if (userId) {
|
|
72
|
+
req.toolAuth = {
|
|
73
|
+
userId,
|
|
74
|
+
source,
|
|
75
|
+
scope: 'write',
|
|
76
|
+
...(opts.agentPath ? { agentPath: opts.agentPath } : {}),
|
|
77
|
+
} as typeof req.toolAuth;
|
|
78
|
+
}
|
|
68
79
|
next();
|
|
69
80
|
},
|
|
70
81
|
async () => 'runner@x.eu',
|
|
82
|
+
opts.agentScope,
|
|
71
83
|
),
|
|
72
84
|
);
|
|
73
85
|
httpServer = await new Promise<HttpServer>((r) => {
|
|
@@ -133,6 +145,37 @@ describe('POST /agent/local-tools/:slug/variables', () => {
|
|
|
133
145
|
expect(resolve).not.toHaveBeenCalled();
|
|
134
146
|
});
|
|
135
147
|
|
|
148
|
+
it("releases only the variables of a connector the connection's .agent file loads", async () => {
|
|
149
|
+
const agent = { path: 'Agents/ops.agent', name: 'Ops' };
|
|
150
|
+
const loading = (connectors: string[]) => ({
|
|
151
|
+
resolve: async () => ConnectionScope.narrowed(agent, { skills: [], connectors, platformTools: null }, 'fp'),
|
|
152
|
+
});
|
|
153
|
+
// A folder entry over the tool's plugin loads it.
|
|
154
|
+
const inScope = await mount({
|
|
155
|
+
manuals: [GIT_TOOL],
|
|
156
|
+
secrets: { git_GITHUB_TOKEN: 'ghp_secret' },
|
|
157
|
+
agentPath: agent.path,
|
|
158
|
+
agentScope: loading(['Plugins/Engineering']),
|
|
159
|
+
});
|
|
160
|
+
expect((await fetch(`${inScope.base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(200);
|
|
161
|
+
await new Promise<void>((r) => httpServer!.close(() => r()));
|
|
162
|
+
|
|
163
|
+
// A readable tool the file does not load is as absent as an unreadable one.
|
|
164
|
+
const outOfScope = await mount({
|
|
165
|
+
manuals: [GIT_TOOL],
|
|
166
|
+
secrets: { git_GITHUB_TOKEN: 'ghp_secret' },
|
|
167
|
+
agentPath: agent.path,
|
|
168
|
+
agentScope: loading(['Plugins/billing.tool']),
|
|
169
|
+
});
|
|
170
|
+
expect((await fetch(`${outOfScope.base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(404);
|
|
171
|
+
expect(outOfScope.resolve).not.toHaveBeenCalled();
|
|
172
|
+
await new Promise<void>((r) => httpServer!.close(() => r()));
|
|
173
|
+
|
|
174
|
+
// A narrowed connection with no resolver wired loads nothing.
|
|
175
|
+
const unwired = await mount({ manuals: [GIT_TOOL], agentPath: agent.path });
|
|
176
|
+
expect((await fetch(`${unwired.base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(404);
|
|
177
|
+
});
|
|
178
|
+
|
|
136
179
|
it('403s an unauthenticated caller', async () => {
|
|
137
180
|
const { base } = await mount({ userId: '', manuals: [GIT_TOOL] });
|
|
138
181
|
expect((await fetch(`${base}/agent/local-tools/git/variables`, { method: 'POST' })).status).toBe(403);
|
|
@@ -5,6 +5,7 @@ const log = logger('declared-variables');
|
|
|
5
5
|
import { utcpNamespacedKey } from '../../shared/utcp-namespace.js';
|
|
6
6
|
import type { IToolManualService } from '../tool-manuals/index.js';
|
|
7
7
|
import type { ISecretsVaultService } from '../secrets-vault/index.js';
|
|
8
|
+
import { resolveConnectionScope, type IAgentScopeResolver } from '../agent-scope/index.js';
|
|
8
9
|
|
|
9
10
|
type ResolveUserEmail = (userId: string) => Promise<string | undefined>;
|
|
10
11
|
|
|
@@ -48,6 +49,12 @@ export function createDeclaredVariableRoutes(
|
|
|
48
49
|
secretsVault: ISecretsVaultService,
|
|
49
50
|
manualAuth: RequestHandler,
|
|
50
51
|
resolveUserEmail: ResolveUserEmail,
|
|
52
|
+
/**
|
|
53
|
+
* Resolves the scope of a connection made as an agent configuration: such a
|
|
54
|
+
* connection is handed only the variables of a connector the file loads.
|
|
55
|
+
* Absent, a narrowed connection is handed none.
|
|
56
|
+
*/
|
|
57
|
+
agentScope?: Pick<IAgentScopeResolver, 'resolve'>,
|
|
51
58
|
): express.Router {
|
|
52
59
|
const router = express.Router();
|
|
53
60
|
|
|
@@ -125,7 +132,11 @@ export function createDeclaredVariableRoutes(
|
|
|
125
132
|
// `listAccessible` already applies the per-file read verdict, so an
|
|
126
133
|
// unreadable manual is simply absent — same fail-closed read model the
|
|
127
134
|
// catalog uses.
|
|
128
|
-
const
|
|
135
|
+
const found = (await toolManualService.listAccessible(who.email)).find((m) => m.slug === slug);
|
|
136
|
+
// A connection made as an agent configuration is handed only the variables of
|
|
137
|
+
// a connector it loads; another is as absent as an unreadable one.
|
|
138
|
+
const scope = found ? await resolveConnectionScope(agentScope, who.email, req.toolAuth?.agentPath) : null;
|
|
139
|
+
const manual = found && scope?.allowsConnector(found.path) ? found : undefined;
|
|
129
140
|
if (!manual) return void res.status(404).json({ error: 'Not found' });
|
|
130
141
|
if (manual.remote !== false) {
|
|
131
142
|
return void res.status(409).json({
|
|
@@ -281,6 +281,18 @@ describe('open_page: refusals', () => {
|
|
|
281
281
|
expect(embedService.mintForUser).not.toHaveBeenCalled();
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
+
it('refuses a tmp ref — no knowledge-base page — reading and minting nothing', async () => {
|
|
285
|
+
const read = vi.fn<ReadForTool>();
|
|
286
|
+
const { call, embedService } = await serve({ read });
|
|
287
|
+
const { status, body } = await call({ path: '__tmp__/chat-uploads/plan.xlsx' });
|
|
288
|
+
expect(status).toBe(400);
|
|
289
|
+
expect(body.kind).toBe('tmp-ref-read-only');
|
|
290
|
+
expect(body.error).toContain('open_page does not take one there');
|
|
291
|
+
expect(body.error).toContain('copy_file');
|
|
292
|
+
expect(read).not.toHaveBeenCalled();
|
|
293
|
+
expect(embedService.mintForUser).not.toHaveBeenCalled();
|
|
294
|
+
});
|
|
295
|
+
|
|
284
296
|
it('refuses a path outside the knowledge base, and a missing one', async () => {
|
|
285
297
|
const { call, embedService } = await serve();
|
|
286
298
|
expect((await call({ path: '' })).status).toBe(400);
|
|
@@ -6,6 +6,7 @@ import type { KbContext } from '../../shared/kb-context.js';
|
|
|
6
6
|
import type { ReadForTool } from '../workspace/index.js';
|
|
7
7
|
import type { IEmbedService } from './embed.interface.js';
|
|
8
8
|
import { requireExternalSource } from '../tool-auth/index.js';
|
|
9
|
+
import { assertTmpRefsTaken } from '../kb-fs/index.js';
|
|
9
10
|
import { SESSION_ID_INPUT, type ToolDescriptionNotes, sessionIdInputOf } from '../workspace/index.js';
|
|
10
11
|
|
|
11
12
|
/** The tool name, in one place: the app manifest keys its view by it. */
|
|
@@ -149,6 +150,10 @@ export function registerEmbedTools(
|
|
|
149
150
|
toolHandler(async (args, ctx: ToolContext) => {
|
|
150
151
|
const raw = typeof args.path === 'string' ? args.path.trim() : '';
|
|
151
152
|
if (!raw) throw new ToolError('`path` is required.', 400);
|
|
153
|
+
// A tmp ref (`__tmp__/…`) is no knowledge-base page: refused by the one
|
|
154
|
+
// admission gate the normalised tools pass, before `toRepoRelative`
|
|
155
|
+
// would read it as the repository path `<kbDirName>/__tmp__/…`.
|
|
156
|
+
assertTmpRefsTaken(OPEN_PAGE_TOOL, { path: raw });
|
|
152
157
|
const heading = typeof args.heading === 'string' && args.heading.trim() ? args.heading.trim() : undefined;
|
|
153
158
|
const repoRelative = toRepoRelative(raw, kbDirName);
|
|
154
159
|
if (repoRelative === null) throw new ToolError(OUTSIDE_REPO(kbDirName), 400);
|
|
@@ -1482,6 +1482,31 @@ describe('LockingFilesystem — a destination that is taken is refused, never re
|
|
|
1482
1482
|
expect(await fs.readFile(onDisk(TAKEN), 'utf-8')).toBe('the winner of the race');
|
|
1483
1483
|
});
|
|
1484
1484
|
|
|
1485
|
+
/**
|
|
1486
|
+
* The same race for an EXCLUSIVE write (`overwrite: false`), which is how
|
|
1487
|
+
* `copy_file` lands bytes read out of the workspace's tmp folder: the
|
|
1488
|
+
* refusal is the clash sentence, and the lock releases untouched rather
|
|
1489
|
+
* than discarding the winner's queued file.
|
|
1490
|
+
*/
|
|
1491
|
+
it('an exclusive write onto a taken name is the same refusal, and releases UNTOUCHED', async () => {
|
|
1492
|
+
const workflow = makeWorkflow();
|
|
1493
|
+
const layer = new LockingFilesystem(
|
|
1494
|
+
{ basePath: root, contained: true },
|
|
1495
|
+
{ workflow, workspaceId: 'ws-feat', branch: 'feat', user: USER, kbDirName: KB },
|
|
1496
|
+
);
|
|
1497
|
+
await fs.writeFile(onDisk(TAKEN), 'the winner of the race');
|
|
1498
|
+
|
|
1499
|
+
await expect(layer.writeFile(TAKEN, 'the loser', { overwrite: false })).rejects.toMatchObject({
|
|
1500
|
+
name: 'DestinationTakenError',
|
|
1501
|
+
message: 'A file named taken.md already exists in KnowledgeBase.',
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
expect(workflow.releaseLockUntouched).toHaveBeenCalledWith('ws-feat', 'feat', TAKEN, USER);
|
|
1505
|
+
expect(workflow.releaseLockNoCommit).not.toHaveBeenCalled();
|
|
1506
|
+
expect(workflow.releaseLock).not.toHaveBeenCalled();
|
|
1507
|
+
expect(await fs.readFile(onDisk(TAKEN), 'utf-8')).toBe('the winner of the race');
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1485
1510
|
it.skipIf(process.platform === 'win32')('answers the clash sentence when a DANGLING link holds the name', async () => {
|
|
1486
1511
|
// A link to nothing still owns the directory entry, so the exclusive
|
|
1487
1512
|
// create fails — and an existence probe that follows links would say the
|
|
@@ -231,8 +231,8 @@ describe('a line break in a path — one name to the gates, two object names to
|
|
|
231
231
|
});
|
|
232
232
|
|
|
233
233
|
it('refuses it through normalizePathArgs, which is where the MCP tools meet the rule', () => {
|
|
234
|
-
expect(() => normalizePathArgs({ path: INJECTED }, KB)).toThrow(/control character/);
|
|
235
|
-
expect(() => normalizePathArgs({ files: [{ path: INJECTED }] }, KB)).toThrow(/control character/);
|
|
234
|
+
expect(() => normalizePathArgs({ path: INJECTED }, KB, { tool: 'read_file' })).toThrow(/control character/);
|
|
235
|
+
expect(() => normalizePathArgs({ files: [{ path: INJECTED }] }, KB, { tool: 'read_file' })).toThrow(/control character/);
|
|
236
236
|
});
|
|
237
237
|
});
|
|
238
238
|
|
|
@@ -360,6 +360,8 @@ describe('assertRepoRootNameFree', () => {
|
|
|
360
360
|
});
|
|
361
361
|
|
|
362
362
|
describe('normalizePathArgs', () => {
|
|
363
|
+
const T = { tool: 'read_file' };
|
|
364
|
+
|
|
363
365
|
it('normalizes every path-shaped tool argument, batch entries included', () => {
|
|
364
366
|
expect(
|
|
365
367
|
normalizePathArgs(
|
|
@@ -372,6 +374,7 @@ describe('normalizePathArgs', () => {
|
|
|
372
374
|
files: [{ path: 'e.md', content: '/x' }],
|
|
373
375
|
},
|
|
374
376
|
KB,
|
|
377
|
+
T,
|
|
375
378
|
),
|
|
376
379
|
).toEqual({
|
|
377
380
|
branch: '/main',
|
|
@@ -385,22 +388,64 @@ describe('normalizePathArgs', () => {
|
|
|
385
388
|
|
|
386
389
|
it('leaves a spill ref alone — it belongs to no workspace', () => {
|
|
387
390
|
expect(
|
|
388
|
-
normalizePathArgs({ path: '__tool_chain_spill__/abc.json' }, KB, (v) => v.startsWith('__tool_chain_spill__/')),
|
|
391
|
+
normalizePathArgs({ path: '__tool_chain_spill__/abc.json' }, KB, { tool: 'read_file', skip: (v) => v.startsWith('__tool_chain_spill__/') }),
|
|
389
392
|
).toEqual({ path: '__tool_chain_spill__/abc.json' });
|
|
390
393
|
});
|
|
391
394
|
|
|
395
|
+
it('still takes the earlier call form, a skip predicate or nothing, and refuses a tmp ref in every input there', () => {
|
|
396
|
+
const spill = (v: string) => v.startsWith('__tool_chain_spill__/');
|
|
397
|
+
expect(normalizePathArgs({ path: '__tool_chain_spill__/abc.json' }, KB, spill)).toEqual({ path: '__tool_chain_spill__/abc.json' });
|
|
398
|
+
expect(normalizePathArgs({ path: 'a.md' }, KB)).toEqual({ path: 'knowledge-base/a.md' });
|
|
399
|
+
for (const legacy of [undefined, spill]) {
|
|
400
|
+
expect(() => normalizePathArgs({ path: '__tmp__/x.md' }, KB, legacy)).toThrow(/this tool does not take one there/);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
392
404
|
it('leaves an absent or empty path to the handler\'s own required-argument message', () => {
|
|
393
|
-
expect(normalizePathArgs({ path: undefined }, KB)).toEqual({ path: undefined });
|
|
394
|
-
expect(normalizePathArgs({ path: '' }, KB)).toEqual({ path: '' });
|
|
395
|
-
expect(normalizePathArgs({ branch: 'main' }, KB)).toEqual({ branch: 'main' });
|
|
405
|
+
expect(normalizePathArgs({ path: undefined }, KB, T)).toEqual({ path: undefined });
|
|
406
|
+
expect(normalizePathArgs({ path: '' }, KB, T)).toEqual({ path: '' });
|
|
407
|
+
expect(normalizePathArgs({ branch: 'main' }, KB, T)).toEqual({ branch: 'main' });
|
|
396
408
|
});
|
|
397
409
|
|
|
398
410
|
it('collapses the spellings so two callers take one lock row', () => {
|
|
399
|
-
expect(normalizePathArgs({ path: './knowledge-base//a.md' }, KB)).toEqual({ path: 'knowledge-base/a.md' });
|
|
411
|
+
expect(normalizePathArgs({ path: './knowledge-base//a.md' }, KB, T)).toEqual({ path: 'knowledge-base/a.md' });
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('admits a tmp ref, as written, in exactly the inputs the tool names', () => {
|
|
415
|
+
expect(normalizePathArgs({ src: '__tmp__/a.pdf', dest: 'a.pdf' }, KB, { tool: 'copy_file', tmpRefInputs: ['src'] })).toEqual({
|
|
416
|
+
src: '__tmp__/a.pdf',
|
|
417
|
+
dest: 'knowledge-base/a.pdf',
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('refuses a tmp ref in every other input, lists included, naming the tool and copy_file', () => {
|
|
422
|
+
const calls: Array<Record<string, unknown>> = [
|
|
423
|
+
{ path: '__tmp__/a.md' },
|
|
424
|
+
{ src: '__tmp__/a.md' },
|
|
425
|
+
{ dest: '__tmp__/a.md' },
|
|
426
|
+
{ destination: '__tmp__' },
|
|
427
|
+
{ files: [{ path: 'ok.md' }, { path: '__tmp__/a.md' }] },
|
|
428
|
+
{ paths: ['ok.md', '__tmp__/a.md'] },
|
|
429
|
+
{ resolutions: [{ path: 'ok.md', choose: 'ours' }, { path: '__tmp__/a.md', content: 'x' }] },
|
|
430
|
+
];
|
|
431
|
+
for (const args of calls) {
|
|
432
|
+
expect(() => normalizePathArgs(args, KB, { tool: 'save_file' })).toThrow(
|
|
433
|
+
/is a tmp ref, and save_file does not take one there: the workspace's tmp folder is read-only through the ref\..*copy_file/,
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
// A tool that takes a ref in `src` still refuses one in `dest`.
|
|
437
|
+
expect(() => normalizePathArgs({ dest: '__tmp__/a.md' }, KB, { tool: 'copy_file', tmpRefInputs: ['src'] })).toThrow(
|
|
438
|
+
/copy_file does not take one there/,
|
|
439
|
+
);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('reads a path that only contains the prefix as an ordinary repository path', () => {
|
|
443
|
+
expect(normalizePathArgs({ path: 'knowledge-base/__tmp__/a.md' }, KB, T)).toEqual({ path: 'knowledge-base/__tmp__/a.md' });
|
|
444
|
+
expect(normalizePathArgs({ path: '/knowledge-base/__tmp__/a.md' }, KB, T)).toEqual({ path: 'knowledge-base/__tmp__/a.md' });
|
|
400
445
|
});
|
|
401
446
|
|
|
402
447
|
it('refuses a traversing tool argument', () => {
|
|
403
|
-
expect(() => normalizePathArgs({ path: '../escape.md' }, KB)).toThrow(/outside the knowledge base repository/);
|
|
448
|
+
expect(() => normalizePathArgs({ path: '../escape.md' }, KB, T)).toThrow(/outside the knowledge base repository/);
|
|
404
449
|
});
|
|
405
450
|
});
|
|
406
451
|
|
|
@@ -8,6 +8,7 @@ export { WorkspaceMutex, WorkspaceIdMutex } from './mutex.js';
|
|
|
8
8
|
export { readRefSha, readRevSha, type RefFileAnswer } from './ref-files.js';
|
|
9
9
|
export { gitDirOf, readGitfile } from './git-dir.js';
|
|
10
10
|
export { normalizeWorkspacePath } from './repo-path.js';
|
|
11
|
+
export { TMP_REF_PREFIX, assertTmpRefsTaken, isTmpRef, tmpRefReadOnlyRefusal } from './tmp-ref.js';
|
|
11
12
|
// Reached by other modules; exported here so they stop importing the files.
|
|
12
13
|
export { BevelIgnoreStack } from './bevel-ignore.js';
|
|
13
14
|
export { assertValidBranchName, assertValidRelativePath, isBranchAuthoredBy, isOwnSuggestionsBranch } from './branch-name.js';
|
|
@@ -18,3 +19,4 @@ export { LockingFilesystem, MoveLockedError, MoveRacedError } from './locking-fi
|
|
|
18
19
|
export { ReadOnlyFilesystem } from './read-only-filesystem.js';
|
|
19
20
|
export { normalizeRepositoryAddress, sameRepository } from './remote-url.js';
|
|
20
21
|
export { assertInsideRepo, assertKbDirNameFree, assertRepoRootNameFree, assertRepoRootNameFreeArgs, hasControlCharacter, isInsideRepo, normalizePathArgs } from './repo-path.js';
|
|
22
|
+
export type { NormalizePathArgsOptions } from './repo-path.js';
|
|
@@ -226,9 +226,15 @@ export class LockingFilesystem extends GitGuardedFilesystem {
|
|
|
226
226
|
const plan = await this.planCreate(inputPath, 'file');
|
|
227
227
|
if (plan?.kind === 'seed-access-md') await this.seedAccessMd(plan);
|
|
228
228
|
const toWrite = content;
|
|
229
|
+
// An EXCLUSIVE write (`overwrite: false`, the `wx` flag) that finds the
|
|
230
|
+
// name taken is refused as a copy is (`asTakenRefusal`): the refusal is a
|
|
231
|
+
// `DestinationTakenError`, so the lock is released untouched. Left as the
|
|
232
|
+
// raw failure it would be a failed op, and the discard that follows resets
|
|
233
|
+
// the PATH — throwing away the winner's file, whose commit is still queued.
|
|
234
|
+
const land = () => super.writeFile(inputPath, toWrite, options);
|
|
229
235
|
return this.withLock(
|
|
230
236
|
inputPath,
|
|
231
|
-
() =>
|
|
237
|
+
() => (options?.overwrite === false ? this.asTakenRefusal(inputPath, land) : land()),
|
|
232
238
|
(check || validate) &&
|
|
233
239
|
(async () => {
|
|
234
240
|
// The caller's verdict first: it decides whether this write happens
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { WorkflowValidationError } from '../../shared/domain-errors.js';
|
|
3
3
|
import { sanitizedPath } from '../../shared/printable.js';
|
|
4
|
+
import { assertTmpRefsTaken, isTmpRef } from './tmp-ref.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Workspace-relative paths and the repository folder.
|
|
@@ -312,6 +313,16 @@ export function assertKbDirNameFree(
|
|
|
312
313
|
);
|
|
313
314
|
}
|
|
314
315
|
|
|
316
|
+
/** How {@link normalizePathArgs} treats one tool's call. */
|
|
317
|
+
export interface NormalizePathArgsOptions {
|
|
318
|
+
/** The tool the call is to, named in a tmp-ref refusal. */
|
|
319
|
+
tool: string;
|
|
320
|
+
/** The inputs of `tool` that take a tmp ref (`__tmp__/…`); every other refuses one. */
|
|
321
|
+
tmpRefInputs?: readonly string[];
|
|
322
|
+
/** A value that is no workspace path at all, passed to the handler as written. */
|
|
323
|
+
skip?: (value: string) => boolean;
|
|
324
|
+
}
|
|
325
|
+
|
|
315
326
|
/** The tool inputs that carry a workspace path. */
|
|
316
327
|
const PATH_ARG_KEYS = ['path', 'src', 'dest', 'destination'] as const;
|
|
317
328
|
|
|
@@ -321,19 +332,34 @@ const PATH_ARG_KEYS = ['path', 'src', 'dest', 'destination'] as const;
|
|
|
321
332
|
* copy. This is where the MCP surface meets the normaliser — one call, before
|
|
322
333
|
* any handler runs, so no tool keeps a resolver of its own.
|
|
323
334
|
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
335
|
+
* It is also where a tmp ref (`__tmp__/…`) is admitted or refused, first:
|
|
336
|
+
* {@link assertTmpRefsTaken} refuses one in every input of `tool` that
|
|
337
|
+
* `tmpRefInputs` does not name, and a ref in an input that does name it
|
|
338
|
+
* reaches the handler as written — so no tool whose paths pass through here
|
|
339
|
+
* can read the ref as the repository path `<kbDirName>/__tmp__/…`.
|
|
340
|
+
*
|
|
341
|
+
* `skip` is for the other input that is not a workspace path at all:
|
|
342
|
+
* `read_file` also takes a `__tool_chain_spill__/…` ref, which belongs to no
|
|
343
|
+
* workspace and must reach the handler as written. An absent or non-string
|
|
344
|
+
* value is left alone too — the handler's own required-argument message is
|
|
345
|
+
* better than a path refusal about `undefined`.
|
|
346
|
+
*
|
|
347
|
+
* The earlier call form — a `skip` predicate (or nothing) as the third
|
|
348
|
+
* argument — is still taken, for a distribution's own tool written against
|
|
349
|
+
* it: such a call names no tool and takes a tmp ref in no input, so the ref is
|
|
350
|
+
* refused there as read-only, under the name "this tool".
|
|
329
351
|
*/
|
|
330
352
|
export function normalizePathArgs(
|
|
331
353
|
args: Record<string, unknown>,
|
|
332
354
|
kbDirName: string,
|
|
333
|
-
|
|
355
|
+
options?: NormalizePathArgsOptions | ((value: string) => boolean),
|
|
334
356
|
): Record<string, unknown> {
|
|
357
|
+
const { tool, tmpRefInputs, skip } =
|
|
358
|
+
typeof options === 'object' ? options : { tool: 'this tool', tmpRefInputs: undefined, skip: options };
|
|
359
|
+
assertTmpRefsTaken(tool, args, tmpRefInputs);
|
|
360
|
+
// Every tmp ref still here is in an input that takes one.
|
|
335
361
|
const one = (value: unknown): unknown =>
|
|
336
|
-
typeof value !== 'string' || value.length === 0 || skip?.(value)
|
|
362
|
+
typeof value !== 'string' || value.length === 0 || isTmpRef(value) || skip?.(value)
|
|
337
363
|
? value
|
|
338
364
|
: normalizeWorkspacePath(value, kbDirName);
|
|
339
365
|
const out: Record<string, unknown> = { ...args };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { WorkflowValidationError } from '../../shared/domain-errors.js';
|
|
2
|
+
import { sanitizedPath } from '../../shared/printable.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The tmp ref's admission: which tool inputs may carry one, and the refusal
|
|
6
|
+
* every other input answers. What the ref REACHES is `workspace/workspace-tmp.ts`'s;
|
|
7
|
+
* whether a tool may be HANDED one is decided here, once, by
|
|
8
|
+
* {@link assertTmpRefsTaken} — which {@link normalizePathArgs} runs before it
|
|
9
|
+
* places any path under the checkout, so no tool surface that takes a path
|
|
10
|
+
* can turn `__tmp__/x` into the repository path `<kbDirName>/__tmp__/x` and
|
|
11
|
+
* write, read or show it as if the ref had been honoured. A surface that
|
|
12
|
+
* resolves its paths without the normaliser (`open_page`) calls the gate
|
|
13
|
+
* itself.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** The ref's prefix. A ref is `__tmp__/<path inside the workspace's tmp folder>`. */
|
|
17
|
+
export const TMP_REF_PREFIX = '__tmp__/';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Whether a tool argument is a tmp ref rather than a repository path: the
|
|
21
|
+
* prefix as written, or the prefix's own folder name alone (which names the
|
|
22
|
+
* folder, and is refused as such by the resolver). A path that only CONTAINS
|
|
23
|
+
* the name — `/__tmp__/x`, `knowledge-base/__tmp__/x` — is an ordinary
|
|
24
|
+
* repository path.
|
|
25
|
+
*/
|
|
26
|
+
export function isTmpRef(value: unknown): boolean {
|
|
27
|
+
return typeof value === 'string' && (value === TMP_REF_PREFIX.slice(0, -1) || value.startsWith(TMP_REF_PREFIX));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Why a tool refuses a tmp ref in an input that is not one of the four that
|
|
32
|
+
* take it: the folder is read-only through the ref, and a file is kept by
|
|
33
|
+
* copying it into the repository. A 400 with a `kind` to branch on, before
|
|
34
|
+
* any gate, lock or write.
|
|
35
|
+
*/
|
|
36
|
+
export function tmpRefReadOnlyRefusal(ref: string, tool: string): WorkflowValidationError {
|
|
37
|
+
const shown = sanitizedPath(ref);
|
|
38
|
+
return new WorkflowValidationError(
|
|
39
|
+
`"${shown}" is a tmp ref, and ${tool} does not take one there: the workspace's tmp folder is read-only through ` +
|
|
40
|
+
'the ref. read_file, file_stat and grep read the one file it names; to keep the file, copy_file it to a ' +
|
|
41
|
+
'repository path (the ref as `src`, the repository path as `dest`).',
|
|
42
|
+
{ kind: 'tmp-ref-read-only', path: shown, tool },
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The tool inputs that carry a workspace path as a string. */
|
|
47
|
+
const PATH_INPUTS = ['path', 'src', 'dest', 'destination'] as const;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Every path a tool call carries, with the input it came in: the string
|
|
51
|
+
* inputs, and for EVERY list input, each entry that is a path itself
|
|
52
|
+
* (`paths` of `request_file_download`) or an object carrying one in `path`
|
|
53
|
+
* (`files[].path` of a batch write, `resolutions[].path` of
|
|
54
|
+
* `update_branch`). Read by shape rather than by a list of tool inputs, so a
|
|
55
|
+
* list a tool adds later is gated without anyone remembering to name it here.
|
|
56
|
+
*/
|
|
57
|
+
function pathEntries(args: Record<string, unknown>): Array<[string, unknown]> {
|
|
58
|
+
const entries: Array<[string, unknown]> = PATH_INPUTS.map((key) => [key, args[key]]);
|
|
59
|
+
for (const [key, value] of Object.entries(args)) {
|
|
60
|
+
if (!Array.isArray(value)) continue;
|
|
61
|
+
for (const item of value as unknown[]) {
|
|
62
|
+
if (typeof item === 'string') {
|
|
63
|
+
if (key === 'paths') entries.push([key, item]);
|
|
64
|
+
} else if (item && typeof item === 'object') {
|
|
65
|
+
entries.push([key, (item as Record<string, unknown>).path]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return entries;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Refuse a tmp ref in any input of `tool` that is not in `takes` — with
|
|
74
|
+
* {@link tmpRefReadOnlyRefusal}, before anything else runs. `takes` is empty
|
|
75
|
+
* for every tool but the four reads of the one file a ref names.
|
|
76
|
+
*/
|
|
77
|
+
export function assertTmpRefsTaken(tool: string, args: Record<string, unknown>, takes: readonly string[] = []): void {
|
|
78
|
+
for (const [key, value] of pathEntries(args)) {
|
|
79
|
+
if (isTmpRef(value) && !takes.includes(key)) throw tmpRefReadOnlyRefusal(value as string, tool);
|
|
80
|
+
}
|
|
81
|
+
}
|