circle-ir-ai 2.27.0 → 2.28.2
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/CHANGELOG.md +172 -0
- package/dist/agents/mastra/workflow.d.ts +15 -0
- package/dist/agents/mastra/workflow.d.ts.map +1 -1
- package/dist/agents/mastra/workflow.js +33 -12
- package/dist/agents/mastra/workflow.js.map +1 -1
- package/dist/cache/backend.d.ts +62 -10
- package/dist/cache/backend.d.ts.map +1 -1
- package/dist/cache/backend.js +20 -4
- package/dist/cache/backend.js.map +1 -1
- package/dist/cache/backends/index.d.ts +30 -18
- package/dist/cache/backends/index.d.ts.map +1 -1
- package/dist/cache/backends/index.js +37 -29
- package/dist/cache/backends/index.js.map +1 -1
- package/dist/cache/backends/layout.d.ts +31 -0
- package/dist/cache/backends/layout.d.ts.map +1 -0
- package/dist/cache/backends/layout.js +46 -0
- package/dist/cache/backends/layout.js.map +1 -0
- package/dist/cache/backends/local-fs.d.ts +14 -11
- package/dist/cache/backends/local-fs.d.ts.map +1 -1
- package/dist/cache/backends/local-fs.js +30 -18
- package/dist/cache/backends/local-fs.js.map +1 -1
- package/dist/cache/backends/memory.d.ts +11 -6
- package/dist/cache/backends/memory.d.ts.map +1 -1
- package/dist/cache/backends/memory.js +13 -5
- package/dist/cache/backends/memory.js.map +1 -1
- package/dist/cache/backends/r2.d.ts +17 -17
- package/dist/cache/backends/r2.d.ts.map +1 -1
- package/dist/cache/backends/r2.js +14 -16
- package/dist/cache/backends/r2.js.map +1 -1
- package/dist/cache/classification-cache.d.ts +76 -34
- package/dist/cache/classification-cache.d.ts.map +1 -1
- package/dist/cache/classification-cache.js +110 -58
- package/dist/cache/classification-cache.js.map +1 -1
- package/dist/cache/coordinate.d.ts +59 -0
- package/dist/cache/coordinate.d.ts.map +1 -0
- package/dist/cache/coordinate.js +240 -0
- package/dist/cache/coordinate.js.map +1 -0
- package/dist/cache/discovery-cache.d.ts +112 -36
- package/dist/cache/discovery-cache.d.ts.map +1 -1
- package/dist/cache/discovery-cache.js +163 -42
- package/dist/cache/discovery-cache.js.map +1 -1
- package/dist/cache/tree-cache.d.ts +48 -53
- package/dist/cache/tree-cache.d.ts.map +1 -1
- package/dist/cache/tree-cache.js +87 -72
- package/dist/cache/tree-cache.js.map +1 -1
- package/dist/cache/verify-cache.d.ts +26 -21
- package/dist/cache/verify-cache.d.ts.map +1 -1
- package/dist/cache/verify-cache.js +37 -29
- package/dist/cache/verify-cache.js.map +1 -1
- package/dist/llm/ax-client.d.ts +3 -16
- package/dist/llm/ax-client.d.ts.map +1 -1
- package/dist/llm/ax-client.js +49 -2
- package/dist/llm/ax-client.js.map +1 -1
- package/dist/llm/batch-classifier.d.ts.map +1 -1
- package/dist/llm/batch-classifier.js +1 -0
- package/dist/llm/batch-classifier.js.map +1 -1
- package/dist/llm/config.d.ts +34 -8
- package/dist/llm/config.d.ts.map +1 -1
- package/dist/llm/config.js +52 -12
- package/dist/llm/config.js.map +1 -1
- package/dist/llm/discovery.d.ts.map +1 -1
- package/dist/llm/discovery.js +50 -4
- package/dist/llm/discovery.js.map +1 -1
- package/dist/llm/verification.d.ts.map +1 -1
- package/dist/llm/verification.js +45 -1
- package/dist/llm/verification.js.map +1 -1
- package/dist/security-scan/scanner.d.ts +15 -0
- package/dist/security-scan/scanner.d.ts.map +1 -1
- package/dist/security-scan/scanner.js +34 -0
- package/dist/security-scan/scanner.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package coordinate detection for cache path layout.
|
|
3
|
+
*
|
|
4
|
+
* The tree-cache backend (R2 / local-fs) uses the coordinate to build a
|
|
5
|
+
* package-aware key layout:
|
|
6
|
+
*
|
|
7
|
+
* tree/<ecosystem>/<name>@<version>/<4hex>/<sha>.json (published coords)
|
|
8
|
+
* tree/_local/<name>@<version>/<4hex>/<sha>.json (repo-root manifest)
|
|
9
|
+
* tree/_unowned/<4hex>/<sha>.json (no coordinate)
|
|
10
|
+
*
|
|
11
|
+
* Coordinate is resolved ONCE per scan from the repo root. We deliberately
|
|
12
|
+
* keep the extractor minimal (only manifest files we can parse with zero
|
|
13
|
+
* deps) so it can be folded into hot paths without measurable overhead.
|
|
14
|
+
*
|
|
15
|
+
* What's intentionally *not* in scope here:
|
|
16
|
+
* - per-file coordinate from `node_modules/<pkg>/...` paths (deferred until
|
|
17
|
+
* we actually scan transitive deps)
|
|
18
|
+
* - lockfile-derived coordinates
|
|
19
|
+
* - Maven `<artifactId>` resolution from a multi-module reactor
|
|
20
|
+
*
|
|
21
|
+
* For the first-party github sweep use case, every file in a repo shares
|
|
22
|
+
* the same coordinate (or falls through to `_unowned`), which is exactly
|
|
23
|
+
* what we want for cross-runner cache sharing.
|
|
24
|
+
*/
|
|
25
|
+
import * as fs from 'fs';
|
|
26
|
+
import * as path from 'path';
|
|
27
|
+
/**
|
|
28
|
+
* The sentinel coordinate emitted when the repo has no detectable manifest.
|
|
29
|
+
*
|
|
30
|
+
* Tree-cache content still gets cached, just under `_unowned/<4hex>/<sha>`.
|
|
31
|
+
* Re-using the singleton means the path layout is constant for un-coord'd
|
|
32
|
+
* scans and avoids per-file object allocation.
|
|
33
|
+
*/
|
|
34
|
+
export const UNOWNED = Object.freeze({
|
|
35
|
+
ecosystem: '_unowned',
|
|
36
|
+
name: '_',
|
|
37
|
+
version: '_',
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Render the coordinate as a stable path prefix and a stable hash prefix.
|
|
41
|
+
*
|
|
42
|
+
* `pathSegment` is what the backend uses to build the R2 / local-fs key.
|
|
43
|
+
* `hashPrefix` is what cache-key composers fold into the content hash so
|
|
44
|
+
* the SHA itself encodes the coordinate (no collision across packages).
|
|
45
|
+
*/
|
|
46
|
+
export function coordinateSegments(c) {
|
|
47
|
+
if (c.ecosystem === '_unowned') {
|
|
48
|
+
return { pathSegment: '_unowned', hashPrefix: '' };
|
|
49
|
+
}
|
|
50
|
+
const slug = `${c.name}@${c.version}`;
|
|
51
|
+
return {
|
|
52
|
+
pathSegment: `${c.ecosystem}/${slug}`,
|
|
53
|
+
hashPrefix: `${c.ecosystem}\0${slug}`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Detect a package coordinate from the repo root manifest files.
|
|
58
|
+
*
|
|
59
|
+
* Synchronous because it runs once per scan at scanner entry and the
|
|
60
|
+
* disk reads are tiny (`package.json` / `pom.xml`).
|
|
61
|
+
*/
|
|
62
|
+
export function detectCoordinate(repoRoot) {
|
|
63
|
+
// npm — package.json
|
|
64
|
+
const pkgJsonPath = path.join(repoRoot, 'package.json');
|
|
65
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
66
|
+
try {
|
|
67
|
+
const raw = fs.readFileSync(pkgJsonPath, 'utf8');
|
|
68
|
+
const json = JSON.parse(raw);
|
|
69
|
+
const name = typeof json.name === 'string' ? json.name.trim() : '';
|
|
70
|
+
const version = typeof json.version === 'string' ? json.version.trim() : '';
|
|
71
|
+
if (name && version) {
|
|
72
|
+
// `private: true` packages are by definition not published — use
|
|
73
|
+
// `_local` so the layout still groups them but doesn't pollute
|
|
74
|
+
// an ecosystem namespace with unpublishable coords.
|
|
75
|
+
const isPrivate = json.private === true;
|
|
76
|
+
return {
|
|
77
|
+
ecosystem: isPrivate ? '_local' : 'npm',
|
|
78
|
+
name: name.toLowerCase(),
|
|
79
|
+
version,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (name) {
|
|
83
|
+
return { ecosystem: '_local', name: name.toLowerCase(), version: 'HEAD' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// fallthrough
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// maven — pom.xml (regex-only; no XML parser dep)
|
|
91
|
+
const pomPath = path.join(repoRoot, 'pom.xml');
|
|
92
|
+
if (fs.existsSync(pomPath)) {
|
|
93
|
+
try {
|
|
94
|
+
const raw = fs.readFileSync(pomPath, 'utf8');
|
|
95
|
+
const coord = parsePomCoordinate(raw);
|
|
96
|
+
if (coord)
|
|
97
|
+
return coord;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// fallthrough
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// pyproject.toml — minimal `[project] name = "...", version = "..."` lift
|
|
104
|
+
const pyprojectPath = path.join(repoRoot, 'pyproject.toml');
|
|
105
|
+
if (fs.existsSync(pyprojectPath)) {
|
|
106
|
+
try {
|
|
107
|
+
const raw = fs.readFileSync(pyprojectPath, 'utf8');
|
|
108
|
+
const coord = parsePyprojectCoordinate(raw);
|
|
109
|
+
if (coord)
|
|
110
|
+
return coord;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// fallthrough
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Cargo.toml — `[package] name = "...", version = "..."`
|
|
117
|
+
const cargoPath = path.join(repoRoot, 'Cargo.toml');
|
|
118
|
+
if (fs.existsSync(cargoPath)) {
|
|
119
|
+
try {
|
|
120
|
+
const raw = fs.readFileSync(cargoPath, 'utf8');
|
|
121
|
+
const coord = parseCargoCoordinate(raw);
|
|
122
|
+
if (coord)
|
|
123
|
+
return coord;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// fallthrough
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// go.mod — `module <path>` (no version in manifest; use HEAD)
|
|
130
|
+
const goModPath = path.join(repoRoot, 'go.mod');
|
|
131
|
+
if (fs.existsSync(goModPath)) {
|
|
132
|
+
try {
|
|
133
|
+
const raw = fs.readFileSync(goModPath, 'utf8');
|
|
134
|
+
const match = /^module\s+(\S+)\s*$/m.exec(raw);
|
|
135
|
+
if (match?.[1]) {
|
|
136
|
+
return { ecosystem: 'go', name: match[1].toLowerCase(), version: 'HEAD' };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// fallthrough
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Fallback: use repo-root basename as a `_local/<dir>@HEAD` coordinate so
|
|
144
|
+
// re-scans of the same on-disk repo still share cache, even without a
|
|
145
|
+
// manifest. The 4-hex shard under `_unowned` is reserved for truly
|
|
146
|
+
// anonymous content (e.g. ad-hoc file inputs from CI).
|
|
147
|
+
const basename = path.basename(path.resolve(repoRoot));
|
|
148
|
+
if (basename && basename !== '/' && basename !== '.') {
|
|
149
|
+
return { ecosystem: '_local', name: basename.toLowerCase(), version: 'HEAD' };
|
|
150
|
+
}
|
|
151
|
+
return UNOWNED;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Parse `<groupId>:<artifactId>:<version>` from the *top-level* of a pom.
|
|
155
|
+
*
|
|
156
|
+
* We intentionally read the project's own `<groupId>/<artifactId>/<version>`
|
|
157
|
+
* and fall through to `<parent>` only when the child block omits them
|
|
158
|
+
* (standard Maven inheritance). Sub-modules are not recursed; the cache
|
|
159
|
+
* shares the parent coordinate which is fine for cross-runner sharing.
|
|
160
|
+
*/
|
|
161
|
+
function parsePomCoordinate(xml) {
|
|
162
|
+
// Strip XML comments to avoid commented-out parent blocks shadowing real ones.
|
|
163
|
+
const cleaned = xml.replace(/<!--[\s\S]*?-->/g, '');
|
|
164
|
+
// Find the top-level <project> children only — i.e. tags that appear
|
|
165
|
+
// before any sub-element opens. Cheap approximation: take the first
|
|
166
|
+
// occurrence of each tag at depth-1 by finding the first match.
|
|
167
|
+
const projectMatch = /<project\b[^>]*>([\s\S]*?)<\/project>/.exec(cleaned);
|
|
168
|
+
if (!projectMatch)
|
|
169
|
+
return null;
|
|
170
|
+
const body = projectMatch[1];
|
|
171
|
+
const direct = readDirectChildren(body);
|
|
172
|
+
let groupId = direct.groupId;
|
|
173
|
+
let artifactId = direct.artifactId;
|
|
174
|
+
let version = direct.version;
|
|
175
|
+
if (!groupId || !version) {
|
|
176
|
+
const parentMatch = /<parent\b[^>]*>([\s\S]*?)<\/parent>/.exec(body);
|
|
177
|
+
if (parentMatch) {
|
|
178
|
+
const parent = readDirectChildren(parentMatch[1]);
|
|
179
|
+
groupId ??= parent.groupId;
|
|
180
|
+
version ??= parent.version;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!groupId || !artifactId || !version)
|
|
184
|
+
return null;
|
|
185
|
+
return {
|
|
186
|
+
ecosystem: 'maven',
|
|
187
|
+
name: `${groupId.toLowerCase()}:${artifactId.toLowerCase()}`,
|
|
188
|
+
version,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function readDirectChildren(body) {
|
|
192
|
+
const pick = (tag) => {
|
|
193
|
+
const re = new RegExp(`<${tag}>\\s*([^<\\s][^<]*?)\\s*</${tag}>`);
|
|
194
|
+
const m = re.exec(body);
|
|
195
|
+
return m?.[1]?.trim();
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
groupId: pick('groupId'),
|
|
199
|
+
artifactId: pick('artifactId'),
|
|
200
|
+
version: pick('version'),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function parsePyprojectCoordinate(toml) {
|
|
204
|
+
// Look for `[project]` table, then `name = "..."` and `version = "..."`
|
|
205
|
+
// inside it. Handle `[tool.poetry]` as a fallback for legacy Poetry
|
|
206
|
+
// layouts.
|
|
207
|
+
const tryTable = (header) => {
|
|
208
|
+
const re = new RegExp(`\\[${header}\\]([\\s\\S]*?)(?=\\n\\[|$)`, 'm');
|
|
209
|
+
const m = re.exec(toml);
|
|
210
|
+
if (!m)
|
|
211
|
+
return null;
|
|
212
|
+
const body = m[1];
|
|
213
|
+
const nameMatch = /^\s*name\s*=\s*['"]([^'"]+)['"]/m.exec(body);
|
|
214
|
+
const versionMatch = /^\s*version\s*=\s*['"]([^'"]+)['"]/m.exec(body);
|
|
215
|
+
if (!nameMatch || !versionMatch)
|
|
216
|
+
return null;
|
|
217
|
+
return {
|
|
218
|
+
ecosystem: 'pypi',
|
|
219
|
+
name: nameMatch[1].toLowerCase(),
|
|
220
|
+
version: versionMatch[1],
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
return tryTable('project') ?? tryTable('tool\\.poetry');
|
|
224
|
+
}
|
|
225
|
+
function parseCargoCoordinate(toml) {
|
|
226
|
+
const m = /\[package\]([\s\S]*?)(?=\n\[|$)/m.exec(toml);
|
|
227
|
+
if (!m)
|
|
228
|
+
return null;
|
|
229
|
+
const body = m[1];
|
|
230
|
+
const nameMatch = /^\s*name\s*=\s*['"]([^'"]+)['"]/m.exec(body);
|
|
231
|
+
const versionMatch = /^\s*version\s*=\s*['"]([^'"]+)['"]/m.exec(body);
|
|
232
|
+
if (!nameMatch || !versionMatch)
|
|
233
|
+
return null;
|
|
234
|
+
return {
|
|
235
|
+
ecosystem: 'cargo',
|
|
236
|
+
name: nameMatch[1].toLowerCase(),
|
|
237
|
+
version: versionMatch[1],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=coordinate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate.js","sourceRoot":"","sources":["../../src/cache/coordinate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,71 +1,147 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Discovery Cache
|
|
3
3
|
*
|
|
4
|
-
* Persistent on-disk cache for LLM discovery results per
|
|
5
|
-
*
|
|
6
|
-
* #155). Sits in front of the in-memory `_discoveryDedupCache` from
|
|
7
|
-
* discovery.ts (#136) so that identical method-level discovery requests
|
|
8
|
-
* survive across processes, CI runs, and developer iterations — not just
|
|
9
|
-
* within one process.
|
|
4
|
+
* Persistent on-disk cache for LLM discovery results per method
|
|
5
|
+
* (cognium-ai#134, P2 of epic #155, key reshape #158 Round 2).
|
|
10
6
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* Sits in front of the in-memory `_discoveryDedupCache` from
|
|
8
|
+
* discovery.ts (#136) so identical method-level discovery requests
|
|
9
|
+
* survive across processes, CI runs, and developer iterations.
|
|
14
10
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
11
|
+
* **Key composition (Round 2 — 2.28.0):**
|
|
12
|
+
*
|
|
13
|
+
* content = methodSha + '\0' + sortedCweSet + '\0' + language
|
|
14
|
+
* + '\0' + PROMPT_VER
|
|
17
15
|
* classifier = 'discovery-method'
|
|
18
|
-
* fingerprint =
|
|
16
|
+
* fingerprint = getDiscoveryModelFingerprint() // discovery model ONLY
|
|
17
|
+
*
|
|
18
|
+
* **D6 fix:** dropped `className` and `methodName` from the key. They
|
|
19
|
+
* were redundant — if two different method *bodies* hash to the same
|
|
20
|
+
* SHA-256, they are byte-identical and the LLM's answer would be too.
|
|
21
|
+
* Including names also broke cache sharing on refactors that renamed
|
|
22
|
+
* a method without changing its body. (Names are still passed to the
|
|
23
|
+
* LLM as context inside the prompt; we just don't key the cache on
|
|
24
|
+
* them.)
|
|
25
|
+
*
|
|
26
|
+
* **D6 also adds `language`:** the same byte-identical snippet means
|
|
27
|
+
* very different things in Java vs Kotlin vs Scala — without language
|
|
28
|
+
* in the key, a Java result could be reused for a Kotlin call. Likely
|
|
29
|
+
* benign in practice but a correctness gap worth closing.
|
|
19
30
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* discovery entries).
|
|
31
|
+
* **D2 fix:** uses the discovery-only model fingerprint instead of the
|
|
32
|
+
* joint disc+verify+component fingerprint, so swapping the verify
|
|
33
|
+
* model doesn't invalidate discovery results.
|
|
24
34
|
*
|
|
25
|
-
*
|
|
26
|
-
* - Edit one method → only that method's entry invalidates
|
|
27
|
-
* (methodCode SHA-256 is part of the key)
|
|
28
|
-
* - Add a new CWE → existing entries still hit for old CWE-sets;
|
|
29
|
-
* new CWE-set requests miss
|
|
30
|
-
* - Change disc model → entire cache invalidates
|
|
31
|
-
* (fingerprint is part of the singleton key)
|
|
35
|
+
* Cache namespace: `discovery` (separate from `tree` and `verify`).
|
|
32
36
|
*
|
|
33
37
|
* Env toggles:
|
|
34
|
-
* LLM_DISCOVERY_CACHE=0 → disable persistent discovery cache
|
|
35
|
-
* (in-memory dedup from #136 still applies)
|
|
38
|
+
* LLM_DISCOVERY_CACHE=0 → disable persistent discovery cache.
|
|
36
39
|
*/
|
|
37
40
|
import { ClassificationCache } from './classification-cache.js';
|
|
38
41
|
import { type LLMConfig } from '../llm/config.js';
|
|
39
42
|
import type { DiscoveryResult } from '../llm/discovery.js';
|
|
43
|
+
/** Source item shape returned by `discoverSourcesForLanguage`. */
|
|
44
|
+
export interface LangDiscoveredSource {
|
|
45
|
+
line: number;
|
|
46
|
+
variable: string;
|
|
47
|
+
type: string;
|
|
48
|
+
confidence: number;
|
|
49
|
+
reasoning: string;
|
|
50
|
+
}
|
|
51
|
+
/** Sink item shape returned by `discoverSinksForLanguage`. */
|
|
52
|
+
export interface LangDiscoveredSink {
|
|
53
|
+
line: number;
|
|
54
|
+
method: string;
|
|
55
|
+
type: string;
|
|
56
|
+
cwe: string;
|
|
57
|
+
argPositions: number[];
|
|
58
|
+
confidence: number;
|
|
59
|
+
reasoning: string;
|
|
60
|
+
}
|
|
40
61
|
/**
|
|
41
62
|
* Get the discovery-phase persistent cache, keyed by per-phase model
|
|
42
|
-
* fingerprint. When `LLM_DISCOVERY_CACHE=0` returns a disabled cache
|
|
43
|
-
* no-ops on get/set (callers don't need to branch).
|
|
63
|
+
* fingerprint. When `LLM_DISCOVERY_CACHE=0` returns a disabled cache.
|
|
44
64
|
*/
|
|
45
65
|
export declare function getDiscoveryCache(config?: LLMConfig): ClassificationCache<DiscoveryResult>;
|
|
46
66
|
/** Reset the module-scoped singleton — test-only. */
|
|
47
67
|
export declare function resetDiscoveryCache(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Get the persistent cache for `discoverSourcesForLanguage` (cognium-ai#161).
|
|
70
|
+
* Uses the same `'discovery'` namespace and per-phase discovery model
|
|
71
|
+
* fingerprint as the per-CWE cache, but a different classifier so the two
|
|
72
|
+
* never collide. Honors `LLM_DISCOVERY_CACHE=0` to disable.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getDiscoverSourcesLanguageCache(config?: LLMConfig): ClassificationCache<LangDiscoveredSource[]>;
|
|
75
|
+
/**
|
|
76
|
+
* Get the persistent cache for `discoverSinksForLanguage` (cognium-ai#161).
|
|
77
|
+
* See `getDiscoverSourcesLanguageCache` for the namespace/fingerprint
|
|
78
|
+
* rationale.
|
|
79
|
+
*/
|
|
80
|
+
export declare function getDiscoverSinksLanguageCache(config?: LLMConfig): ClassificationCache<LangDiscoveredSink[]>;
|
|
81
|
+
/**
|
|
82
|
+
* Compute the `(content, classifier)` tuple for the source-discoverer
|
|
83
|
+
* cache. Components (in order):
|
|
84
|
+
* - language — Java vs Kotlin etc. (same rationale as D6 above)
|
|
85
|
+
* - methodSha — code edit invalidates only that entry
|
|
86
|
+
* - functionName — same body called as different methods is a distinct
|
|
87
|
+
* prompt (the name is templated into the user prompt)
|
|
88
|
+
* - moduleRole — passed verbatim into the prompt
|
|
89
|
+
* - existingSourcesSha — the "what we already found" context changes
|
|
90
|
+
* the prompt, so it must change the key
|
|
91
|
+
* - LANG_DISCOVERER_PROMPT_VER — global invalidator for the language
|
|
92
|
+
* discoverer prompt template
|
|
93
|
+
*/
|
|
94
|
+
export declare function computeDiscoverSourcesLanguageCacheKey(input: {
|
|
95
|
+
language: string;
|
|
96
|
+
code: string;
|
|
97
|
+
functionName: string;
|
|
98
|
+
moduleRole: string;
|
|
99
|
+
existingSources: string;
|
|
100
|
+
}): {
|
|
101
|
+
content: string;
|
|
102
|
+
classifier: string;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Compute the `(content, classifier)` tuple for the sink-discoverer
|
|
106
|
+
* cache. Mirrors `computeDiscoverSourcesLanguageCacheKey` but uses the
|
|
107
|
+
* sink-prompt context (`methodCalls`, `existingSinks`) instead of the
|
|
108
|
+
* source-prompt context.
|
|
109
|
+
*/
|
|
110
|
+
export declare function computeDiscoverSinksLanguageCacheKey(input: {
|
|
111
|
+
language: string;
|
|
112
|
+
code: string;
|
|
113
|
+
functionName: string;
|
|
114
|
+
methodCalls: string;
|
|
115
|
+
existingSinks: string;
|
|
116
|
+
}): {
|
|
117
|
+
content: string;
|
|
118
|
+
classifier: string;
|
|
119
|
+
};
|
|
120
|
+
/** Test-only: expose the language-discoverer prompt-version sentinel. */
|
|
121
|
+
export declare function getLangDiscovererCachePromptVersion(): string;
|
|
48
122
|
/**
|
|
49
123
|
* Compute the `(content, classifier)` tuple for the underlying
|
|
50
124
|
* ClassificationCache. The fingerprint dimension is folded in by the
|
|
51
|
-
* cache itself
|
|
125
|
+
* cache itself.
|
|
52
126
|
*
|
|
53
127
|
* Components:
|
|
54
|
-
* -
|
|
55
|
-
* - methodName — per-method granularity (matches the per-method LLM call)
|
|
56
|
-
* - methodCode SHA-256 — method edit invalidates only that method's entry
|
|
128
|
+
* - methodCode SHA-256 — method edit invalidates only that entry
|
|
57
129
|
* - sortedCweSet — different CWE-set requests on the same method don't
|
|
58
|
-
* reuse each other's prompts
|
|
59
|
-
* -
|
|
60
|
-
*
|
|
130
|
+
* reuse each other's prompts
|
|
131
|
+
* - language — Java vs Kotlin etc. (D6 fix)
|
|
132
|
+
* - PROMPT_VER — global discovery-prompt invalidator
|
|
133
|
+
*
|
|
134
|
+
* Note: `className` / `methodName` are intentionally NOT in the key
|
|
135
|
+
* (D6 fix). They're prompt-context, not identity.
|
|
61
136
|
*/
|
|
62
137
|
export declare function computeDiscoveryCacheKey(input: {
|
|
63
|
-
className: string;
|
|
64
|
-
methodName: string;
|
|
65
138
|
methodCode: string;
|
|
66
139
|
targetCWEs: ReadonlyArray<string>;
|
|
140
|
+
language: string;
|
|
67
141
|
}): {
|
|
68
142
|
content: string;
|
|
69
143
|
classifier: string;
|
|
70
144
|
};
|
|
145
|
+
/** Test-only: expose the prompt-version sentinel. */
|
|
146
|
+
export declare function getDiscoveryCachePromptVersion(): string;
|
|
71
147
|
//# sourceMappingURL=discovery-cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery-cache.d.ts","sourceRoot":"","sources":["../../src/cache/discovery-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"discovery-cache.d.ts","sourceRoot":"","sources":["../../src/cache/discovery-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAIH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAuB3D,kEAAkE;AAClE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,eAAe,CAAC,CAetC;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,oBAAoB,EAAE,CAAC,CAkB7C;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,kBAAkB,EAAE,CAAC,CAkB3C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sCAAsC,CAAC,KAAK,EAAE;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAc1C;AAED;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,KAAK,EAAE;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAe1C;AAED,yEAAyE;AACzE,wBAAgB,mCAAmC,IAAI,MAAM,CAE5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAO1C;AAED,qDAAqD;AACrD,wBAAgB,8BAA8B,IAAI,MAAM,CAEvD"}
|