@bevel-software/platform-core-backend 0.7.5 → 0.9.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/LICENSE +202 -202
- package/THIRD-PARTY-NOTICES.md +428 -454
- package/dist/core/core-ports.d.ts +8 -1
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +75 -18
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +12 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +69 -36
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +8 -7
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +1 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +6 -6
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-declarations.d.ts +5 -5
- package/dist/modules/access/access-declarations.js +3 -3
- package/dist/modules/access/access-mutation.service.d.ts +3 -3
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +3 -3
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-splice.js +4 -4
- package/dist/modules/access/access-splice.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +3 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +22 -20
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/creator-access.d.ts +2 -2
- package/dist/modules/access/creator-access.js +5 -5
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/access/render-roles-yaml.d.ts +22 -0
- package/dist/modules/access/render-roles-yaml.d.ts.map +1 -0
- package/dist/modules/access/render-roles-yaml.js +56 -0
- package/dist/modules/access/render-roles-yaml.js.map +1 -0
- package/dist/modules/access/roles-admin.service.d.ts +33 -5
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +44 -19
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/code-mode/code-mode-names.d.ts +5 -13
- package/dist/modules/code-mode/code-mode-names.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode-names.js +5 -27
- package/dist/modules/code-mode/code-mode-names.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +29 -7
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +11 -52
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +33 -395
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/plugins/index.d.ts +7 -0
- package/dist/modules/plugins/index.d.ts.map +1 -0
- package/dist/modules/plugins/index.js +6 -0
- package/dist/modules/plugins/index.js.map +1 -0
- package/dist/modules/plugins/join-proposals.d.ts +53 -0
- package/dist/modules/plugins/join-proposals.d.ts.map +1 -0
- package/dist/modules/plugins/join-proposals.js +67 -0
- package/dist/modules/plugins/join-proposals.js.map +1 -0
- package/dist/modules/plugins/join-requests.service.d.ts +81 -0
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -0
- package/dist/modules/plugins/join-requests.service.js +135 -0
- package/dist/modules/plugins/join-requests.service.js.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts +134 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.js +344 -0
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -0
- package/dist/modules/plugins/plugins.contract.d.ts +106 -0
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.contract.js +36 -0
- package/dist/modules/plugins/plugins.contract.js.map +1 -0
- package/dist/modules/plugins/plugins.routes.d.ts +42 -0
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.routes.js +379 -0
- package/dist/modules/plugins/plugins.routes.js.map +1 -0
- package/dist/modules/plugins/plugins.service.d.ts +60 -0
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.service.js +172 -0
- package/dist/modules/plugins/plugins.service.js.map +1 -0
- package/dist/modules/settings/setup.routes.d.ts +10 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +111 -6
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +2 -2
- package/dist/modules/skills/pending-skills.service.js +7 -7
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +4 -4
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.contract.js +1 -1
- package/dist/modules/skills/skills.service.js +5 -5
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +65 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.js +276 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +92 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +328 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +38 -12
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.js +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +13 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +233 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +74 -37
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +6 -3
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.js +2 -2
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workspace/kb-seed.service.d.ts +2 -2
- package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -1
- package/dist/modules/workspace/kb-seed.service.js +43 -10
- package/dist/modules/workspace/kb-seed.service.js.map +1 -1
- package/dist/modules/workspace/plugins-migration.d.ts +50 -0
- package/dist/modules/workspace/plugins-migration.d.ts.map +1 -0
- package/dist/modules/workspace/plugins-migration.js +379 -0
- package/dist/modules/workspace/plugins-migration.js.map +1 -0
- package/dist/modules/workspace/startup/kb-git.d.ts +23 -0
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -0
- package/dist/modules/workspace/startup/kb-git.js +86 -0
- package/dist/modules/workspace/startup/kb-git.js.map +1 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +74 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -0
- package/dist/modules/workspace/startup/kb-startup-runner.js +528 -0
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -0
- package/dist/modules/workspace/startup/on-server-start.d.ts +105 -0
- package/dist/modules/workspace/startup/on-server-start.d.ts.map +1 -0
- package/dist/modules/workspace/startup/on-server-start.js +21 -0
- package/dist/modules/workspace/startup/on-server-start.js.map +1 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +46 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +492 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +23 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js +69 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +17 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.js +109 -0
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -0
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +103 -0
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/template-files.step.js +337 -0
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.js +3 -3
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +0 -9
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +2 -30
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/shared/kb-layout.test.js +3 -3
- package/dist/shared/kb-layout.test.js.map +1 -1
- package/dist/shared/utcp-namespace.d.ts +6 -27
- package/dist/shared/utcp-namespace.d.ts.map +1 -1
- package/dist/shared/utcp-namespace.js +6 -63
- package/dist/shared/utcp-namespace.js.map +1 -1
- package/dist/shared/variable-refs.d.ts +42 -0
- package/dist/shared/variable-refs.d.ts.map +1 -0
- package/dist/shared/variable-refs.js +60 -0
- package/dist/shared/variable-refs.js.map +1 -0
- package/kb-template/.bevelignore +1 -1
- package/kb-template/AGENTS.md +88 -35
- package/kb-template/KnowledgeBase/How to get started.md +10 -10
- package/kb-template/access.md +36 -36
- package/kb-template/gitignore.template +16 -0
- package/migrations/meta/0000_snapshot.json +1479 -1479
- package/package.json +5 -4
- package/src/assets.ts +25 -25
- package/src/core/core-ports.ts +113 -106
- package/src/core/create-core-server.ts +85 -20
- package/src/core/create-core-services.ts +83 -42
- package/src/index.ts +69 -69
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +98 -98
- package/src/modules/access/__tests__/access-declarations.test.ts +28 -28
- package/src/modules/access/__tests__/access-md-format.test.ts +18 -18
- package/src/modules/access/__tests__/access-mutation.service.test.ts +5 -5
- package/src/modules/access/__tests__/access-splice.test.ts +2 -2
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +16 -16
- package/src/modules/access/__tests__/grant-sources.test.ts +12 -12
- package/src/modules/access/__tests__/roles-admin.service.test.ts +36 -2
- package/src/modules/access/access-control.interface.ts +8 -7
- package/src/modules/access/access-control.service.ts +7 -7
- package/src/modules/access/access-declarations.ts +5 -5
- package/src/modules/access/access-mutation.service.ts +3 -3
- package/src/modules/access/access-splice.ts +4 -4
- package/src/modules/access/access.routes.ts +23 -20
- package/src/modules/access/creator-access.ts +5 -5
- package/src/modules/access/render-roles-yaml.ts +65 -0
- package/src/modules/access/roles-admin.service.ts +45 -16
- package/src/modules/admin/admin-access.routes.ts +29 -29
- package/src/modules/auth/__tests__/auth.routes.test.ts +91 -91
- package/src/modules/auth/__tests__/rate-limit.test.ts +36 -36
- package/src/modules/auth/rate-limit.ts +45 -45
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +67 -0
- package/src/modules/code-mode/code-mode-names.ts +10 -36
- package/src/modules/code-mode/code-mode.tool.ts +27 -7
- package/src/modules/database/connection.ts +15 -15
- package/src/modules/database/schema.ts +11 -11
- package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +150 -150
- package/src/modules/mcp/mcp.service.ts +57 -435
- package/src/modules/{groups → plugins}/__tests__/join-proposals.test.ts +1 -1
- package/src/modules/{groups → plugins}/__tests__/join-requests.service.test.ts +7 -7
- package/src/modules/{groups/__tests__/group-index.service.test.ts → plugins/__tests__/plugin-index.service.test.ts} +41 -41
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +312 -0
- package/src/modules/{groups/__tests__/groups.routes.test.ts → plugins/__tests__/plugins.routes.test.ts} +100 -100
- package/src/modules/plugins/index.ts +17 -0
- package/src/modules/{groups → plugins}/join-proposals.ts +2 -2
- package/src/modules/{groups → plugins}/join-requests.service.ts +8 -8
- package/src/modules/{groups/group-provision.service.ts → plugins/plugin-provision.service.ts} +139 -69
- package/src/modules/{groups/groups.contract.ts → plugins/plugins.contract.ts} +26 -26
- package/src/modules/{groups/groups.routes.ts → plugins/plugins.routes.ts} +102 -102
- package/src/modules/{groups/groups.service.ts → plugins/plugins.service.ts} +43 -43
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +143 -143
- package/src/modules/settings/__tests__/setup.routes.test.ts +124 -3
- package/src/modules/settings/setup.routes.ts +112 -5
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +14 -14
- package/src/modules/skills/__tests__/skills.service.test.ts +13 -13
- package/src/modules/skills/pending-skills.service.ts +7 -7
- package/src/modules/skills/skills.contract.ts +4 -4
- package/src/modules/skills/skills.service.ts +5 -5
- package/src/modules/tool-auth/llm-usage-meter.ts +19 -19
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +198 -0
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +346 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +101 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +3 -3
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +27 -27
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +3 -3
- package/src/modules/tool-manuals/mcp-json-discovery.ts +328 -0
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +434 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +35 -12
- package/src/modules/tool-manuals/tool-manuals.routes.ts +222 -1
- package/src/modules/tool-manuals/tool-manuals.service.ts +82 -42
- package/src/modules/tool-manuals/tool-manuals.tools.ts +6 -3
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/branch-name.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +13 -8
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
- package/src/modules/workflow/git/git.service.ts +2 -2
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +1 -1
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
- package/src/modules/workflow/workflow-hooks.ts +101 -101
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +237 -237
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +236 -236
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +179 -179
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +320 -320
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +337 -337
- package/src/modules/workspace/__tests__/workspace.service.test.ts +27 -0
- package/src/modules/workspace/bevel-ignore.ts +66 -66
- package/src/modules/workspace/session-sink.ts +25 -25
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +495 -0
- package/src/modules/workspace/startup/kb-git.ts +94 -0
- package/src/modules/workspace/startup/kb-startup-runner.ts +597 -0
- package/src/modules/workspace/startup/on-server-start.ts +97 -0
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +636 -0
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +561 -0
- package/src/modules/workspace/startup/steps/roles-yaml.step.ts +71 -0
- package/src/modules/workspace/startup/steps/seed-tree.ts +115 -0
- package/src/modules/workspace/startup/steps/template-files.step.ts +360 -0
- package/src/modules/workspace/workspace.routes.ts +3 -3
- package/src/modules/workspace/workspace.service.ts +2 -35
- package/src/modules/workspace/workspace.tools.ts +922 -922
- package/src/shared/__tests__/join-request.test.ts +13 -13
- package/src/shared/__tests__/kb-layout.plugin.test.ts +45 -0
- package/src/shared/kb-layout.test.ts +3 -3
- package/src/shared/utcp-namespace.ts +10 -68
- package/src/shared/variable-refs.ts +64 -0
- package/src/modules/groups/__tests__/group-provision.service.test.ts +0 -247
- package/src/modules/groups/index.ts +0 -17
- package/src/modules/workspace/__tests__/kb-seed.service.test.ts +0 -444
- package/src/modules/workspace/kb-seed.interface.ts +0 -36
- package/src/modules/workspace/kb-seed.service.ts +0 -555
- package/src/shared/__tests__/kb-layout.group.test.ts +0 -45
- /package/kb-template/{Groups → Plugins}/.gitkeep +0 -0
|
@@ -1,237 +1,237 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
recordOntologyRead,
|
|
5
|
-
assertOntologyWriteAllowed,
|
|
6
|
-
assertShellAllowedWithinOntology,
|
|
7
|
-
MissingSessionError,
|
|
8
|
-
type SessionOntologyGate,
|
|
9
|
-
} from '../session-ontology.gate.js';
|
|
10
|
-
import type { ToolContext } from '../../tool-helpers/tool.contract.js';
|
|
11
|
-
import type { ISessionOntologyService, OperationDecision } from '../../workflow/session-ontology.service.js';
|
|
12
|
-
import { WorkflowHooks } from '../../workflow/workflow-hooks.js';
|
|
13
|
-
import { ontologyOf } from '../../../shared/kb-layout.js';
|
|
14
|
-
|
|
15
|
-
const KB = 'knowledge-base';
|
|
16
|
-
const P_FILE = 'KnowledgeBase/Product/Knowledge/Foo.md';
|
|
17
|
-
const PL_FILE = 'KnowledgeBase/Platform/Knowledge/Bar.md';
|
|
18
|
-
const NEUTRAL = 'access.md';
|
|
19
|
-
const RECOVERY_EMAIL = 'recovery-bot@bevel.local';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* In-memory stand-in for the touched-set service — applies the real
|
|
23
|
-
* reads-record / writes-block-once-multi rule against a per-session set, so the
|
|
24
|
-
* gate's interaction with it is exercised end to end without a DB.
|
|
25
|
-
*/
|
|
26
|
-
function makeFakeService(): ISessionOntologyService {
|
|
27
|
-
const touched = new Map<string, Set<string>>();
|
|
28
|
-
const setFor = (s: string) => touched.get(s) ?? new Set<string>();
|
|
29
|
-
return {
|
|
30
|
-
async checkOperation(sessionId, wsPath, isWrite): Promise<OperationDecision> {
|
|
31
|
-
const ont = ontologyOf(wsPath, KB);
|
|
32
|
-
const set = setFor(sessionId);
|
|
33
|
-
if (ont === null) return { allow: true, touched: [...set].sort() };
|
|
34
|
-
if (isWrite) {
|
|
35
|
-
const spanned = new Set(set).add(ont);
|
|
36
|
-
if (spanned.size > 1) {
|
|
37
|
-
return {
|
|
38
|
-
allow: false,
|
|
39
|
-
reason: set.size >= 2 ? 'multi-ontology' : 'different-ontology',
|
|
40
|
-
touched: [...set].sort(),
|
|
41
|
-
attempted: ont,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
set.add(ont);
|
|
46
|
-
touched.set(sessionId, set);
|
|
47
|
-
return { allow: true, touched: [...set].sort() };
|
|
48
|
-
},
|
|
49
|
-
checkOntology: async () => ({ allow: true, touched: [] }),
|
|
50
|
-
getTouched: async (s) => [...setFor(s)].sort(),
|
|
51
|
-
delete: async (s) => { touched.delete(s); },
|
|
52
|
-
sweepOlderThan: async () => 0,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Build a gate wired the way the CORE composition wires it: the fake
|
|
58
|
-
* touched-set service plus an EMPTY hooks registry. Core registers no
|
|
59
|
-
* `preWrite` hook, so the gate only TRACKS touches and never blocks — the
|
|
60
|
-
* blocking assertions (the ontology-block hook the enterprise composition
|
|
61
|
-
* root registers) live with that hook in the enterprise repo.
|
|
62
|
-
*/
|
|
63
|
-
function makeGate(): SessionOntologyGate {
|
|
64
|
-
const service = makeFakeService();
|
|
65
|
-
const hooks = new WorkflowHooks();
|
|
66
|
-
return { service, enabled: true, kbDirName: KB, recoveryBotEmail: RECOVERY_EMAIL, hooks };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function ctx(overrides: Partial<ToolContext> = {}): ToolContext {
|
|
70
|
-
return {
|
|
71
|
-
user: { id: 'u1', email: 'dev@bevel.local', name: 'Dev' },
|
|
72
|
-
scope: 'write',
|
|
73
|
-
source: 'internal',
|
|
74
|
-
sessionId: 's1',
|
|
75
|
-
abortSignal: new AbortController().signal,
|
|
76
|
-
workspaceService: {} as never,
|
|
77
|
-
workflowService: {} as never,
|
|
78
|
-
events: {} as never,
|
|
79
|
-
getFilesystem: async () => ({} as never),
|
|
80
|
-
...overrides,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
describe('recordOntologyRead', () => {
|
|
85
|
-
let gate: SessionOntologyGate;
|
|
86
|
-
|
|
87
|
-
beforeEach(() => {
|
|
88
|
-
gate = makeGate();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('does nothing when the flag is disabled', async () => {
|
|
92
|
-
gate.enabled = false;
|
|
93
|
-
await expect(recordOntologyRead(gate, ctx(), P_FILE)).resolves.toBeUndefined();
|
|
94
|
-
expect(await gate.service.getTouched('s1')).toEqual([]);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('skips neutral paths (no record)', async () => {
|
|
98
|
-
await expect(recordOntologyRead(gate, ctx(), NEUTRAL)).resolves.toBeUndefined();
|
|
99
|
-
expect(await gate.service.getTouched('s1')).toEqual([]);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('skips non-agent (browser session) callers', async () => {
|
|
103
|
-
await expect(
|
|
104
|
-
recordOntologyRead(gate, ctx({ source: 'session', sessionId: undefined }), P_FILE),
|
|
105
|
-
).resolves.toBeUndefined();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('skips the recovery bot by identity', async () => {
|
|
109
|
-
await expect(
|
|
110
|
-
recordOntologyRead(
|
|
111
|
-
gate,
|
|
112
|
-
ctx({ user: { id: 'bot', email: 'Recovery-Bot@Bevel.Local', name: 'bot' }, sessionId: undefined }),
|
|
113
|
-
PL_FILE,
|
|
114
|
-
),
|
|
115
|
-
).resolves.toBeUndefined();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('fails closed when an agent read of a named path has no session id', async () => {
|
|
119
|
-
await expect(recordOntologyRead(gate, ctx({ sessionId: undefined }), P_FILE)).rejects.toBeInstanceOf(
|
|
120
|
-
MissingSessionError,
|
|
121
|
-
);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('allows a neutral read without a session id', async () => {
|
|
125
|
-
await expect(recordOntologyRead(gate, ctx({ sessionId: undefined }), NEUTRAL)).resolves.toBeUndefined();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('records a read and allows it', async () => {
|
|
129
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
130
|
-
expect(await gate.service.getTouched('s1')).toEqual(['KnowledgeBase/Product']);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('allows reads across ontologies (recording both)', async () => {
|
|
134
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
135
|
-
await expect(recordOntologyRead(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
136
|
-
expect(await gate.service.getTouched('s1')).toEqual(['KnowledgeBase/Platform', 'KnowledgeBase/Product']);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('never blocks — a read into a second ontology still passes (delete_file relies on this)', async () => {
|
|
140
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
141
|
-
// A delete_file in a different ontology records its touch but never throws.
|
|
142
|
-
await expect(recordOntologyRead(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe('assertOntologyWriteAllowed', () => {
|
|
147
|
-
let gate: SessionOntologyGate;
|
|
148
|
-
|
|
149
|
-
beforeEach(() => {
|
|
150
|
-
gate = makeGate();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('does nothing when the flag is disabled', async () => {
|
|
154
|
-
gate.enabled = false;
|
|
155
|
-
// Even a cross-ontology write must pass when disabled.
|
|
156
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
157
|
-
await expect(assertOntologyWriteAllowed(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('skips neutral paths (no record, no block)', async () => {
|
|
161
|
-
await expect(assertOntologyWriteAllowed(gate, ctx(), NEUTRAL)).resolves.toBeUndefined();
|
|
162
|
-
expect(await gate.service.getTouched('s1')).toEqual([]);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('fails closed when an agent write of a named path has no session id', async () => {
|
|
166
|
-
await expect(assertOntologyWriteAllowed(gate, ctx({ sessionId: undefined }), P_FILE)).rejects.toBeInstanceOf(
|
|
167
|
-
MissingSessionError,
|
|
168
|
-
);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('allows a write confined to the one touched ontology', async () => {
|
|
172
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
173
|
-
await expect(assertOntologyWriteAllowed(gate, ctx(), P_FILE)).resolves.toBeUndefined();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// NOTE (core split): the "blocks a write to a different ontology" and
|
|
177
|
-
// "blocks ALL writes once the session has read across two ontologies" cases
|
|
178
|
-
// moved with the ontology-block hook (`registerOntologyBlockHook`) to the
|
|
179
|
-
// enterprise repo — core registers no preWrite hook, so a core-only
|
|
180
|
-
// deployment TRACKS but never blocks. The tests below pin exactly that.
|
|
181
|
-
|
|
182
|
-
it('core: allows a write to a different ontology (tracking only, no block hook)', async () => {
|
|
183
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
184
|
-
await expect(assertOntologyWriteAllowed(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('core: still allows writes after reading across two ontologies (no block hook)', async () => {
|
|
188
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
189
|
-
await recordOntologyRead(gate, ctx(), PL_FILE);
|
|
190
|
-
await expect(assertOntologyWriteAllowed(gate, ctx(), P_FILE)).resolves.toBeUndefined();
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('assertShellAllowedWithinOntology (shell / no-path tools)', () => {
|
|
195
|
-
let gate: SessionOntologyGate;
|
|
196
|
-
|
|
197
|
-
beforeEach(() => {
|
|
198
|
-
gate = makeGate();
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('allows a fresh session (nothing touched)', async () => {
|
|
202
|
-
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('allows a session confined to a single ontology', async () => {
|
|
206
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
207
|
-
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// NOTE (core split): the "blocks once the session has crossed into two
|
|
211
|
-
// ontologies" case moved with the enterprise ontology-block hook. Core
|
|
212
|
-
// registers no preWrite hook, so shell is never refused here.
|
|
213
|
-
it('core: allows shell even after crossing into two ontologies (no block hook)', async () => {
|
|
214
|
-
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
215
|
-
await recordOntologyRead(gate, ctx(), PL_FILE);
|
|
216
|
-
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('does nothing when the flag is disabled', async () => {
|
|
220
|
-
gate.enabled = false;
|
|
221
|
-
await gate.service.checkOperation('s1', P_FILE, false);
|
|
222
|
-
await gate.service.checkOperation('s1', PL_FILE, false);
|
|
223
|
-
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('exempts the recovery bot', async () => {
|
|
227
|
-
await gate.service.checkOperation('s1', P_FILE, false);
|
|
228
|
-
await gate.service.checkOperation('s1', PL_FILE, false);
|
|
229
|
-
await expect(
|
|
230
|
-
assertShellAllowedWithinOntology(gate, ctx({ user: { id: 'bot', email: RECOVERY_EMAIL, name: 'bot' } })),
|
|
231
|
-
).resolves.toBeUndefined();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('allows when there is no session id (nothing to evaluate)', async () => {
|
|
235
|
-
await expect(assertShellAllowedWithinOntology(gate, ctx({ sessionId: undefined }))).resolves.toBeUndefined();
|
|
236
|
-
});
|
|
237
|
-
});
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
recordOntologyRead,
|
|
5
|
+
assertOntologyWriteAllowed,
|
|
6
|
+
assertShellAllowedWithinOntology,
|
|
7
|
+
MissingSessionError,
|
|
8
|
+
type SessionOntologyGate,
|
|
9
|
+
} from '../session-ontology.gate.js';
|
|
10
|
+
import type { ToolContext } from '../../tool-helpers/tool.contract.js';
|
|
11
|
+
import type { ISessionOntologyService, OperationDecision } from '../../workflow/session-ontology.service.js';
|
|
12
|
+
import { WorkflowHooks } from '../../workflow/workflow-hooks.js';
|
|
13
|
+
import { ontologyOf } from '../../../shared/kb-layout.js';
|
|
14
|
+
|
|
15
|
+
const KB = 'knowledge-base';
|
|
16
|
+
const P_FILE = 'KnowledgeBase/Product/Knowledge/Foo.md';
|
|
17
|
+
const PL_FILE = 'KnowledgeBase/Platform/Knowledge/Bar.md';
|
|
18
|
+
const NEUTRAL = 'access.md';
|
|
19
|
+
const RECOVERY_EMAIL = 'recovery-bot@bevel.local';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* In-memory stand-in for the touched-set service — applies the real
|
|
23
|
+
* reads-record / writes-block-once-multi rule against a per-session set, so the
|
|
24
|
+
* gate's interaction with it is exercised end to end without a DB.
|
|
25
|
+
*/
|
|
26
|
+
function makeFakeService(): ISessionOntologyService {
|
|
27
|
+
const touched = new Map<string, Set<string>>();
|
|
28
|
+
const setFor = (s: string) => touched.get(s) ?? new Set<string>();
|
|
29
|
+
return {
|
|
30
|
+
async checkOperation(sessionId, wsPath, isWrite): Promise<OperationDecision> {
|
|
31
|
+
const ont = ontologyOf(wsPath, KB);
|
|
32
|
+
const set = setFor(sessionId);
|
|
33
|
+
if (ont === null) return { allow: true, touched: [...set].sort() };
|
|
34
|
+
if (isWrite) {
|
|
35
|
+
const spanned = new Set(set).add(ont);
|
|
36
|
+
if (spanned.size > 1) {
|
|
37
|
+
return {
|
|
38
|
+
allow: false,
|
|
39
|
+
reason: set.size >= 2 ? 'multi-ontology' : 'different-ontology',
|
|
40
|
+
touched: [...set].sort(),
|
|
41
|
+
attempted: ont,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
set.add(ont);
|
|
46
|
+
touched.set(sessionId, set);
|
|
47
|
+
return { allow: true, touched: [...set].sort() };
|
|
48
|
+
},
|
|
49
|
+
checkOntology: async () => ({ allow: true, touched: [] }),
|
|
50
|
+
getTouched: async (s) => [...setFor(s)].sort(),
|
|
51
|
+
delete: async (s) => { touched.delete(s); },
|
|
52
|
+
sweepOlderThan: async () => 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build a gate wired the way the CORE composition wires it: the fake
|
|
58
|
+
* touched-set service plus an EMPTY hooks registry. Core registers no
|
|
59
|
+
* `preWrite` hook, so the gate only TRACKS touches and never blocks — the
|
|
60
|
+
* blocking assertions (the ontology-block hook the enterprise composition
|
|
61
|
+
* root registers) live with that hook in the enterprise repo.
|
|
62
|
+
*/
|
|
63
|
+
function makeGate(): SessionOntologyGate {
|
|
64
|
+
const service = makeFakeService();
|
|
65
|
+
const hooks = new WorkflowHooks();
|
|
66
|
+
return { service, enabled: true, kbDirName: KB, recoveryBotEmail: RECOVERY_EMAIL, hooks };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function ctx(overrides: Partial<ToolContext> = {}): ToolContext {
|
|
70
|
+
return {
|
|
71
|
+
user: { id: 'u1', email: 'dev@bevel.local', name: 'Dev' },
|
|
72
|
+
scope: 'write',
|
|
73
|
+
source: 'internal',
|
|
74
|
+
sessionId: 's1',
|
|
75
|
+
abortSignal: new AbortController().signal,
|
|
76
|
+
workspaceService: {} as never,
|
|
77
|
+
workflowService: {} as never,
|
|
78
|
+
events: {} as never,
|
|
79
|
+
getFilesystem: async () => ({} as never),
|
|
80
|
+
...overrides,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
describe('recordOntologyRead', () => {
|
|
85
|
+
let gate: SessionOntologyGate;
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
gate = makeGate();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('does nothing when the flag is disabled', async () => {
|
|
92
|
+
gate.enabled = false;
|
|
93
|
+
await expect(recordOntologyRead(gate, ctx(), P_FILE)).resolves.toBeUndefined();
|
|
94
|
+
expect(await gate.service.getTouched('s1')).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('skips neutral paths (no record)', async () => {
|
|
98
|
+
await expect(recordOntologyRead(gate, ctx(), NEUTRAL)).resolves.toBeUndefined();
|
|
99
|
+
expect(await gate.service.getTouched('s1')).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('skips non-agent (browser session) callers', async () => {
|
|
103
|
+
await expect(
|
|
104
|
+
recordOntologyRead(gate, ctx({ source: 'session', sessionId: undefined }), P_FILE),
|
|
105
|
+
).resolves.toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('skips the recovery bot by identity', async () => {
|
|
109
|
+
await expect(
|
|
110
|
+
recordOntologyRead(
|
|
111
|
+
gate,
|
|
112
|
+
ctx({ user: { id: 'bot', email: 'Recovery-Bot@Bevel.Local', name: 'bot' }, sessionId: undefined }),
|
|
113
|
+
PL_FILE,
|
|
114
|
+
),
|
|
115
|
+
).resolves.toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('fails closed when an agent read of a named path has no session id', async () => {
|
|
119
|
+
await expect(recordOntologyRead(gate, ctx({ sessionId: undefined }), P_FILE)).rejects.toBeInstanceOf(
|
|
120
|
+
MissingSessionError,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('allows a neutral read without a session id', async () => {
|
|
125
|
+
await expect(recordOntologyRead(gate, ctx({ sessionId: undefined }), NEUTRAL)).resolves.toBeUndefined();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('records a read and allows it', async () => {
|
|
129
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
130
|
+
expect(await gate.service.getTouched('s1')).toEqual(['KnowledgeBase/Product']);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('allows reads across ontologies (recording both)', async () => {
|
|
134
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
135
|
+
await expect(recordOntologyRead(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
136
|
+
expect(await gate.service.getTouched('s1')).toEqual(['KnowledgeBase/Platform', 'KnowledgeBase/Product']);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('never blocks — a read into a second ontology still passes (delete_file relies on this)', async () => {
|
|
140
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
141
|
+
// A delete_file in a different ontology records its touch but never throws.
|
|
142
|
+
await expect(recordOntologyRead(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('assertOntologyWriteAllowed', () => {
|
|
147
|
+
let gate: SessionOntologyGate;
|
|
148
|
+
|
|
149
|
+
beforeEach(() => {
|
|
150
|
+
gate = makeGate();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('does nothing when the flag is disabled', async () => {
|
|
154
|
+
gate.enabled = false;
|
|
155
|
+
// Even a cross-ontology write must pass when disabled.
|
|
156
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
157
|
+
await expect(assertOntologyWriteAllowed(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('skips neutral paths (no record, no block)', async () => {
|
|
161
|
+
await expect(assertOntologyWriteAllowed(gate, ctx(), NEUTRAL)).resolves.toBeUndefined();
|
|
162
|
+
expect(await gate.service.getTouched('s1')).toEqual([]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('fails closed when an agent write of a named path has no session id', async () => {
|
|
166
|
+
await expect(assertOntologyWriteAllowed(gate, ctx({ sessionId: undefined }), P_FILE)).rejects.toBeInstanceOf(
|
|
167
|
+
MissingSessionError,
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('allows a write confined to the one touched ontology', async () => {
|
|
172
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
173
|
+
await expect(assertOntologyWriteAllowed(gate, ctx(), P_FILE)).resolves.toBeUndefined();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// NOTE (core split): the "blocks a write to a different ontology" and
|
|
177
|
+
// "blocks ALL writes once the session has read across two ontologies" cases
|
|
178
|
+
// moved with the ontology-block hook (`registerOntologyBlockHook`) to the
|
|
179
|
+
// enterprise repo — core registers no preWrite hook, so a core-only
|
|
180
|
+
// deployment TRACKS but never blocks. The tests below pin exactly that.
|
|
181
|
+
|
|
182
|
+
it('core: allows a write to a different ontology (tracking only, no block hook)', async () => {
|
|
183
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
184
|
+
await expect(assertOntologyWriteAllowed(gate, ctx(), PL_FILE)).resolves.toBeUndefined();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('core: still allows writes after reading across two ontologies (no block hook)', async () => {
|
|
188
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
189
|
+
await recordOntologyRead(gate, ctx(), PL_FILE);
|
|
190
|
+
await expect(assertOntologyWriteAllowed(gate, ctx(), P_FILE)).resolves.toBeUndefined();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('assertShellAllowedWithinOntology (shell / no-path tools)', () => {
|
|
195
|
+
let gate: SessionOntologyGate;
|
|
196
|
+
|
|
197
|
+
beforeEach(() => {
|
|
198
|
+
gate = makeGate();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('allows a fresh session (nothing touched)', async () => {
|
|
202
|
+
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('allows a session confined to a single ontology', async () => {
|
|
206
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
207
|
+
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// NOTE (core split): the "blocks once the session has crossed into two
|
|
211
|
+
// ontologies" case moved with the enterprise ontology-block hook. Core
|
|
212
|
+
// registers no preWrite hook, so shell is never refused here.
|
|
213
|
+
it('core: allows shell even after crossing into two ontologies (no block hook)', async () => {
|
|
214
|
+
await recordOntologyRead(gate, ctx(), P_FILE);
|
|
215
|
+
await recordOntologyRead(gate, ctx(), PL_FILE);
|
|
216
|
+
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('does nothing when the flag is disabled', async () => {
|
|
220
|
+
gate.enabled = false;
|
|
221
|
+
await gate.service.checkOperation('s1', P_FILE, false);
|
|
222
|
+
await gate.service.checkOperation('s1', PL_FILE, false);
|
|
223
|
+
await expect(assertShellAllowedWithinOntology(gate, ctx())).resolves.toBeUndefined();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('exempts the recovery bot', async () => {
|
|
227
|
+
await gate.service.checkOperation('s1', P_FILE, false);
|
|
228
|
+
await gate.service.checkOperation('s1', PL_FILE, false);
|
|
229
|
+
await expect(
|
|
230
|
+
assertShellAllowedWithinOntology(gate, ctx({ user: { id: 'bot', email: RECOVERY_EMAIL, name: 'bot' } })),
|
|
231
|
+
).resolves.toBeUndefined();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('allows when there is no session id (nothing to evaluate)', async () => {
|
|
235
|
+
await expect(assertShellAllowedWithinOntology(gate, ctx({ sessionId: undefined }))).resolves.toBeUndefined();
|
|
236
|
+
});
|
|
237
|
+
});
|