ghagga-core 2.0.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/README.md +51 -0
- package/dist/agents/consensus.d.ts +68 -0
- package/dist/agents/consensus.d.ts.map +1 -0
- package/dist/agents/consensus.js +216 -0
- package/dist/agents/consensus.js.map +1 -0
- package/dist/agents/prompts.d.ts +18 -0
- package/dist/agents/prompts.d.ts.map +1 -0
- package/dist/agents/prompts.js +194 -0
- package/dist/agents/prompts.js.map +1 -0
- package/dist/agents/simple.d.ts +49 -0
- package/dist/agents/simple.d.ts.map +1 -0
- package/dist/agents/simple.js +135 -0
- package/dist/agents/simple.js.map +1 -0
- package/dist/agents/workflow.d.ts +40 -0
- package/dist/agents/workflow.d.ts.map +1 -0
- package/dist/agents/workflow.js +127 -0
- package/dist/agents/workflow.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/context.d.ts +23 -0
- package/dist/memory/context.d.ts.map +1 -0
- package/dist/memory/context.js +36 -0
- package/dist/memory/context.js.map +1 -0
- package/dist/memory/persist.d.ts +22 -0
- package/dist/memory/persist.d.ts.map +1 -0
- package/dist/memory/persist.js +103 -0
- package/dist/memory/persist.js.map +1 -0
- package/dist/memory/privacy.d.ts +19 -0
- package/dist/memory/privacy.d.ts.map +1 -0
- package/dist/memory/privacy.js +77 -0
- package/dist/memory/privacy.js.map +1 -0
- package/dist/memory/search.d.ts +20 -0
- package/dist/memory/search.d.ts.map +1 -0
- package/dist/memory/search.js +76 -0
- package/dist/memory/search.js.map +1 -0
- package/dist/pipeline.d.ts +30 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +267 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/providers/fallback.d.ts +46 -0
- package/dist/providers/fallback.d.ts.map +1 -0
- package/dist/providers/fallback.js +84 -0
- package/dist/providers/fallback.js.map +1 -0
- package/dist/providers/index.d.ts +40 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +76 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/tools/cpd.d.ts +24 -0
- package/dist/tools/cpd.d.ts.map +1 -0
- package/dist/tools/cpd.js +130 -0
- package/dist/tools/cpd.js.map +1 -0
- package/dist/tools/runner.d.ts +19 -0
- package/dist/tools/runner.d.ts.map +1 -0
- package/dist/tools/runner.js +61 -0
- package/dist/tools/runner.js.map +1 -0
- package/dist/tools/semgrep.d.ts +12 -0
- package/dist/tools/semgrep.d.ts.map +1 -0
- package/dist/tools/semgrep.js +97 -0
- package/dist/tools/semgrep.js.map +1 -0
- package/dist/tools/trivy.d.ts +11 -0
- package/dist/tools/trivy.d.ts.map +1 -0
- package/dist/tools/trivy.js +74 -0
- package/dist/tools/trivy.js.map +1 -0
- package/dist/types.d.ts +168 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/diff.d.ts +53 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +103 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/stack-detect.d.ts +15 -0
- package/dist/utils/stack-detect.d.ts.map +1 -0
- package/dist/utils/stack-detect.js +54 -0
- package/dist/utils/stack-detect.js.map +1 -0
- package/dist/utils/token-budget.d.ts +31 -0
- package/dist/utils/token-budget.d.ts.map +1 -0
- package/dist/utils/token-budget.js +62 -0
- package/dist/utils/token-budget.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple review agent.
|
|
3
|
+
*
|
|
4
|
+
* Runs a single LLM call with the full diff and context.
|
|
5
|
+
* Best for small-to-medium PRs where parallel specialists
|
|
6
|
+
* would be overkill.
|
|
7
|
+
*/
|
|
8
|
+
import { generateText } from 'ai';
|
|
9
|
+
import { createModel } from '../providers/index.js';
|
|
10
|
+
import { SIMPLE_REVIEW_SYSTEM, buildMemoryContext, } from './prompts.js';
|
|
11
|
+
// ─── Response Parsing ───────────────────────────────────────────
|
|
12
|
+
/** Valid severity values for type-safe parsing */
|
|
13
|
+
const VALID_SEVERITIES = new Set([
|
|
14
|
+
'critical',
|
|
15
|
+
'high',
|
|
16
|
+
'medium',
|
|
17
|
+
'low',
|
|
18
|
+
'info',
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* Parse the structured LLM response into a ReviewResult.
|
|
22
|
+
*
|
|
23
|
+
* Extracts STATUS, SUMMARY, and FINDINGS sections using regex
|
|
24
|
+
* patterns that match the format defined in SIMPLE_REVIEW_SYSTEM.
|
|
25
|
+
*/
|
|
26
|
+
function parseReviewResponse(text, provider, model, tokensUsed, executionTimeMs, memoryContext) {
|
|
27
|
+
// Extract STATUS
|
|
28
|
+
const statusMatch = /STATUS:\s*(PASSED|FAILED|NEEDS_HUMAN_REVIEW|SKIPPED)/i.exec(text);
|
|
29
|
+
const status = statusMatch?.[1]?.toUpperCase() ?? 'NEEDS_HUMAN_REVIEW';
|
|
30
|
+
// Extract SUMMARY
|
|
31
|
+
const summaryMatch = /SUMMARY:\s*(.+?)(?:\n(?:FINDINGS:|$))/is.exec(text);
|
|
32
|
+
const summary = summaryMatch?.[1]?.trim() ?? 'Review completed but summary could not be parsed.';
|
|
33
|
+
// Extract FINDINGS
|
|
34
|
+
const findings = parseFindingsBlock(text);
|
|
35
|
+
return {
|
|
36
|
+
status,
|
|
37
|
+
summary,
|
|
38
|
+
findings,
|
|
39
|
+
staticAnalysis: {
|
|
40
|
+
semgrep: { status: 'skipped', findings: [], executionTimeMs: 0 },
|
|
41
|
+
trivy: { status: 'skipped', findings: [], executionTimeMs: 0 },
|
|
42
|
+
cpd: { status: 'skipped', findings: [], executionTimeMs: 0 },
|
|
43
|
+
},
|
|
44
|
+
memoryContext,
|
|
45
|
+
metadata: {
|
|
46
|
+
mode: 'simple',
|
|
47
|
+
provider,
|
|
48
|
+
model,
|
|
49
|
+
tokensUsed,
|
|
50
|
+
executionTimeMs,
|
|
51
|
+
toolsRun: [],
|
|
52
|
+
toolsSkipped: [],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse the FINDINGS block from the LLM response.
|
|
58
|
+
*
|
|
59
|
+
* Each finding follows this format:
|
|
60
|
+
* - SEVERITY: critical
|
|
61
|
+
* CATEGORY: security
|
|
62
|
+
* FILE: src/auth.ts
|
|
63
|
+
* LINE: 42
|
|
64
|
+
* MESSAGE: SQL injection vulnerability
|
|
65
|
+
* SUGGESTION: Use parameterized queries
|
|
66
|
+
*/
|
|
67
|
+
function parseFindingsBlock(text) {
|
|
68
|
+
const findings = [];
|
|
69
|
+
// Match each finding block
|
|
70
|
+
const findingPattern = /- SEVERITY:\s*(\S+)\s*\n\s*CATEGORY:\s*(\S+)\s*\n\s*FILE:\s*(.+?)\s*\n\s*LINE:\s*(.+?)\s*\n\s*MESSAGE:\s*(.+?)\s*\n\s*SUGGESTION:\s*(.+?)(?=\n\s*- SEVERITY:|\n*$)/gis;
|
|
71
|
+
let match;
|
|
72
|
+
while ((match = findingPattern.exec(text)) !== null) {
|
|
73
|
+
const rawSeverity = match[1].toLowerCase();
|
|
74
|
+
const severity = VALID_SEVERITIES.has(rawSeverity) ? rawSeverity : 'info';
|
|
75
|
+
const lineStr = match[4].trim();
|
|
76
|
+
const line = lineStr === 'N/A' ? undefined : parseInt(lineStr, 10) || undefined;
|
|
77
|
+
findings.push({
|
|
78
|
+
severity,
|
|
79
|
+
category: match[2].trim().toLowerCase(),
|
|
80
|
+
file: match[3].trim(),
|
|
81
|
+
line,
|
|
82
|
+
message: match[5].trim(),
|
|
83
|
+
suggestion: match[6].trim(),
|
|
84
|
+
source: 'ai',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return findings;
|
|
88
|
+
}
|
|
89
|
+
// ─── Main Function ──────────────────────────────────────────────
|
|
90
|
+
/**
|
|
91
|
+
* Run a simple (single-pass) code review.
|
|
92
|
+
*
|
|
93
|
+
* Combines the system prompt with all context layers (static analysis,
|
|
94
|
+
* memory, stack hints) and the diff into a single LLM call.
|
|
95
|
+
*
|
|
96
|
+
* @param input - Review input with diff, provider config, and context
|
|
97
|
+
* @returns Parsed ReviewResult
|
|
98
|
+
*/
|
|
99
|
+
export async function runSimpleReview(input) {
|
|
100
|
+
const { diff, provider, model, apiKey, staticContext, memoryContext, stackHints } = input;
|
|
101
|
+
const emit = input.onProgress ?? (() => { });
|
|
102
|
+
const startTime = Date.now();
|
|
103
|
+
// Build the full system prompt with all context layers
|
|
104
|
+
const system = [
|
|
105
|
+
SIMPLE_REVIEW_SYSTEM,
|
|
106
|
+
staticContext,
|
|
107
|
+
buildMemoryContext(memoryContext),
|
|
108
|
+
stackHints,
|
|
109
|
+
]
|
|
110
|
+
.filter(Boolean)
|
|
111
|
+
.join('\n');
|
|
112
|
+
// Build the user prompt with the diff
|
|
113
|
+
const prompt = `Please review the following code changes:\n\n\`\`\`diff\n${diff}\n\`\`\``;
|
|
114
|
+
const languageModel = createModel(provider, model, apiKey);
|
|
115
|
+
emit({
|
|
116
|
+
step: 'simple-call',
|
|
117
|
+
message: `Calling ${provider}/${model} for single-pass review...`,
|
|
118
|
+
});
|
|
119
|
+
const result = await generateText({
|
|
120
|
+
model: languageModel,
|
|
121
|
+
system,
|
|
122
|
+
prompt,
|
|
123
|
+
temperature: 0.3,
|
|
124
|
+
});
|
|
125
|
+
const executionTimeMs = Date.now() - startTime;
|
|
126
|
+
const tokensUsed = (result.usage?.promptTokens ?? 0) + (result.usage?.completionTokens ?? 0);
|
|
127
|
+
emit({
|
|
128
|
+
step: 'simple-done',
|
|
129
|
+
message: `Review complete — ${tokensUsed} tokens, ${(executionTimeMs / 1000).toFixed(1)}s`,
|
|
130
|
+
});
|
|
131
|
+
return parseReviewResponse(result.text, provider, model, tokensUsed, executionTimeMs, memoryContext);
|
|
132
|
+
}
|
|
133
|
+
// Re-export the parser for use in workflow and consensus modes
|
|
134
|
+
export { parseReviewResponse, parseFindingsBlock };
|
|
135
|
+
//# sourceMappingURL=simple.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simple.js","sourceRoot":"","sources":["../../src/agents/simple.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow review agent (multi-specialist).
|
|
3
|
+
*
|
|
4
|
+
* Runs 5 specialist reviewers in parallel, then synthesizes their
|
|
5
|
+
* findings into a single unified review. Best for medium-to-large PRs
|
|
6
|
+
* where different aspects need focused attention.
|
|
7
|
+
*
|
|
8
|
+
* Specialists:
|
|
9
|
+
* 1. Scope Analysis — what changed, what's affected
|
|
10
|
+
* 2. Coding Standards — naming, formatting, DRY
|
|
11
|
+
* 3. Error Handling — null safety, edge cases, exceptions
|
|
12
|
+
* 4. Security Audit — injection, XSS, auth, data exposure
|
|
13
|
+
* 5. Performance — complexity, N+1, memory, resources
|
|
14
|
+
*
|
|
15
|
+
* After all specialists complete, a synthesis step merges and
|
|
16
|
+
* deduplicates findings into the final STATUS/SUMMARY/FINDINGS.
|
|
17
|
+
*/
|
|
18
|
+
import type { LLMProvider, ProgressCallback, ReviewResult } from '../types.js';
|
|
19
|
+
export interface WorkflowReviewInput {
|
|
20
|
+
diff: string;
|
|
21
|
+
provider: LLMProvider;
|
|
22
|
+
model: string;
|
|
23
|
+
apiKey: string;
|
|
24
|
+
staticContext: string;
|
|
25
|
+
memoryContext: string | null;
|
|
26
|
+
stackHints: string;
|
|
27
|
+
onProgress?: ProgressCallback;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run a workflow (multi-specialist) code review.
|
|
31
|
+
*
|
|
32
|
+
* 1. Launch 5 specialist reviews in parallel with Promise.allSettled
|
|
33
|
+
* 2. Collect all specialist outputs (including failures)
|
|
34
|
+
* 3. Run a synthesis step to merge findings into a unified review
|
|
35
|
+
*
|
|
36
|
+
* @param input - Review input with diff, provider config, and context
|
|
37
|
+
* @returns Parsed ReviewResult from the synthesis step
|
|
38
|
+
*/
|
|
39
|
+
export declare function runWorkflowReview(input: WorkflowReviewInput): Promise<ReviewResult>;
|
|
40
|
+
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/agents/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EAEb,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAoBD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkHzF"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow review agent (multi-specialist).
|
|
3
|
+
*
|
|
4
|
+
* Runs 5 specialist reviewers in parallel, then synthesizes their
|
|
5
|
+
* findings into a single unified review. Best for medium-to-large PRs
|
|
6
|
+
* where different aspects need focused attention.
|
|
7
|
+
*
|
|
8
|
+
* Specialists:
|
|
9
|
+
* 1. Scope Analysis — what changed, what's affected
|
|
10
|
+
* 2. Coding Standards — naming, formatting, DRY
|
|
11
|
+
* 3. Error Handling — null safety, edge cases, exceptions
|
|
12
|
+
* 4. Security Audit — injection, XSS, auth, data exposure
|
|
13
|
+
* 5. Performance — complexity, N+1, memory, resources
|
|
14
|
+
*
|
|
15
|
+
* After all specialists complete, a synthesis step merges and
|
|
16
|
+
* deduplicates findings into the final STATUS/SUMMARY/FINDINGS.
|
|
17
|
+
*/
|
|
18
|
+
import { generateText } from 'ai';
|
|
19
|
+
import { createModel } from '../providers/index.js';
|
|
20
|
+
import { WORKFLOW_SCOPE_SYSTEM, WORKFLOW_STANDARDS_SYSTEM, WORKFLOW_ERRORS_SYSTEM, WORKFLOW_SECURITY_SYSTEM, WORKFLOW_PERFORMANCE_SYSTEM, WORKFLOW_SYNTHESIS_SYSTEM, buildMemoryContext, } from './prompts.js';
|
|
21
|
+
import { parseReviewResponse } from './simple.js';
|
|
22
|
+
// ─── Specialist Configuration ───────────────────────────────────
|
|
23
|
+
const SPECIALISTS = [
|
|
24
|
+
{ name: 'scope-analysis', label: 'Scope Analysis', system: WORKFLOW_SCOPE_SYSTEM },
|
|
25
|
+
{ name: 'coding-standards', label: 'Coding Standards', system: WORKFLOW_STANDARDS_SYSTEM },
|
|
26
|
+
{ name: 'error-handling', label: 'Error Handling', system: WORKFLOW_ERRORS_SYSTEM },
|
|
27
|
+
{ name: 'security-audit', label: 'Security Audit', system: WORKFLOW_SECURITY_SYSTEM },
|
|
28
|
+
{ name: 'performance-review', label: 'Performance', system: WORKFLOW_PERFORMANCE_SYSTEM },
|
|
29
|
+
];
|
|
30
|
+
// ─── Main Function ──────────────────────────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* Run a workflow (multi-specialist) code review.
|
|
33
|
+
*
|
|
34
|
+
* 1. Launch 5 specialist reviews in parallel with Promise.allSettled
|
|
35
|
+
* 2. Collect all specialist outputs (including failures)
|
|
36
|
+
* 3. Run a synthesis step to merge findings into a unified review
|
|
37
|
+
*
|
|
38
|
+
* @param input - Review input with diff, provider config, and context
|
|
39
|
+
* @returns Parsed ReviewResult from the synthesis step
|
|
40
|
+
*/
|
|
41
|
+
export async function runWorkflowReview(input) {
|
|
42
|
+
const { diff, provider, model, apiKey, staticContext, memoryContext, stackHints } = input;
|
|
43
|
+
const emit = input.onProgress ?? (() => { });
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
const languageModel = createModel(provider, model, apiKey);
|
|
46
|
+
emit({
|
|
47
|
+
step: 'workflow-start',
|
|
48
|
+
message: `Launching ${SPECIALISTS.length} specialist reviewers in parallel`,
|
|
49
|
+
detail: SPECIALISTS.map((s) => ` → ${s.label}`).join('\n'),
|
|
50
|
+
});
|
|
51
|
+
// Build the user prompt (same for all specialists)
|
|
52
|
+
const userPrompt = `Review the following code changes:\n\n\`\`\`diff\n${diff}\n\`\`\``;
|
|
53
|
+
// ── Step 1: Run all specialists in parallel ────────────────
|
|
54
|
+
const specialistPromises = SPECIALISTS.map(async (specialist) => {
|
|
55
|
+
const system = [
|
|
56
|
+
specialist.system,
|
|
57
|
+
staticContext,
|
|
58
|
+
buildMemoryContext(memoryContext),
|
|
59
|
+
stackHints,
|
|
60
|
+
]
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.join('\n');
|
|
63
|
+
const result = await generateText({
|
|
64
|
+
model: languageModel,
|
|
65
|
+
system,
|
|
66
|
+
prompt: userPrompt,
|
|
67
|
+
temperature: 0.3,
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
name: specialist.name,
|
|
71
|
+
label: specialist.label,
|
|
72
|
+
text: result.text,
|
|
73
|
+
tokensUsed: (result.usage?.promptTokens ?? 0) + (result.usage?.completionTokens ?? 0),
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
const results = await Promise.allSettled(specialistPromises);
|
|
77
|
+
// ── Step 2: Collect results ────────────────────────────────
|
|
78
|
+
let totalTokens = 0;
|
|
79
|
+
const specialistOutputs = [];
|
|
80
|
+
for (let i = 0; i < results.length; i++) {
|
|
81
|
+
const result = results[i];
|
|
82
|
+
const spec = SPECIALISTS[i];
|
|
83
|
+
if (result.status === 'fulfilled') {
|
|
84
|
+
totalTokens += result.value.tokensUsed;
|
|
85
|
+
specialistOutputs.push(`### ${result.value.label}\n\n${result.value.text}`);
|
|
86
|
+
emit({
|
|
87
|
+
step: `specialist-${spec.name}`,
|
|
88
|
+
message: `✓ ${spec.label} — ${result.value.tokensUsed} tokens`,
|
|
89
|
+
detail: result.value.text,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Include error information in synthesis so it's aware of gaps
|
|
94
|
+
specialistOutputs.push(`### [FAILED] Specialist\n\nThis specialist could not complete: ${String(result.reason)}`);
|
|
95
|
+
emit({
|
|
96
|
+
step: `specialist-${spec.name}`,
|
|
97
|
+
message: `✗ ${spec.label} — FAILED: ${String(result.reason)}`,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
emit({
|
|
102
|
+
step: 'workflow-synthesis',
|
|
103
|
+
message: `Synthesizing ${specialistOutputs.length} specialist outputs...`,
|
|
104
|
+
});
|
|
105
|
+
// ── Step 3: Synthesis ──────────────────────────────────────
|
|
106
|
+
const synthesisPrompt = [
|
|
107
|
+
'Below are the findings from 5 specialist reviewers. Synthesize them into a final review.\n',
|
|
108
|
+
...specialistOutputs,
|
|
109
|
+
'\n\n---\n\nNow provide the unified review in the required format.',
|
|
110
|
+
].join('\n\n');
|
|
111
|
+
const synthesisResult = await generateText({
|
|
112
|
+
model: languageModel,
|
|
113
|
+
system: WORKFLOW_SYNTHESIS_SYSTEM,
|
|
114
|
+
prompt: synthesisPrompt,
|
|
115
|
+
temperature: 0.3,
|
|
116
|
+
});
|
|
117
|
+
const synthesisTokens = (synthesisResult.usage?.promptTokens ?? 0) +
|
|
118
|
+
(synthesisResult.usage?.completionTokens ?? 0);
|
|
119
|
+
totalTokens += synthesisTokens;
|
|
120
|
+
const executionTimeMs = Date.now() - startTime;
|
|
121
|
+
// Parse the synthesis output using the same parser as simple mode
|
|
122
|
+
const reviewResult = parseReviewResponse(synthesisResult.text, provider, model, totalTokens, executionTimeMs, memoryContext);
|
|
123
|
+
// Override mode in metadata
|
|
124
|
+
reviewResult.metadata.mode = 'workflow';
|
|
125
|
+
return reviewResult;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/agents/workflow.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ghagga/core — Public API
|
|
3
|
+
*
|
|
4
|
+
* The core review engine for GHAGGA v2.
|
|
5
|
+
* This module re-exports the pipeline entry point and all public types.
|
|
6
|
+
*/
|
|
7
|
+
export { reviewPipeline } from './pipeline.js';
|
|
8
|
+
export type { ReviewInput, ReviewSettings, ReviewContext, ReviewMode, LLMProvider, ReviewLevel, ReviewResult, ReviewFinding, ReviewMetadata, ReviewStatus, FindingSeverity, FindingSource, StaticAnalysisResult, ToolResult, ToolStatus, WorkflowSpecialist, ConsensusStance, ConsensusVote, ObservationType, MemoryObservation, ProgressCallback, ProgressEvent, } from './types.js';
|
|
9
|
+
export { DEFAULT_SETTINGS, DEFAULT_MODELS } from './types.js';
|
|
10
|
+
export { detectStacks } from './utils/stack-detect.js';
|
|
11
|
+
export { parseDiffFiles, filterIgnoredFiles, truncateDiff } from './utils/diff.js';
|
|
12
|
+
export type { DiffFile } from './utils/diff.js';
|
|
13
|
+
export { getContextWindow, calculateTokenBudget } from './utils/token-budget.js';
|
|
14
|
+
export { createProvider, createModel } from './providers/index.js';
|
|
15
|
+
export { generateWithFallback } from './providers/fallback.js';
|
|
16
|
+
export type { FallbackOptions, FallbackResult } from './providers/fallback.js';
|
|
17
|
+
export { formatMemoryContext } from './memory/context.js';
|
|
18
|
+
export { stripPrivateData } from './memory/privacy.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAI/C,YAAY,EAEV,WAAW,EACX,cAAc,EACd,aAAa,EACb,UAAU,EACV,WAAW,EACX,WAAW,EAGX,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EAGb,oBAAoB,EACpB,UAAU,EACV,UAAU,EAGV,kBAAkB,EAClB,eAAe,EACf,aAAa,EAGb,eAAe,EACf,iBAAiB,EAGjB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAI9D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACnF,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAIjF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAI/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ghagga/core — Public API
|
|
3
|
+
*
|
|
4
|
+
* The core review engine for GHAGGA v2.
|
|
5
|
+
* This module re-exports the pipeline entry point and all public types.
|
|
6
|
+
*/
|
|
7
|
+
// ─── Pipeline ───────────────────────────────────────────────────
|
|
8
|
+
export { reviewPipeline } from './pipeline.js';
|
|
9
|
+
// ─── Constants ──────────────────────────────────────────────────
|
|
10
|
+
export { DEFAULT_SETTINGS, DEFAULT_MODELS } from './types.js';
|
|
11
|
+
// ─── Utilities (for advanced usage) ─────────────────────────────
|
|
12
|
+
export { detectStacks } from './utils/stack-detect.js';
|
|
13
|
+
export { parseDiffFiles, filterIgnoredFiles, truncateDiff } from './utils/diff.js';
|
|
14
|
+
export { getContextWindow, calculateTokenBudget } from './utils/token-budget.js';
|
|
15
|
+
// ─── Providers (for direct model access) ────────────────────────
|
|
16
|
+
export { createProvider, createModel } from './providers/index.js';
|
|
17
|
+
export { generateWithFallback } from './providers/fallback.js';
|
|
18
|
+
// ─── Memory (for custom memory integrations) ────────────────────
|
|
19
|
+
export { formatMemoryContext } from './memory/context.js';
|
|
20
|
+
export { stripPrivateData } from './memory/privacy.js';
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,mEAAmE;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAwC/C,mEAAmE;AAEnE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9D,mEAAmE;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEjF,mEAAmE;AAEnE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAG/D,mEAAmE;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format memory observations for prompt injection.
|
|
3
|
+
*
|
|
4
|
+
* Takes raw observation objects from the database and formats them
|
|
5
|
+
* into a human-readable context block that can be appended to
|
|
6
|
+
* agent system prompts.
|
|
7
|
+
*/
|
|
8
|
+
export interface ObservationForContext {
|
|
9
|
+
type: string;
|
|
10
|
+
title: string;
|
|
11
|
+
content: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Format an array of memory observations into a prompt-ready string.
|
|
15
|
+
*
|
|
16
|
+
* The output is structured with headers and bullet points so the LLM
|
|
17
|
+
* can easily parse and reference past knowledge during its review.
|
|
18
|
+
*
|
|
19
|
+
* @param observations - Array of observation objects with type, title, and content
|
|
20
|
+
* @returns Formatted string for prompt injection, or empty string if no observations
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatMemoryContext(observations: ObservationForContext[]): string;
|
|
23
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/memory/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,qBAAqB,EAAE,GACpC,MAAM,CAuBR"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format memory observations for prompt injection.
|
|
3
|
+
*
|
|
4
|
+
* Takes raw observation objects from the database and formats them
|
|
5
|
+
* into a human-readable context block that can be appended to
|
|
6
|
+
* agent system prompts.
|
|
7
|
+
*/
|
|
8
|
+
// ─── Main Function ──────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Format an array of memory observations into a prompt-ready string.
|
|
11
|
+
*
|
|
12
|
+
* The output is structured with headers and bullet points so the LLM
|
|
13
|
+
* can easily parse and reference past knowledge during its review.
|
|
14
|
+
*
|
|
15
|
+
* @param observations - Array of observation objects with type, title, and content
|
|
16
|
+
* @returns Formatted string for prompt injection, or empty string if no observations
|
|
17
|
+
*/
|
|
18
|
+
export function formatMemoryContext(observations) {
|
|
19
|
+
if (observations.length === 0)
|
|
20
|
+
return '';
|
|
21
|
+
const lines = [
|
|
22
|
+
'## Past Review Memory',
|
|
23
|
+
'',
|
|
24
|
+
'The following observations were learned from previous reviews of this project:',
|
|
25
|
+
'',
|
|
26
|
+
];
|
|
27
|
+
for (const obs of observations) {
|
|
28
|
+
lines.push(`### [${obs.type.toUpperCase()}] ${obs.title}`);
|
|
29
|
+
lines.push('');
|
|
30
|
+
lines.push(obs.content);
|
|
31
|
+
lines.push('');
|
|
32
|
+
}
|
|
33
|
+
lines.push('> Use these past observations to give more informed, context-aware reviews.', '> Do not repeat findings that match these known patterns unless the issue persists.');
|
|
34
|
+
return lines.join('\n');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/memory/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,mEAAmE;AAEnE;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAqC;IAErC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,KAAK,GAAa;QACtB,uBAAuB;QACvB,EAAE;QACF,gFAAgF;QAChF,EAAE;KACH,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,6EAA6E,EAC7E,qFAAqF,CACtF,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persist review observations to memory.
|
|
3
|
+
*
|
|
4
|
+
* After a review completes, this module extracts key findings
|
|
5
|
+
* and saves them as memory observations so future reviews of
|
|
6
|
+
* the same project can benefit from past context.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReviewResult } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Persist notable review findings as memory observations.
|
|
11
|
+
*
|
|
12
|
+
* Creates a memory session for the review, extracts significant
|
|
13
|
+
* findings, strips private data, and saves them via ghagga-db.
|
|
14
|
+
* Gracefully handles database errors without propagating them.
|
|
15
|
+
*
|
|
16
|
+
* @param db - Database instance (typed as unknown for loose coupling)
|
|
17
|
+
* @param project - Project identifier (e.g., "owner/repo")
|
|
18
|
+
* @param prNumber - Pull request number
|
|
19
|
+
* @param result - The completed review result
|
|
20
|
+
*/
|
|
21
|
+
export declare function persistReviewObservations(db: unknown, project: string, prNumber: number, result: ReviewResult): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=persist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAkC,MAAM,aAAa,CAAC;AAoChF;;;;;;;;;;;GAWG;AACH,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,OAAO,EACX,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persist review observations to memory.
|
|
3
|
+
*
|
|
4
|
+
* After a review completes, this module extracts key findings
|
|
5
|
+
* and saves them as memory observations so future reviews of
|
|
6
|
+
* the same project can benefit from past context.
|
|
7
|
+
*/
|
|
8
|
+
import { saveObservation, createMemorySession, endMemorySession } from 'ghagga-db';
|
|
9
|
+
import { stripPrivateData } from './privacy.js';
|
|
10
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Map a finding's category to an observation type.
|
|
13
|
+
* This determines how the finding is stored and retrieved.
|
|
14
|
+
*/
|
|
15
|
+
function findingToObservationType(finding) {
|
|
16
|
+
switch (finding.category) {
|
|
17
|
+
case 'security':
|
|
18
|
+
return 'discovery';
|
|
19
|
+
case 'bug':
|
|
20
|
+
return 'bugfix';
|
|
21
|
+
case 'performance':
|
|
22
|
+
return 'pattern';
|
|
23
|
+
case 'style':
|
|
24
|
+
case 'maintainability':
|
|
25
|
+
return 'pattern';
|
|
26
|
+
case 'error-handling':
|
|
27
|
+
return 'learning';
|
|
28
|
+
default:
|
|
29
|
+
return 'learning';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if a finding is significant enough to persist.
|
|
34
|
+
* We only save high and critical findings to avoid noise.
|
|
35
|
+
*/
|
|
36
|
+
function isSignificantFinding(finding) {
|
|
37
|
+
return finding.severity === 'critical' || finding.severity === 'high';
|
|
38
|
+
}
|
|
39
|
+
// ─── Main Function ──────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Persist notable review findings as memory observations.
|
|
42
|
+
*
|
|
43
|
+
* Creates a memory session for the review, extracts significant
|
|
44
|
+
* findings, strips private data, and saves them via ghagga-db.
|
|
45
|
+
* Gracefully handles database errors without propagating them.
|
|
46
|
+
*
|
|
47
|
+
* @param db - Database instance (typed as unknown for loose coupling)
|
|
48
|
+
* @param project - Project identifier (e.g., "owner/repo")
|
|
49
|
+
* @param prNumber - Pull request number
|
|
50
|
+
* @param result - The completed review result
|
|
51
|
+
*/
|
|
52
|
+
export async function persistReviewObservations(db, project, prNumber, result) {
|
|
53
|
+
try {
|
|
54
|
+
if (!db)
|
|
55
|
+
return;
|
|
56
|
+
const typedDb = db;
|
|
57
|
+
// Create a memory session for this review
|
|
58
|
+
const session = await createMemorySession(typedDb, { project, prNumber });
|
|
59
|
+
// Extract significant findings as observations
|
|
60
|
+
const significantFindings = result.findings.filter(isSignificantFinding);
|
|
61
|
+
for (const finding of significantFindings) {
|
|
62
|
+
const sanitizedMessage = stripPrivateData(finding.message);
|
|
63
|
+
const sanitizedSuggestion = finding.suggestion
|
|
64
|
+
? stripPrivateData(finding.suggestion)
|
|
65
|
+
: undefined;
|
|
66
|
+
const content = [
|
|
67
|
+
`[${finding.severity.toUpperCase()}] ${finding.category}`,
|
|
68
|
+
`File: ${finding.file}${finding.line ? `:${finding.line}` : ''}`,
|
|
69
|
+
`Issue: ${sanitizedMessage}`,
|
|
70
|
+
sanitizedSuggestion ? `Fix: ${sanitizedSuggestion}` : '',
|
|
71
|
+
]
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.join('\n');
|
|
74
|
+
await saveObservation(typedDb, {
|
|
75
|
+
sessionId: session.id,
|
|
76
|
+
project,
|
|
77
|
+
type: findingToObservationType(finding),
|
|
78
|
+
title: `${finding.category}: ${sanitizedMessage.slice(0, 80)}`,
|
|
79
|
+
content,
|
|
80
|
+
filePaths: [finding.file],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Save a summary observation for the overall review
|
|
84
|
+
if (significantFindings.length > 0) {
|
|
85
|
+
await saveObservation(typedDb, {
|
|
86
|
+
sessionId: session.id,
|
|
87
|
+
project,
|
|
88
|
+
type: 'decision',
|
|
89
|
+
title: `PR #${prNumber} review: ${result.status}`,
|
|
90
|
+
content: stripPrivateData(result.summary),
|
|
91
|
+
topicKey: `pr-${prNumber}-review`,
|
|
92
|
+
filePaths: significantFindings.map((f) => f.file),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// End the session with a summary
|
|
96
|
+
await endMemorySession(typedDb, session.id, `Review of PR #${prNumber}: ${result.status} with ${significantFindings.length} significant findings.`);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
// Memory persistence is optional — never let it break the pipeline
|
|
100
|
+
console.warn('[ghagga] Failed to persist review observations (non-fatal):', error instanceof Error ? error.message : String(error));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=persist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,mEAAmE;AAEnE;;;GAGG;AACH,SAAS,wBAAwB,CAAC,OAAsB;IACtD,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,UAAU;YACb,OAAO,WAAW,CAAC;QACrB,KAAK,KAAK;YACR,OAAO,QAAQ,CAAC;QAClB,KAAK,aAAa;YAChB,OAAO,SAAS,CAAC;QACnB,KAAK,OAAO,CAAC;QACb,KAAK,iBAAiB;YACpB,OAAO,SAAS,CAAC;QACnB,KAAK,gBAAgB;YACnB,OAAO,UAAU,CAAC;QACpB;YACE,OAAO,UAAU,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,OAAsB;IAClD,OAAO,OAAO,CAAC,QAAQ,KAAK,UAAU,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM,CAAC;AACxE,CAAC;AAED,mEAAmE;AAEnE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,EAAW,EACX,OAAe,EACf,QAAgB,EAChB,MAAoB;IAEpB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE;YAAE,OAAO;QAEhB,MAAM,OAAO,GAAG,EAA2C,CAAC;QAE5D,0CAA0C;QAC1C,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QAE1E,+CAA+C;QAC/C,MAAM,mBAAmB,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAEzE,KAAK,MAAM,OAAO,IAAI,mBAAmB,EAAE,CAAC;YAC1C,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC3D,MAAM,mBAAmB,GAAG,OAAO,CAAC,UAAU;gBAC5C,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC;gBACtC,CAAC,CAAC,SAAS,CAAC;YAEd,MAAM,OAAO,GAAG;gBACd,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,QAAQ,EAAE;gBACzD,SAAS,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBAChE,UAAU,gBAAgB,EAAE;gBAC5B,mBAAmB,CAAC,CAAC,CAAC,QAAQ,mBAAmB,EAAE,CAAC,CAAC,CAAC,EAAE;aACzD;iBACE,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,IAAI,CAAC,CAAC;YAEd,MAAM,eAAe,CAAC,OAAO,EAAE;gBAC7B,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,OAAO;gBACP,IAAI,EAAE,wBAAwB,CAAC,OAAO,CAAC;gBACvC,KAAK,EAAE,GAAG,OAAO,CAAC,QAAQ,KAAK,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;gBAC9D,OAAO;gBACP,SAAS,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC;QAED,oDAAoD;QACpD,IAAI,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,eAAe,CAAC,OAAO,EAAE;gBAC7B,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,OAAO;gBACP,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,OAAO,QAAQ,YAAY,MAAM,CAAC,MAAM,EAAE;gBACjD,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC;gBACzC,QAAQ,EAAE,MAAM,QAAQ,SAAS;gBACjC,SAAS,EAAE,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClD,CAAC,CAAC;QACL,CAAC;QAED,iCAAiC;QACjC,MAAM,gBAAgB,CACpB,OAAO,EACP,OAAO,CAAC,EAAE,EACV,iBAAiB,QAAQ,KAAK,MAAM,CAAC,MAAM,SAAS,mBAAmB,CAAC,MAAM,wBAAwB,CACvG,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mEAAmE;QACnE,OAAO,CAAC,IAAI,CACV,6DAA6D,EAC7D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy-safe text sanitization.
|
|
3
|
+
*
|
|
4
|
+
* Strips sensitive data (API keys, tokens, passwords) from text
|
|
5
|
+
* before it gets persisted to memory. This ensures that even if
|
|
6
|
+
* a diff contains credentials, they won't be stored in the database.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Strip sensitive data from text before persisting to memory.
|
|
10
|
+
*
|
|
11
|
+
* Applies all known secret patterns and replaces matches with
|
|
12
|
+
* human-readable redaction labels. The patterns are applied in
|
|
13
|
+
* order, so more specific patterns take precedence.
|
|
14
|
+
*
|
|
15
|
+
* @param text - The text to sanitize
|
|
16
|
+
* @returns Sanitized text with secrets replaced by redaction labels
|
|
17
|
+
*/
|
|
18
|
+
export declare function stripPrivateData(text: string): string;
|
|
19
|
+
//# sourceMappingURL=privacy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUrD"}
|