circle-ir-ai 2.22.0 → 2.26.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 +307 -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/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/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/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 +39 -0
- package/dist/llm/verification.js.map +1 -1
- package/package.json +1 -1
|
@@ -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"}
|
|
@@ -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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
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 * as crypto from 'crypto';
|
|
49
|
+
import * as fs from 'fs';
|
|
50
|
+
import * as path from 'path';
|
|
51
|
+
import { createRequire } from 'module';
|
|
52
|
+
import { ClassificationCache } from './classification-cache.js';
|
|
53
|
+
const TREE_CACHE_SUBDIR = path.join('.cognium', 'cache', 'tree');
|
|
54
|
+
const TREE_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days — structural, stable
|
|
55
|
+
/**
|
|
56
|
+
* Module-scoped singleton — distinct from the verify/discovery
|
|
57
|
+
* singletons so the tree cache (which doesn't depend on the LLM model)
|
|
58
|
+
* doesn't get invalidated when the operator swaps discovery model.
|
|
59
|
+
*/
|
|
60
|
+
let _instance = null;
|
|
61
|
+
/**
|
|
62
|
+
* Read package versions once at module load (best-effort). Wrapped in a
|
|
63
|
+
* try/catch so unit tests that don't ship a package.json next to the
|
|
64
|
+
* source can still construct the cache; in that case the version
|
|
65
|
+
* component degrades to 'unknown' and the cache still works (just isn't
|
|
66
|
+
* cross-version-safe).
|
|
67
|
+
*/
|
|
68
|
+
function safeReadVersion(spec, baseUrl) {
|
|
69
|
+
try {
|
|
70
|
+
const require = createRequire(baseUrl);
|
|
71
|
+
return require(spec).version || 'unknown';
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return 'unknown';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const _circleIrVersion = safeReadVersion('circle-ir/package.json', import.meta.url);
|
|
78
|
+
const _circleIrAiVersion = (() => {
|
|
79
|
+
// The package's own version is harder to resolve via require, so read
|
|
80
|
+
// it directly from disk by walking up from this file.
|
|
81
|
+
try {
|
|
82
|
+
// Compiled output sits at dist/cache/tree-cache.js → ../../package.json
|
|
83
|
+
const candidates = [
|
|
84
|
+
path.resolve(new URL(import.meta.url).pathname, '../../../package.json'),
|
|
85
|
+
path.resolve(new URL(import.meta.url).pathname, '../../package.json'),
|
|
86
|
+
];
|
|
87
|
+
for (const p of candidates) {
|
|
88
|
+
if (fs.existsSync(p)) {
|
|
89
|
+
const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
90
|
+
if (pkg.name === 'circle-ir-ai' && pkg.version) {
|
|
91
|
+
return pkg.version;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* fallthrough */
|
|
98
|
+
}
|
|
99
|
+
return 'unknown';
|
|
100
|
+
})();
|
|
101
|
+
/**
|
|
102
|
+
* Get the tree-cache singleton. When `LLM_TREE_CACHE=0` returns a
|
|
103
|
+
* disabled cache that no-ops on get/set (callers don't need to branch).
|
|
104
|
+
*/
|
|
105
|
+
export function getTreeCache() {
|
|
106
|
+
const enabled = process.env.LLM_TREE_CACHE !== '0';
|
|
107
|
+
if (!_instance) {
|
|
108
|
+
_instance = new ClassificationCache({
|
|
109
|
+
cacheDir: path.join(process.cwd(), TREE_CACHE_SUBDIR),
|
|
110
|
+
maxAge: TREE_CACHE_TTL_MS,
|
|
111
|
+
enabled,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return _instance;
|
|
115
|
+
}
|
|
116
|
+
/** Reset the module-scoped singleton — test-only. */
|
|
117
|
+
export function resetTreeCache() {
|
|
118
|
+
_instance = null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Compute the `(content, classifier)` tuple for the underlying
|
|
122
|
+
* ClassificationCache.
|
|
123
|
+
*
|
|
124
|
+
* Components:
|
|
125
|
+
* - source code SHA-256 — file edit invalidates only that file's entry
|
|
126
|
+
* - circleIrVersion — bumping circle-ir invalidates all entries
|
|
127
|
+
* - circleIrAiVersion — bumping circle-ir-ai (which may change
|
|
128
|
+
* workflow-level transforms applied to pattern-match output)
|
|
129
|
+
* invalidates all entries
|
|
130
|
+
* - language — same source, different language tag must miss (the
|
|
131
|
+
* analyzer's behavior depends on the language argument)
|
|
132
|
+
* - ruleSetFingerprint — caller-supplied digest of the active rule
|
|
133
|
+
* set (disabledPasses + projectProfile). Empty string is fine for
|
|
134
|
+
* the default rule set.
|
|
135
|
+
*/
|
|
136
|
+
export function computeTreeCacheKey(input) {
|
|
137
|
+
const sourceSha = crypto.createHash('sha256').update(input.sourceCode).digest('hex');
|
|
138
|
+
const ruleSet = input.ruleSetFingerprint ?? '';
|
|
139
|
+
return {
|
|
140
|
+
content: `${sourceSha}\0${_circleIrVersion}\0${_circleIrAiVersion}\0${input.language}\0${ruleSet}`,
|
|
141
|
+
classifier: 'tree-pattern-match',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Compute a stable fingerprint over an arbitrary rule-set descriptor
|
|
146
|
+
* (disabledPasses array + projectProfile object). Result is suitable to
|
|
147
|
+
* pass as `ruleSetFingerprint` to `computeTreeCacheKey`. Order-stable
|
|
148
|
+
* for arrays so re-ordering `--disable a,b` vs `--disable b,a` still
|
|
149
|
+
* hits the cache.
|
|
150
|
+
*/
|
|
151
|
+
export function computeRuleSetFingerprint(input) {
|
|
152
|
+
const passes = [...(input.disabledPasses ?? [])].sort().join(',');
|
|
153
|
+
// projectProfile JSON-stringifies; key order in objects matters only
|
|
154
|
+
// if the producer is non-deterministic — `@cognium/project-profile-detect`
|
|
155
|
+
// emits stable keys, so this is fine. If a future profile producer is
|
|
156
|
+
// less stable we can switch to a sorted-keys serializer.
|
|
157
|
+
const profile = input.projectProfile === undefined
|
|
158
|
+
? ''
|
|
159
|
+
: JSON.stringify(input.projectProfile);
|
|
160
|
+
if (!passes && !profile)
|
|
161
|
+
return '';
|
|
162
|
+
return crypto.createHash('sha256').update(`${passes}\0${profile}`).digest('hex');
|
|
163
|
+
}
|
|
164
|
+
/** Test-only: expose the captured engine versions for assertions. */
|
|
165
|
+
export function getTreeCacheVersions() {
|
|
166
|
+
return {
|
|
167
|
+
circleIrVersion: _circleIrVersion,
|
|
168
|
+
circleIrAiVersion: _circleIrAiVersion,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=tree-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-cache.js","sourceRoot":"","sources":["../../src/cache/tree-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify Cache
|
|
3
|
+
*
|
|
4
|
+
* Persistent on-disk cache for LLM verification batch results
|
|
5
|
+
* (cognium-ai#152, P1 of epic #155). Sits in front of the in-memory
|
|
6
|
+
* `_verifierDedupCache` from verification.ts (#138 step 3) so that
|
|
7
|
+
* identical `(file SHA, pair-set)` requests survive across processes,
|
|
8
|
+
* CI runs, and developer iterations — not just within one process.
|
|
9
|
+
*
|
|
10
|
+
* Built on top of `ClassificationCache<BatchVerificationResult>` (which
|
|
11
|
+
* already carries the cognium-ai#151 model-fingerprint key dimension),
|
|
12
|
+
* with verify-specific key construction:
|
|
13
|
+
*
|
|
14
|
+
* content = SHA-256(code) + '\\0' + pairsFingerprint
|
|
15
|
+
* classifier = 'verify-batch'
|
|
16
|
+
* fingerprint = getPhaseModelFingerprint(config) // includes verify model
|
|
17
|
+
*
|
|
18
|
+
* Cache directory: `.cognium/cache/verify/` (separate from the
|
|
19
|
+
* discovery-phase `.cognium/cache/` so a `rm -rf .cognium/cache/verify/`
|
|
20
|
+
* only nukes verifier entries).
|
|
21
|
+
*
|
|
22
|
+
* Invalidation matrix (acceptance criteria #152):
|
|
23
|
+
* - Edit one file in a project → only that file's entries invalidate
|
|
24
|
+
* (file SHA-256 is part of the cache content key)
|
|
25
|
+
* - Add a new sink to the engine → existing entries still hit if the
|
|
26
|
+
* new sink doesn't change the pair-set for that file
|
|
27
|
+
* - Change verification model → entire cache invalidates
|
|
28
|
+
* (model fingerprint is part of the singleton key)
|
|
29
|
+
*
|
|
30
|
+
* Env toggles:
|
|
31
|
+
* LLM_VERIFY_CACHE=0 → disable persistent verify cache (in-memory
|
|
32
|
+
* dedup from #138 still applies)
|
|
33
|
+
*/
|
|
34
|
+
import { ClassificationCache } from './classification-cache.js';
|
|
35
|
+
import { type LLMConfig } from '../llm/config.js';
|
|
36
|
+
import type { BatchVerificationInput, BatchVerificationResult } from '../llm/verification.js';
|
|
37
|
+
/**
|
|
38
|
+
* Get the verify-phase persistent cache, keyed by per-phase model
|
|
39
|
+
* fingerprint. When `LLM_VERIFY_CACHE=0` returns a disabled cache that
|
|
40
|
+
* no-ops on get/set (callers don't need to branch).
|
|
41
|
+
*/
|
|
42
|
+
export declare function getVerifyCache(config?: LLMConfig): ClassificationCache<BatchVerificationResult>;
|
|
43
|
+
/** Reset the module-scoped singleton — test-only. */
|
|
44
|
+
export declare function resetVerifyCache(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Compute the `(content, classifier)` tuple for the underlying
|
|
47
|
+
* ClassificationCache. The fingerprint dimension is folded in by the
|
|
48
|
+
* cache itself (see classification-cache.ts:computeKey).
|
|
49
|
+
*
|
|
50
|
+
* Components:
|
|
51
|
+
* - file SHA-256 — file edit invalidates only that file's entry
|
|
52
|
+
* - pairs fingerprint — sort-invariant over the source/sink set
|
|
53
|
+
* - classifier 'verify-batch' — namespace for future
|
|
54
|
+
* 'verify-pair' / 'verify-flow' tiers without collision
|
|
55
|
+
*/
|
|
56
|
+
export declare function computeVerifyCacheKey(input: BatchVerificationInput): {
|
|
57
|
+
content: string;
|
|
58
|
+
classifier: string;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=verify-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-cache.d.ts","sourceRoot":"","sources":["../../src/cache/verify-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAa9F;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,uBAAuB,CAAC,CAc9C;AAED,qDAAqD;AACrD,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC;AAiBD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,sBAAsB,GAC5B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAOzC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify Cache
|
|
3
|
+
*
|
|
4
|
+
* Persistent on-disk cache for LLM verification batch results
|
|
5
|
+
* (cognium-ai#152, P1 of epic #155). Sits in front of the in-memory
|
|
6
|
+
* `_verifierDedupCache` from verification.ts (#138 step 3) so that
|
|
7
|
+
* identical `(file SHA, pair-set)` requests survive across processes,
|
|
8
|
+
* CI runs, and developer iterations — not just within one process.
|
|
9
|
+
*
|
|
10
|
+
* Built on top of `ClassificationCache<BatchVerificationResult>` (which
|
|
11
|
+
* already carries the cognium-ai#151 model-fingerprint key dimension),
|
|
12
|
+
* with verify-specific key construction:
|
|
13
|
+
*
|
|
14
|
+
* content = SHA-256(code) + '\\0' + pairsFingerprint
|
|
15
|
+
* classifier = 'verify-batch'
|
|
16
|
+
* fingerprint = getPhaseModelFingerprint(config) // includes verify model
|
|
17
|
+
*
|
|
18
|
+
* Cache directory: `.cognium/cache/verify/` (separate from the
|
|
19
|
+
* discovery-phase `.cognium/cache/` so a `rm -rf .cognium/cache/verify/`
|
|
20
|
+
* only nukes verifier entries).
|
|
21
|
+
*
|
|
22
|
+
* Invalidation matrix (acceptance criteria #152):
|
|
23
|
+
* - Edit one file in a project → only that file's entries invalidate
|
|
24
|
+
* (file SHA-256 is part of the cache content key)
|
|
25
|
+
* - Add a new sink to the engine → existing entries still hit if the
|
|
26
|
+
* new sink doesn't change the pair-set for that file
|
|
27
|
+
* - Change verification model → entire cache invalidates
|
|
28
|
+
* (model fingerprint is part of the singleton key)
|
|
29
|
+
*
|
|
30
|
+
* Env toggles:
|
|
31
|
+
* LLM_VERIFY_CACHE=0 → disable persistent verify cache (in-memory
|
|
32
|
+
* dedup from #138 still applies)
|
|
33
|
+
*/
|
|
34
|
+
import * as crypto from 'crypto';
|
|
35
|
+
import * as path from 'path';
|
|
36
|
+
import { ClassificationCache } from './classification-cache.js';
|
|
37
|
+
import { getDefaultLLMConfig, getPhaseModelFingerprint } from '../llm/config.js';
|
|
38
|
+
const VERIFY_CACHE_SUBDIR = path.join('.cognium', 'cache', 'verify');
|
|
39
|
+
/**
|
|
40
|
+
* Module-scoped singleton — distinct from the discovery-phase singleton
|
|
41
|
+
* in `classification-cache.ts` so the two phases can carry different
|
|
42
|
+
* model fingerprints (e.g. cheap discovery + strong verifier) without
|
|
43
|
+
* thrashing each other's cache instance.
|
|
44
|
+
*/
|
|
45
|
+
let _instance = null;
|
|
46
|
+
let _instanceFingerprint = '';
|
|
47
|
+
/**
|
|
48
|
+
* Get the verify-phase persistent cache, keyed by per-phase model
|
|
49
|
+
* fingerprint. When `LLM_VERIFY_CACHE=0` returns a disabled cache that
|
|
50
|
+
* no-ops on get/set (callers don't need to branch).
|
|
51
|
+
*/
|
|
52
|
+
export function getVerifyCache(config = getDefaultLLMConfig()) {
|
|
53
|
+
const enabled = process.env.LLM_VERIFY_CACHE !== '0';
|
|
54
|
+
const fingerprint = getPhaseModelFingerprint(config);
|
|
55
|
+
if (!_instance || _instanceFingerprint !== fingerprint || _instance.getModelFingerprint() !== fingerprint) {
|
|
56
|
+
_instance = new ClassificationCache({
|
|
57
|
+
cacheDir: path.join(process.cwd(), VERIFY_CACHE_SUBDIR),
|
|
58
|
+
modelFingerprint: fingerprint,
|
|
59
|
+
enabled,
|
|
60
|
+
});
|
|
61
|
+
_instanceFingerprint = fingerprint;
|
|
62
|
+
}
|
|
63
|
+
return _instance;
|
|
64
|
+
}
|
|
65
|
+
/** Reset the module-scoped singleton — test-only. */
|
|
66
|
+
export function resetVerifyCache() {
|
|
67
|
+
_instance = null;
|
|
68
|
+
_instanceFingerprint = '';
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Stable sort over the source/sink pair-set; never mutates the caller's
|
|
72
|
+
* arrays. Mirrors `_serializeVerifierPairs` in verification.ts so
|
|
73
|
+
* persistent + in-memory dedup agree on what counts as "the same call".
|
|
74
|
+
*/
|
|
75
|
+
function serializeVerifyPairs(input) {
|
|
76
|
+
const sources = [...input.sources]
|
|
77
|
+
.map((s) => `${s.line}|${s.type}|${s.variable ?? ''}`)
|
|
78
|
+
.sort();
|
|
79
|
+
const sinks = [...input.sinks]
|
|
80
|
+
.map((s) => `${s.line}|${s.type}|${s.cwe}|${s.method ?? ''}`)
|
|
81
|
+
.sort();
|
|
82
|
+
return JSON.stringify({ sources, sinks });
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Compute the `(content, classifier)` tuple for the underlying
|
|
86
|
+
* ClassificationCache. The fingerprint dimension is folded in by the
|
|
87
|
+
* cache itself (see classification-cache.ts:computeKey).
|
|
88
|
+
*
|
|
89
|
+
* Components:
|
|
90
|
+
* - file SHA-256 — file edit invalidates only that file's entry
|
|
91
|
+
* - pairs fingerprint — sort-invariant over the source/sink set
|
|
92
|
+
* - classifier 'verify-batch' — namespace for future
|
|
93
|
+
* 'verify-pair' / 'verify-flow' tiers without collision
|
|
94
|
+
*/
|
|
95
|
+
export function computeVerifyCacheKey(input) {
|
|
96
|
+
const fileSha = crypto.createHash('sha256').update(input.code).digest('hex');
|
|
97
|
+
const pairsFp = crypto.createHash('sha256').update(serializeVerifyPairs(input)).digest('hex');
|
|
98
|
+
return {
|
|
99
|
+
content: `${fileSha}\0${pairsFp}`,
|
|
100
|
+
classifier: 'verify-batch',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=verify-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-cache.js","sourceRoot":"","sources":["../../src/cache/verify-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;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,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAErE;;;;;GAKG;AACH,IAAI,SAAS,GAAwD,IAAI,CAAC;AAC1E,IAAI,oBAAoB,GAAG,EAAE,CAAC;AAE9B;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAoB,mBAAmB,EAAE;IAEzC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,GAAG,CAAC;IACrD,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,CAA0B;YAC3D,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,mBAAmB,CAAC;YACvD,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,gBAAgB;IAC9B,SAAS,GAAG,IAAI,CAAC;IACjB,oBAAoB,GAAG,EAAE,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,KAA6B;IACzD,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;SAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;SACrD,IAAI,EAAE,CAAC;IACV,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;SAC5D,IAAI,EAAE,CAAC;IACV,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAA6B;IAE7B,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9F,OAAO;QACL,OAAO,EAAE,GAAG,OAAO,KAAK,OAAO,EAAE;QACjC,UAAU,EAAE,cAAc;KAC3B,CAAC;AACJ,CAAC"}
|