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.
Files changed (71) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/dist/agents/mastra/workflow.d.ts.map +1 -1
  3. package/dist/agents/mastra/workflow.js +41 -1
  4. package/dist/agents/mastra/workflow.js.map +1 -1
  5. package/dist/cache/backend.d.ts +52 -0
  6. package/dist/cache/backend.d.ts.map +1 -0
  7. package/dist/cache/backend.js +19 -0
  8. package/dist/cache/backend.js.map +1 -0
  9. package/dist/cache/backends/index.d.ts +32 -0
  10. package/dist/cache/backends/index.d.ts.map +1 -0
  11. package/dist/cache/backends/index.js +76 -0
  12. package/dist/cache/backends/index.js.map +1 -0
  13. package/dist/cache/backends/local-fs.d.ts +31 -0
  14. package/dist/cache/backends/local-fs.d.ts.map +1 -0
  15. package/dist/cache/backends/local-fs.js +101 -0
  16. package/dist/cache/backends/local-fs.js.map +1 -0
  17. package/dist/cache/backends/memory.d.ts +19 -0
  18. package/dist/cache/backends/memory.d.ts.map +1 -0
  19. package/dist/cache/backends/memory.js +30 -0
  20. package/dist/cache/backends/memory.js.map +1 -0
  21. package/dist/cache/backends/r2.d.ts +51 -0
  22. package/dist/cache/backends/r2.d.ts.map +1 -0
  23. package/dist/cache/backends/r2.js +116 -0
  24. package/dist/cache/backends/r2.js.map +1 -0
  25. package/dist/cache/classification-cache.d.ts +23 -9
  26. package/dist/cache/classification-cache.d.ts.map +1 -1
  27. package/dist/cache/classification-cache.js +37 -64
  28. package/dist/cache/classification-cache.js.map +1 -1
  29. package/dist/cache/discovery-cache.d.ts +71 -0
  30. package/dist/cache/discovery-cache.d.ts.map +1 -0
  31. package/dist/cache/discovery-cache.js +95 -0
  32. package/dist/cache/discovery-cache.js.map +1 -0
  33. package/dist/cache/index.d.ts +4 -0
  34. package/dist/cache/index.d.ts.map +1 -1
  35. package/dist/cache/index.js +2 -0
  36. package/dist/cache/index.js.map +1 -1
  37. package/dist/cache/tree-cache.d.ts +111 -0
  38. package/dist/cache/tree-cache.d.ts.map +1 -0
  39. package/dist/cache/tree-cache.js +171 -0
  40. package/dist/cache/tree-cache.js.map +1 -0
  41. package/dist/cache/verify-cache.d.ts +60 -0
  42. package/dist/cache/verify-cache.d.ts.map +1 -0
  43. package/dist/cache/verify-cache.js +103 -0
  44. package/dist/cache/verify-cache.js.map +1 -0
  45. package/dist/cluster/cluster.d.ts +174 -0
  46. package/dist/cluster/cluster.d.ts.map +1 -0
  47. package/dist/cluster/cluster.js +392 -0
  48. package/dist/cluster/cluster.js.map +1 -0
  49. package/dist/cluster/index.d.ts +11 -0
  50. package/dist/cluster/index.d.ts.map +1 -0
  51. package/dist/cluster/index.js +10 -0
  52. package/dist/cluster/index.js.map +1 -0
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/llm/batch-classifier.js +2 -2
  58. package/dist/llm/batch-classifier.js.map +1 -1
  59. package/dist/llm/call-logger.d.ts +36 -0
  60. package/dist/llm/call-logger.d.ts.map +1 -1
  61. package/dist/llm/call-logger.js +36 -0
  62. package/dist/llm/call-logger.js.map +1 -1
  63. package/dist/llm/discovery.d.ts +1 -1
  64. package/dist/llm/discovery.d.ts.map +1 -1
  65. package/dist/llm/discovery.js +39 -0
  66. package/dist/llm/discovery.js.map +1 -1
  67. package/dist/llm/verification.d.ts +1 -1
  68. package/dist/llm/verification.d.ts.map +1 -1
  69. package/dist/llm/verification.js +37 -0
  70. package/dist/llm/verification.js.map +1 -1
  71. 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;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,MAAM,WAAW,0BAA0B;IACzC,iEAAiE;IACjE,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;CAC3B;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,WAAW,CAAyC;IAC5D,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;gBAEP,OAAO,GAAE,0BAA+B;IAWpD,mEAAmE;IACnE,mBAAmB,IAAI,MAAM;IAI7B,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,OAAO;IAIf,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAmClD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI;IAwBzD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAIjD,KAAK,IAAI,IAAI;IAeb,KAAK,IAAI;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CA4BzE;AASD,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,mBAAmB,CAMhG;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
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
- if (this.enabled) {
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
- // Disk cache
85
- const cachePath = this.getCachePath(key);
83
+ // Backend (disk / R2 / etc.)
86
84
  try {
87
- if (fs.existsSync(cachePath)) {
88
- const data = fs.readFileSync(cachePath, 'utf-8');
89
- const entry = JSON.parse(data);
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
- else {
96
- fs.unlinkSync(cachePath);
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
- // Ignore read errors
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
- if (!fs.existsSync(cacheSubdir)) {
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
- try {
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 (fs.existsSync(this.cacheDir)) {
126
+ if (this.backend.clear) {
139
127
  try {
140
- fs.rmSync(this.cacheDir, { recursive: true, force: true });
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 (fs.existsSync(this.cacheDir)) {
135
+ if (this.backend.count) {
151
136
  try {
152
- const subdirs = fs.readdirSync(this.cacheDir);
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":"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"}
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"}
@@ -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"}
@@ -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
@@ -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"}