@zivis/mcp 0.1.0-alpha.21 → 0.1.0-alpha.23
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/matcher/ast-eval.d.ts +27 -0
- package/dist/matcher/ast-eval.js +132 -0
- package/dist/matcher/ast-eval.js.map +1 -0
- package/dist/matcher/finding.d.ts +76 -0
- package/dist/matcher/finding.js +14 -0
- package/dist/matcher/finding.js.map +1 -0
- package/dist/matcher/index.d.ts +30 -0
- package/dist/matcher/index.js +255 -0
- package/dist/matcher/index.js.map +1 -0
- package/dist/matcher/inference-candidates.d.ts +54 -0
- package/dist/matcher/inference-candidates.js +106 -0
- package/dist/matcher/inference-candidates.js.map +1 -0
- package/dist/matcher/walker.d.ts +14 -0
- package/dist/matcher/walker.js +79 -0
- package/dist/matcher/walker.js.map +1 -0
- package/dist/pattern-pack/index.d.ts +154 -0
- package/dist/pattern-pack/index.js +224 -0
- package/dist/pattern-pack/index.js.map +1 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/agents/privilege-separation.yaml +224 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/prompting/self-consistency-no-isolation.yaml +237 -0
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/security/late-org-filter.yaml +266 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/external_validator_on_facts.yaml +37 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/human_approval_gate.yaml +44 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/langgraph_state_machine_with_per_node_tools.yaml +35 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/per_sample_retrieval_diversification.yaml +38 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/prisma_extension_or_middleware_attaching_org_filter.yaml +31 -0
- package/dist/pattern-packs/zivis-public-0.1.0/controls/row_level_security_policy.yaml +37 -0
- package/dist/pattern-packs/zivis-public-0.1.0/manifest.json +129 -0
- package/dist/pattern-packs/zivis-public-0.1.0/prompts/agents/privilege-separation/identify.md +54 -0
- package/dist/pattern-packs/zivis-public-0.1.0/prompts/prompting/self-consistency-no-isolation/identify.md +60 -0
- package/dist/pattern-packs/zivis-public-0.1.0/prompts/security/late-org-filter/identify.md +78 -0
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.js +1 -7
- package/dist/prompts/index.js.map +1 -1
- package/dist/server.js +36 -166
- package/dist/server.js.map +1 -1
- package/dist/tools/enterprise/get-threat-model-artifact.d.ts +30 -0
- package/dist/tools/enterprise/get-threat-model-artifact.js +80 -0
- package/dist/tools/enterprise/get-threat-model-artifact.js.map +1 -0
- package/dist/tools/enterprise/get-threat-model.d.ts +1 -1
- package/dist/tools/enterprise/get-threat-model.js +59 -11
- package/dist/tools/enterprise/get-threat-model.js.map +1 -1
- package/dist/tools/enterprise/list-threat-model-artifacts.d.ts +67 -0
- package/dist/tools/enterprise/list-threat-model-artifacts.js +121 -0
- package/dist/tools/enterprise/list-threat-model-artifacts.js.map +1 -0
- package/dist/tools/enterprise/manage-actor.d.ts +1 -1
- package/dist/tools/enterprise/manage-actor.js +14 -1
- package/dist/tools/enterprise/manage-actor.js.map +1 -1
- package/dist/tools/enterprise/manage-attack-scenario.d.ts +1 -1
- package/dist/tools/enterprise/manage-attack-scenario.js +13 -1
- package/dist/tools/enterprise/manage-attack-scenario.js.map +1 -1
- package/dist/tools/enterprise/manage-component.d.ts +1 -1
- package/dist/tools/enterprise/manage-component.js +20 -1
- package/dist/tools/enterprise/manage-component.js.map +1 -1
- package/dist/tools/enterprise/manage-data-asset.d.ts +2 -2
- package/dist/tools/enterprise/manage-data-asset.js +17 -1
- package/dist/tools/enterprise/manage-data-asset.js.map +1 -1
- package/dist/tools/enterprise/manage-data-flow.d.ts +2 -2
- package/dist/tools/enterprise/manage-data-flow.js +19 -1
- package/dist/tools/enterprise/manage-data-flow.js.map +1 -1
- package/dist/tools/enterprise/manage-security-control.d.ts +1 -1
- package/dist/tools/enterprise/manage-security-control.js +18 -1
- package/dist/tools/enterprise/manage-security-control.js.map +1 -1
- package/dist/tools/enterprise/manage-test-case.d.ts +2 -2
- package/dist/tools/enterprise/manage-test-case.js +8 -0
- package/dist/tools/enterprise/manage-test-case.js.map +1 -1
- package/dist/tools/get-started.d.ts +1 -1
- package/dist/tools/get-started.js +185 -8
- package/dist/tools/get-started.js.map +1 -1
- package/dist/tools/inspect.d.ts +38 -0
- package/dist/tools/inspect.js +252 -0
- package/dist/tools/inspect.js.map +1 -0
- package/dist/tools/manage-application.d.ts +1 -1
- package/dist/tools/security-review.js +67 -50
- package/dist/tools/security-review.js.map +1 -1
- package/dist/tools/threat-get-capsule.d.ts +25 -0
- package/dist/tools/threat-get-capsule.js +64 -0
- package/dist/tools/threat-get-capsule.js.map +1 -0
- package/dist/tools/threat-get-inference-prompt.d.ts +32 -0
- package/dist/tools/threat-get-inference-prompt.js +91 -0
- package/dist/tools/threat-get-inference-prompt.js.map +1 -0
- package/dist/tools/threat-guide.d.ts +39 -0
- package/dist/tools/threat-guide.js +111 -0
- package/dist/tools/threat-guide.js.map +1 -0
- package/dist/tools/threat-list-relevant-capsules.d.ts +30 -0
- package/dist/tools/threat-list-relevant-capsules.js +176 -0
- package/dist/tools/threat-list-relevant-capsules.js.map +1 -0
- package/dist/tools/threat-run-matcher.d.ts +27 -0
- package/dist/tools/threat-run-matcher.js +164 -0
- package/dist/tools/threat-run-matcher.js.map +1 -0
- package/package.json +13 -3
- package/dist/prompts/prep-for-launch.d.ts +0 -9
- package/dist/prompts/prep-for-launch.js +0 -62
- package/dist/prompts/prep-for-launch.js.map +0 -1
- package/dist/prompts/secure-my-app.d.ts +0 -9
- package/dist/prompts/secure-my-app.js +0 -70
- package/dist/prompts/secure-my-app.js.map +0 -1
- package/dist/prompts/weekly-check.d.ts +0 -9
- package/dist/prompts/weekly-check.js +0 -48
- package/dist/prompts/weekly-check.js.map +0 -1
- package/dist/tools/enterprise/capture-evidence.d.ts +0 -54
- package/dist/tools/enterprise/capture-evidence.js +0 -72
- package/dist/tools/enterprise/capture-evidence.js.map +0 -1
- package/dist/tools/enterprise/create-report-ticket.d.ts +0 -34
- package/dist/tools/enterprise/create-report-ticket.js +0 -44
- package/dist/tools/enterprise/create-report-ticket.js.map +0 -1
- package/dist/tools/enterprise/end-pentest-session.d.ts +0 -33
- package/dist/tools/enterprise/end-pentest-session.js +0 -42
- package/dist/tools/enterprise/end-pentest-session.js.map +0 -1
- package/dist/tools/enterprise/generate-report.d.ts +0 -55
- package/dist/tools/enterprise/generate-report.js +0 -111
- package/dist/tools/enterprise/generate-report.js.map +0 -1
- package/dist/tools/enterprise/get-campaign.d.ts +0 -27
- package/dist/tools/enterprise/get-campaign.js +0 -31
- package/dist/tools/enterprise/get-campaign.js.map +0 -1
- package/dist/tools/enterprise/get-report-status.d.ts +0 -27
- package/dist/tools/enterprise/get-report-status.js +0 -38
- package/dist/tools/enterprise/get-report-status.js.map +0 -1
- package/dist/tools/enterprise/list-agents.d.ts +0 -42
- package/dist/tools/enterprise/list-agents.js +0 -95
- package/dist/tools/enterprise/list-agents.js.map +0 -1
- package/dist/tools/enterprise/list-campaigns.d.ts +0 -37
- package/dist/tools/enterprise/list-campaigns.js +0 -60
- package/dist/tools/enterprise/list-campaigns.js.map +0 -1
- package/dist/tools/enterprise/list-report-issues.d.ts +0 -36
- package/dist/tools/enterprise/list-report-issues.js +0 -50
- package/dist/tools/enterprise/list-report-issues.js.map +0 -1
- package/dist/tools/enterprise/manage-campaign-agent-instance.d.ts +0 -42
- package/dist/tools/enterprise/manage-campaign-agent-instance.js +0 -68
- package/dist/tools/enterprise/manage-campaign-agent-instance.js.map +0 -1
- package/dist/tools/enterprise/manage-campaign-target.d.ts +0 -44
- package/dist/tools/enterprise/manage-campaign-target.js +0 -70
- package/dist/tools/enterprise/manage-campaign-target.js.map +0 -1
- package/dist/tools/enterprise/manage-campaign.d.ts +0 -62
- package/dist/tools/enterprise/manage-campaign.js +0 -114
- package/dist/tools/enterprise/manage-campaign.js.map +0 -1
- package/dist/tools/enterprise/manage-finding.d.ts +0 -69
- package/dist/tools/enterprise/manage-finding.js +0 -106
- package/dist/tools/enterprise/manage-finding.js.map +0 -1
- package/dist/tools/enterprise/promote-finding-to-issue.d.ts +0 -36
- package/dist/tools/enterprise/promote-finding-to-issue.js +0 -65
- package/dist/tools/enterprise/promote-finding-to-issue.js.map +0 -1
- package/dist/tools/enterprise/publish-report-version.d.ts +0 -34
- package/dist/tools/enterprise/publish-report-version.js +0 -75
- package/dist/tools/enterprise/publish-report-version.js.map +0 -1
- package/dist/tools/enterprise/publish-test-to-library.d.ts +0 -31
- package/dist/tools/enterprise/publish-test-to-library.js +0 -40
- package/dist/tools/enterprise/publish-test-to-library.js.map +0 -1
- package/dist/tools/enterprise/record-test-result.d.ts +0 -67
- package/dist/tools/enterprise/record-test-result.js +0 -75
- package/dist/tools/enterprise/record-test-result.js.map +0 -1
- package/dist/tools/enterprise/start-pentest-session.d.ts +0 -35
- package/dist/tools/enterprise/start-pentest-session.js +0 -50
- package/dist/tools/enterprise/start-pentest-session.js.map +0 -1
- package/dist/tools/enterprise/sync-report-section.d.ts +0 -41
- package/dist/tools/enterprise/sync-report-section.js +0 -79
- package/dist/tools/enterprise/sync-report-section.js.map +0 -1
- package/dist/tools/enterprise/update-report-issue.d.ts +0 -55
- package/dist/tools/enterprise/update-report-issue.js +0 -69
- package/dist/tools/enterprise/update-report-issue.js.map +0 -1
- package/dist/tools/get-agent-test-results.d.ts +0 -41
- package/dist/tools/get-agent-test-results.js +0 -86
- package/dist/tools/get-agent-test-results.js.map +0 -1
- package/dist/tools/get-application-auth-config.d.ts +0 -27
- package/dist/tools/get-application-auth-config.js +0 -44
- package/dist/tools/get-application-auth-config.js.map +0 -1
- package/dist/tools/get-artifact-content.d.ts +0 -29
- package/dist/tools/get-artifact-content.js +0 -86
- package/dist/tools/get-artifact-content.js.map +0 -1
- package/dist/tools/get-finding-details.d.ts +0 -22
- package/dist/tools/get-finding-details.js +0 -61
- package/dist/tools/get-finding-details.js.map +0 -1
- package/dist/tools/get-findings.d.ts +0 -51
- package/dist/tools/get-findings.js +0 -124
- package/dist/tools/get-findings.js.map +0 -1
- package/dist/tools/get-recon-scan.d.ts +0 -27
- package/dist/tools/get-recon-scan.js +0 -46
- package/dist/tools/get-recon-scan.js.map +0 -1
- package/dist/tools/get-scan-output.d.ts +0 -27
- package/dist/tools/get-scan-output.js +0 -64
- package/dist/tools/get-scan-output.js.map +0 -1
- package/dist/tools/get-scenario-details.d.ts +0 -27
- package/dist/tools/get-scenario-details.js +0 -46
- package/dist/tools/get-scenario-details.js.map +0 -1
- package/dist/tools/get-test-case-results.d.ts +0 -37
- package/dist/tools/get-test-case-results.js +0 -66
- package/dist/tools/get-test-case-results.js.map +0 -1
- package/dist/tools/get-test-details.d.ts +0 -29
- package/dist/tools/get-test-details.js +0 -49
- package/dist/tools/get-test-details.js.map +0 -1
- package/dist/tools/list-agent-test-cases.d.ts +0 -41
- package/dist/tools/list-agent-test-cases.js +0 -73
- package/dist/tools/list-agent-test-cases.js.map +0 -1
- package/dist/tools/list-recon-scans.d.ts +0 -36
- package/dist/tools/list-recon-scans.js +0 -74
- package/dist/tools/list-recon-scans.js.map +0 -1
- package/dist/tools/list-scan-artifacts.d.ts +0 -41
- package/dist/tools/list-scan-artifacts.js +0 -63
- package/dist/tools/list-scan-artifacts.js.map +0 -1
- package/dist/tools/list-scans.d.ts +0 -38
- package/dist/tools/list-scans.js +0 -125
- package/dist/tools/list-scans.js.map +0 -1
- package/dist/tools/list-test-library.d.ts +0 -44
- package/dist/tools/list-test-library.js +0 -83
- package/dist/tools/list-test-library.js.map +0 -1
- package/dist/tools/list-test-scenarios.d.ts +0 -25
- package/dist/tools/list-test-scenarios.js +0 -94
- package/dist/tools/list-test-scenarios.js.map +0 -1
- package/dist/tools/local-scan.d.ts +0 -40
- package/dist/tools/local-scan.js +0 -261
- package/dist/tools/local-scan.js.map +0 -1
- package/dist/tools/run-test-scenario.d.ts +0 -37
- package/dist/tools/run-test-scenario.js +0 -70
- package/dist/tools/run-test-scenario.js.map +0 -1
- package/dist/tools/scan-pr.d.ts +0 -27
- package/dist/tools/scan-pr.js +0 -51
- package/dist/tools/scan-pr.js.map +0 -1
- package/dist/tools/setup-red-team-target.d.ts +0 -73
- package/dist/tools/setup-red-team-target.js +0 -461
- package/dist/tools/setup-red-team-target.js.map +0 -1
- package/dist/tools/triage-finding.d.ts +0 -41
- package/dist/tools/triage-finding.js +0 -125
- package/dist/tools/triage-finding.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST strategy evaluator.
|
|
3
|
+
*
|
|
4
|
+
* Wraps @ast-grep/napi. Capsule strategies of code:AST carry a YAML rule
|
|
5
|
+
* body in `expression` (already parsed by js-yaml when the capsule was
|
|
6
|
+
* loaded). We run that rule against source files of the strategy's
|
|
7
|
+
* language and collect line spans as evidence.
|
|
8
|
+
*
|
|
9
|
+
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md (Phase 3 — Matching engine)
|
|
10
|
+
*/
|
|
11
|
+
export type AstLanguage = "typescript" | "javascript" | "python" | "go";
|
|
12
|
+
export interface AstMatch {
|
|
13
|
+
file: string;
|
|
14
|
+
start_line: number;
|
|
15
|
+
end_line: number;
|
|
16
|
+
matched_text: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run an ast-grep rule (already parsed from the capsule YAML) against the
|
|
20
|
+
* given source files and return all matches.
|
|
21
|
+
*
|
|
22
|
+
* The expression is the capsule's `strategy.expression` field. Capsules
|
|
23
|
+
* may write the rule as either:
|
|
24
|
+
* - a raw string (parsed as YAML) containing a top-level `rule:` block, or
|
|
25
|
+
* - a structured object (already an `{ rule: { ... } }` shape)
|
|
26
|
+
*/
|
|
27
|
+
export declare function evaluateAstStrategy(expression: unknown, language: AstLanguage, files: string[], rootDir: string): Promise<AstMatch[]>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST strategy evaluator.
|
|
3
|
+
*
|
|
4
|
+
* Wraps @ast-grep/napi. Capsule strategies of code:AST carry a YAML rule
|
|
5
|
+
* body in `expression` (already parsed by js-yaml when the capsule was
|
|
6
|
+
* loaded). We run that rule against source files of the strategy's
|
|
7
|
+
* language and collect line spans as evidence.
|
|
8
|
+
*
|
|
9
|
+
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md (Phase 3 — Matching engine)
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from "node:fs/promises";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import * as yaml from "js-yaml";
|
|
14
|
+
import * as napi from "@ast-grep/napi";
|
|
15
|
+
let registered = false;
|
|
16
|
+
async function ensureLanguagesRegistered() {
|
|
17
|
+
if (registered)
|
|
18
|
+
return;
|
|
19
|
+
// ts/js are built into napi; python and go come from dynamic language packs.
|
|
20
|
+
const py = (await import("@ast-grep/lang-python")).default;
|
|
21
|
+
const go = (await import("@ast-grep/lang-go")).default;
|
|
22
|
+
napi.registerDynamicLanguage({ python: py, go: go });
|
|
23
|
+
registered = true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Run an ast-grep rule (already parsed from the capsule YAML) against the
|
|
27
|
+
* given source files and return all matches.
|
|
28
|
+
*
|
|
29
|
+
* The expression is the capsule's `strategy.expression` field. Capsules
|
|
30
|
+
* may write the rule as either:
|
|
31
|
+
* - a raw string (parsed as YAML) containing a top-level `rule:` block, or
|
|
32
|
+
* - a structured object (already an `{ rule: { ... } }` shape)
|
|
33
|
+
*/
|
|
34
|
+
export async function evaluateAstStrategy(expression, language, files, rootDir) {
|
|
35
|
+
await ensureLanguagesRegistered();
|
|
36
|
+
const rule = normalizeExpressionToRule(expression);
|
|
37
|
+
if (!rule)
|
|
38
|
+
return [];
|
|
39
|
+
const matches = [];
|
|
40
|
+
const langKey = languageKey(language);
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
let raw;
|
|
43
|
+
try {
|
|
44
|
+
raw = await fs.readFile(file, "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!raw)
|
|
50
|
+
continue;
|
|
51
|
+
let root;
|
|
52
|
+
try {
|
|
53
|
+
root = napi.parse(langKey, raw);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
let foundNodes;
|
|
59
|
+
try {
|
|
60
|
+
// Cast: napi types restrict findAll to string|number, but at runtime it
|
|
61
|
+
// accepts a NapiConfig object (which is what capsule rules deserialize to).
|
|
62
|
+
foundNodes = root.root().findAll(rule);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// ast-grep throws on malformed rules; skip this strategy on this file.
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
for (const node of foundNodes) {
|
|
69
|
+
const range = node.range();
|
|
70
|
+
matches.push({
|
|
71
|
+
file: relPosix(rootDir, file),
|
|
72
|
+
start_line: (range.start.line ?? 0) + 1,
|
|
73
|
+
end_line: (range.end.line ?? 0) + 1,
|
|
74
|
+
matched_text: truncate(node.text(), 240),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return matches;
|
|
79
|
+
}
|
|
80
|
+
// ── Helpers ──
|
|
81
|
+
function normalizeExpressionToRule(expression) {
|
|
82
|
+
if (typeof expression === "string") {
|
|
83
|
+
try {
|
|
84
|
+
return ensureRuleWrapper(yaml.load(expression));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (expression && typeof expression === "object") {
|
|
91
|
+
return ensureRuleWrapper(expression);
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* ast-grep's findAll requires a top-level `rule:` wrapper. Capsule AST
|
|
97
|
+
* strategies have it explicit in YAML; control detection rules carry just
|
|
98
|
+
* the bare rule body. Wrap consistently here so both shapes evaluate.
|
|
99
|
+
*
|
|
100
|
+
* NapiConfig keys recognized as outer-level: rule, constraints, utils,
|
|
101
|
+
* filters, language, transform, fix. Any of those at the top means we're
|
|
102
|
+
* already wrapped.
|
|
103
|
+
*/
|
|
104
|
+
function ensureRuleWrapper(parsed) {
|
|
105
|
+
if (!parsed || typeof parsed !== "object")
|
|
106
|
+
return parsed;
|
|
107
|
+
const obj = parsed;
|
|
108
|
+
if ("rule" in obj)
|
|
109
|
+
return obj;
|
|
110
|
+
return { rule: obj };
|
|
111
|
+
}
|
|
112
|
+
function languageKey(lang) {
|
|
113
|
+
// ts and tsx are separate engines in napi; we use ts for .ts/.tsx and
|
|
114
|
+
// js for .js/.jsx/.mjs/.cjs. The matcher passes the file's language tag,
|
|
115
|
+
// not the file extension — so .tsx files routed as 'typescript' work.
|
|
116
|
+
switch (lang) {
|
|
117
|
+
case "typescript": return "ts";
|
|
118
|
+
case "javascript": return "js";
|
|
119
|
+
case "python": return "python";
|
|
120
|
+
case "go": return "go";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function relPosix(rootDir, abs) {
|
|
124
|
+
const rel = path.relative(rootDir, abs);
|
|
125
|
+
return rel.split(path.sep).join("/");
|
|
126
|
+
}
|
|
127
|
+
function truncate(s, max) {
|
|
128
|
+
if (s.length <= max)
|
|
129
|
+
return s;
|
|
130
|
+
return s.slice(0, max) + "…";
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=ast-eval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-eval.js","sourceRoot":"","sources":["../../src/matcher/ast-eval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized Finding type emitted by the matcher.
|
|
3
|
+
*
|
|
4
|
+
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md
|
|
5
|
+
* "Normalize outputs to a single finding schema (patternId/version,
|
|
6
|
+
* matched graph evidence, confidence, lane, explanation, remediation)."
|
|
7
|
+
*
|
|
8
|
+
* Lane determination:
|
|
9
|
+
* - trusted: count(fired deterministic strategies) >= required_evidence_count
|
|
10
|
+
* - inferred: any strategy fired but bar not met (host LLM should confirm)
|
|
11
|
+
* - (no finding emitted otherwise)
|
|
12
|
+
*/
|
|
13
|
+
export type Lane = "trusted" | "inferred";
|
|
14
|
+
export interface MatchedEvidence {
|
|
15
|
+
/** Where the strategy fired. */
|
|
16
|
+
file: string;
|
|
17
|
+
start_line: number;
|
|
18
|
+
end_line?: number;
|
|
19
|
+
/** Truncated source snippet for human review (NOT for re-execution). */
|
|
20
|
+
matched_text?: string;
|
|
21
|
+
/** Which strategy code emitted this evidence (AST, GRAPH, META, INFER, ...). */
|
|
22
|
+
strategy_code: string;
|
|
23
|
+
}
|
|
24
|
+
export interface FiredStrategy {
|
|
25
|
+
code: string;
|
|
26
|
+
language?: string;
|
|
27
|
+
trusted: boolean;
|
|
28
|
+
confidence_weight: number;
|
|
29
|
+
match_count: number;
|
|
30
|
+
}
|
|
31
|
+
export interface InferenceCandidate {
|
|
32
|
+
prompt_ref: string;
|
|
33
|
+
expected_output_schema?: unknown;
|
|
34
|
+
minimum_context_required?: {
|
|
35
|
+
lines?: number;
|
|
36
|
+
symbols?: string[];
|
|
37
|
+
};
|
|
38
|
+
/** Why the host LLM should run inference on this finding. */
|
|
39
|
+
reason: string;
|
|
40
|
+
}
|
|
41
|
+
export interface Finding {
|
|
42
|
+
pattern_id: string;
|
|
43
|
+
pattern_version: string;
|
|
44
|
+
lane: Lane;
|
|
45
|
+
/** 0..1 — sum of fired-strategy weights, capped at 1. */
|
|
46
|
+
confidence: number;
|
|
47
|
+
/** Strategies that contributed (AST matches, INFER candidate, etc.). */
|
|
48
|
+
fired_strategies: FiredStrategy[];
|
|
49
|
+
/** Evidence spans from deterministic strategies that fired. */
|
|
50
|
+
evidence: MatchedEvidence[];
|
|
51
|
+
/** Present iff the capsule has an INFER strategy — host LLM should pick this up. */
|
|
52
|
+
inference_candidate?: InferenceCandidate;
|
|
53
|
+
/** Surfaced from the capsule for the host's framing. */
|
|
54
|
+
safe_summary?: string;
|
|
55
|
+
threats?: unknown[];
|
|
56
|
+
repair_contract?: unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface MatcherSummary {
|
|
59
|
+
trusted_count: number;
|
|
60
|
+
inferred_count: number;
|
|
61
|
+
total: number;
|
|
62
|
+
}
|
|
63
|
+
export interface MatcherResult {
|
|
64
|
+
pack: {
|
|
65
|
+
id: string;
|
|
66
|
+
version: string;
|
|
67
|
+
};
|
|
68
|
+
artifact_path: string;
|
|
69
|
+
cwd: string;
|
|
70
|
+
capsules_relevant: number;
|
|
71
|
+
capsules_evaluated: number;
|
|
72
|
+
ast_files_scanned: number;
|
|
73
|
+
duration_ms: number;
|
|
74
|
+
summary: MatcherSummary;
|
|
75
|
+
findings: Finding[];
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized Finding type emitted by the matcher.
|
|
3
|
+
*
|
|
4
|
+
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md
|
|
5
|
+
* "Normalize outputs to a single finding schema (patternId/version,
|
|
6
|
+
* matched graph evidence, confidence, lane, explanation, remediation)."
|
|
7
|
+
*
|
|
8
|
+
* Lane determination:
|
|
9
|
+
* - trusted: count(fired deterministic strategies) >= required_evidence_count
|
|
10
|
+
* - inferred: any strategy fired but bar not met (host LLM should confirm)
|
|
11
|
+
* - (no finding emitted otherwise)
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=finding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding.js","sourceRoot":"","sources":["../../src/matcher/finding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 matcher — runs the local public pack against a repo graph
|
|
3
|
+
* artifact + the source files in cwd.
|
|
4
|
+
*
|
|
5
|
+
* v1 scope (locked):
|
|
6
|
+
* - AST strategies are evaluated for real (via @ast-grep/napi)
|
|
7
|
+
* - INFER strategies emit inference candidates; host LLM runs them
|
|
8
|
+
* - GRAPH / META / DEP / CONFIG / STRUCT / REGEX / SEM / RECON: not
|
|
9
|
+
* evaluated yet, but visible in `fired_strategies` as `match_count: 0`
|
|
10
|
+
* so we don't lose the contract.
|
|
11
|
+
*
|
|
12
|
+
* Lane assignment per finding:
|
|
13
|
+
* - trusted: count of fired deterministic (trusted: true) strategies
|
|
14
|
+
* reaches `required_evidence_count`
|
|
15
|
+
* - inferred: any strategy fired but bar not met
|
|
16
|
+
* - (no finding) if nothing fired
|
|
17
|
+
*
|
|
18
|
+
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md (Phase 3)
|
|
19
|
+
*/
|
|
20
|
+
import type { MatcherResult } from "./finding.js";
|
|
21
|
+
export type { Finding, FiredStrategy, MatchedEvidence, MatcherResult, Lane } from "./finding.js";
|
|
22
|
+
export interface RunMatcherOpts {
|
|
23
|
+
/** Path to the RepoGraphArtifactV1 JSON produced by `zivis inspect`. */
|
|
24
|
+
artifactPath: string;
|
|
25
|
+
/** Repo root for source-file walking (capsule AST strategies match against this). */
|
|
26
|
+
cwd: string;
|
|
27
|
+
/** Override default pack id. */
|
|
28
|
+
packId?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function runMatcher(opts: RunMatcherOpts): Promise<MatcherResult>;
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 matcher — runs the local public pack against a repo graph
|
|
3
|
+
* artifact + the source files in cwd.
|
|
4
|
+
*
|
|
5
|
+
* v1 scope (locked):
|
|
6
|
+
* - AST strategies are evaluated for real (via @ast-grep/napi)
|
|
7
|
+
* - INFER strategies emit inference candidates; host LLM runs them
|
|
8
|
+
* - GRAPH / META / DEP / CONFIG / STRUCT / REGEX / SEM / RECON: not
|
|
9
|
+
* evaluated yet, but visible in `fired_strategies` as `match_count: 0`
|
|
10
|
+
* so we don't lose the contract.
|
|
11
|
+
*
|
|
12
|
+
* Lane assignment per finding:
|
|
13
|
+
* - trusted: count of fired deterministic (trusted: true) strategies
|
|
14
|
+
* reaches `required_evidence_count`
|
|
15
|
+
* - inferred: any strategy fired but bar not met
|
|
16
|
+
* - (no finding) if nothing fired
|
|
17
|
+
*
|
|
18
|
+
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md (Phase 3)
|
|
19
|
+
*/
|
|
20
|
+
import * as fs from "node:fs/promises";
|
|
21
|
+
import * as fssync from "node:fs";
|
|
22
|
+
import { loadActivePack, findRelevantCapsules, getControl, } from "../pattern-pack/index.js";
|
|
23
|
+
import { walkLanguageFiles } from "./walker.js";
|
|
24
|
+
import { evaluateAstStrategy } from "./ast-eval.js";
|
|
25
|
+
const SUPPORTED_AST_LANGS = new Set([
|
|
26
|
+
"typescript",
|
|
27
|
+
"javascript",
|
|
28
|
+
"python",
|
|
29
|
+
"go",
|
|
30
|
+
]);
|
|
31
|
+
export async function runMatcher(opts) {
|
|
32
|
+
const startedAt = Date.now();
|
|
33
|
+
const handle = await loadActivePack(opts.packId);
|
|
34
|
+
// Load and parse the artifact.
|
|
35
|
+
const artifact = await readArtifact(opts.artifactPath);
|
|
36
|
+
const relevant = await findRelevantCapsules(handle, artifact);
|
|
37
|
+
// Walk source files once for the languages any relevant capsule needs.
|
|
38
|
+
const langsNeeded = collectAstLanguagesNeeded(relevant);
|
|
39
|
+
const walked = langsNeeded.length > 0
|
|
40
|
+
? await walkLanguageFiles(opts.cwd, langsNeeded)
|
|
41
|
+
: { filesByLanguage: new Map(), filesScanned: 0, filesSkipped: 0 };
|
|
42
|
+
// Pre-compute "which controls are present in this codebase" for META eval.
|
|
43
|
+
// Controls are evaluated once per matcher run, not per capsule, since multiple
|
|
44
|
+
// capsules may reference the same control.
|
|
45
|
+
const controlPresence = await evaluateAllControls(handle, opts.cwd, walked.filesByLanguage);
|
|
46
|
+
const findings = [];
|
|
47
|
+
for (const cap of relevant) {
|
|
48
|
+
const finding = await evaluateCapsule(cap, opts.cwd, walked.filesByLanguage, controlPresence);
|
|
49
|
+
if (finding)
|
|
50
|
+
findings.push(finding);
|
|
51
|
+
}
|
|
52
|
+
const summary = {
|
|
53
|
+
trusted_count: findings.filter((f) => f.lane === "trusted").length,
|
|
54
|
+
inferred_count: findings.filter((f) => f.lane === "inferred").length,
|
|
55
|
+
total: findings.length,
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
pack: { id: handle.manifest.pack_id, version: handle.manifest.version },
|
|
59
|
+
artifact_path: opts.artifactPath,
|
|
60
|
+
cwd: opts.cwd,
|
|
61
|
+
capsules_relevant: relevant.length,
|
|
62
|
+
capsules_evaluated: relevant.length,
|
|
63
|
+
ast_files_scanned: walked.filesScanned,
|
|
64
|
+
duration_ms: Date.now() - startedAt,
|
|
65
|
+
summary,
|
|
66
|
+
findings,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// ── Capsule evaluation ──
|
|
70
|
+
async function evaluateCapsule(cap, cwd, filesByLanguage, controlPresence) {
|
|
71
|
+
const strategies = (cap.strategies ?? []);
|
|
72
|
+
const fired = [];
|
|
73
|
+
const evidence = [];
|
|
74
|
+
let inferenceCandidate;
|
|
75
|
+
for (const strategy of strategies) {
|
|
76
|
+
const code = strategy.code;
|
|
77
|
+
const trusted = strategy.trusted !== false && code !== "INFER" && code !== "SEM" && code !== "REGEX";
|
|
78
|
+
if (code === "AST") {
|
|
79
|
+
const lang = (strategy.language ?? "").toLowerCase();
|
|
80
|
+
if (!SUPPORTED_AST_LANGS.has(lang)) {
|
|
81
|
+
fired.push({ code, language: lang, trusted, confidence_weight: strategy.confidence_weight, match_count: 0 });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const files = filesByLanguage.get(lang) ?? [];
|
|
85
|
+
if (files.length === 0) {
|
|
86
|
+
fired.push({ code, language: lang, trusted, confidence_weight: strategy.confidence_weight, match_count: 0 });
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const matches = await evaluateAstStrategy(strategy.expression, lang, files, cwd);
|
|
90
|
+
fired.push({
|
|
91
|
+
code,
|
|
92
|
+
language: lang,
|
|
93
|
+
trusted,
|
|
94
|
+
confidence_weight: strategy.confidence_weight,
|
|
95
|
+
match_count: matches.length,
|
|
96
|
+
});
|
|
97
|
+
for (const m of matches) {
|
|
98
|
+
evidence.push({
|
|
99
|
+
file: m.file,
|
|
100
|
+
start_line: m.start_line,
|
|
101
|
+
end_line: m.end_line,
|
|
102
|
+
matched_text: m.matched_text,
|
|
103
|
+
strategy_code: code,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (code === "META") {
|
|
108
|
+
const result = evaluateMetaStrategy(strategy.expression, controlPresence);
|
|
109
|
+
fired.push({
|
|
110
|
+
code,
|
|
111
|
+
trusted,
|
|
112
|
+
confidence_weight: strategy.confidence_weight,
|
|
113
|
+
match_count: result.fired ? 1 : 0,
|
|
114
|
+
});
|
|
115
|
+
if (result.fired) {
|
|
116
|
+
evidence.push({
|
|
117
|
+
file: "(synthetic)",
|
|
118
|
+
start_line: 0,
|
|
119
|
+
matched_text: `META: none_of_controls satisfied; absent: [${result.absent.join(", ")}]`,
|
|
120
|
+
strategy_code: code,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else if (code === "INFER") {
|
|
125
|
+
// Always emit a candidate so the host LLM can run inference for this
|
|
126
|
+
// capsule. Lane stays inferred-or-better based on the trusted strategies.
|
|
127
|
+
if (cap.model_task_prompt_ref) {
|
|
128
|
+
inferenceCandidate = {
|
|
129
|
+
prompt_ref: cap.model_task_prompt_ref,
|
|
130
|
+
expected_output_schema: cap.expected_output_schema,
|
|
131
|
+
minimum_context_required: cap.minimum_context_required,
|
|
132
|
+
reason: "Capsule has an INFER strategy. Use zivis_threat_get_inference_prompt to retrieve the prompt bundle, gather the indicated code spans, and run the inference in your model.",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
fired.push({ code, trusted: false, confidence_weight: strategy.confidence_weight, match_count: 1 });
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// GRAPH / DEP / CONFIG / STRUCT / SEM / REGEX / RECON not evaluated yet.
|
|
139
|
+
fired.push({ code, trusted, confidence_weight: strategy.confidence_weight, match_count: 0 });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Did anything actually fire? (match_count > 0 OR INFER emitted)
|
|
143
|
+
const anyFired = fired.some((f) => f.match_count > 0);
|
|
144
|
+
if (!anyFired)
|
|
145
|
+
return undefined;
|
|
146
|
+
// Lane: trusted iff enough deterministic strategies fired.
|
|
147
|
+
const trustedFired = fired.filter((f) => f.trusted && f.match_count > 0).length;
|
|
148
|
+
const requiredCount = cap.required_evidence_count ?? 1;
|
|
149
|
+
const lane = trustedFired >= requiredCount ? "trusted" : "inferred";
|
|
150
|
+
// Confidence: sum of weights for strategies that fired (capped at 1.0).
|
|
151
|
+
const confidence = Math.min(1, fired.reduce((acc, f) => (f.match_count > 0 ? acc + f.confidence_weight : acc), 0));
|
|
152
|
+
return {
|
|
153
|
+
pattern_id: cap.id,
|
|
154
|
+
pattern_version: cap.version,
|
|
155
|
+
lane,
|
|
156
|
+
confidence,
|
|
157
|
+
fired_strategies: fired,
|
|
158
|
+
evidence,
|
|
159
|
+
inference_candidate: inferenceCandidate,
|
|
160
|
+
safe_summary: cap.safe_summary,
|
|
161
|
+
threats: cap.threats,
|
|
162
|
+
repair_contract: cap.repair_contract,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// ── Helpers ──
|
|
166
|
+
async function readArtifact(p) {
|
|
167
|
+
if (!fssync.existsSync(p)) {
|
|
168
|
+
throw new Error(`Artifact not found: ${p}`);
|
|
169
|
+
}
|
|
170
|
+
const raw = await fs.readFile(p, "utf8");
|
|
171
|
+
return JSON.parse(raw);
|
|
172
|
+
}
|
|
173
|
+
function collectAstLanguagesNeeded(capsules) {
|
|
174
|
+
const set = new Set();
|
|
175
|
+
for (const cap of capsules) {
|
|
176
|
+
for (const strategy of cap.strategies) {
|
|
177
|
+
if (strategy.code !== "AST")
|
|
178
|
+
continue;
|
|
179
|
+
const lang = (strategy.language ?? "").toLowerCase();
|
|
180
|
+
if (SUPPORTED_AST_LANGS.has(lang))
|
|
181
|
+
set.add(lang);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Controls' detection rules also need their languages walked. Add all
|
|
185
|
+
// common ones so control eval has files to scan; cheap cost since the
|
|
186
|
+
// walker already filters by extension.
|
|
187
|
+
set.add("typescript");
|
|
188
|
+
set.add("python");
|
|
189
|
+
return [...set];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Evaluate a META strategy expression against pre-computed control presence.
|
|
193
|
+
*
|
|
194
|
+
* v1 supports `none_of_controls` (the only shape currently in the capsules).
|
|
195
|
+
* Semantics: META fires iff at least one referenced control is checkable
|
|
196
|
+
* AND every checkable control is absent. Unchecked controls are ignored
|
|
197
|
+
* conservatively — we don't fire META unless we have *some* signal.
|
|
198
|
+
*/
|
|
199
|
+
function evaluateMetaStrategy(expression, presence) {
|
|
200
|
+
const expr = expression;
|
|
201
|
+
if (!expr || typeof expr !== "object")
|
|
202
|
+
return { fired: false, absent: [] };
|
|
203
|
+
if (expr.none_of_controls && expr.none_of_controls.length > 0) {
|
|
204
|
+
const checkable = expr.none_of_controls.filter((id) => presence.get(id)?.checkable);
|
|
205
|
+
if (checkable.length === 0)
|
|
206
|
+
return { fired: false, absent: [] };
|
|
207
|
+
const absent = checkable.filter((id) => !presence.get(id).present);
|
|
208
|
+
if (absent.length === checkable.length) {
|
|
209
|
+
return { fired: true, absent };
|
|
210
|
+
}
|
|
211
|
+
return { fired: false, absent };
|
|
212
|
+
}
|
|
213
|
+
// all_of_controls / any_of_controls — placeholder for future shapes.
|
|
214
|
+
// For v1 only none_of_controls is implemented; treat others as no-fire.
|
|
215
|
+
return { fired: false, absent: [] };
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Evaluate every control in the loaded pack once against the source files.
|
|
219
|
+
* Returns a map { control_id → { present, checkable } } that META eval reads.
|
|
220
|
+
*
|
|
221
|
+
* `checkable: false` means the control's detection rules don't apply to any
|
|
222
|
+
* language present in this codebase — we have no signal either way.
|
|
223
|
+
*/
|
|
224
|
+
async function evaluateAllControls(handle, cwd, filesByLanguage) {
|
|
225
|
+
const out = new Map();
|
|
226
|
+
const controlEntries = handle.manifest.controls ?? [];
|
|
227
|
+
for (const entry of controlEntries) {
|
|
228
|
+
let control;
|
|
229
|
+
try {
|
|
230
|
+
control = await getControl(handle, entry.id);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
let present = false;
|
|
236
|
+
let checkable = false;
|
|
237
|
+
for (const det of control.detection ?? []) {
|
|
238
|
+
const lang = det.language;
|
|
239
|
+
if (!SUPPORTED_AST_LANGS.has(lang))
|
|
240
|
+
continue;
|
|
241
|
+
const files = filesByLanguage.get(lang) ?? [];
|
|
242
|
+
if (files.length === 0)
|
|
243
|
+
continue;
|
|
244
|
+
checkable = true;
|
|
245
|
+
const matches = await evaluateAstStrategy(det.rule, lang, files, cwd);
|
|
246
|
+
if (matches.length > 0) {
|
|
247
|
+
present = true;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
out.set(control.id, { present, checkable });
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/matcher/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aggregate inference candidates from matcher findings into a top-level
|
|
3
|
+
* bundle the host LLM can't ignore.
|
|
4
|
+
*
|
|
5
|
+
* The matcher already attaches `inference_candidate` to each finding (when
|
|
6
|
+
* the capsule has an INFER strategy with model_task_prompt_ref), but if
|
|
7
|
+
* the host LLM only reads the headline it'll never call
|
|
8
|
+
* `zivis_threat_get_inference_prompt` and the inference never happens.
|
|
9
|
+
*
|
|
10
|
+
* This module promotes inference candidates to a structured top-level
|
|
11
|
+
* `inference_candidates[]` block with the prompt text bundled inline,
|
|
12
|
+
* plus a `host_required_action` directive. The host LLM gets everything
|
|
13
|
+
* it needs to run inference in one tool result — no extra calls.
|
|
14
|
+
*
|
|
15
|
+
* This is "Option A" from the local-inference triage. Modes B (local LLM
|
|
16
|
+
* sidecar) and C (ZIVIS-hosted cloud inference) are roadmap items.
|
|
17
|
+
*/
|
|
18
|
+
import { type Capsule } from "../pattern-pack/index.js";
|
|
19
|
+
import type { Finding } from "./finding.js";
|
|
20
|
+
export interface BundledInferenceCandidate {
|
|
21
|
+
candidate_id: string;
|
|
22
|
+
capsule_id: string;
|
|
23
|
+
capsule_version: string;
|
|
24
|
+
pattern_title?: string;
|
|
25
|
+
/** Full inference prompt text — already inlined; do not call getInferencePrompt again. */
|
|
26
|
+
prompt: string;
|
|
27
|
+
/** JSON Schema the host LLM's structured output must validate against. */
|
|
28
|
+
expected_output_schema: unknown;
|
|
29
|
+
/** Code spans the host LLM should attach to the prompt context. */
|
|
30
|
+
evidence_focus: Array<{
|
|
31
|
+
file: string;
|
|
32
|
+
start_line: number;
|
|
33
|
+
end_line?: number;
|
|
34
|
+
note?: string;
|
|
35
|
+
}>;
|
|
36
|
+
/** Per-capsule guidance on how much surrounding context the host should grab. */
|
|
37
|
+
minimum_context_required?: {
|
|
38
|
+
lines?: number;
|
|
39
|
+
symbols?: string[];
|
|
40
|
+
};
|
|
41
|
+
/** Plain-English instructions for the host LLM, repeated per candidate. */
|
|
42
|
+
instructions_for_host: string;
|
|
43
|
+
}
|
|
44
|
+
export interface InferenceBundle {
|
|
45
|
+
inference_candidates: BundledInferenceCandidate[];
|
|
46
|
+
host_required_action: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build the inline inference bundle for a list of findings. Loads the pack
|
|
50
|
+
* once, fetches each capsule's inference prompt, and packages it with the
|
|
51
|
+
* deterministic-strategy evidence so the host can run inference without
|
|
52
|
+
* any further MCP round-trip.
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildInferenceBundle(findings: Finding[], capsuleByPatternId?: Map<string, Capsule>): Promise<InferenceBundle>;
|