circle-ir-ai 2.22.0 → 2.27.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/CHANGELOG.md +390 -0
- package/dist/agents/mastra/workflow.d.ts.map +1 -1
- package/dist/agents/mastra/workflow.js +41 -1
- package/dist/agents/mastra/workflow.js.map +1 -1
- package/dist/cache/backend.d.ts +52 -0
- package/dist/cache/backend.d.ts.map +1 -0
- package/dist/cache/backend.js +19 -0
- package/dist/cache/backend.js.map +1 -0
- package/dist/cache/backends/index.d.ts +32 -0
- package/dist/cache/backends/index.d.ts.map +1 -0
- package/dist/cache/backends/index.js +76 -0
- package/dist/cache/backends/index.js.map +1 -0
- package/dist/cache/backends/local-fs.d.ts +31 -0
- package/dist/cache/backends/local-fs.d.ts.map +1 -0
- package/dist/cache/backends/local-fs.js +101 -0
- package/dist/cache/backends/local-fs.js.map +1 -0
- package/dist/cache/backends/memory.d.ts +19 -0
- package/dist/cache/backends/memory.d.ts.map +1 -0
- package/dist/cache/backends/memory.js +30 -0
- package/dist/cache/backends/memory.js.map +1 -0
- package/dist/cache/backends/r2.d.ts +51 -0
- package/dist/cache/backends/r2.d.ts.map +1 -0
- package/dist/cache/backends/r2.js +116 -0
- package/dist/cache/backends/r2.js.map +1 -0
- package/dist/cache/classification-cache.d.ts +23 -9
- package/dist/cache/classification-cache.d.ts.map +1 -1
- package/dist/cache/classification-cache.js +37 -64
- package/dist/cache/classification-cache.js.map +1 -1
- package/dist/cache/discovery-cache.d.ts +71 -0
- package/dist/cache/discovery-cache.d.ts.map +1 -0
- package/dist/cache/discovery-cache.js +95 -0
- package/dist/cache/discovery-cache.js.map +1 -0
- package/dist/cache/index.d.ts +4 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +2 -0
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/tree-cache.d.ts +111 -0
- package/dist/cache/tree-cache.d.ts.map +1 -0
- package/dist/cache/tree-cache.js +171 -0
- package/dist/cache/tree-cache.js.map +1 -0
- package/dist/cache/verify-cache.d.ts +60 -0
- package/dist/cache/verify-cache.d.ts.map +1 -0
- package/dist/cache/verify-cache.js +103 -0
- package/dist/cache/verify-cache.js.map +1 -0
- package/dist/cluster/cluster.d.ts +174 -0
- package/dist/cluster/cluster.d.ts.map +1 -0
- package/dist/cluster/cluster.js +392 -0
- package/dist/cluster/cluster.js.map +1 -0
- package/dist/cluster/index.d.ts +11 -0
- package/dist/cluster/index.d.ts.map +1 -0
- package/dist/cluster/index.js +10 -0
- package/dist/cluster/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/batch-classifier.js +2 -2
- package/dist/llm/batch-classifier.js.map +1 -1
- package/dist/llm/call-logger.d.ts +36 -0
- package/dist/llm/call-logger.d.ts.map +1 -1
- package/dist/llm/call-logger.js +36 -0
- package/dist/llm/call-logger.js.map +1 -1
- package/dist/llm/discovery.d.ts +1 -1
- package/dist/llm/discovery.d.ts.map +1 -1
- package/dist/llm/discovery.js +39 -0
- package/dist/llm/discovery.js.map +1 -1
- package/dist/llm/verification.d.ts +1 -1
- package/dist/llm/verification.d.ts.map +1 -1
- package/dist/llm/verification.js +37 -0
- package/dist/llm/verification.js.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classification-cache.d.ts","sourceRoot":"","sources":["../../src/cache/classification-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"classification-cache.d.ts","sourceRoot":"","sources":["../../src/cache/classification-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,MAAM,WAAW,0BAA0B;IACzC;oEACgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAUD,qBAAa,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC1C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;gBAEP,OAAO,GAAE,0BAA+B;IAQpD,mEAAmE;IACnE,mBAAmB,IAAI,MAAM;IAI7B,6EAA6E;IAC7E,cAAc,IAAI,MAAM;IAIxB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,OAAO;IAIT,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAoC3D,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAclE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,KAAK,IAAI,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAazG;AASD,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,mBAAmB,CAMhG;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* - Cache directory: .cognium/cache/
|
|
12
12
|
* - Hit/miss tracking for observability
|
|
13
13
|
*
|
|
14
|
+
* Storage is delegated to a `CacheBackend` (cognium-ai#158 Acceptance #4).
|
|
15
|
+
* Default = `LocalFSBackend` (preserves ≤2.26.0 on-disk shape). Selecting
|
|
16
|
+
* `LLM_CACHE_BACKEND=r2` swaps to Cloudflare R2 transparently so two
|
|
17
|
+
* runners on different machines share the same cache.
|
|
18
|
+
*
|
|
14
19
|
* Model dimension (cognium-ai#151 P0 spike):
|
|
15
20
|
* When `modelFingerprint` is supplied (typically via
|
|
16
21
|
* `getPhaseModelFingerprint()` from `../llm/config.ts`) it becomes part
|
|
@@ -24,15 +29,16 @@
|
|
|
24
29
|
* ≤2.21.0) keep working — the first run after wire-through is a cache
|
|
25
30
|
* miss only for the call sites that opt into the fingerprint.
|
|
26
31
|
*/
|
|
27
|
-
import * as fs from 'fs';
|
|
28
32
|
import * as path from 'path';
|
|
29
33
|
import * as crypto from 'crypto';
|
|
34
|
+
import { makeBackend } from './backends/index.js';
|
|
30
35
|
const DEFAULT_MAX_AGE = 24 * 60 * 60 * 1000; // 24 hours
|
|
31
36
|
export class ClassificationCache {
|
|
32
37
|
cacheDir;
|
|
33
38
|
maxAge;
|
|
34
39
|
enabled;
|
|
35
40
|
modelFingerprint;
|
|
41
|
+
backend;
|
|
36
42
|
memoryCache = new Map();
|
|
37
43
|
hits = 0;
|
|
38
44
|
misses = 0;
|
|
@@ -41,14 +47,16 @@ export class ClassificationCache {
|
|
|
41
47
|
this.maxAge = options.maxAge ?? DEFAULT_MAX_AGE;
|
|
42
48
|
this.enabled = options.enabled ?? true;
|
|
43
49
|
this.modelFingerprint = options.modelFingerprint ?? '';
|
|
44
|
-
|
|
45
|
-
this.ensureCacheDir();
|
|
46
|
-
}
|
|
50
|
+
this.backend = options.backend ?? makeBackend(this.cacheDir);
|
|
47
51
|
}
|
|
48
52
|
/** Per-phase model identity used in cache key (cognium-ai#151). */
|
|
49
53
|
getModelFingerprint() {
|
|
50
54
|
return this.modelFingerprint;
|
|
51
55
|
}
|
|
56
|
+
/** Backend kind identifier (`local-fs` | `memory` | `r2`). Observability. */
|
|
57
|
+
getBackendKind() {
|
|
58
|
+
return this.backend.kind;
|
|
59
|
+
}
|
|
52
60
|
computeKey(content, classifier) {
|
|
53
61
|
// 3-component key when fingerprint present (cognium-ai#151). When
|
|
54
62
|
// empty falls through to the prior 2-component shape via the trailing
|
|
@@ -59,118 +67,83 @@ export class ClassificationCache {
|
|
|
59
67
|
: content + '\0' + classifier;
|
|
60
68
|
return crypto.createHash('sha256').update(parts).digest('hex');
|
|
61
69
|
}
|
|
62
|
-
getCachePath(key) {
|
|
63
|
-
const subdir = key.substring(0, 2);
|
|
64
|
-
return path.join(this.cacheDir, subdir, `${key}.json`);
|
|
65
|
-
}
|
|
66
|
-
ensureCacheDir() {
|
|
67
|
-
if (!fs.existsSync(this.cacheDir)) {
|
|
68
|
-
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
70
|
isValid(entry) {
|
|
72
71
|
return (Date.now() - entry.timestamp) <= this.maxAge;
|
|
73
72
|
}
|
|
74
|
-
get(content, classifier) {
|
|
73
|
+
async get(content, classifier) {
|
|
75
74
|
if (!this.enabled)
|
|
76
75
|
return null;
|
|
77
76
|
const key = this.computeKey(content, classifier);
|
|
78
|
-
// Memory cache
|
|
77
|
+
// Memory cache (process-local hot path)
|
|
79
78
|
const memEntry = this.memoryCache.get(key);
|
|
80
79
|
if (memEntry && this.isValid(memEntry)) {
|
|
81
80
|
this.hits++;
|
|
82
81
|
return memEntry.result;
|
|
83
82
|
}
|
|
84
|
-
//
|
|
85
|
-
const cachePath = this.getCachePath(key);
|
|
83
|
+
// Backend (disk / R2 / etc.)
|
|
86
84
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const entry = JSON.parse(
|
|
85
|
+
const raw = await this.backend.get(key);
|
|
86
|
+
if (raw !== null) {
|
|
87
|
+
const entry = JSON.parse(raw);
|
|
90
88
|
if (this.isValid(entry)) {
|
|
91
89
|
this.memoryCache.set(key, entry);
|
|
92
90
|
this.hits++;
|
|
93
91
|
return entry.result;
|
|
94
92
|
}
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
// Stale — best-effort drop. We don't await the deletion to
|
|
94
|
+
// keep the read path fast.
|
|
95
|
+
if (this.backend.delete) {
|
|
96
|
+
void this.backend.delete(key);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
catch {
|
|
101
|
-
//
|
|
101
|
+
// Decode / I/O error: treat as miss.
|
|
102
102
|
}
|
|
103
103
|
this.misses++;
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
|
-
set(content, classifier, result) {
|
|
106
|
+
async set(content, classifier, result) {
|
|
107
107
|
if (!this.enabled)
|
|
108
108
|
return;
|
|
109
109
|
const key = this.computeKey(content, classifier);
|
|
110
110
|
const entry = { key, timestamp: Date.now(), result };
|
|
111
111
|
this.memoryCache.set(key, entry);
|
|
112
|
-
// #31: atomic write (tmp + rename) so concurrent processes don't
|
|
113
|
-
// corrupt each other's cache files. Same pattern as file-cache.ts.
|
|
114
|
-
const cachePath = this.getCachePath(key);
|
|
115
|
-
const cacheSubdir = path.dirname(cachePath);
|
|
116
|
-
const tmpPath = `${cachePath}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
|
|
117
112
|
try {
|
|
118
|
-
|
|
119
|
-
fs.mkdirSync(cacheSubdir, { recursive: true });
|
|
120
|
-
}
|
|
121
|
-
fs.writeFileSync(tmpPath, JSON.stringify(entry));
|
|
122
|
-
fs.renameSync(tmpPath, cachePath);
|
|
113
|
+
await this.backend.set(key, JSON.stringify(entry));
|
|
123
114
|
}
|
|
124
115
|
catch {
|
|
125
|
-
|
|
126
|
-
fs.unlinkSync(tmpPath);
|
|
127
|
-
}
|
|
128
|
-
catch { /* ignore */ }
|
|
116
|
+
// Cache write is best-effort; never fail the caller.
|
|
129
117
|
}
|
|
130
118
|
}
|
|
131
|
-
has(content, classifier) {
|
|
132
|
-
return this.get(content, classifier) !== null;
|
|
119
|
+
async has(content, classifier) {
|
|
120
|
+
return (await this.get(content, classifier)) !== null;
|
|
133
121
|
}
|
|
134
|
-
clear() {
|
|
122
|
+
async clear() {
|
|
135
123
|
this.memoryCache.clear();
|
|
136
124
|
this.hits = 0;
|
|
137
125
|
this.misses = 0;
|
|
138
|
-
if (
|
|
126
|
+
if (this.backend.clear) {
|
|
139
127
|
try {
|
|
140
|
-
|
|
141
|
-
this.ensureCacheDir();
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
// Ignore errors
|
|
128
|
+
await this.backend.clear();
|
|
145
129
|
}
|
|
130
|
+
catch { /* ignore */ }
|
|
146
131
|
}
|
|
147
132
|
}
|
|
148
|
-
stats() {
|
|
133
|
+
async stats() {
|
|
149
134
|
let diskEntries = 0;
|
|
150
|
-
if (
|
|
135
|
+
if (this.backend.count) {
|
|
151
136
|
try {
|
|
152
|
-
|
|
153
|
-
for (const subdir of subdirs) {
|
|
154
|
-
const subdirPath = path.join(this.cacheDir, subdir);
|
|
155
|
-
try {
|
|
156
|
-
if (fs.statSync(subdirPath).isDirectory()) {
|
|
157
|
-
diskEntries += fs.readdirSync(subdirPath).length;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
// Skip unreadable subdirs
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
// Ignore
|
|
137
|
+
diskEntries = await this.backend.count();
|
|
167
138
|
}
|
|
139
|
+
catch { /* ignore */ }
|
|
168
140
|
}
|
|
169
141
|
const total = this.hits + this.misses;
|
|
170
142
|
return {
|
|
171
143
|
memoryEntries: this.memoryCache.size,
|
|
172
144
|
diskEntries,
|
|
173
145
|
hitRate: total > 0 ? this.hits / total : 0,
|
|
146
|
+
backend: this.backend.kind,
|
|
174
147
|
};
|
|
175
148
|
}
|
|
176
149
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classification-cache.js","sourceRoot":"","sources":["../../src/cache/classification-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"classification-cache.js","sourceRoot":"","sources":["../../src/cache/classification-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery Cache
|
|
3
|
+
*
|
|
4
|
+
* Persistent on-disk cache for LLM discovery results per (className,
|
|
5
|
+
* methodName, methodCode, CWE-set) tuple (cognium-ai#134, P2 of epic
|
|
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.
|
|
10
|
+
*
|
|
11
|
+
* Built on top of `ClassificationCache<DiscoveryResult>` (which already
|
|
12
|
+
* carries the cognium-ai#151 model-fingerprint key dimension), with
|
|
13
|
+
* discovery-specific key construction:
|
|
14
|
+
*
|
|
15
|
+
* content = className + '\\0' + methodName + '\\0' +
|
|
16
|
+
* SHA-256(methodCode) + '\\0' + sortedCweSet
|
|
17
|
+
* classifier = 'discovery-method'
|
|
18
|
+
* fingerprint = getPhaseModelFingerprint(config) // includes discovery model
|
|
19
|
+
*
|
|
20
|
+
* Cache directory: `.cognium/cache/discovery/` (separate from the
|
|
21
|
+
* verify-phase `.cognium/cache/verify/` so each phase can be cleared
|
|
22
|
+
* independently — `rm -rf .cognium/cache/discovery/` only nukes
|
|
23
|
+
* discovery entries).
|
|
24
|
+
*
|
|
25
|
+
* Invalidation matrix (#134 acceptance criteria):
|
|
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)
|
|
32
|
+
*
|
|
33
|
+
* Env toggles:
|
|
34
|
+
* LLM_DISCOVERY_CACHE=0 → disable persistent discovery cache
|
|
35
|
+
* (in-memory dedup from #136 still applies)
|
|
36
|
+
*/
|
|
37
|
+
import { ClassificationCache } from './classification-cache.js';
|
|
38
|
+
import { type LLMConfig } from '../llm/config.js';
|
|
39
|
+
import type { DiscoveryResult } from '../llm/discovery.js';
|
|
40
|
+
/**
|
|
41
|
+
* Get the discovery-phase persistent cache, keyed by per-phase model
|
|
42
|
+
* fingerprint. When `LLM_DISCOVERY_CACHE=0` returns a disabled cache that
|
|
43
|
+
* no-ops on get/set (callers don't need to branch).
|
|
44
|
+
*/
|
|
45
|
+
export declare function getDiscoveryCache(config?: LLMConfig): ClassificationCache<DiscoveryResult>;
|
|
46
|
+
/** Reset the module-scoped singleton — test-only. */
|
|
47
|
+
export declare function resetDiscoveryCache(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Compute the `(content, classifier)` tuple for the underlying
|
|
50
|
+
* ClassificationCache. The fingerprint dimension is folded in by the
|
|
51
|
+
* cache itself (see classification-cache.ts:computeKey).
|
|
52
|
+
*
|
|
53
|
+
* Components:
|
|
54
|
+
* - className — disambiguates same-named methods across classes
|
|
55
|
+
* - methodName — per-method granularity (matches the per-method LLM call)
|
|
56
|
+
* - methodCode SHA-256 — method edit invalidates only that method's entry
|
|
57
|
+
* - sortedCweSet — different CWE-set requests on the same method don't
|
|
58
|
+
* reuse each other's prompts (different system prompt, different answer)
|
|
59
|
+
* - classifier 'discovery-method' — namespace for future
|
|
60
|
+
* 'discovery-file' / 'discovery-class' tiers without collision
|
|
61
|
+
*/
|
|
62
|
+
export declare function computeDiscoveryCacheKey(input: {
|
|
63
|
+
className: string;
|
|
64
|
+
methodName: string;
|
|
65
|
+
methodCode: string;
|
|
66
|
+
targetCWEs: ReadonlyArray<string>;
|
|
67
|
+
}): {
|
|
68
|
+
content: string;
|
|
69
|
+
classifier: string;
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=discovery-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-cache.d.ts","sourceRoot":"","sources":["../../src/cache/discovery-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAIH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAa3D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,eAAe,CAAC,CActC;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACnC,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAO1C"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery Cache
|
|
3
|
+
*
|
|
4
|
+
* Persistent on-disk cache for LLM discovery results per (className,
|
|
5
|
+
* methodName, methodCode, CWE-set) tuple (cognium-ai#134, P2 of epic
|
|
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.
|
|
10
|
+
*
|
|
11
|
+
* Built on top of `ClassificationCache<DiscoveryResult>` (which already
|
|
12
|
+
* carries the cognium-ai#151 model-fingerprint key dimension), with
|
|
13
|
+
* discovery-specific key construction:
|
|
14
|
+
*
|
|
15
|
+
* content = className + '\\0' + methodName + '\\0' +
|
|
16
|
+
* SHA-256(methodCode) + '\\0' + sortedCweSet
|
|
17
|
+
* classifier = 'discovery-method'
|
|
18
|
+
* fingerprint = getPhaseModelFingerprint(config) // includes discovery model
|
|
19
|
+
*
|
|
20
|
+
* Cache directory: `.cognium/cache/discovery/` (separate from the
|
|
21
|
+
* verify-phase `.cognium/cache/verify/` so each phase can be cleared
|
|
22
|
+
* independently — `rm -rf .cognium/cache/discovery/` only nukes
|
|
23
|
+
* discovery entries).
|
|
24
|
+
*
|
|
25
|
+
* Invalidation matrix (#134 acceptance criteria):
|
|
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)
|
|
32
|
+
*
|
|
33
|
+
* Env toggles:
|
|
34
|
+
* LLM_DISCOVERY_CACHE=0 → disable persistent discovery cache
|
|
35
|
+
* (in-memory dedup from #136 still applies)
|
|
36
|
+
*/
|
|
37
|
+
import * as crypto from 'crypto';
|
|
38
|
+
import * as path from 'path';
|
|
39
|
+
import { ClassificationCache } from './classification-cache.js';
|
|
40
|
+
import { getDefaultLLMConfig, getPhaseModelFingerprint } from '../llm/config.js';
|
|
41
|
+
const DISCOVERY_CACHE_SUBDIR = path.join('.cognium', 'cache', 'discovery');
|
|
42
|
+
/**
|
|
43
|
+
* Module-scoped singleton — distinct from the discovery-phase singleton
|
|
44
|
+
* in `classification-cache.ts` and the verify-phase singleton in
|
|
45
|
+
* `verify-cache.ts` so each phase can carry its own model fingerprint
|
|
46
|
+
* (e.g. cheap discovery + strong verifier) without thrashing.
|
|
47
|
+
*/
|
|
48
|
+
let _instance = null;
|
|
49
|
+
let _instanceFingerprint = '';
|
|
50
|
+
/**
|
|
51
|
+
* Get the discovery-phase persistent cache, keyed by per-phase model
|
|
52
|
+
* fingerprint. When `LLM_DISCOVERY_CACHE=0` returns a disabled cache that
|
|
53
|
+
* no-ops on get/set (callers don't need to branch).
|
|
54
|
+
*/
|
|
55
|
+
export function getDiscoveryCache(config = getDefaultLLMConfig()) {
|
|
56
|
+
const enabled = process.env.LLM_DISCOVERY_CACHE !== '0';
|
|
57
|
+
const fingerprint = getPhaseModelFingerprint(config);
|
|
58
|
+
if (!_instance || _instanceFingerprint !== fingerprint || _instance.getModelFingerprint() !== fingerprint) {
|
|
59
|
+
_instance = new ClassificationCache({
|
|
60
|
+
cacheDir: path.join(process.cwd(), DISCOVERY_CACHE_SUBDIR),
|
|
61
|
+
modelFingerprint: fingerprint,
|
|
62
|
+
enabled,
|
|
63
|
+
});
|
|
64
|
+
_instanceFingerprint = fingerprint;
|
|
65
|
+
}
|
|
66
|
+
return _instance;
|
|
67
|
+
}
|
|
68
|
+
/** Reset the module-scoped singleton — test-only. */
|
|
69
|
+
export function resetDiscoveryCache() {
|
|
70
|
+
_instance = null;
|
|
71
|
+
_instanceFingerprint = '';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compute the `(content, classifier)` tuple for the underlying
|
|
75
|
+
* ClassificationCache. The fingerprint dimension is folded in by the
|
|
76
|
+
* cache itself (see classification-cache.ts:computeKey).
|
|
77
|
+
*
|
|
78
|
+
* Components:
|
|
79
|
+
* - className — disambiguates same-named methods across classes
|
|
80
|
+
* - methodName — per-method granularity (matches the per-method LLM call)
|
|
81
|
+
* - methodCode SHA-256 — method edit invalidates only that method's entry
|
|
82
|
+
* - sortedCweSet — different CWE-set requests on the same method don't
|
|
83
|
+
* reuse each other's prompts (different system prompt, different answer)
|
|
84
|
+
* - classifier 'discovery-method' — namespace for future
|
|
85
|
+
* 'discovery-file' / 'discovery-class' tiers without collision
|
|
86
|
+
*/
|
|
87
|
+
export function computeDiscoveryCacheKey(input) {
|
|
88
|
+
const methodSha = crypto.createHash('sha256').update(input.methodCode).digest('hex');
|
|
89
|
+
const sortedCweSet = [...input.targetCWEs].sort().join(',');
|
|
90
|
+
return {
|
|
91
|
+
content: `${input.className}\0${input.methodName}\0${methodSha}\0${sortedCweSet}`,
|
|
92
|
+
classifier: 'discovery-method',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=discovery-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-cache.js","sourceRoot":"","sources":["../../src/cache/discovery-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAkB,MAAM,kBAAkB,CAAC;AAGjG,MAAM,sBAAsB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,IAAI,SAAS,GAAgD,IAAI,CAAC;AAClE,IAAI,oBAAoB,GAAG,EAAE,CAAC;AAE9B;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAoB,mBAAmB,EAAE;IAEzC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,GAAG,CAAC;IACxD,MAAM,WAAW,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAErD,IAAI,CAAC,SAAS,IAAI,oBAAoB,KAAK,WAAW,IAAI,SAAS,CAAC,mBAAmB,EAAE,KAAK,WAAW,EAAE,CAAC;QAC1G,SAAS,GAAG,IAAI,mBAAmB,CAAkB;YACnD,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC;YAC1D,gBAAgB,EAAE,WAAW;YAC7B,OAAO;SACR,CAAC,CAAC;QACH,oBAAoB,GAAG,WAAW,CAAC;IACrC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,mBAAmB;IACjC,SAAS,GAAG,IAAI,CAAC;IACjB,oBAAoB,GAAG,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAKxC;IACC,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrF,MAAM,YAAY,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5D,OAAO;QACL,OAAO,EAAE,GAAG,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,UAAU,KAAK,SAAS,KAAK,YAAY,EAAE;QACjF,UAAU,EAAE,kBAAkB;KAC/B,CAAC;AACJ,CAAC"}
|
package/dist/cache/index.d.ts
CHANGED
|
@@ -3,4 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { FileCache, getAnalysisCache, resetAnalysisCache } from './file-cache.js';
|
|
5
5
|
export type { CacheEntry, FileCacheOptions } from './file-cache.js';
|
|
6
|
+
export { ClassificationCache, getClassificationCache, resetClassificationCache } from './classification-cache.js';
|
|
7
|
+
export type { ClassificationCacheOptions } from './classification-cache.js';
|
|
8
|
+
export type { CacheBackend } from './backend.js';
|
|
9
|
+
export { LocalFSBackend, MemoryBackend, R2Backend, makeBackend, getBackendKind } from './backends/index.js';
|
|
6
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAClH,YAAY,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAG5E,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/cache/index.js
CHANGED
|
@@ -2,4 +2,6 @@
|
|
|
2
2
|
* Cache module exports
|
|
3
3
|
*/
|
|
4
4
|
export { FileCache, getAnalysisCache, resetAnalysisCache } from './file-cache.js';
|
|
5
|
+
export { ClassificationCache, getClassificationCache, resetClassificationCache } from './classification-cache.js';
|
|
6
|
+
export { LocalFSBackend, MemoryBackend, R2Backend, makeBackend, getBackendKind } from './backends/index.js';
|
|
5
7
|
//# sourceMappingURL=index.js.map
|
package/dist/cache/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGlF,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAKlH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree Cache
|
|
3
|
+
*
|
|
4
|
+
* Persistent on-disk cache for `runPatternMatch` output (cognium-ai#153,
|
|
5
|
+
* P3 of epic #155). Pattern-match output is a pure function of file
|
|
6
|
+
* content + engine version, so caching it eliminates the parse +
|
|
7
|
+
* AST-walk + SAST-rule firing on warm runs.
|
|
8
|
+
*
|
|
9
|
+
* Empirically (v2.20.6 gson re-verify, 2026-06-29) the scan stage ate
|
|
10
|
+
* 179s of 358s end-to-end — ~half the wall-clock is re-discovering
|
|
11
|
+
* structure circle-ir already knows. Caching the pattern-match seam
|
|
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.
|
|
16
|
+
*
|
|
17
|
+
* **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
|
+
*
|
|
23
|
+
* **Key composition:**
|
|
24
|
+
* content = SHA-256(sourceCode) + '\0' +
|
|
25
|
+
* circleIrVersion + '\0' +
|
|
26
|
+
* circleIrAiVersion + '\0' +
|
|
27
|
+
* language + '\0' +
|
|
28
|
+
* ruleSetFingerprint
|
|
29
|
+
* classifier = 'tree-pattern-match'
|
|
30
|
+
*
|
|
31
|
+
* Any of: file edit, circle-ir bump, circle-ir-ai bump, language
|
|
32
|
+
* change, or rule-set change → cache miss.
|
|
33
|
+
*
|
|
34
|
+
* **Storage:** `ClassificationCache<PatternMatchResult>` under
|
|
35
|
+
* `.cognium/cache/tree/`, 7-day TTL (longer than discovery cache
|
|
36
|
+
* because the input is structural and stable — pattern-match output
|
|
37
|
+
* doesn't drift between scans the way an LLM call would).
|
|
38
|
+
*
|
|
39
|
+
* **Env toggles:**
|
|
40
|
+
* 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
|
+
*/
|
|
48
|
+
import { ClassificationCache } from './classification-cache.js';
|
|
49
|
+
import type { TaintSource, TaintSink, TypeInfo, DFG, SastFinding } from 'circle-ir';
|
|
50
|
+
/**
|
|
51
|
+
* 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.
|
|
55
|
+
*/
|
|
56
|
+
export interface PatternMatchResult {
|
|
57
|
+
patternSources: TaintSource[];
|
|
58
|
+
patternSinks: TaintSink[];
|
|
59
|
+
types: TypeInfo[];
|
|
60
|
+
imports: string[];
|
|
61
|
+
sastFindings: SastFinding[];
|
|
62
|
+
dfg: DFG;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the tree-cache singleton. When `LLM_TREE_CACHE=0` returns a
|
|
66
|
+
* disabled cache that no-ops on get/set (callers don't need to branch).
|
|
67
|
+
*/
|
|
68
|
+
export declare function getTreeCache(): ClassificationCache<PatternMatchResult>;
|
|
69
|
+
/** Reset the module-scoped singleton — test-only. */
|
|
70
|
+
export declare function resetTreeCache(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Compute the `(content, classifier)` tuple for the underlying
|
|
73
|
+
* 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
|
+
*/
|
|
87
|
+
export declare function computeTreeCacheKey(input: {
|
|
88
|
+
sourceCode: string;
|
|
89
|
+
language: string;
|
|
90
|
+
ruleSetFingerprint?: string;
|
|
91
|
+
}): {
|
|
92
|
+
content: string;
|
|
93
|
+
classifier: string;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Compute a stable fingerprint over an arbitrary rule-set descriptor
|
|
97
|
+
* (disabledPasses array + projectProfile object). Result is suitable to
|
|
98
|
+
* pass as `ruleSetFingerprint` to `computeTreeCacheKey`. Order-stable
|
|
99
|
+
* for arrays so re-ordering `--disable a,b` vs `--disable b,a` still
|
|
100
|
+
* hits the cache.
|
|
101
|
+
*/
|
|
102
|
+
export declare function computeRuleSetFingerprint(input: {
|
|
103
|
+
disabledPasses?: string[];
|
|
104
|
+
projectProfile?: unknown;
|
|
105
|
+
}): string;
|
|
106
|
+
/** Test-only: expose the captured engine versions for assertions. */
|
|
107
|
+
export declare function getTreeCacheVersions(): {
|
|
108
|
+
circleIrVersion: string;
|
|
109
|
+
circleIrAiVersion: string;
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=tree-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-cache.d.ts","sourceRoot":"","sources":["../../src/cache/tree-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;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;AAKpF;;;;;GAKG;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;AAiDD;;;GAGG;AACH,wBAAgB,YAAY,IAAI,mBAAmB,CAAC,kBAAkB,CAAC,CAUtE;AAED,qDAAqD;AACrD,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;;;;;;;;;;;GAeG;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,CAO1C;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,MAAM,CAWT;AAED,qEAAqE;AACrE,wBAAgB,oBAAoB,IAAI;IACtC,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAKA"}
|