@rigour-labs/mcp 6.5.3 → 6.6.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/dist/tools/context-handlers.js +2 -1
- package/dist/tools/execution-handlers.d.ts +2 -1
- package/dist/tools/execution-handlers.js +8 -7
- package/dist/tools/index-handlers.js +3 -43
- package/dist/tools/memory-handlers.js +1 -1
- package/dist/tools/memory-recall.js +2 -1
- package/dist/tools/pattern-handlers.js +3 -1
- package/dist/tools/review-handler.js +4 -2
- package/package.json +2 -2
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import fs from 'fs-extra';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { createHash } from 'crypto';
|
|
14
|
-
import { getTaskContextStats, getTaskCostStats, getCacheStats, explainContext, getSemanticQueryCache, setSemanticQueryCache, findRelatedSemanticQueryCache, filterExistingEditScope, estimateTokenCount, ensureAutomaticIndex, getApplicableLessons, searchTeamKnowledge, recordScopeOffer, } from '@rigour-labs/core';
|
|
14
|
+
import { getTaskContextStats, getTaskCostStats, getCacheStats, explainContext, getSemanticQueryCache, setSemanticQueryCache, findRelatedSemanticQueryCache, filterExistingEditScope, estimateTokenCount, ensureAutomaticIndex, getApplicableLessons, recordLessonsServed, searchTeamKnowledge, recordScopeOffer, } from '@rigour-labs/core';
|
|
15
15
|
import { loadPatternIndex, getDefaultIndexPath, generateEmbedding, semanticSearch, } from '@rigour-labs/core/pattern-index';
|
|
16
16
|
import { buildTelemetryMeta, getWorkspaceCommitSha } from '../utils/context-telemetry.js';
|
|
17
17
|
import { appendContextFooter } from '../utils/context-footer.js';
|
|
@@ -262,6 +262,7 @@ export async function handleContextScope(cwd, query, limit = 10, taskId, agentId
|
|
|
262
262
|
getApplicableLessons(cwd, agentId, process.env.RIGOUR_TEAM_ID),
|
|
263
263
|
searchTeamKnowledge(query, Math.min(limit, 8)),
|
|
264
264
|
]);
|
|
265
|
+
recordLessonsServed(cwd, 'context', applicableLearning.lessons.map(lesson => lesson.subject));
|
|
265
266
|
let fullFileTokens = 0;
|
|
266
267
|
const offered = [];
|
|
267
268
|
for (const file of editScope) {
|
|
@@ -19,6 +19,7 @@ export declare function handleRunSupervised(opts: {
|
|
|
19
19
|
}): Promise<ToolResult>;
|
|
20
20
|
/**
|
|
21
21
|
* Fail-closed: timeout with no human decision → timeout-deny (not approve).
|
|
22
|
+
* Only a decision Studio signed with this request's token counts; any other line is ignored.
|
|
22
23
|
*/
|
|
23
|
-
export declare function pollArbitration(cwd: string, rid: string, timeout: number): Promise<string | null>;
|
|
24
|
+
export declare function pollArbitration(cwd: string, rid: string, token: string, timeout: number): Promise<string | null>;
|
|
24
25
|
export {};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import fs from "fs-extra";
|
|
8
8
|
import path from "path";
|
|
9
|
-
import { FixPacketService, runTypedCommand, evaluateTypedCommand, issueArbitrationToken, } from "@rigour-labs/core";
|
|
9
|
+
import { FixPacketService, runTypedCommand, evaluateTypedCommand, issueArbitrationToken, verifyArbitrationDecision, } from "@rigour-labs/core";
|
|
10
10
|
import { logStudioEvent } from '../utils/config.js';
|
|
11
11
|
import { notifyProgress } from '../utils/notifications.js';
|
|
12
12
|
async function requireHumanApproval(cwd, command, requestId, tool) {
|
|
@@ -16,10 +16,9 @@ async function requireHumanApproval(cwd, command, requestId, tool) {
|
|
|
16
16
|
requestId,
|
|
17
17
|
tool,
|
|
18
18
|
command,
|
|
19
|
-
arbitrationToken,
|
|
20
19
|
});
|
|
21
20
|
console.error(`[RIGOUR] Waiting for human arbitration for command: ${command}`);
|
|
22
|
-
const decision = await pollArbitration(cwd, requestId, 60000);
|
|
21
|
+
const decision = await pollArbitration(cwd, requestId, arbitrationToken, 60000);
|
|
23
22
|
if (decision === 'approve')
|
|
24
23
|
return null;
|
|
25
24
|
await logStudioEvent(cwd, {
|
|
@@ -230,19 +229,20 @@ function formatFixPacketForSupervisor(fixPacket) {
|
|
|
230
229
|
}
|
|
231
230
|
/**
|
|
232
231
|
* Fail-closed: timeout with no human decision → timeout-deny (not approve).
|
|
232
|
+
* Only a decision Studio signed with this request's token counts; any other line is ignored.
|
|
233
233
|
*/
|
|
234
|
-
export async function pollArbitration(cwd, rid, timeout) {
|
|
234
|
+
export async function pollArbitration(cwd, rid, token, timeout) {
|
|
235
235
|
const eventsPath = path.join(cwd, '.rigour/events.jsonl');
|
|
236
236
|
const maxIterations = Math.max(1, Math.ceil(timeout / 1000));
|
|
237
237
|
for (let i = 0; i < maxIterations; i++) {
|
|
238
|
-
const found = await readArbitrationDecision(eventsPath, rid);
|
|
238
|
+
const found = await readArbitrationDecision(eventsPath, rid, token);
|
|
239
239
|
if (found)
|
|
240
240
|
return found;
|
|
241
241
|
await sleepMs(1000);
|
|
242
242
|
}
|
|
243
243
|
return "timeout-deny";
|
|
244
244
|
}
|
|
245
|
-
async function readArbitrationDecision(eventsPath, rid) {
|
|
245
|
+
async function readArbitrationDecision(eventsPath, rid, token) {
|
|
246
246
|
if (!(await fs.pathExists(eventsPath)))
|
|
247
247
|
return null;
|
|
248
248
|
const content = await fs.readFile(eventsPath, 'utf-8');
|
|
@@ -255,7 +255,8 @@ async function readArbitrationDecision(eventsPath, rid) {
|
|
|
255
255
|
catch {
|
|
256
256
|
continue;
|
|
257
257
|
}
|
|
258
|
-
if (event.tool === 'human_arbitration' && event.requestId === rid
|
|
258
|
+
if (event.tool === 'human_arbitration' && event.requestId === rid
|
|
259
|
+
&& verifyArbitrationDecision(token, rid, event.decision, event.proof)) {
|
|
259
260
|
return event.decision;
|
|
260
261
|
}
|
|
261
262
|
}
|
|
@@ -3,50 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Wraps PatternIndexer for agent-accessible index build/update.
|
|
5
5
|
*/
|
|
6
|
-
import path from 'path';
|
|
7
6
|
import { PatternIndexer, savePatternIndex, loadPatternIndex, getDefaultIndexPath, } from '@rigour-labs/core/pattern-index';
|
|
8
|
-
import {
|
|
7
|
+
import { recordIndexChoice } from '@rigour-labs/core';
|
|
9
8
|
import { notifyProgress } from '../utils/notifications.js';
|
|
10
|
-
import { buildTelemetryMeta
|
|
9
|
+
import { buildTelemetryMeta } from '../utils/context-telemetry.js';
|
|
11
10
|
import { appendContextFooter } from '../utils/context-footer.js';
|
|
12
|
-
import fs from 'fs-extra';
|
|
13
|
-
async function syncIndexToCache(cwd, index) {
|
|
14
|
-
const commitSha = await getWorkspaceCommitSha(cwd);
|
|
15
|
-
const repo = path.basename(cwd);
|
|
16
|
-
const byFile = new Map();
|
|
17
|
-
for (const pattern of index.patterns) {
|
|
18
|
-
const list = byFile.get(pattern.file) ?? [];
|
|
19
|
-
list.push(pattern);
|
|
20
|
-
byFile.set(pattern.file, list);
|
|
21
|
-
}
|
|
22
|
-
for (const [filePath, patterns] of byFile) {
|
|
23
|
-
const absPath = path.join(cwd, filePath);
|
|
24
|
-
let content = '';
|
|
25
|
-
try {
|
|
26
|
-
if (await fs.pathExists(absPath)) {
|
|
27
|
-
content = await fs.readFile(absPath, 'utf-8');
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
content = patterns.map(p => p.signature ?? p.name).join('\n');
|
|
32
|
-
}
|
|
33
|
-
await setStaticCache(repo, 'main', filePath, content || filePath, {
|
|
34
|
-
exports: patterns.map(p => p.name),
|
|
35
|
-
rigourPatterns: patterns.map(p => `${p.type}:${p.name}`),
|
|
36
|
-
ownership: path.dirname(filePath),
|
|
37
|
-
}, cwd);
|
|
38
|
-
const componentName = path.dirname(filePath) || filePath;
|
|
39
|
-
const dossier = {
|
|
40
|
-
component: componentName,
|
|
41
|
-
responsibility: `Indexed patterns in ${filePath}`,
|
|
42
|
-
canonicalFiles: [filePath],
|
|
43
|
-
contracts: patterns.filter(p => p.type === 'interface' || p.type === 'type').map(p => p.name),
|
|
44
|
-
directConsumers: [],
|
|
45
|
-
validationCommands: [],
|
|
46
|
-
};
|
|
47
|
-
await setComponentCache(componentName, commitSha, dossier, `index-${index.lastUpdated}`, '3', cwd);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
11
|
export async function handleIndex(cwd, options = {}) {
|
|
51
12
|
const indexPath = options.output || getDefaultIndexPath(cwd);
|
|
52
13
|
const candidateEstimate = 'Full codebase AST scan for pattern extraction';
|
|
@@ -63,7 +24,6 @@ export async function handleIndex(cwd, options = {}) {
|
|
|
63
24
|
index = await indexer.buildIndex();
|
|
64
25
|
}
|
|
65
26
|
await savePatternIndex(index, indexPath);
|
|
66
|
-
await syncIndexToCache(cwd, index);
|
|
67
27
|
await recordIndexChoice(cwd, index, semantic);
|
|
68
28
|
notifyProgress('info', 'Pattern index complete');
|
|
69
29
|
const byType = Object.entries(index.stats.byType)
|
|
@@ -76,7 +36,7 @@ export async function handleIndex(cwd, options = {}) {
|
|
|
76
36
|
text += `- Duration: ${index.stats.indexDurationMs}ms\n`;
|
|
77
37
|
text += `- Search by meaning: ${semantic ? (index.patterns.some(p => p.embedding?.length) ? 'on' : 'unavailable (local model did not load; name matching still works)') : 'off'}\n`;
|
|
78
38
|
text += `- Types: ${byType}\n\n`;
|
|
79
|
-
text += `
|
|
39
|
+
text += `Use rigour_context_scope before reading files.`;
|
|
80
40
|
const telemetry = buildTelemetryMeta({
|
|
81
41
|
candidateText: candidateEstimate,
|
|
82
42
|
returnedText: text,
|
|
@@ -145,7 +145,7 @@ async function shareWithTeam(cwd, key, value) {
|
|
|
145
145
|
if (!(await loadTeamConfiguration())) {
|
|
146
146
|
return '\nKept as a team candidate on this machine; it is shared once team mode is configured (rigour team configure).';
|
|
147
147
|
}
|
|
148
|
-
return '\nShared with your team as a candidate. Teammates\' agents receive it once someone
|
|
148
|
+
return '\nShared with your team as a candidate. Teammates\' agents receive it once someone shares it from Studio (How it learns).';
|
|
149
149
|
}
|
|
150
150
|
export async function handleForget(cwd, key, scope = 'repo') {
|
|
151
151
|
const store = await loadMemory(cwd, scope);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Every returned memory passes the credential scan first; nothing is cached.
|
|
12
12
|
*/
|
|
13
|
-
import { formatDLPAlert, createDLPAuditEntry, listKnowledgeLessons, rankByMeaning, rankMemories, scanInputForCredentials, searchTeamKnowledge } from '@rigour-labs/core';
|
|
13
|
+
import { formatDLPAlert, createDLPAuditEntry, listKnowledgeLessons, rankByMeaning, rankMemories, recordLessonsServed, scanInputForCredentials, searchTeamKnowledge } from '@rigour-labs/core';
|
|
14
14
|
import { loadMemory } from '../utils/config.js';
|
|
15
15
|
import { appendDLPAudit, getIndexHealthBlock, wrapRecallResult } from './memory-handlers.js';
|
|
16
16
|
export async function handleRecall(cwd, args = {}) {
|
|
@@ -51,6 +51,7 @@ async function recallQuery(cwd, query, entries, candidateText) {
|
|
|
51
51
|
searchTeamKnowledge(query, 3),
|
|
52
52
|
]);
|
|
53
53
|
const teamOnly = team.status === 'ready' ? team.candidates.filter(c => !lessons.some(l => l.id === c.lessonId)) : [];
|
|
54
|
+
recordLessonsServed(cwd, 'recall', [...lessons.map(l => l.subject), ...teamOnly.map(c => c.subject)]);
|
|
54
55
|
const sections = [];
|
|
55
56
|
if (ranked.length) {
|
|
56
57
|
sections.push(`MEMORIES MATCHING "${query}":\n\n` + ranked.map(m => `## ${m.key} (${m.scope}, ${m.match} ${m.score.toFixed(2)})\n${m.value}`).join('\n\n'));
|
|
@@ -9,7 +9,7 @@ import path from "path";
|
|
|
9
9
|
import yaml from "yaml";
|
|
10
10
|
import fs from "fs-extra";
|
|
11
11
|
import { assessPattern, loadPatternIndex, getDefaultIndexPath, SecurityDetector, } from "@rigour-labs/core/pattern-index";
|
|
12
|
-
import { ConfigSchema, estimateTokenCount } from "@rigour-labs/core";
|
|
12
|
+
import { ConfigSchema, estimateTokenCount, recordReuseSuggested } from "@rigour-labs/core";
|
|
13
13
|
import { notifyProgress } from '../utils/notifications.js';
|
|
14
14
|
import { buildTelemetryMeta } from '../utils/context-telemetry.js';
|
|
15
15
|
import { appendContextFooter } from '../utils/context-footer.js';
|
|
@@ -90,6 +90,8 @@ export async function handleCheckPattern(cwd, patternName, type, intent, file, s
|
|
|
90
90
|
resultText += action === 'BLOCK' ? `🚨 PATTERN REINVENTION DETECTED\n` : `💡 SIMILAR PATTERN EXISTS\n`;
|
|
91
91
|
resultText += `"${best.pattern.name}" in ${best.pattern.file}:${best.pattern.line} (${best.matchType}, ${best.confidence}%)\n`;
|
|
92
92
|
resultText += `SUGGESTION: ${assessment.suggestion}\n\n`;
|
|
93
|
+
if (action !== 'ALLOW')
|
|
94
|
+
recordReuseSuggested(cwd, patternName, `${best.pattern.name} in ${best.pattern.file}:${best.pattern.line}`, action);
|
|
93
95
|
}
|
|
94
96
|
if (assessment.deprecations.length) {
|
|
95
97
|
resultText += `⚠️ THE EXISTING "${matchedPattern?.name}" USES SOMETHING DEPRECATED\n`;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* With no diff, the change is taken from git: uncommitted work (what the
|
|
6
6
|
* agent just wrote, new files included), or the branch against `base`.
|
|
7
7
|
*/
|
|
8
|
-
import { acknowledgeReview, buildReviewTask, diffFromGit, recordFixLessons, recordReviewOutcome, reviewChange, toReviewFinding } from "@rigour-labs/core";
|
|
8
|
+
import { acknowledgeReview, buildReviewTask, diffFromGit, recordFixLessons, recordLessonsServed, recordReviewOutcome, reviewChange, toReviewFinding } from "@rigour-labs/core";
|
|
9
9
|
import { notifyProgress } from '../utils/notifications.js';
|
|
10
10
|
export async function handleReview(config, cwd, args) {
|
|
11
11
|
notifyProgress("info", args.diff ? "Reviewing the provided diff..." : `Reviewing ${args.base ? `this branch against ${args.base}` : "uncommitted changes"}...`);
|
|
@@ -13,7 +13,9 @@ export async function handleReview(config, cwd, args) {
|
|
|
13
13
|
const diff = args.diff ?? diffFromGit(cwd, args.base ? { mode: 'base', base: args.base } : { mode: 'working' });
|
|
14
14
|
const result = await reviewChange({ cwd, config, diff, files: args.files });
|
|
15
15
|
const task = args.mode === 'agent' ? buildReviewTask(cwd, diff, config.gates.deep?.router, config.gates.deep?.review_lessons, config.gates.deep?.repo_rules) : undefined;
|
|
16
|
-
const capture = recordReviewOutcome(cwd, result.findings, Object.keys(result.changedLines));
|
|
16
|
+
const capture = recordReviewOutcome(cwd, result.findings, Object.keys(result.changedLines), 'review');
|
|
17
|
+
if (task)
|
|
18
|
+
recordLessonsServed(cwd, 'review', task.lessons.map(l => l.text));
|
|
17
19
|
await recordFixLessons(cwd, capture.fixes).catch(() => undefined); // learning never fails a review
|
|
18
20
|
const stats = result.report?.stats;
|
|
19
21
|
return text({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/mcp",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.0",
|
|
4
4
|
"description": "MCP server + live dashboard for AI code governance — OWASP LLM Top 10 (10/10), real-time MCP App UI, 25+ security patterns, memory and lessons recalled by meaning, hallucinated import detection, multi-agent governance. Works with Claude, Cursor, VS Code, ChatGPT, Goose, Windsurf. Industry presets for HIPAA, SOC2, FedRAMP.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"execa": "^8.0.1",
|
|
66
66
|
"fs-extra": "^11.4.0",
|
|
67
67
|
"yaml": "^2.8.2",
|
|
68
|
-
"@rigour-labs/core": "6.
|
|
68
|
+
"@rigour-labs/core": "6.6.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.0.3",
|