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
|
@@ -1,62 +1,77 @@
|
|
|
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 * as crypto from 'crypto';
|
|
38
41
|
import * as path from 'path';
|
|
39
42
|
import { ClassificationCache } from './classification-cache.js';
|
|
40
|
-
import { getDefaultLLMConfig,
|
|
41
|
-
const DISCOVERY_CACHE_SUBDIR = path.join('.cognium', 'cache'
|
|
43
|
+
import { getDefaultLLMConfig, getDiscoveryModelFingerprint } from '../llm/config.js';
|
|
44
|
+
const DISCOVERY_CACHE_SUBDIR = path.join('.cognium', 'cache');
|
|
45
|
+
/**
|
|
46
|
+
* Prompt-version sentinel for discovery. Bump when the discovery prompt
|
|
47
|
+
* template materially changes (e.g. new few-shot examples, different
|
|
48
|
+
* instructions for `exploitable`, new output schema).
|
|
49
|
+
*/
|
|
50
|
+
const PROMPT_VER = 'disc-v1';
|
|
42
51
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
52
|
+
* Separate prompt-version sentinel for the language-specific discoverers
|
|
53
|
+
* (`discoverSourcesForLanguage` / `discoverSinksForLanguage`). These use
|
|
54
|
+
* a different prompt template (`getPrompt(language, 'discoverSources' |
|
|
55
|
+
* 'discoverSinks')`) than the per-CWE `discoverInMethod` flow, so they
|
|
56
|
+
* need an independent version dimension. cognium-ai#161.
|
|
47
57
|
*/
|
|
58
|
+
const LANG_DISCOVERER_PROMPT_VER = 'lang-disc-v1';
|
|
48
59
|
let _instance = null;
|
|
49
60
|
let _instanceFingerprint = '';
|
|
61
|
+
let _sourcesLangInstance = null;
|
|
62
|
+
let _sourcesLangFingerprint = '';
|
|
63
|
+
let _sinksLangInstance = null;
|
|
64
|
+
let _sinksLangFingerprint = '';
|
|
50
65
|
/**
|
|
51
66
|
* Get the discovery-phase persistent cache, keyed by per-phase model
|
|
52
|
-
* fingerprint. When `LLM_DISCOVERY_CACHE=0` returns a disabled cache
|
|
53
|
-
* no-ops on get/set (callers don't need to branch).
|
|
67
|
+
* fingerprint. When `LLM_DISCOVERY_CACHE=0` returns a disabled cache.
|
|
54
68
|
*/
|
|
55
69
|
export function getDiscoveryCache(config = getDefaultLLMConfig()) {
|
|
56
70
|
const enabled = process.env.LLM_DISCOVERY_CACHE !== '0';
|
|
57
|
-
const fingerprint =
|
|
71
|
+
const fingerprint = getDiscoveryModelFingerprint(config);
|
|
58
72
|
if (!_instance || _instanceFingerprint !== fingerprint || _instance.getModelFingerprint() !== fingerprint) {
|
|
59
73
|
_instance = new ClassificationCache({
|
|
74
|
+
namespace: 'discovery',
|
|
60
75
|
cacheDir: path.join(process.cwd(), DISCOVERY_CACHE_SUBDIR),
|
|
61
76
|
modelFingerprint: fingerprint,
|
|
62
77
|
enabled,
|
|
@@ -69,27 +84,133 @@ export function getDiscoveryCache(config = getDefaultLLMConfig()) {
|
|
|
69
84
|
export function resetDiscoveryCache() {
|
|
70
85
|
_instance = null;
|
|
71
86
|
_instanceFingerprint = '';
|
|
87
|
+
_sourcesLangInstance = null;
|
|
88
|
+
_sourcesLangFingerprint = '';
|
|
89
|
+
_sinksLangInstance = null;
|
|
90
|
+
_sinksLangFingerprint = '';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the persistent cache for `discoverSourcesForLanguage` (cognium-ai#161).
|
|
94
|
+
* Uses the same `'discovery'` namespace and per-phase discovery model
|
|
95
|
+
* fingerprint as the per-CWE cache, but a different classifier so the two
|
|
96
|
+
* never collide. Honors `LLM_DISCOVERY_CACHE=0` to disable.
|
|
97
|
+
*/
|
|
98
|
+
export function getDiscoverSourcesLanguageCache(config = getDefaultLLMConfig()) {
|
|
99
|
+
const enabled = process.env.LLM_DISCOVERY_CACHE !== '0';
|
|
100
|
+
const fingerprint = getDiscoveryModelFingerprint(config);
|
|
101
|
+
if (!_sourcesLangInstance
|
|
102
|
+
|| _sourcesLangFingerprint !== fingerprint
|
|
103
|
+
|| _sourcesLangInstance.getModelFingerprint() !== fingerprint) {
|
|
104
|
+
_sourcesLangInstance = new ClassificationCache({
|
|
105
|
+
namespace: 'discovery',
|
|
106
|
+
cacheDir: path.join(process.cwd(), DISCOVERY_CACHE_SUBDIR),
|
|
107
|
+
modelFingerprint: fingerprint,
|
|
108
|
+
enabled,
|
|
109
|
+
});
|
|
110
|
+
_sourcesLangFingerprint = fingerprint;
|
|
111
|
+
}
|
|
112
|
+
return _sourcesLangInstance;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the persistent cache for `discoverSinksForLanguage` (cognium-ai#161).
|
|
116
|
+
* See `getDiscoverSourcesLanguageCache` for the namespace/fingerprint
|
|
117
|
+
* rationale.
|
|
118
|
+
*/
|
|
119
|
+
export function getDiscoverSinksLanguageCache(config = getDefaultLLMConfig()) {
|
|
120
|
+
const enabled = process.env.LLM_DISCOVERY_CACHE !== '0';
|
|
121
|
+
const fingerprint = getDiscoveryModelFingerprint(config);
|
|
122
|
+
if (!_sinksLangInstance
|
|
123
|
+
|| _sinksLangFingerprint !== fingerprint
|
|
124
|
+
|| _sinksLangInstance.getModelFingerprint() !== fingerprint) {
|
|
125
|
+
_sinksLangInstance = new ClassificationCache({
|
|
126
|
+
namespace: 'discovery',
|
|
127
|
+
cacheDir: path.join(process.cwd(), DISCOVERY_CACHE_SUBDIR),
|
|
128
|
+
modelFingerprint: fingerprint,
|
|
129
|
+
enabled,
|
|
130
|
+
});
|
|
131
|
+
_sinksLangFingerprint = fingerprint;
|
|
132
|
+
}
|
|
133
|
+
return _sinksLangInstance;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Compute the `(content, classifier)` tuple for the source-discoverer
|
|
137
|
+
* cache. Components (in order):
|
|
138
|
+
* - language — Java vs Kotlin etc. (same rationale as D6 above)
|
|
139
|
+
* - methodSha — code edit invalidates only that entry
|
|
140
|
+
* - functionName — same body called as different methods is a distinct
|
|
141
|
+
* prompt (the name is templated into the user prompt)
|
|
142
|
+
* - moduleRole — passed verbatim into the prompt
|
|
143
|
+
* - existingSourcesSha — the "what we already found" context changes
|
|
144
|
+
* the prompt, so it must change the key
|
|
145
|
+
* - LANG_DISCOVERER_PROMPT_VER — global invalidator for the language
|
|
146
|
+
* discoverer prompt template
|
|
147
|
+
*/
|
|
148
|
+
export function computeDiscoverSourcesLanguageCacheKey(input) {
|
|
149
|
+
const methodSha = crypto.createHash('sha256').update(input.code).digest('hex');
|
|
150
|
+
const existingSha = crypto.createHash('sha256').update(input.existingSources).digest('hex');
|
|
151
|
+
return {
|
|
152
|
+
content: [
|
|
153
|
+
input.language,
|
|
154
|
+
methodSha,
|
|
155
|
+
input.functionName,
|
|
156
|
+
input.moduleRole,
|
|
157
|
+
existingSha,
|
|
158
|
+
LANG_DISCOVERER_PROMPT_VER,
|
|
159
|
+
].join('\0'),
|
|
160
|
+
classifier: 'discover-sources-lang',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Compute the `(content, classifier)` tuple for the sink-discoverer
|
|
165
|
+
* cache. Mirrors `computeDiscoverSourcesLanguageCacheKey` but uses the
|
|
166
|
+
* sink-prompt context (`methodCalls`, `existingSinks`) instead of the
|
|
167
|
+
* source-prompt context.
|
|
168
|
+
*/
|
|
169
|
+
export function computeDiscoverSinksLanguageCacheKey(input) {
|
|
170
|
+
const methodSha = crypto.createHash('sha256').update(input.code).digest('hex');
|
|
171
|
+
const methodCallsSha = crypto.createHash('sha256').update(input.methodCalls).digest('hex');
|
|
172
|
+
const existingSha = crypto.createHash('sha256').update(input.existingSinks).digest('hex');
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
input.language,
|
|
176
|
+
methodSha,
|
|
177
|
+
input.functionName,
|
|
178
|
+
methodCallsSha,
|
|
179
|
+
existingSha,
|
|
180
|
+
LANG_DISCOVERER_PROMPT_VER,
|
|
181
|
+
].join('\0'),
|
|
182
|
+
classifier: 'discover-sinks-lang',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** Test-only: expose the language-discoverer prompt-version sentinel. */
|
|
186
|
+
export function getLangDiscovererCachePromptVersion() {
|
|
187
|
+
return LANG_DISCOVERER_PROMPT_VER;
|
|
72
188
|
}
|
|
73
189
|
/**
|
|
74
190
|
* Compute the `(content, classifier)` tuple for the underlying
|
|
75
191
|
* ClassificationCache. The fingerprint dimension is folded in by the
|
|
76
|
-
* cache itself
|
|
192
|
+
* cache itself.
|
|
77
193
|
*
|
|
78
194
|
* Components:
|
|
79
|
-
* -
|
|
80
|
-
* - methodName — per-method granularity (matches the per-method LLM call)
|
|
81
|
-
* - methodCode SHA-256 — method edit invalidates only that method's entry
|
|
195
|
+
* - methodCode SHA-256 — method edit invalidates only that entry
|
|
82
196
|
* - sortedCweSet — different CWE-set requests on the same method don't
|
|
83
|
-
* reuse each other's prompts
|
|
84
|
-
* -
|
|
85
|
-
*
|
|
197
|
+
* reuse each other's prompts
|
|
198
|
+
* - language — Java vs Kotlin etc. (D6 fix)
|
|
199
|
+
* - PROMPT_VER — global discovery-prompt invalidator
|
|
200
|
+
*
|
|
201
|
+
* Note: `className` / `methodName` are intentionally NOT in the key
|
|
202
|
+
* (D6 fix). They're prompt-context, not identity.
|
|
86
203
|
*/
|
|
87
204
|
export function computeDiscoveryCacheKey(input) {
|
|
88
205
|
const methodSha = crypto.createHash('sha256').update(input.methodCode).digest('hex');
|
|
89
206
|
const sortedCweSet = [...input.targetCWEs].sort().join(',');
|
|
90
207
|
return {
|
|
91
|
-
content:
|
|
208
|
+
content: [methodSha, sortedCweSet, input.language, PROMPT_VER].join('\0'),
|
|
92
209
|
classifier: 'discovery-method',
|
|
93
210
|
};
|
|
94
211
|
}
|
|
212
|
+
/** Test-only: expose the prompt-version sentinel. */
|
|
213
|
+
export function getDiscoveryCachePromptVersion() {
|
|
214
|
+
return PROMPT_VER;
|
|
215
|
+
}
|
|
95
216
|
//# sourceMappingURL=discovery-cache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery-cache.js","sourceRoot":"","sources":["../../src/cache/discovery-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"discovery-cache.js","sourceRoot":"","sources":["../../src/cache/discovery-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,56 +2,45 @@
|
|
|
2
2
|
* Tree Cache
|
|
3
3
|
*
|
|
4
4
|
* Persistent on-disk cache for `runPatternMatch` output (cognium-ai#153,
|
|
5
|
-
* P3 of epic #155
|
|
6
|
-
* content + engine version, so caching it eliminates the parse +
|
|
7
|
-
* AST-walk + SAST-rule firing on warm runs.
|
|
5
|
+
* P3 of epic #155, key reshape #158 Round 2).
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* is the smallest empirical win in the cache stack (no LLM-token
|
|
13
|
-
* reduction, only CPU + wall-clock), but it stacks cleanly on the
|
|
14
|
-
* #145 static pre-filter — a cached tree means the pre-filter has
|
|
15
|
-
* more to chew on, which reduces enrichment calls downstream.
|
|
7
|
+
* Pattern-match output is a pure function of file content + engine
|
|
8
|
+
* version + active rule set, so caching it eliminates the parse +
|
|
9
|
+
* AST-walk + SAST-rule firing on warm runs.
|
|
16
10
|
*
|
|
17
11
|
* **Scope:** caches `PatternMatchResult` only, NOT full `CircleIR`.
|
|
18
|
-
* The full graph (cross-file CFG, project-level type hierarchy) is
|
|
19
|
-
* too entangled with project context to cache safely without a full
|
|
20
|
-
* `CircleIR.serialize()` upstream — that's a separate D3 design
|
|
21
|
-
* question, file separately if pursued.
|
|
22
12
|
*
|
|
23
|
-
* **Key composition:**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* ruleSetFingerprint
|
|
13
|
+
* **Key composition (Round 2 — 2.28.0):**
|
|
14
|
+
*
|
|
15
|
+
* content = sourceSha + '\0' + circleIrVersion + '\0' +
|
|
16
|
+
* circleIrAiVersion + '\0' + language + '\0' +
|
|
17
|
+
* ruleSetFingerprint + '\0' + PROMPT_VER
|
|
29
18
|
* classifier = 'tree-pattern-match'
|
|
30
19
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
20
|
+
* The composed (content, classifier) is then fed into ClassificationCache,
|
|
21
|
+
* which folds in `SCHEMA_VER`, the model fingerprint (`''` for tree since
|
|
22
|
+
* the static path doesn't call any LLM), and the coordinate hash-prefix.
|
|
33
23
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
24
|
+
* Any of: file edit, circle-ir bump, circle-ir-ai bump, language change,
|
|
25
|
+
* rule-set change, prompt-version bump, schema bump → cache miss.
|
|
26
|
+
*
|
|
27
|
+
* **D1 fix (Round 2):** `computeRuleSetFingerprint` now accepts an
|
|
28
|
+
* optional `repoRoot` and, when the projectProfile is a Map, normalizes
|
|
29
|
+
* its keys to repo-relative paths before sorting. This eliminates the
|
|
30
|
+
* absolute-path leak that caused cross-runner tree-cache misses in the
|
|
31
|
+
* 2-repo R2 validation: runner-A wrote keys hashing
|
|
32
|
+
* `/tmp/r2-validation/repos/A/...`, runner-B wrote keys hashing
|
|
33
|
+
* `/tmp/r2-validation/repos/B/...`, and the bucket doubled instead of
|
|
34
|
+
* sharing.
|
|
38
35
|
*
|
|
39
36
|
* **Env toggles:**
|
|
40
37
|
* LLM_TREE_CACHE=0 → disable persistent tree cache.
|
|
41
|
-
*
|
|
42
|
-
* **Versioning note:** unlike discovery-cache / verify-cache, this
|
|
43
|
-
* cache is NOT keyed on the LLM model fingerprint (pattern-match is
|
|
44
|
-
* static — circle-ir doesn't call any LLM). It IS keyed on the
|
|
45
|
-
* circle-ir and circle-ir-ai package versions because either bump
|
|
46
|
-
* can change the workflow-level transforms applied to the result.
|
|
47
38
|
*/
|
|
48
39
|
import { ClassificationCache } from './classification-cache.js';
|
|
49
40
|
import type { TaintSource, TaintSink, TypeInfo, DFG, SastFinding } from 'circle-ir';
|
|
50
41
|
/**
|
|
51
42
|
* Shape persisted to disk. Mirrors the return of `runPatternMatch` in
|
|
52
|
-
* `src/agents/mastra/workflow.ts`. All fields are plain JSON-serializable
|
|
53
|
-
* (no class instances, no cycles) so the underlying ClassificationCache
|
|
54
|
-
* round-trip is safe.
|
|
43
|
+
* `src/agents/mastra/workflow.ts`. All fields are plain JSON-serializable.
|
|
55
44
|
*/
|
|
56
45
|
export interface PatternMatchResult {
|
|
57
46
|
patternSources: TaintSource[];
|
|
@@ -63,7 +52,7 @@ export interface PatternMatchResult {
|
|
|
63
52
|
}
|
|
64
53
|
/**
|
|
65
54
|
* Get the tree-cache singleton. When `LLM_TREE_CACHE=0` returns a
|
|
66
|
-
* disabled cache that no-ops on get/set
|
|
55
|
+
* disabled cache that no-ops on get/set.
|
|
67
56
|
*/
|
|
68
57
|
export declare function getTreeCache(): ClassificationCache<PatternMatchResult>;
|
|
69
58
|
/** Reset the module-scoped singleton — test-only. */
|
|
@@ -71,18 +60,6 @@ export declare function resetTreeCache(): void;
|
|
|
71
60
|
/**
|
|
72
61
|
* Compute the `(content, classifier)` tuple for the underlying
|
|
73
62
|
* ClassificationCache.
|
|
74
|
-
*
|
|
75
|
-
* Components:
|
|
76
|
-
* - source code SHA-256 — file edit invalidates only that file's entry
|
|
77
|
-
* - circleIrVersion — bumping circle-ir invalidates all entries
|
|
78
|
-
* - circleIrAiVersion — bumping circle-ir-ai (which may change
|
|
79
|
-
* workflow-level transforms applied to pattern-match output)
|
|
80
|
-
* invalidates all entries
|
|
81
|
-
* - language — same source, different language tag must miss (the
|
|
82
|
-
* analyzer's behavior depends on the language argument)
|
|
83
|
-
* - ruleSetFingerprint — caller-supplied digest of the active rule
|
|
84
|
-
* set (disabledPasses + projectProfile). Empty string is fine for
|
|
85
|
-
* the default rule set.
|
|
86
63
|
*/
|
|
87
64
|
export declare function computeTreeCacheKey(input: {
|
|
88
65
|
sourceCode: string;
|
|
@@ -93,19 +70,37 @@ export declare function computeTreeCacheKey(input: {
|
|
|
93
70
|
classifier: string;
|
|
94
71
|
};
|
|
95
72
|
/**
|
|
96
|
-
* Compute a stable fingerprint over an arbitrary rule-set descriptor
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
73
|
+
* Compute a stable fingerprint over an arbitrary rule-set descriptor.
|
|
74
|
+
*
|
|
75
|
+
* Order-stable for `disabledPasses` so re-ordering `--disable a,b` vs
|
|
76
|
+
* `--disable b,a` still hits the cache.
|
|
77
|
+
*
|
|
78
|
+
* D1 (Round 2): when `projectProfile` is a `Map<string, ProjectProfile>`
|
|
79
|
+
* keyed by absolute file paths, the keys are relativized to `repoRoot`
|
|
80
|
+
* and sorted. Without this, two runners that check out the same repo to
|
|
81
|
+
* different absolute paths produce different fingerprints and the
|
|
82
|
+
* cross-runner R2 cache never shares.
|
|
83
|
+
*
|
|
84
|
+
* When `repoRoot` is omitted and `projectProfile` is a Map, we fall back
|
|
85
|
+
* to dropping the keys entirely and hashing only the sorted *values*
|
|
86
|
+
* (still order-stable, still cross-runner-safe; loses the per-file
|
|
87
|
+
* granularity but no caller currently needs it for fingerprinting).
|
|
101
88
|
*/
|
|
102
89
|
export declare function computeRuleSetFingerprint(input: {
|
|
103
90
|
disabledPasses?: string[];
|
|
104
91
|
projectProfile?: unknown;
|
|
92
|
+
repoRoot?: string;
|
|
105
93
|
}): string;
|
|
94
|
+
/**
|
|
95
|
+
* Render a project-profile descriptor as a stable, cross-runner-safe
|
|
96
|
+
* string. Exported for the test suite to assert on the D1 fix.
|
|
97
|
+
*/
|
|
98
|
+
export declare function normalizeProjectProfile(profile: unknown, repoRoot?: string): string;
|
|
106
99
|
/** Test-only: expose the captured engine versions for assertions. */
|
|
107
100
|
export declare function getTreeCacheVersions(): {
|
|
108
101
|
circleIrVersion: string;
|
|
109
102
|
circleIrAiVersion: string;
|
|
110
103
|
};
|
|
104
|
+
/** Test-only: expose the prompt-version sentinel. */
|
|
105
|
+
export declare function getTreeCachePromptVersion(): string;
|
|
111
106
|
//# sourceMappingURL=tree-cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-cache.d.ts","sourceRoot":"","sources":["../../src/cache/tree-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tree-cache.d.ts","sourceRoot":"","sources":["../../src/cache/tree-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAMH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAcpF;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,WAAW,EAAE,CAAC;IAC9B,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,GAAG,EAAE,GAAG,CAAC;CACV;AAkCD;;;GAGG;AACH,wBAAgB,YAAY,IAAI,mBAAmB,CAAC,kBAAkB,CAAC,CAYtE;AAED,qDAAqD;AACrD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAc1C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAKT;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAsBnF;AAED,qEAAqE;AACrE,wBAAgB,oBAAoB,IAAI;IACtC,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAKA;AAED,qDAAqD;AACrD,wBAAgB,yBAAyB,IAAI,MAAM,CAElD"}
|