@bevel-software/platform-core-backend 0.7.5 → 0.8.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 +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +51 -7
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +5 -3
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +30 -18
- 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.js +19 -19
- 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/roles-admin.service.d.ts +18 -4
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +14 -4
- 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/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/workspace.routes.js +3 -3
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +26 -0
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +83 -12
- 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/migrations/meta/0000_snapshot.json +1479 -1479
- package/package.json +5 -4
- package/src/assets.ts +25 -25
- package/src/core/core-ports.ts +106 -106
- package/src/core/create-core-server.ts +55 -9
- package/src/core/create-core-services.ts +40 -20
- 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 +13 -1
- 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 +20 -20
- package/src/modules/access/creator-access.ts +5 -5
- package/src/modules/access/roles-admin.service.ts +13 -2
- 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/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__/kb-seed.service.test.ts +81 -13
- package/src/modules/workspace/__tests__/plugins-migration.test.ts +427 -0
- 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 +116 -0
- package/src/modules/workspace/bevel-ignore.ts +66 -66
- package/src/modules/workspace/kb-seed.service.ts +38 -9
- package/src/modules/workspace/plugins-migration.ts +479 -0
- package/src/modules/workspace/session-sink.ts +25 -25
- package/src/modules/workspace/workspace.routes.ts +3 -3
- package/src/modules/workspace/workspace.service.ts +85 -14
- 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/shared/__tests__/kb-layout.group.test.ts +0 -45
- /package/kb-template/{Groups → Plugins}/.gitkeep +0 -0
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import type { ValidationReport } from '@bevel-software/platform-shared';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Workflow lifecycle hooks — the generic seam that replaced the
|
|
5
|
-
* constructor-injected KB validator (and the core-owned ontology write block).
|
|
6
|
-
* Core code invokes the hooks at fixed lifecycle points; the modules that OWN
|
|
7
|
-
* the behavior (the enterprise kb/kb-graph modules today) register handlers in
|
|
8
|
-
* the composition root, right after `createCoreServices` returns. Core
|
|
9
|
-
* registers none, so a core-only deployment runs every hook point as a no-op.
|
|
10
|
-
*
|
|
11
|
-
* Two hook kinds with deliberately different failure semantics:
|
|
12
|
-
*
|
|
13
|
-
* - `commitValidation` — ADVISORY. Runs at commit time in `GitService`
|
|
14
|
-
* (`commit` / `commitFile`), exactly where the injected `IKbValidator`
|
|
15
|
-
* used to run. A returned report with `mustFix` entries is logged/surfaced
|
|
16
|
-
* but NEVER blocks the commit, and a hook that throws is caught and logged
|
|
17
|
-
* (see CLAUDE.md "Validation is advisory"). The signature mirrors the old
|
|
18
|
-
* `runValidation(workspaceId)` shape so `KbValidatorService` registers
|
|
19
|
-
* with a one-line adapter.
|
|
20
|
-
*
|
|
21
|
-
* - `preWrite` — BLOCKING. Runs before a gated agent write (file-write
|
|
22
|
-
* tools) or a write-capable shell command; a hook that throws REJECTS the
|
|
23
|
-
* operation (the error propagates to the tool caller). This is where the
|
|
24
|
-
* enterprise ontology-session block registers; core registers nothing, so
|
|
25
|
-
* core never blocks (touch TRACKING stays core — see
|
|
26
|
-
* `session-ontology.gate.ts`).
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/** What `GitService` knows at the advisory commit-validation point. */
|
|
30
|
-
export interface CommitValidationContext {
|
|
31
|
-
workspaceId: string;
|
|
32
|
-
branch: string;
|
|
33
|
-
/**
|
|
34
|
-
* Repo-relative paths the commit will include — the full touched set for
|
|
35
|
-
* `commit()`, a single element for the one-file `commitFile()` path.
|
|
36
|
-
*/
|
|
37
|
-
paths: string[];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Advisory commit-time validation. Return a report to have the caller log /
|
|
42
|
-
* surface it (or `void` for "nothing to say"). Throwing is tolerated — the
|
|
43
|
-
* caller catches and logs, never blocks the commit.
|
|
44
|
-
*/
|
|
45
|
-
export type CommitValidationHook = (
|
|
46
|
-
ctx: CommitValidationContext,
|
|
47
|
-
) => Promise<ValidationReport | void>;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* What the pre-write gate knows when it consults the blocking hooks. The core
|
|
51
|
-
* gate has already resolved all skip conditions (boundary flag off, neutral
|
|
52
|
-
* path, non-agent caller, recovery bot, missing session id — see
|
|
53
|
-
* `resolveGatedSession`), so by hook time there is always a session id.
|
|
54
|
-
*/
|
|
55
|
-
export type PreWriteContext =
|
|
56
|
-
/** A path-addressed content write (write_file/edit_file/mkdir/move/copy/unzip…). */
|
|
57
|
-
| { kind: 'file-write'; sessionId: string; wsPath: string }
|
|
58
|
-
/** A write-capable operation with no resolvable target path (execute_command). */
|
|
59
|
-
| { kind: 'shell'; sessionId: string };
|
|
60
|
-
|
|
61
|
-
/** Blocking pre-write hook: throw to reject the write; return to allow. */
|
|
62
|
-
export type PreWriteHook = (ctx: PreWriteContext) => Promise<void>;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* The registry instance. ONE per composition (created in
|
|
66
|
-
* `createCoreServices`, shared by `GitService` and exposed as
|
|
67
|
-
* `WorkflowService.hooks` for the composition root to register against).
|
|
68
|
-
*/
|
|
69
|
-
export class WorkflowHooks {
|
|
70
|
-
private readonly commitValidation: CommitValidationHook[] = [];
|
|
71
|
-
private readonly preWrite: PreWriteHook[] = [];
|
|
72
|
-
|
|
73
|
-
/** Register an advisory commit-time validation hook. */
|
|
74
|
-
onCommitValidation(hook: CommitValidationHook): void {
|
|
75
|
-
this.commitValidation.push(hook);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Register a blocking pre-write hook. */
|
|
79
|
-
onPreWrite(hook: PreWriteHook): void {
|
|
80
|
-
this.preWrite.push(hook);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* The registered commit-validation hooks, for the caller (`GitService`) to
|
|
85
|
-
* iterate with its own advisory catch-and-log semantics.
|
|
86
|
-
*/
|
|
87
|
-
commitValidationHooks(): readonly CommitValidationHook[] {
|
|
88
|
-
return this.commitValidation;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Run every blocking pre-write hook in registration order. The first throw
|
|
93
|
-
* propagates and rejects the write; with no hooks registered (core-only)
|
|
94
|
-
* this resolves immediately.
|
|
95
|
-
*/
|
|
96
|
-
async runPreWrite(ctx: PreWriteContext): Promise<void> {
|
|
97
|
-
for (const hook of this.preWrite) {
|
|
98
|
-
await hook(ctx);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
1
|
+
import type { ValidationReport } from '@bevel-software/platform-shared';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Workflow lifecycle hooks — the generic seam that replaced the
|
|
5
|
+
* constructor-injected KB validator (and the core-owned ontology write block).
|
|
6
|
+
* Core code invokes the hooks at fixed lifecycle points; the modules that OWN
|
|
7
|
+
* the behavior (the enterprise kb/kb-graph modules today) register handlers in
|
|
8
|
+
* the composition root, right after `createCoreServices` returns. Core
|
|
9
|
+
* registers none, so a core-only deployment runs every hook point as a no-op.
|
|
10
|
+
*
|
|
11
|
+
* Two hook kinds with deliberately different failure semantics:
|
|
12
|
+
*
|
|
13
|
+
* - `commitValidation` — ADVISORY. Runs at commit time in `GitService`
|
|
14
|
+
* (`commit` / `commitFile`), exactly where the injected `IKbValidator`
|
|
15
|
+
* used to run. A returned report with `mustFix` entries is logged/surfaced
|
|
16
|
+
* but NEVER blocks the commit, and a hook that throws is caught and logged
|
|
17
|
+
* (see CLAUDE.md "Validation is advisory"). The signature mirrors the old
|
|
18
|
+
* `runValidation(workspaceId)` shape so `KbValidatorService` registers
|
|
19
|
+
* with a one-line adapter.
|
|
20
|
+
*
|
|
21
|
+
* - `preWrite` — BLOCKING. Runs before a gated agent write (file-write
|
|
22
|
+
* tools) or a write-capable shell command; a hook that throws REJECTS the
|
|
23
|
+
* operation (the error propagates to the tool caller). This is where the
|
|
24
|
+
* enterprise ontology-session block registers; core registers nothing, so
|
|
25
|
+
* core never blocks (touch TRACKING stays core — see
|
|
26
|
+
* `session-ontology.gate.ts`).
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** What `GitService` knows at the advisory commit-validation point. */
|
|
30
|
+
export interface CommitValidationContext {
|
|
31
|
+
workspaceId: string;
|
|
32
|
+
branch: string;
|
|
33
|
+
/**
|
|
34
|
+
* Repo-relative paths the commit will include — the full touched set for
|
|
35
|
+
* `commit()`, a single element for the one-file `commitFile()` path.
|
|
36
|
+
*/
|
|
37
|
+
paths: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Advisory commit-time validation. Return a report to have the caller log /
|
|
42
|
+
* surface it (or `void` for "nothing to say"). Throwing is tolerated — the
|
|
43
|
+
* caller catches and logs, never blocks the commit.
|
|
44
|
+
*/
|
|
45
|
+
export type CommitValidationHook = (
|
|
46
|
+
ctx: CommitValidationContext,
|
|
47
|
+
) => Promise<ValidationReport | void>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* What the pre-write gate knows when it consults the blocking hooks. The core
|
|
51
|
+
* gate has already resolved all skip conditions (boundary flag off, neutral
|
|
52
|
+
* path, non-agent caller, recovery bot, missing session id — see
|
|
53
|
+
* `resolveGatedSession`), so by hook time there is always a session id.
|
|
54
|
+
*/
|
|
55
|
+
export type PreWriteContext =
|
|
56
|
+
/** A path-addressed content write (write_file/edit_file/mkdir/move/copy/unzip…). */
|
|
57
|
+
| { kind: 'file-write'; sessionId: string; wsPath: string }
|
|
58
|
+
/** A write-capable operation with no resolvable target path (execute_command). */
|
|
59
|
+
| { kind: 'shell'; sessionId: string };
|
|
60
|
+
|
|
61
|
+
/** Blocking pre-write hook: throw to reject the write; return to allow. */
|
|
62
|
+
export type PreWriteHook = (ctx: PreWriteContext) => Promise<void>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The registry instance. ONE per composition (created in
|
|
66
|
+
* `createCoreServices`, shared by `GitService` and exposed as
|
|
67
|
+
* `WorkflowService.hooks` for the composition root to register against).
|
|
68
|
+
*/
|
|
69
|
+
export class WorkflowHooks {
|
|
70
|
+
private readonly commitValidation: CommitValidationHook[] = [];
|
|
71
|
+
private readonly preWrite: PreWriteHook[] = [];
|
|
72
|
+
|
|
73
|
+
/** Register an advisory commit-time validation hook. */
|
|
74
|
+
onCommitValidation(hook: CommitValidationHook): void {
|
|
75
|
+
this.commitValidation.push(hook);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Register a blocking pre-write hook. */
|
|
79
|
+
onPreWrite(hook: PreWriteHook): void {
|
|
80
|
+
this.preWrite.push(hook);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The registered commit-validation hooks, for the caller (`GitService`) to
|
|
85
|
+
* iterate with its own advisory catch-and-log semantics.
|
|
86
|
+
*/
|
|
87
|
+
commitValidationHooks(): readonly CommitValidationHook[] {
|
|
88
|
+
return this.commitValidation;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Run every blocking pre-write hook in registration order. The first throw
|
|
93
|
+
* propagates and rejects the write; with no hooks registered (core-only)
|
|
94
|
+
* this resolves immediately.
|
|
95
|
+
*/
|
|
96
|
+
async runPreWrite(ctx: PreWriteContext): Promise<void> {
|
|
97
|
+
for (const hook of this.preWrite) {
|
|
98
|
+
await hook(ctx);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -119,7 +119,7 @@ describe('KbSeedService', () => {
|
|
|
119
119
|
expect(await exists(path.join(dir, '.gitignore'))).toBe(true);
|
|
120
120
|
// Core's two roots are seeded (kept present via their .gitkeep).
|
|
121
121
|
expect(await exists(path.join(dir, 'KnowledgeBase/.gitkeep'))).toBe(true);
|
|
122
|
-
expect(await exists(path.join(dir, '
|
|
122
|
+
expect(await exists(path.join(dir, 'Plugins/.gitkeep'))).toBe(true);
|
|
123
123
|
// …and only those two. `Data/`, `Agents/` and `Pipelines/` scaffold an
|
|
124
124
|
// agentic execution layer this platform does not have, so seeding them
|
|
125
125
|
// would hand every operator three empty folders nothing can fill.
|
|
@@ -156,7 +156,7 @@ describe('KbSeedService', () => {
|
|
|
156
156
|
'.bevelignore': 'x',
|
|
157
157
|
'.gitignore': 'x',
|
|
158
158
|
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
159
|
-
'
|
|
159
|
+
'Plugins/.gitkeep': '',
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
await makeSeeder(upstream).ensureRemoteSeeded();
|
|
@@ -172,7 +172,7 @@ describe('KbSeedService', () => {
|
|
|
172
172
|
'access.md': 'CUSTOM ACCESS RULES',
|
|
173
173
|
// AGENTS.md / .bevelignore / .gitignore are absent → should be added.
|
|
174
174
|
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
175
|
-
'
|
|
175
|
+
'Plugins/.gitkeep': '',
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
// Simulate the app loading the branch: a fresh clone, then top-up on it.
|
|
@@ -201,7 +201,7 @@ describe('KbSeedService', () => {
|
|
|
201
201
|
'.gitignore': '',
|
|
202
202
|
'access.md': 'CUSTOM ACCESS RULES',
|
|
203
203
|
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
204
|
-
'
|
|
204
|
+
'Plugins/.gitkeep': '',
|
|
205
205
|
});
|
|
206
206
|
|
|
207
207
|
const repoDir = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
@@ -240,7 +240,7 @@ describe('KbSeedService', () => {
|
|
|
240
240
|
'.gitignore': '',
|
|
241
241
|
'access.md': 'CUSTOM ACCESS RULES',
|
|
242
242
|
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
243
|
-
'
|
|
243
|
+
'Plugins/.gitkeep': '',
|
|
244
244
|
});
|
|
245
245
|
|
|
246
246
|
const first = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
@@ -254,7 +254,7 @@ describe('KbSeedService', () => {
|
|
|
254
254
|
const upstream = await seededUpstream(PROTECTED, {
|
|
255
255
|
'roles.yaml': 'roles:\n Admin:\n - keep@example.com\n',
|
|
256
256
|
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
257
|
-
'
|
|
257
|
+
'Plugins/.gitkeep': '',
|
|
258
258
|
});
|
|
259
259
|
|
|
260
260
|
const first = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
@@ -270,6 +270,74 @@ describe('KbSeedService', () => {
|
|
|
270
270
|
expect(after2).toBe(after1);
|
|
271
271
|
});
|
|
272
272
|
|
|
273
|
+
it('a note-only migration outcome stages nothing and raises no top-up warning', async () => {
|
|
274
|
+
const template = await fs.readFile(
|
|
275
|
+
path.join(__dirname, '../../../../kb-template/AGENTS.md'),
|
|
276
|
+
'utf8',
|
|
277
|
+
);
|
|
278
|
+
const upstream = await seededUpstream(PROTECTED, {
|
|
279
|
+
'roles.yaml': 'roles:\n Admin:\n - keep@example.com\n',
|
|
280
|
+
'access.md': 'x',
|
|
281
|
+
'AGENTS.md': template,
|
|
282
|
+
'.bevelignore': 'AGENTS.md\nPlugins/\n',
|
|
283
|
+
'.gitignore': '',
|
|
284
|
+
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
285
|
+
'Plugins/GTM/access.md': 'write:\n - Admin\n',
|
|
286
|
+
// An unparsable manifest keeps this manual a `.tool` — its variables
|
|
287
|
+
// have nowhere to go. The migration NOTES the refusal every run and
|
|
288
|
+
// changes nothing; staging on the note used to reach `git commit`
|
|
289
|
+
// with an empty index and warn about it on every boot.
|
|
290
|
+
'Plugins/GTM/plugin.json': '{ not json at all',
|
|
291
|
+
'Plugins/GTM/vendor.tool': JSON.stringify({
|
|
292
|
+
name: 'vendor',
|
|
293
|
+
type: 'mcp',
|
|
294
|
+
url: 'https://mcp.vendor.example/mcp',
|
|
295
|
+
variables: [{ name: 'VENDOR_KEY', scope: 'user' }],
|
|
296
|
+
}),
|
|
297
|
+
});
|
|
298
|
+
const repoDir = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
299
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
300
|
+
try {
|
|
301
|
+
await makeSeeder(upstream).topUpWorkspace(repoDir, DEFAULT_BRANCH);
|
|
302
|
+
expect(warn.mock.calls.flat().join(' ')).not.toMatch(/top-up/i);
|
|
303
|
+
} finally {
|
|
304
|
+
warn.mockRestore();
|
|
305
|
+
}
|
|
306
|
+
// Only the pre-seed commit — a note is not a change.
|
|
307
|
+
expect(await headCommitCount(root, upstream, DEFAULT_BRANCH)).toBe(1);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('the root rename rewrites the stale .bevelignore rule and commits it along', async () => {
|
|
311
|
+
const template = await fs.readFile(
|
|
312
|
+
path.join(__dirname, '../../../../kb-template/AGENTS.md'),
|
|
313
|
+
'utf8',
|
|
314
|
+
);
|
|
315
|
+
const upstream = await seededUpstream(PROTECTED, {
|
|
316
|
+
'roles.yaml': 'roles:\n Admin:\n - keep@example.com\n',
|
|
317
|
+
'access.md': 'x',
|
|
318
|
+
'AGENTS.md': template,
|
|
319
|
+
// The pre-rename rule: without the rewrite the migrated KB has a rule
|
|
320
|
+
// for a root that no longer exists and none for the one that does.
|
|
321
|
+
'.bevelignore': 'AGENTS.md\nGroups/\n',
|
|
322
|
+
'.gitignore': '',
|
|
323
|
+
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
324
|
+
'Groups/GTM/access.md': 'write:\n - Admin\n',
|
|
325
|
+
});
|
|
326
|
+
const repoDir = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
327
|
+
await makeSeeder(upstream).topUpWorkspace(repoDir, DEFAULT_BRANCH);
|
|
328
|
+
|
|
329
|
+
// An independent clone proves the rewrite was STAGED (the ignore file
|
|
330
|
+
// is a repo-root path outside the two roots' pathspecs) and pushed.
|
|
331
|
+
const dir = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
332
|
+
expect(await exists(path.join(dir, 'Plugins/GTM/access.md'))).toBe(true);
|
|
333
|
+
const ignore = await fs.readFile(path.join(dir, '.bevelignore'), 'utf8');
|
|
334
|
+
expect(ignore.split('\n').map((l) => l.trim())).toContain('Plugins/');
|
|
335
|
+
expect(ignore).not.toContain('Groups/');
|
|
336
|
+
expect(ignore).toContain('AGENTS.md');
|
|
337
|
+
const log = await git(dir, ['log', '-1', '--pretty=%s']);
|
|
338
|
+
expect(log.trim()).toBe('Move Groups/ to Plugins/ (Agent Plugins layout)');
|
|
339
|
+
});
|
|
340
|
+
|
|
273
341
|
});
|
|
274
342
|
|
|
275
343
|
/**
|
|
@@ -292,7 +360,7 @@ describe('KbSeedService', () => {
|
|
|
292
360
|
describe('the pre-rename ignore file', () => {
|
|
293
361
|
const legacy = (ignore: string): Record<string, string> => ({
|
|
294
362
|
'KnowledgeBase/.gitkeep': '',
|
|
295
|
-
'
|
|
363
|
+
'Plugins/.gitkeep': '',
|
|
296
364
|
'access.md': '---\nwrite:\n - Admin\n---\n',
|
|
297
365
|
'roles.yaml': 'roles:\n Admin:\n - a@example.com\n',
|
|
298
366
|
'.gitignore': '',
|
|
@@ -335,7 +403,7 @@ describe('KbSeedService', () => {
|
|
|
335
403
|
const dir = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
336
404
|
// Core's two, still there.
|
|
337
405
|
expect(await exists(path.join(dir, 'KnowledgeBase/.gitkeep'))).toBe(true);
|
|
338
|
-
expect(await exists(path.join(dir, '
|
|
406
|
+
expect(await exists(path.join(dir, 'Plugins/.gitkeep'))).toBe(true);
|
|
339
407
|
// …and the three this distribution asked for.
|
|
340
408
|
for (const claimed of ['Data', 'Agents', 'Pipelines']) {
|
|
341
409
|
expect(await exists(path.join(dir, `${claimed}/.gitkeep`))).toBe(true);
|
|
@@ -379,7 +447,7 @@ describe('KbSeedService', () => {
|
|
|
379
447
|
});
|
|
380
448
|
|
|
381
449
|
/**
|
|
382
|
-
* A FILE named `
|
|
450
|
+
* A FILE named `Plugins` used to satisfy the presence check — `fs.access`
|
|
383
451
|
* answers "is there something here?" — so the seeder skipped it, said
|
|
384
452
|
* nothing, and left a knowledge base permanently missing a root it claims
|
|
385
453
|
* to guarantee. Now it says so.
|
|
@@ -396,18 +464,18 @@ describe('KbSeedService', () => {
|
|
|
396
464
|
'.gitignore': '',
|
|
397
465
|
'KnowledgeBase/.gitkeep': '',
|
|
398
466
|
// Not a folder.
|
|
399
|
-
|
|
467
|
+
Plugins: 'this is a file, not the plugins root',
|
|
400
468
|
});
|
|
401
469
|
const repoDir = await checkout(root, upstream, DEFAULT_BRANCH);
|
|
402
470
|
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
403
471
|
try {
|
|
404
472
|
await makeSeeder(upstream).topUpWorkspace(repoDir, DEFAULT_BRANCH);
|
|
405
|
-
expect(warn.mock.calls.flat().join(' ')).toMatch(/
|
|
473
|
+
expect(warn.mock.calls.flat().join(' ')).toMatch(/Plugins.*not a directory/);
|
|
406
474
|
} finally {
|
|
407
475
|
warn.mockRestore();
|
|
408
476
|
}
|
|
409
477
|
// …and it did not quietly replace the file either.
|
|
410
|
-
expect((await fs.lstat(path.join(repoDir, '
|
|
478
|
+
expect((await fs.lstat(path.join(repoDir, 'Plugins'))).isFile()).toBe(true);
|
|
411
479
|
});
|
|
412
480
|
|
|
413
481
|
/**
|
|
@@ -422,7 +490,7 @@ describe('KbSeedService', () => {
|
|
|
422
490
|
const upstream = await seededUpstream(PROTECTED, {
|
|
423
491
|
'roles.yaml': 'roles:\n Admin:\n - keep@example.com\n',
|
|
424
492
|
'KnowledgeBase/Real/Knowledge/.gitkeep': '',
|
|
425
|
-
'
|
|
493
|
+
'Plugins/.gitkeep': '',
|
|
426
494
|
});
|
|
427
495
|
// Branch a suggestion off the default and load it.
|
|
428
496
|
const seed = await checkout(root, upstream, DEFAULT_BRANCH);
|