@danielblomma/cortex-mcp 2.2.4 → 2.4.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.
@@ -490,6 +490,58 @@ test("resolveTokenBudgetChoice: explicit caps and full-model opt-out win over au
490
490
  });
491
491
  });
492
492
 
493
+ test("resolveEffectiveTokenBudget: auto degrades when model max is too expensive for headroom", async () => {
494
+ const { resolveEffectiveTokenBudget, resolveTokenBudgetChoice } = await import("../dist/embedScheduler.js");
495
+ const choice = resolveTokenBudgetChoice(undefined, 2237);
496
+ const degraded = resolveEffectiveTokenBudget({
497
+ choice,
498
+ modelMaxTokens: 8192,
499
+ memoryBytes: 7e9,
500
+ sessions: 2
501
+ });
502
+ assert.equal(degraded.mode, "auto_degraded");
503
+ assert.equal(degraded.cap, 2048);
504
+ assert.match(degraded.reason, /memory_headroom/);
505
+ });
506
+
507
+ test("resolveEffectiveTokenBudget: auto keeps model max when headroom is sufficient", async () => {
508
+ const { resolveEffectiveTokenBudget, resolveTokenBudgetChoice } = await import("../dist/embedScheduler.js");
509
+ const choice = resolveTokenBudgetChoice("auto", 2237);
510
+ assert.deepEqual(
511
+ resolveEffectiveTokenBudget({
512
+ choice,
513
+ modelMaxTokens: 8192,
514
+ memoryBytes: 64e9,
515
+ sessions: 2
516
+ }),
517
+ choice
518
+ );
519
+ });
520
+
521
+ test("resolveEffectiveTokenBudget: explicit caps and full-model opt-out are never degraded", async () => {
522
+ const { resolveEffectiveTokenBudget, resolveTokenBudgetChoice } = await import("../dist/embedScheduler.js");
523
+ const explicit = resolveTokenBudgetChoice("4096", 2237);
524
+ assert.deepEqual(
525
+ resolveEffectiveTokenBudget({
526
+ choice: explicit,
527
+ modelMaxTokens: 8192,
528
+ memoryBytes: 1e9,
529
+ sessions: 4
530
+ }),
531
+ explicit
532
+ );
533
+ const full = resolveTokenBudgetChoice("full", 2237);
534
+ assert.deepEqual(
535
+ resolveEffectiveTokenBudget({
536
+ choice: full,
537
+ modelMaxTokens: 8192,
538
+ memoryBytes: 1e9,
539
+ sessions: 4
540
+ }),
541
+ full
542
+ );
543
+ });
544
+
493
545
  test("createTokenCounter: uses the tokenizer, clamps at model max, survives failures", async () => {
494
546
  const { createTokenCounter } = await import("../dist/embedScheduler.js");
495
547
  const tokenizer = (text) => ({ input_ids: { dims: [1, text.length * 2] } });
@@ -0,0 +1,322 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { execFileSync } from "node:child_process";
7
+ import {
8
+ buildPatternReviewContext,
9
+ createLocalPatternRunner,
10
+ PATTERN_REVIEW_QUESTION,
11
+ } from "../dist/enterprise/reviews/pattern-context.js";
12
+ import {
13
+ buildContextReviewAuditInput,
14
+ registerEnterpriseTools,
15
+ } from "../dist/enterprise/tools/enterprise.js";
16
+
17
+ function evidence(overrides = {}) {
18
+ return {
19
+ query: "error handling",
20
+ query_source: "explicit",
21
+ local_pattern_found: false,
22
+ fallback_used: false,
23
+ evidence_order: ["same_file", "same_module", "same_feature_area", "repo_wide"],
24
+ tiers: [
25
+ { name: "same_file", evidence: [] },
26
+ { name: "same_module", evidence: [] },
27
+ { name: "same_feature_area", evidence: [] },
28
+ { name: "repo_wide", evidence: [] },
29
+ ],
30
+ ...overrides,
31
+ };
32
+ }
33
+
34
+ test("composes deterministic non-blocking pattern context without inventing pass or fail", async () => {
35
+ const runner = async ({ target }) => {
36
+ if (target === "missing.ts") {
37
+ throw new Error("Pattern target was not found in indexed context: /private/repo/missing.ts");
38
+ }
39
+ if (target === "error.ts") {
40
+ throw new Error("secret internal stack detail");
41
+ }
42
+ if (target === "a.ts") {
43
+ return evidence({
44
+ local_pattern_found: true,
45
+ tiers: [
46
+ {
47
+ name: "same_file",
48
+ evidence: [{ id: "chunk:a", path: "a.ts", start_line: 2, end_line: 8 }],
49
+ },
50
+ ],
51
+ });
52
+ }
53
+ if (target === "b.ts") {
54
+ return evidence({
55
+ fallback_used: true,
56
+ tiers: [
57
+ {
58
+ name: "repo_wide",
59
+ evidence: [{ id: "file:docs/review.md", path: "docs/review.md" }],
60
+ },
61
+ ],
62
+ });
63
+ }
64
+ return evidence();
65
+ };
66
+
67
+ const result = await buildPatternReviewContext({
68
+ files: ["z.ts", "a.ts", "b.ts", "a.ts", "../secret.ts", "c.ts", "missing.ts", "error.ts"],
69
+ enabled: true,
70
+ query: "error handling",
71
+ topK: 2,
72
+ limit: 10,
73
+ runner,
74
+ });
75
+
76
+ assert.equal(result.enabled, true);
77
+ assert.equal(result.non_blocking, true);
78
+ assert.equal(result.affects_policy_summary, false);
79
+ assert.equal(result.review_question, PATTERN_REVIEW_QUESTION);
80
+ assert.deepEqual(result.targets.map((target) => target.path), [
81
+ "a.ts",
82
+ "b.ts",
83
+ "c.ts",
84
+ "error.ts",
85
+ "missing.ts",
86
+ "z.ts",
87
+ ]);
88
+ assert.deepEqual(result.targets.map((target) => target.status), [
89
+ "local_evidence",
90
+ "repo_fallback",
91
+ "no_evidence",
92
+ "error",
93
+ "not_indexed",
94
+ "no_evidence",
95
+ ]);
96
+ assert.equal(result.targets[0].tiers[0].evidence[0].start_line, 2);
97
+ assert.equal(result.targets[3].message, "Pattern evidence could not be produced for this target.");
98
+ assert.equal(result.targets[3].local_pattern_found, false);
99
+ assert.equal(result.targets[3].fallback_used, false);
100
+ assert.equal(result.targets[3].tiers.length, 4);
101
+ assert.equal(result.targets[4].query, "error handling");
102
+ const targetKeys = Object.keys(result.targets[0]).sort();
103
+ for (const target of result.targets) {
104
+ assert.deepEqual(Object.keys(target).sort(), targetKeys);
105
+ }
106
+ assert.doesNotMatch(JSON.stringify(result), /secret internal stack detail|\/private\/repo/u);
107
+ assert.deepEqual(result.summary, {
108
+ requested: 8,
109
+ eligible: 6,
110
+ analyzed: 6,
111
+ local_evidence: 1,
112
+ repo_fallback: 1,
113
+ no_evidence: 2,
114
+ not_indexed: 1,
115
+ errors: 1,
116
+ omitted: 0,
117
+ invalid_paths: 1,
118
+ });
119
+ });
120
+
121
+ test("sanitizes runtime warnings and unsafe citation paths", async () => {
122
+ const result = await buildPatternReviewContext({
123
+ files: ["src/a.ts"],
124
+ enabled: true,
125
+ topK: 5,
126
+ limit: 1,
127
+ runner: async () => evidence({
128
+ warning: "model cache failed at /Users/alice/private/model and C:\\Users\\alice\\model",
129
+ tiers: [
130
+ {
131
+ name: "same_file",
132
+ evidence: [
133
+ { id: "chunk:src/a.ts:run", path: "src/a.ts", start_line: 1, end_line: 3 },
134
+ { id: "chunk:/Users/alice/private", path: "/Users/alice/private/a.ts" },
135
+ { id: "chunk:C:/Users/alice/private", path: "C:\\Users\\alice\\private\\a.ts" },
136
+ { id: "chunk:../secret", path: "../secret.ts" },
137
+ ],
138
+ },
139
+ ],
140
+ }),
141
+ });
142
+
143
+ assert.equal(result.targets[0].status, "local_evidence");
144
+ assert.equal(result.targets[0].tiers[0].evidence.length, 1);
145
+ assert.equal(result.targets[0].citations_dropped, 3);
146
+ assert.equal(result.targets[0].warning, "Pattern evidence completed with local runtime warnings.");
147
+ assert.doesNotMatch(JSON.stringify(result), /Users[\\/]alice|\.\.\/secret/u);
148
+ });
149
+
150
+ test("audit projection records query size but not query text", () => {
151
+ const input = buildContextReviewAuditInput({
152
+ scope: "changed",
153
+ include_passed: true,
154
+ include_pattern_evidence: true,
155
+ pattern_query: "SECRET_API_KEY=do-not-retain",
156
+ pattern_top_k: 2,
157
+ pattern_limit: 10,
158
+ });
159
+
160
+ assert.equal(input.pattern_query_present, true);
161
+ assert.equal(input.pattern_query_length, 28);
162
+ assert.equal("pattern_query" in input, false);
163
+ assert.doesNotMatch(JSON.stringify(input), /SECRET_API_KEY/u);
164
+ });
165
+
166
+ test("bounds review targets after normalized deterministic ordering", async () => {
167
+ const calls = [];
168
+ const result = await buildPatternReviewContext({
169
+ files: ["z.ts", "./b.ts", "a.ts", "b.ts"],
170
+ enabled: true,
171
+ topK: 1,
172
+ limit: 2,
173
+ runner: async ({ target }) => {
174
+ calls.push(target);
175
+ return evidence();
176
+ },
177
+ });
178
+
179
+ assert.deepEqual(calls, ["a.ts", "b.ts"]);
180
+ assert.equal(result.summary.requested, 4);
181
+ assert.equal(result.summary.eligible, 3);
182
+ assert.equal(result.summary.analyzed, 2);
183
+ assert.equal(result.summary.omitted, 1);
184
+ });
185
+
186
+ test("default local pattern runner loads context data once across targets", async () => {
187
+ const document = (id, filePath) => ({
188
+ id,
189
+ path: filePath,
190
+ kind: "CODE",
191
+ updated_at: "2026-07-12T00:00:00.000Z",
192
+ source_of_truth: false,
193
+ trust_level: 60,
194
+ status: "active",
195
+ excerpt: "export const value = 1;",
196
+ content: "export const value = 1;",
197
+ });
198
+ const data = {
199
+ documents: [document("file:src/a.ts", "src/a.ts"), document("file:src/b.ts", "src/b.ts")],
200
+ chunks: [],
201
+ rules: [],
202
+ adrs: [],
203
+ modules: [],
204
+ projects: [],
205
+ relations: [],
206
+ ranking: { semantic: 0.4, graph: 0.25, trust: 0.2, recency: 0.15 },
207
+ source: "cache",
208
+ };
209
+ let loads = 0;
210
+ const runner = createLocalPatternRunner(async () => {
211
+ loads += 1;
212
+ return data;
213
+ });
214
+
215
+ await runner({ target: "src/a.ts", top_k: 1 });
216
+ await runner({ target: "src/b.ts", top_k: 1 });
217
+
218
+ assert.equal(loads, 1);
219
+ });
220
+
221
+ test("disabled mode does not invoke pattern retrieval", async () => {
222
+ let called = false;
223
+ const result = await buildPatternReviewContext({
224
+ files: ["a.ts", "b.ts"],
225
+ enabled: false,
226
+ topK: 2,
227
+ limit: 10,
228
+ runner: async () => {
229
+ called = true;
230
+ return evidence();
231
+ },
232
+ });
233
+
234
+ assert.equal(called, false);
235
+ assert.equal(result.enabled, false);
236
+ assert.equal(result.affects_policy_summary, false);
237
+ assert.equal(result.summary.requested, 2);
238
+ assert.deepEqual(result.targets, []);
239
+ });
240
+
241
+ test("context.review includes pattern context without changing validator summary", async () => {
242
+ const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "cortex-pattern-review-tool-"));
243
+ const contextDir = path.join(projectRoot, ".context");
244
+ const previousProjectRoot = process.env.CORTEX_PROJECT_ROOT;
245
+ fs.mkdirSync(path.join(projectRoot, "src"), { recursive: true });
246
+ fs.writeFileSync(path.join(projectRoot, ".gitignore"), ".context/\nignored.ts\n", "utf8");
247
+ execFileSync("git", ["init"], { cwd: projectRoot, stdio: "ignore" });
248
+ execFileSync("git", ["add", ".gitignore"], { cwd: projectRoot, stdio: "ignore" });
249
+ execFileSync("git", ["-c", "user.name=Cortex Test", "-c", "user.email=cortex@example.invalid", "commit", "-m", "fixture"], {
250
+ cwd: projectRoot,
251
+ stdio: "ignore",
252
+ });
253
+ fs.mkdirSync(contextDir, { recursive: true });
254
+ fs.writeFileSync(path.join(projectRoot, "src", "new-file.ts"), "export const value = 1;\n", "utf8");
255
+ fs.writeFileSync(path.join(projectRoot, "ignored.ts"), "ignored\n", "utf8");
256
+ process.env.CORTEX_PROJECT_ROOT = projectRoot;
257
+
258
+ try {
259
+ const tools = new Map();
260
+ const server = {
261
+ registerTool(name, definition, handler) {
262
+ tools.set(name, { definition, handler });
263
+ },
264
+ };
265
+ const config = {
266
+ enterprise: { endpoint: "", api_key: "", base_url: "" },
267
+ telemetry: { enabled: false, endpoint: "", api_key: "", interval_minutes: 10 },
268
+ audit: { enabled: false, retention_days: 90 },
269
+ policy: { enabled: false, endpoint: "", api_key: "", sync_interval_minutes: 240 },
270
+ rbac: { enabled: false, default_role: "developer" },
271
+ validators: {},
272
+ compliance: { frameworks: [], eu_addons: false },
273
+ govern: {
274
+ mode: "off",
275
+ sync_on_startup: false,
276
+ sync_interval_minutes: 60,
277
+ tier_claude: "prevent",
278
+ tier_codex: "prevent",
279
+ tier_copilot: "wrap",
280
+ detect_ungoverned: false,
281
+ govern_endpoint: "",
282
+ },
283
+ };
284
+ registerEnterpriseTools(
285
+ server,
286
+ {},
287
+ null,
288
+ config,
289
+ contextDir,
290
+ { getMergedPolicies: () => [] },
291
+ "2.3.0",
292
+ );
293
+
294
+ const reviewTool = tools.get("context.review");
295
+ assert.ok(reviewTool);
296
+ const result = await reviewTool.handler({
297
+ scope: "changed",
298
+ include_passed: true,
299
+ });
300
+ const output = result.structuredContent;
301
+
302
+ assert.equal(output.summary.total, 0);
303
+ assert.equal("pattern_analyzed" in output.summary, false);
304
+ assert.equal(output.pattern_review.enabled, true);
305
+ assert.equal(output.pattern_review.affects_policy_summary, false);
306
+ assert.equal(output.pattern_review.limit, 10);
307
+ assert.equal(output.pattern_review.top_k_per_tier, 2);
308
+ assert.equal(output.pattern_review.summary.analyzed, 1);
309
+ assert.equal(output.pattern_review.targets[0].path, "src/new-file.ts");
310
+ assert.equal(output.pattern_review.targets[0].status, "not_indexed");
311
+ assert.equal(output.pattern_review.targets[0].local_pattern_found, false);
312
+ assert.equal(output.pattern_review.targets[0].fallback_used, false);
313
+ assert.equal(output.pattern_review.targets[0].tiers.length, 4);
314
+ } finally {
315
+ if (previousProjectRoot === undefined) {
316
+ delete process.env.CORTEX_PROJECT_ROOT;
317
+ } else {
318
+ process.env.CORTEX_PROJECT_ROOT = previousProjectRoot;
319
+ }
320
+ fs.rmSync(projectRoot, { recursive: true, force: true });
321
+ }
322
+ });
@@ -4,13 +4,15 @@ import { mkdtempSync, mkdirSync, realpathSync, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import path from "node:path";
6
6
  import { spawnSync } from "node:child_process";
7
- import { pathToFileURL } from "node:url";
7
+ import { fileURLToPath, pathToFileURL } from "node:url";
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
10
 
9
11
  test("paths resolve the project root from cwd without duplicating .context", () => {
10
12
  const projectRoot = mkdtempSync(path.join(tmpdir(), "cortex-paths-"));
11
13
  const contextDir = path.join(projectRoot, ".context");
12
14
  const mcpDir = path.join(contextDir, "mcp");
13
- const pathsModuleUrl = pathToFileURL(path.resolve("dist/paths.js")).href;
15
+ const pathsModuleUrl = pathToFileURL(path.resolve(__dirname, "..", "dist", "paths.js")).href;
14
16
 
15
17
  mkdirSync(mcpDir, { recursive: true });
16
18
  writeFileSync(path.join(contextDir, "config.yaml"), "source_paths:\n - src\n");
@@ -20,7 +22,7 @@ test("paths resolve the project root from cwd without duplicating .context", ()
20
22
  [
21
23
  "--input-type=module",
22
24
  "-e",
23
- `import { REPO_ROOT, CONTEXT_DIR, CACHE_DIR } from ${JSON.stringify(pathsModuleUrl)}; console.log(JSON.stringify({ REPO_ROOT, CONTEXT_DIR, CACHE_DIR }));`
25
+ `import { REPO_ROOT, CONTEXT_DIR, CACHE_DIR, DEFAULT_RANKING } from ${JSON.stringify(pathsModuleUrl)}; console.log(JSON.stringify({ REPO_ROOT, CONTEXT_DIR, CACHE_DIR, DEFAULT_RANKING }));`
24
26
  ],
25
27
  {
26
28
  cwd: mcpDir,
@@ -35,4 +37,10 @@ test("paths resolve the project root from cwd without duplicating .context", ()
35
37
  assert.equal(parsed.REPO_ROOT, resolvedProjectRoot);
36
38
  assert.equal(parsed.CONTEXT_DIR, path.join(resolvedProjectRoot, ".context"));
37
39
  assert.equal(parsed.CACHE_DIR, path.join(resolvedProjectRoot, ".context", "cache"));
40
+ assert.deepEqual(parsed.DEFAULT_RANKING, {
41
+ semantic: 0.4,
42
+ graph: 0.25,
43
+ trust: 0.2,
44
+ recency: 0.15
45
+ });
38
46
  });