@bevel-software/platform-core-backend 0.33.0 → 0.34.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 +19 -8
- package/agent-guide/html-views.md +231 -5
- package/agent-guide/tool-manuals.md +1 -1
- 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 +14 -7
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +16 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +37 -12
- 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 +17 -9
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +12 -2
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +164 -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 +35 -29
- 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/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.tools.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.js +13 -3
- package/dist/modules/agent-guide/agent-guide.tools.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 +3 -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 +16 -1
- 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/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 +68 -13
- 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/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 +15 -3
- package/dist/modules/secrets-vault/secrets-vault.routes.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 +24 -12
- 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-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/tool-manuals.contract.d.ts +19 -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 +13 -6
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +23 -15
- package/dist/modules/tool-manuals/tool-manuals.service.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 +25 -10
- 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 +14 -9
- 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/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.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +460 -116
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- 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/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 +3 -3
- package/src/__tests__/pg-scratch.test.ts +69 -0
- package/src/__tests__/pg-scratch.ts +31 -0
- package/src/__tests__/slow-ripgrep.ts +65 -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/catalog-revision.ts +24 -2
- package/src/core/core-ports.ts +42 -0
- package/src/core/create-core-server.ts +13 -0
- package/src/core/create-core-services.ts +49 -12
- 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 +2 -0
- 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__/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 +17 -10
- package/src/modules/access/access-control.service.ts +158 -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 +43 -33
- 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/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 +31 -0
- package/src/modules/agent-guide/agent-guide.tools.ts +13 -2
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +1 -0
- package/src/modules/agent-instructions/compose.ts +3 -1
- package/src/modules/agent-instructions/shared-file-rules.ts +17 -1
- 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/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 +107 -17
- 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/secrets-vault.routes.ts +23 -3
- 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 +44 -13
- 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__/can-write.tools.test.ts +3 -1
- package/src/modules/tool-helpers/tool-context.ts +10 -0
- 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.service.test.ts +42 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +3 -3
- package/src/modules/tool-manuals/index.ts +6 -2
- package/src/modules/tool-manuals/tool-manuals.contract.ts +18 -6
- package/src/modules/tool-manuals/tool-manuals.routes.ts +40 -3
- package/src/modules/tool-manuals/tool-manuals.service.ts +31 -15
- package/src/modules/tool-manuals/tool-manuals.tools.ts +28 -11
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +2 -2
- 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 +14 -9
- 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 +2 -2
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +22 -10
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +393 -113
- 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 +500 -155
- package/src/shared/path-order.ts +37 -8
- 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/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
|
@@ -19,6 +19,7 @@ import type { AuthUser } from '@bevel-software/platform-shared';
|
|
|
19
19
|
import '../auth/index.js'; // Express Request augmentation (req.userId / req.userEmail)
|
|
20
20
|
import '../tool-auth/index.js'; // Express Request augmentation (req.toolAuth)
|
|
21
21
|
import { hasGitInternalsSegment } from '../../shared/git-internals.js';
|
|
22
|
+
import { NOT_AVAILABLE_TO_CONNECTION, resolveConnectionScope, type IAgentScopeResolver } from '../agent-scope/index.js';
|
|
22
23
|
|
|
23
24
|
/** Resolve a connection-key/internal-token user id to its email (per-caller ACL). */
|
|
24
25
|
export type ResolveUserEmail = (userId: string) => Promise<string | undefined>;
|
|
@@ -64,6 +65,11 @@ export function createToolManualsAgentRoutes(
|
|
|
64
65
|
*/
|
|
65
66
|
pluginIndex?: Pick<IPluginIndexService, 'catalog'>;
|
|
66
67
|
},
|
|
68
|
+
/**
|
|
69
|
+
* Resolves the scope of a connection made as an agent configuration, so
|
|
70
|
+
* `all-tools` lists only the connectors it loads (`modules/agent-scope`).
|
|
71
|
+
*/
|
|
72
|
+
agentScope?: Pick<IAgentScopeResolver, 'resolve'>,
|
|
67
73
|
): express.Router {
|
|
68
74
|
const router = express.Router();
|
|
69
75
|
|
|
@@ -83,9 +89,18 @@ export function createToolManualsAgentRoutes(
|
|
|
83
89
|
// Default remote-safe: exclude local-only manuals unless the caller (a locally
|
|
84
90
|
// installed MCP server) explicitly opts in with `?remote=false`.
|
|
85
91
|
const remoteOnly = req.query.remote !== 'false';
|
|
86
|
-
|
|
92
|
+
// A connection made as an agent configuration lists only the connectors that
|
|
93
|
+
// file loads, and says which agent it is — the MCP proxy words its
|
|
94
|
+
// refusals and its handshake from that. Everything lists as it always
|
|
95
|
+
// did, with no `connection` at all.
|
|
96
|
+
const scope = await resolveConnectionScope(agentScope, email, req.toolAuth?.agentPath);
|
|
97
|
+
const within = scope.connectorFilter();
|
|
98
|
+
const userManuals = email
|
|
99
|
+
? await toolManualService.toManualCallTemplates(email, within ? { remoteOnly, within } : { remoteOnly })
|
|
100
|
+
: [];
|
|
87
101
|
const manuals: CallTemplate[] = [kbManualTemplate(), ...userManuals];
|
|
88
|
-
|
|
102
|
+
const connection = scope.toWire();
|
|
103
|
+
res.json(connection ? { manuals, connection } : { manuals });
|
|
89
104
|
} catch (err) {
|
|
90
105
|
log.error('all-tools failed:', { err });
|
|
91
106
|
res.status(500).json({ error: 'Failed to list tools' });
|
|
@@ -171,6 +186,24 @@ export function createToolManualsAgentRoutes(
|
|
|
171
186
|
email,
|
|
172
187
|
rels.map((r) => `${pluginRel}/${r}`),
|
|
173
188
|
);
|
|
189
|
+
// A folder with nothing the person may read is absent to them, whatever
|
|
190
|
+
// the connection — so that 404 comes first, and the connection's own
|
|
191
|
+
// refusal below never confirms a folder the person could not see.
|
|
192
|
+
if (![...verdicts.values()].some((v) => v === true)) {
|
|
193
|
+
return void res.status(404).json({ error: 'Not found' });
|
|
194
|
+
}
|
|
195
|
+
// A connection made as an agent configuration materializes only a plugin
|
|
196
|
+
// that defines a connector the file loads — the archive is how the
|
|
197
|
+
// local MCP server runs one, and nothing else asks for it. Checked
|
|
198
|
+
// before any file is opened.
|
|
199
|
+
const scope = await resolveConnectionScope(agentScope, email, req.toolAuth?.agentPath);
|
|
200
|
+
if (!scope.loadsConnectorIn(pluginRel)) {
|
|
201
|
+
const why = scope.describe();
|
|
202
|
+
return void res.status(403).json({
|
|
203
|
+
error: `The plugin "${folder}" is not available to this connection.${why ? ` ${why}` : ''}`,
|
|
204
|
+
code: NOT_AVAILABLE_TO_CONNECTION,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
174
207
|
const zip = new AdmZip();
|
|
175
208
|
let included = 0;
|
|
176
209
|
let bytes = 0;
|
|
@@ -269,7 +302,11 @@ export function createToolManualsAgentRoutes(
|
|
|
269
302
|
const email = await callerEmail(req);
|
|
270
303
|
if (!email) return void res.status(403).json({ error: 'Forbidden' });
|
|
271
304
|
const slug = Array.isArray(req.params.slug) ? req.params.slug[0] : req.params.slug;
|
|
272
|
-
|
|
305
|
+
// A connection made as an agent configuration reads only the manuals of the
|
|
306
|
+
// connectors it loads; another is as absent as one the person cannot read.
|
|
307
|
+
const scope = await resolveConnectionScope(agentScope, email, req.toolAuth?.agentPath);
|
|
308
|
+
const within = scope.connectorFilter();
|
|
309
|
+
const manual = await toolManualService.resolveInlineManual(email, String(slug), within ? { within } : undefined);
|
|
273
310
|
if (!manual) return void res.status(404).json({ error: 'Not found' });
|
|
274
311
|
res.json(manual);
|
|
275
312
|
} catch (err) {
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
EXTERNAL_KB_MANUAL_NAME,
|
|
44
44
|
MAX_CAPABILITIES,
|
|
45
45
|
type IToolManualService,
|
|
46
|
+
type ConnectorScope,
|
|
46
47
|
type ToolManualDescriptor,
|
|
47
48
|
type ToolManualDescriptorBase,
|
|
48
49
|
type ToolManualSummary,
|
|
@@ -304,8 +305,8 @@ export class ToolManualService implements IToolManualService {
|
|
|
304
305
|
return (await this.accessibleManuals(userEmail)).map(toSummary);
|
|
305
306
|
}
|
|
306
307
|
|
|
307
|
-
async listAccessibleCatalog(userEmail: string): Promise<AccessibleCatalog> {
|
|
308
|
-
const { manuals, invalid } = await this.accessibleScan(userEmail);
|
|
308
|
+
async listAccessibleCatalog(userEmail: string, within?: ConnectorScope): Promise<AccessibleCatalog> {
|
|
309
|
+
const { manuals, invalid } = await this.accessibleScan(userEmail, within);
|
|
309
310
|
return { tools: manuals.map(toSummary), invalid };
|
|
310
311
|
}
|
|
311
312
|
|
|
@@ -313,8 +314,8 @@ export class ToolManualService implements IToolManualService {
|
|
|
313
314
|
return (await this.scan()).manuals.map(toSummary);
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
async catalogFingerprints(userEmail: string): Promise<string[]> {
|
|
317
|
-
return (await this.accessibleManuals(userEmail)).map(manualFingerprint);
|
|
317
|
+
async catalogFingerprints(userEmail: string, within?: ConnectorScope): Promise<string[]> {
|
|
318
|
+
return (await this.accessibleManuals(userEmail, within)).map(manualFingerprint);
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
async getDetail(userEmail: string, slug: string): Promise<ToolManualDetail | null> {
|
|
@@ -341,14 +342,15 @@ export class ToolManualService implements IToolManualService {
|
|
|
341
342
|
};
|
|
342
343
|
}
|
|
343
344
|
|
|
344
|
-
async listLocalOnly(userEmail: string): Promise<{ slug: string; name: string; path: string }[]> {
|
|
345
|
-
const manuals = await this.accessibleManuals(userEmail);
|
|
345
|
+
async listLocalOnly(userEmail: string, within?: ConnectorScope): Promise<{ slug: string; name: string; path: string }[]> {
|
|
346
|
+
const manuals = await this.accessibleManuals(userEmail, within);
|
|
346
347
|
return manuals.filter((m) => m.remote === false).map((m) => ({ slug: m.slug, name: m.name, path: m.path }));
|
|
347
348
|
}
|
|
348
349
|
|
|
349
350
|
async listDeclaredOnlyOnBranch(
|
|
350
351
|
userEmail: string,
|
|
351
352
|
branch: string,
|
|
353
|
+
within?: ConnectorScope,
|
|
352
354
|
): Promise<{ name: string; path: string; type: ToolManualType }[]> {
|
|
353
355
|
if (!branch || branch === this.kb.defaultBranch) return [];
|
|
354
356
|
// Only a draft this process already holds a clone of — the one the caller
|
|
@@ -377,7 +379,7 @@ export class ToolManualService implements IToolManualService {
|
|
|
377
379
|
pending.map((m) => m.path),
|
|
378
380
|
);
|
|
379
381
|
return pending
|
|
380
|
-
.filter((m) => allowed.get(m.path) === true)
|
|
382
|
+
.filter((m) => allowed.get(m.path) === true && (within === undefined || within(m.path)))
|
|
381
383
|
.map((m) => ({ name: m.name, path: m.path, type: m.type }));
|
|
382
384
|
}
|
|
383
385
|
|
|
@@ -456,8 +458,11 @@ export class ToolManualService implements IToolManualService {
|
|
|
456
458
|
return { name: m.name, type: m.type, remote: m.remote, healthCheck: m.healthCheck, callTemplate };
|
|
457
459
|
}
|
|
458
460
|
|
|
459
|
-
async toManualCallTemplates(
|
|
460
|
-
|
|
461
|
+
async toManualCallTemplates(
|
|
462
|
+
userEmail: string,
|
|
463
|
+
opts?: { remoteOnly?: boolean; within?: ConnectorScope },
|
|
464
|
+
): Promise<CallTemplate[]> {
|
|
465
|
+
const manuals = await this.accessibleManuals(userEmail, opts?.within);
|
|
461
466
|
const out: CallTemplate[] = [];
|
|
462
467
|
for (const m of manuals) {
|
|
463
468
|
// Remote consumers (the hosted MCP proxy) don't get local-only manuals —
|
|
@@ -474,11 +479,17 @@ export class ToolManualService implements IToolManualService {
|
|
|
474
479
|
return out;
|
|
475
480
|
}
|
|
476
481
|
|
|
477
|
-
async resolveInlineManual(
|
|
482
|
+
async resolveInlineManual(
|
|
483
|
+
userEmail: string,
|
|
484
|
+
slug: string,
|
|
485
|
+
opts?: { within?: ConnectorScope },
|
|
486
|
+
): Promise<UtcpManualDict | null> {
|
|
478
487
|
const found = (await this.scan()).manuals.find((m) => m.slug === slug);
|
|
479
488
|
if (!found || found.type !== 'inline') return null;
|
|
480
489
|
const wsId = this.kb.defaultWorkspaceId();
|
|
481
490
|
if (!(await this.accessControl.canRead(wsId, userEmail, found.path))) return null;
|
|
491
|
+
// After the read gate, as every narrowing is: a connection's scope can only take away.
|
|
492
|
+
if (opts?.within && !opts.within(found.path)) return null;
|
|
482
493
|
try {
|
|
483
494
|
return manualSerializer.validateDict({
|
|
484
495
|
utcp_version: '1.1.0',
|
|
@@ -590,8 +601,8 @@ export class ToolManualService implements IToolManualService {
|
|
|
590
601
|
};
|
|
591
602
|
}
|
|
592
603
|
|
|
593
|
-
private async accessibleManuals(userEmail: string): Promise<ToolManualDescriptor[]> {
|
|
594
|
-
return (await this.accessibleScan(userEmail)).manuals;
|
|
604
|
+
private async accessibleManuals(userEmail: string, within?: ConnectorScope): Promise<ToolManualDescriptor[]> {
|
|
605
|
+
return (await this.accessibleScan(userEmail, within)).manuals;
|
|
595
606
|
}
|
|
596
607
|
|
|
597
608
|
/**
|
|
@@ -599,8 +610,12 @@ export class ToolManualService implements IToolManualService {
|
|
|
599
610
|
* ONE `canReadBatch`. Both halves are paths in the same workspace judged by
|
|
600
611
|
* the same rule, and splitting them into two calls would pay a second
|
|
601
612
|
* round-trip to ask the same question.
|
|
613
|
+
*
|
|
614
|
+
* `within` narrows the answer to a connection's connectors, AFTER the read
|
|
615
|
+
* gate: it can take a manual out of what the caller may read, never put one
|
|
616
|
+
* in. Absent, the answer is the caller's whole readable catalog.
|
|
602
617
|
*/
|
|
603
|
-
private async accessibleScan(userEmail: string): Promise<ScanResult> {
|
|
618
|
+
private async accessibleScan(userEmail: string, within?: ConnectorScope): Promise<ScanResult> {
|
|
604
619
|
const { manuals, invalid } = await this.scan();
|
|
605
620
|
if (manuals.length === 0 && invalid.length === 0) return emptyScan();
|
|
606
621
|
const wsId = this.kb.defaultWorkspaceId();
|
|
@@ -612,9 +627,10 @@ export class ToolManualService implements IToolManualService {
|
|
|
612
627
|
// A REFUSED file is gated the same way a listed one is: its path is a fact
|
|
613
628
|
// about the knowledge base, and "there is a broken tool at this path" is
|
|
614
629
|
// not something to tell someone who may not read that path.
|
|
630
|
+
const inScope = (p: string) => allowed.get(p) === true && (within === undefined || within(p));
|
|
615
631
|
return {
|
|
616
|
-
manuals: manuals.filter((m) =>
|
|
617
|
-
invalid: invalid.filter((i) =>
|
|
632
|
+
manuals: manuals.filter((m) => inScope(m.path)),
|
|
633
|
+
invalid: invalid.filter((i) => inScope(i.path)),
|
|
618
634
|
};
|
|
619
635
|
}
|
|
620
636
|
|
|
@@ -5,7 +5,7 @@ import type { ToolContext, ToolHandlerFactory } from '../tool-helpers/index.js';
|
|
|
5
5
|
import { toolDef, declareRouteTool } from '../tool-helpers/index.js';
|
|
6
6
|
import type { IAccessControl } from '../access/index.js';
|
|
7
7
|
import { utcpNamespacedKey } from '../../shared/utcp-namespace.js';
|
|
8
|
-
import type { IToolManualService } from './tool-manuals.contract.js';
|
|
8
|
+
import type { ConnectorScope, IToolManualService } from './tool-manuals.contract.js';
|
|
9
9
|
import type { HiddenToolSource } from '../../shared/hidden-tools.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -51,7 +51,9 @@ export function registerToolManualsTools(
|
|
|
51
51
|
hiddenTools?: HiddenToolSource;
|
|
52
52
|
},
|
|
53
53
|
): void {
|
|
54
|
-
registry.registerExternalTool((ctx) =>
|
|
54
|
+
registry.registerExternalTool((ctx) =>
|
|
55
|
+
buildListLocalToolsDef(toolManualService, ctx.userEmail, ctx.connectionScope?.connectorFilter()),
|
|
56
|
+
);
|
|
55
57
|
registry.registerInternalTool((ctx) => buildListLocalToolsDef(toolManualService, ctx.userEmail));
|
|
56
58
|
// The def above is built per catalog listing (its description names the
|
|
57
59
|
// tools configured for THIS caller), so declare the arguments here as well:
|
|
@@ -59,12 +61,22 @@ export function registerToolManualsTools(
|
|
|
59
61
|
// against them too.
|
|
60
62
|
declareRouteTool('list_local_tools', LIST_LOCAL_TOOLS_INPUTS);
|
|
61
63
|
|
|
64
|
+
// The local server's discovery read (`discovery`): it asks this route at
|
|
65
|
+
// startup for the connectors it must materialize, whatever tools the
|
|
66
|
+
// connection is offered — a connection made as an agent configuration that omits
|
|
67
|
+
// the tool is answered its connectors, not refused, or the local server
|
|
68
|
+
// could not start for that agent at all.
|
|
62
69
|
router.post(
|
|
63
70
|
'/agent/tools/list_local_tools',
|
|
64
71
|
toolAuth,
|
|
65
|
-
toolHandler(async (_args, ctx: ToolContext) =>
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
toolHandler(async (_args, ctx: ToolContext) => {
|
|
73
|
+
const within = ctx.connectionScope?.connectorFilter();
|
|
74
|
+
return {
|
|
75
|
+
tools: await (within
|
|
76
|
+
? toolManualService.listLocalOnly(ctx.user.email, within)
|
|
77
|
+
: toolManualService.listLocalOnly(ctx.user.email)),
|
|
78
|
+
};
|
|
79
|
+
}, { discovery: true }),
|
|
68
80
|
);
|
|
69
81
|
|
|
70
82
|
// A FUNCTION, not a constant — this factory runs at boot, and on a
|
|
@@ -243,12 +255,17 @@ export function registerToolManualsTools(
|
|
|
243
255
|
toolHandler(async (args, ctx: ToolContext) => {
|
|
244
256
|
// The in-app agent is focused on its own draft; an external caller names it.
|
|
245
257
|
const branch = typeof args.branch === 'string' && args.branch ? args.branch : ctx.focusedBranch;
|
|
258
|
+
const within = ctx.connectionScope?.connectorFilter();
|
|
246
259
|
// The catalog comes back whole — what parsed AND what was refused — from
|
|
247
260
|
// ONE scan, so the refused files cost no second walk of the workspace and
|
|
248
261
|
// the two halves cannot disagree about which files made it.
|
|
249
262
|
const [{ tools: manuals, invalid }, pending] = await Promise.all([
|
|
250
|
-
|
|
251
|
-
|
|
263
|
+
// Only the connectors the caller's connection loads: setup is counted
|
|
264
|
+
// for the tools this connection can call, not every one the person can.
|
|
265
|
+
within
|
|
266
|
+
? toolManualService.listAccessibleCatalog(ctx.user.email, within)
|
|
267
|
+
: toolManualService.listAccessibleCatalog(ctx.user.email),
|
|
268
|
+
branch ? toolManualService.listDeclaredOnlyOnBranch(ctx.user.email, branch, within) : Promise.resolve([]),
|
|
252
269
|
]);
|
|
253
270
|
const allKeys = manuals.flatMap((m) => (m.variables ?? []).map((v) => varKey(m.name, v.name)));
|
|
254
271
|
const status = await deps.variableStatus.statusFor(ctx.user.id, allKeys);
|
|
@@ -301,9 +318,9 @@ export function registerToolManualsTools(
|
|
|
301
318
|
}
|
|
302
319
|
|
|
303
320
|
/** "Local-only tools configured for you: `a`, `b`." (or a none note), filtered to what the caller may read. */
|
|
304
|
-
async function localToolsLine(svc: IToolManualService, userEmail?: string): Promise<string> {
|
|
321
|
+
async function localToolsLine(svc: IToolManualService, userEmail?: string, within?: ConnectorScope): Promise<string> {
|
|
305
322
|
if (!userEmail) return 'No local-only tools are currently configured.';
|
|
306
|
-
const tools = await svc.listLocalOnly(userEmail);
|
|
323
|
+
const tools = await (within ? svc.listLocalOnly(userEmail, within) : svc.listLocalOnly(userEmail));
|
|
307
324
|
if (tools.length === 0) return 'No local-only tools are currently configured.';
|
|
308
325
|
return `Local-only tools configured for you: ${tools.map((t) => `\`${t.name}\``).join(', ')}.`;
|
|
309
326
|
}
|
|
@@ -311,7 +328,7 @@ async function localToolsLine(svc: IToolManualService, userEmail?: string): Prom
|
|
|
311
328
|
/** `list_local_tools` takes nothing; a const so the route and the def share one declaration. */
|
|
312
329
|
const LIST_LOCAL_TOOLS_INPUTS = { type: 'object' as const, properties: {}, additionalProperties: false };
|
|
313
330
|
|
|
314
|
-
async function buildListLocalToolsDef(svc: IToolManualService, userEmail?: string): Promise<UtcpTool> {
|
|
331
|
+
async function buildListLocalToolsDef(svc: IToolManualService, userEmail?: string, within?: ConnectorScope): Promise<UtcpTool> {
|
|
315
332
|
return toolDef({
|
|
316
333
|
name: 'list_local_tools',
|
|
317
334
|
description:
|
|
@@ -324,7 +341,7 @@ async function buildListLocalToolsDef(svc: IToolManualService, userEmail?: strin
|
|
|
324
341
|
'machine. Otherwise each entry gives the tool’s name and its `.tool` file ' +
|
|
325
342
|
'path in the knowledge base — read that file with `read_file` and wire the tool into your local ' +
|
|
326
343
|
'setup by hand. ' +
|
|
327
|
-
(await localToolsLine(svc, userEmail)),
|
|
344
|
+
(await localToolsLine(svc, userEmail, within)),
|
|
328
345
|
path: '/api/agent/tools/list_local_tools',
|
|
329
346
|
inputs: LIST_LOCAL_TOOLS_INPUTS,
|
|
330
347
|
outputs: {
|
|
@@ -15,7 +15,7 @@ import type { UtcpTool } from '../tool.contract.js';
|
|
|
15
15
|
import { createToolHandlerFactory } from '../../tool-helpers/tool-handler.js';
|
|
16
16
|
import type { ToolAuth } from '../../tool-auth/tool-auth.middleware.js';
|
|
17
17
|
import { registerWorkspaceTools } from '../../workspace/workspace.tools.js';
|
|
18
|
-
import {
|
|
18
|
+
import { RipgrepPatternMatcher } from '../../workspace/index.js';
|
|
19
19
|
import { registerWorkflowTools } from '../../workflow/agent-tools/workflow.tools.js';
|
|
20
20
|
import { registerPluginsTools } from '../../plugins/plugins.tools.js';
|
|
21
21
|
import { registerSkillsTools } from '../../skills/skills.tools.js';
|
|
@@ -115,7 +115,7 @@ async function hexisTools(skills: Parameters<typeof registerSkillsTools>[4] = em
|
|
|
115
115
|
// mounted on the same terms (it spawns no worker until a search runs).
|
|
116
116
|
undefined,
|
|
117
117
|
undefined,
|
|
118
|
-
new
|
|
118
|
+
new RipgrepPatternMatcher(),
|
|
119
119
|
);
|
|
120
120
|
registerWorkflowTools(registry, router, toolAuth, toolHandler, kb, async () => true, { canRead: async () => true, canReadAtRef: async () => true, canReadBatch: async () => new Map(), canReadBatchAtRef: async () => new Map(), canWrite: async () => true } as never, { recoveryBotEmail: 'recovery@x', hooks: { runAgentRead: async () => undefined }, notes: { onChange: () => undefined } } as never);
|
|
121
121
|
registerPluginsTools(registry, kb);
|
|
@@ -2,6 +2,7 @@ import express, { type Request, type RequestHandler } from 'express';
|
|
|
2
2
|
import { UtcpManualSerializer } from '@utcp/sdk';
|
|
3
3
|
import { declaresWrite, type IToolRegistry, type ToolManualContext, type UtcpTool } from './tool.contract.js';
|
|
4
4
|
import { requireInternalSource } from '../tool-auth/index.js';
|
|
5
|
+
import { resolveConnectionScope, type IAgentScopeResolver } from '../agent-scope/index.js';
|
|
5
6
|
import type { IToolReadOnlyHints } from './read-only-hints.contract.js';
|
|
6
7
|
|
|
7
8
|
/** Resolve a connection-key/internal-token user id to its email (for per-caller provider defs). */
|
|
@@ -62,6 +63,12 @@ export function createManualRoutes(
|
|
|
62
63
|
* Absent, the manual carries the tools' own tags only.
|
|
63
64
|
*/
|
|
64
65
|
readOnlyHints?: Pick<IToolReadOnlyHints, 'apply'>,
|
|
66
|
+
/**
|
|
67
|
+
* Resolves the scope of a connection made as an agent configuration: the external
|
|
68
|
+
* manual then lists only the platform tools it loads, and the providers word
|
|
69
|
+
* their descriptions for it (`modules/agent-scope`).
|
|
70
|
+
*/
|
|
71
|
+
agentScope?: Pick<IAgentScopeResolver, 'resolve'>,
|
|
65
72
|
): express.Router {
|
|
66
73
|
const router = express.Router();
|
|
67
74
|
|
|
@@ -71,17 +78,31 @@ export function createManualRoutes(
|
|
|
71
78
|
async function context(req: Request): Promise<ToolManualContext> {
|
|
72
79
|
const userId = req.toolAuth?.userId;
|
|
73
80
|
if (!userId) return {};
|
|
74
|
-
|
|
81
|
+
let userEmail: string | undefined;
|
|
75
82
|
try {
|
|
76
|
-
|
|
83
|
+
userEmail = resolveUserEmail ? await resolveUserEmail(userId) : undefined;
|
|
77
84
|
} catch {
|
|
78
|
-
|
|
85
|
+
userEmail = undefined;
|
|
79
86
|
}
|
|
87
|
+
// Everything adds nothing to the context, so its providers are asked
|
|
88
|
+
// exactly what they were asked before connections could be narrowed.
|
|
89
|
+
const agentPath = req.toolAuth?.agentPath;
|
|
90
|
+
const connectionScope = agentPath ? await resolveConnectionScope(agentScope, userEmail, agentPath) : undefined;
|
|
91
|
+
return {
|
|
92
|
+
userId,
|
|
93
|
+
...(userEmail !== undefined ? { userEmail } : {}),
|
|
94
|
+
...(connectionScope ? { connectionScope } : {}),
|
|
95
|
+
};
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
router.get('/agent/utcp', manualAuth, async (req, res) => {
|
|
83
|
-
const
|
|
84
|
-
|
|
99
|
+
const ctx = await context(req);
|
|
100
|
+
const tools = await registry.listExternal(ctx);
|
|
101
|
+
// A connection made as an agent configuration is offered only the platform
|
|
102
|
+
// tools that file loads; a call to another is refused at its route.
|
|
103
|
+
const scope = ctx.connectionScope;
|
|
104
|
+
const offered = scope ? tools.filter((t) => scope.allowsPlatformTool(t.name)) : tools;
|
|
105
|
+
res.json(toManual(readOnlyHints ? readOnlyHints.apply(offered) : offered));
|
|
85
106
|
});
|
|
86
107
|
router.get('/agent/internal/utcp', manualAuth, requireInternalSource, async (req, res) => {
|
|
87
108
|
const internal = await registry.listInternal(await context(req));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JsonSchema, Tool as UtcpTool } from '@utcp/sdk';
|
|
2
|
+
import type { ConnectionScope } from '../agent-scope/index.js';
|
|
2
3
|
|
|
3
4
|
export type { JsonSchema, UtcpTool };
|
|
4
5
|
|
|
@@ -23,6 +24,12 @@ export interface ToolManualContext {
|
|
|
23
24
|
userEmail?: string;
|
|
24
25
|
/** The requesting user's id, when it could be resolved (for per-user gating). */
|
|
25
26
|
userId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* What the requesting connection may load, when it was made as an agent configuration
|
|
29
|
+
* file (`modules/agent-scope`): a provider that names skills or connectors
|
|
30
|
+
* in its description names only those. Absent is Everything.
|
|
31
|
+
*/
|
|
32
|
+
connectionScope?: ConnectionScope;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
/**
|
|
@@ -250,7 +250,7 @@ function makeReviewWorkflow(): IReviewWorkflowService {
|
|
|
250
250
|
describe('WorkflowService — branch delegation', () => {
|
|
251
251
|
it('listBranches delegates to git.listBranches', async () => {
|
|
252
252
|
const git = makeGit();
|
|
253
|
-
const branches = [{ name: 'main', isCurrent: true, isProtected: true,
|
|
253
|
+
const branches = [{ name: 'main', isCurrent: true, isProtected: true, hasRemote: true }];
|
|
254
254
|
(git.listBranches as ReturnType<typeof vi.fn>).mockResolvedValue(branches);
|
|
255
255
|
|
|
256
256
|
const svc = new WorkflowService(makeDb(), git, makePrs(), makeReviewWorkflow(), makeWorkspaceService(), makeAccessControl(), makeFileLockService(), makePendingCommits(), testKbContext(), openChangeGate());
|
|
@@ -344,7 +344,7 @@ describe('WorkflowService.retireRemoteGoneClone — what can bring the branch ba
|
|
|
344
344
|
}),
|
|
345
345
|
createBranch: vi.fn(async (_ws: string, name: string) => {
|
|
346
346
|
order.push('create');
|
|
347
|
-
return { name, isProtected: false,
|
|
347
|
+
return { name, isProtected: false, hasRemote: true };
|
|
348
348
|
}),
|
|
349
349
|
} as unknown as GitService;
|
|
350
350
|
const workspaceService = {
|
|
@@ -104,6 +104,9 @@ const workspaceService = {
|
|
|
104
104
|
const workflowService = {
|
|
105
105
|
listBranches: async (ws: string) => {
|
|
106
106
|
calls.push(['listBranches', ws]);
|
|
107
|
+
// A real listing fetches first; mark when that fetch has landed.
|
|
108
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
109
|
+
calls.push(['listBranches:fetched', ws]);
|
|
107
110
|
return [{ name: 'main', isProtected: true }];
|
|
108
111
|
},
|
|
109
112
|
commitChange: async (ws: string, user: { id: string }, body: unknown) => {
|
|
@@ -117,7 +120,7 @@ const workflowService = {
|
|
|
117
120
|
},
|
|
118
121
|
createBranch: async (ws: string, name: string) => {
|
|
119
122
|
calls.push(['createBranch', ws, name]);
|
|
120
|
-
return { name, isProtected: false,
|
|
123
|
+
return { name, isProtected: false, hasRemote: true };
|
|
121
124
|
},
|
|
122
125
|
acquireLock: async (ws: string, branch: string, path: string) => {
|
|
123
126
|
calls.push(['acquireLock', ws, branch, path]);
|
|
@@ -206,6 +209,10 @@ const workflowService = {
|
|
|
206
209
|
calls.push(['branchStatus', ws]);
|
|
207
210
|
return { branch: ws, hasUpstream: true, unmergedFromUpstream: true };
|
|
208
211
|
},
|
|
212
|
+
branchCounts: async (ws: string) => {
|
|
213
|
+
calls.push(['branchCounts', ws]);
|
|
214
|
+
return { ahead: 3, behind: 1 };
|
|
215
|
+
},
|
|
209
216
|
listPendingChangePaths: async (ws: string) => {
|
|
210
217
|
calls.push(['listPendingChangePaths', ws]);
|
|
211
218
|
return ['knowledge-base/KnowledgeBase/Pending.md'];
|
|
@@ -518,6 +525,24 @@ describe('registerWorkflowTools', () => {
|
|
|
518
525
|
expect(calls).toContainEqual(['listBranches', 'existing-ws']);
|
|
519
526
|
});
|
|
520
527
|
|
|
528
|
+
it('list_branches declares names, protection and remote presence, and no commit counts', async () => {
|
|
529
|
+
await start();
|
|
530
|
+
for (const tools of [await registryRef!.listInternal(), await registryRef!.listExternal()]) {
|
|
531
|
+
const listBranches = tools.find((t) => t.name === 'list_branches')!;
|
|
532
|
+
const branch = (listBranches.outputs as { properties: { branches: { items: { properties: object; required: string[] } } } })
|
|
533
|
+
.properties.branches.items;
|
|
534
|
+
expect(Object.keys(branch.properties).sort()).toEqual(['hasRemote', 'isProtected', 'name']);
|
|
535
|
+
expect([...branch.required].sort()).toEqual(['hasRemote', 'isProtected', 'name']);
|
|
536
|
+
expect(listBranches.description).toContain('{ name, isProtected, hasRemote }');
|
|
537
|
+
expect(listBranches.description).not.toMatch(/\bahead\b,|\bbehind\b,/);
|
|
538
|
+
// The counts are still `branch_status`'s answer, for its one branch.
|
|
539
|
+
const branchStatus = tools.find((t) => t.name === 'branch_status')!;
|
|
540
|
+
expect((branchStatus.outputs as { required: string[] }).required).toEqual(
|
|
541
|
+
expect.arrayContaining(['ahead', 'behind', 'behindRemote']),
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
|
|
521
546
|
it('open_change_request acts on the SOURCE branch\'s workspace', async () => {
|
|
522
547
|
const base = await start();
|
|
523
548
|
const res = await post(`${base}/api/agent/tools/open_change_request`, writeTok(), {
|
|
@@ -1008,6 +1033,26 @@ describe('save_file and the repository folder', () => {
|
|
|
1008
1033
|
expect(calls.some((c) => c[0] === 'acquireLock')).toBe(false);
|
|
1009
1034
|
});
|
|
1010
1035
|
|
|
1036
|
+
it('refuses a tmp ref in every path input, before any lock — save_file, the history tools and update_branch\'s resolutions alike', async () => {
|
|
1037
|
+
const base = await start();
|
|
1038
|
+
const calls_: Array<[string, Record<string, unknown>]> = [
|
|
1039
|
+
['save_file', { path: '__tmp__/chat-uploads/plan.xlsx' }],
|
|
1040
|
+
['read_file_at', { path: '__tmp__/chat-uploads/plan.xlsx', at: 'HEAD' }],
|
|
1041
|
+
['compare_file', { path: '__tmp__/chat-uploads/plan.xlsx' }],
|
|
1042
|
+
['list_file_changes', { path: '__tmp__/chat-uploads/plan.xlsx' }],
|
|
1043
|
+
['update_branch', { resolutions: [{ path: '__tmp__/chat-uploads/plan.xlsx', choose: 'ours' }] }],
|
|
1044
|
+
];
|
|
1045
|
+
for (const [tool, args] of calls_) {
|
|
1046
|
+
const res = await post(`${base}/api/agent/tools/${tool}`, writeTok(), { ...args, branch: WS });
|
|
1047
|
+
expect(res.status, tool).toBe(400);
|
|
1048
|
+
const body = (await res.json()) as { error: string; kind: string };
|
|
1049
|
+
expect(body.kind, tool).toBe('tmp-ref-read-only');
|
|
1050
|
+
expect(body.error).toContain(`${tool} does not take one there`);
|
|
1051
|
+
expect(body.error).toContain('copy_file');
|
|
1052
|
+
}
|
|
1053
|
+
expect(calls.some((c) => c[0] === 'acquireLock')).toBe(false);
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1011
1056
|
it('answers a missing path with a 400, not a crash', async () => {
|
|
1012
1057
|
const base = await start();
|
|
1013
1058
|
const res = await post(`${base}/api/agent/tools/save_file`, writeTok(), { branch: WS });
|
|
@@ -1165,8 +1210,9 @@ describe('history and syncing tools', () => {
|
|
|
1165
1210
|
// The listing double names only `main`, so the draft's own entry is unknown.
|
|
1166
1211
|
isProtected: false,
|
|
1167
1212
|
hasRemote: true,
|
|
1168
|
-
|
|
1169
|
-
|
|
1213
|
+
// Counted for the draft alone, by its own workspace — not read off a listing.
|
|
1214
|
+
ahead: 3,
|
|
1215
|
+
behind: 1,
|
|
1170
1216
|
behindRemote: true,
|
|
1171
1217
|
uncommittedPaths: ['KnowledgeBase/Pending.md'],
|
|
1172
1218
|
forkBase: 'main',
|
|
@@ -1175,6 +1221,11 @@ describe('history and syncing tools', () => {
|
|
|
1175
1221
|
withheldPaths: 0,
|
|
1176
1222
|
});
|
|
1177
1223
|
expect(calls).toContainEqual(['changedPathsBetweenBranches', DRAFT_WS, 'main', DRAFT]);
|
|
1224
|
+
expect(calls).toContainEqual(['branchCounts', DRAFT_WS]);
|
|
1225
|
+
// Counted only once the listing's fetch has refreshed the remote-tracking refs.
|
|
1226
|
+
const fetched = calls.findIndex((c) => c[0] === 'listBranches:fetched' && c[1] === DRAFT_WS);
|
|
1227
|
+
expect(fetched).toBeGreaterThanOrEqual(0);
|
|
1228
|
+
expect(calls.findIndex((c) => c[0] === 'branchCounts')).toBeGreaterThan(fetched);
|
|
1178
1229
|
|
|
1179
1230
|
calls = [];
|
|
1180
1231
|
const named = await post(tool(base, 'branch_status'), writeTok(), { branch: DRAFT, base: 'release' });
|
|
@@ -36,11 +36,9 @@ const branchSchema: JsonSchema = {
|
|
|
36
36
|
properties: {
|
|
37
37
|
name: { type: 'string' },
|
|
38
38
|
isProtected: { type: 'boolean' },
|
|
39
|
-
ahead: { type: ['integer', 'null'], description: 'Commits ahead of upstream / nearest protected base; null if unresolved.' },
|
|
40
|
-
behind: { type: ['integer', 'null'], description: 'Commits behind; null if unresolved.' },
|
|
41
39
|
hasRemote: { type: 'boolean', description: 'True iff `origin/<name>` exists after the last fetch.' },
|
|
42
40
|
},
|
|
43
|
-
required: ['name', 'isProtected', '
|
|
41
|
+
required: ['name', 'isProtected', 'hasRemote'],
|
|
44
42
|
};
|
|
45
43
|
|
|
46
44
|
const commitSchema: JsonSchema = {
|
|
@@ -239,7 +237,9 @@ export function registerWorkflowTools(
|
|
|
239
237
|
// `getFilesystem` — so the branch-less call the tool handler refuses
|
|
240
238
|
// before this runs would otherwise commit to, or fork from, a
|
|
241
239
|
// workspace literally named "undefined".
|
|
242
|
-
|
|
240
|
+
// The normaliser also refuses a tmp ref (`__tmp__/…`) in every path
|
|
241
|
+
// input: none of these tools takes one (see `kb-fs/tmp-ref.ts`).
|
|
242
|
+
(args, ctx) => spec.handler(normalizePathArgs(args, kbDirName, { tool: spec.name }), ctx),
|
|
243
243
|
{ write: spec.write },
|
|
244
244
|
),
|
|
245
245
|
);
|
|
@@ -258,7 +258,8 @@ export function registerWorkflowTools(
|
|
|
258
258
|
name: 'list_branches',
|
|
259
259
|
description:
|
|
260
260
|
'List every draft known to the repository (protected versions + unprotected drafts, ' +
|
|
261
|
-
'local and on the shared remote). Returns `{ branches: [{ name, isProtected,
|
|
261
|
+
'local and on the shared remote). Returns `{ branches: [{ name, isProtected, hasRemote }] }`; ' +
|
|
262
|
+
'how far one branch is ahead or behind is `branch_status`\'s answer. ' +
|
|
262
263
|
'Takes no arguments — branch listing is repo-global, not scoped to any one draft.',
|
|
263
264
|
// Repo-global: the branch set is the same from any clone, so don't require
|
|
264
265
|
// (and risk cloning) a model-named branch — resolve any existing workspace.
|
|
@@ -978,9 +979,13 @@ export function registerWorkflowTools(
|
|
|
978
979
|
// and a save or sync landing between two of them is tolerated — the
|
|
979
980
|
// next call reports it. (The one answer that must be exact, what a
|
|
980
981
|
// merge is of, is taken under one reservation by `update_branch`.)
|
|
981
|
-
|
|
982
|
+
// The counts alone wait for the listing: its fetch refreshes the
|
|
983
|
+
// remote-tracking refs they are measured against.
|
|
984
|
+
const [status, [branches, counts], pending, forkBase] = await Promise.all([
|
|
982
985
|
ctx.workflowService.branchStatus(workspaceId),
|
|
983
|
-
ctx.workflowService
|
|
986
|
+
ctx.workflowService
|
|
987
|
+
.listBranches(workspaceId)
|
|
988
|
+
.then(async (listed) => [listed, await ctx.workflowService.branchCounts(workspaceId)] as const),
|
|
984
989
|
ctx.workflowService.listPendingChangePaths(workspaceId),
|
|
985
990
|
ctx.workflowService.resolveForkBase(workspaceId, branch),
|
|
986
991
|
]);
|
|
@@ -1000,8 +1005,8 @@ export function registerWorkflowTools(
|
|
|
1000
1005
|
branch,
|
|
1001
1006
|
isProtected: info?.isProtected ?? kb.protectedBranches.has(branch),
|
|
1002
1007
|
hasRemote: info?.hasRemote ?? status.hasUpstream,
|
|
1003
|
-
ahead:
|
|
1004
|
-
behind:
|
|
1008
|
+
ahead: counts.ahead,
|
|
1009
|
+
behind: counts.behind,
|
|
1005
1010
|
behindRemote: status.unmergedFromUpstream,
|
|
1006
1011
|
uncommittedPaths,
|
|
1007
1012
|
forkBase,
|