circle-ir-ai 2.27.0 → 2.28.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +172 -0
- package/dist/agents/mastra/workflow.d.ts +15 -0
- package/dist/agents/mastra/workflow.d.ts.map +1 -1
- package/dist/agents/mastra/workflow.js +33 -12
- package/dist/agents/mastra/workflow.js.map +1 -1
- package/dist/cache/backend.d.ts +62 -10
- package/dist/cache/backend.d.ts.map +1 -1
- package/dist/cache/backend.js +20 -4
- package/dist/cache/backend.js.map +1 -1
- package/dist/cache/backends/index.d.ts +30 -18
- package/dist/cache/backends/index.d.ts.map +1 -1
- package/dist/cache/backends/index.js +37 -29
- package/dist/cache/backends/index.js.map +1 -1
- package/dist/cache/backends/layout.d.ts +31 -0
- package/dist/cache/backends/layout.d.ts.map +1 -0
- package/dist/cache/backends/layout.js +46 -0
- package/dist/cache/backends/layout.js.map +1 -0
- package/dist/cache/backends/local-fs.d.ts +14 -11
- package/dist/cache/backends/local-fs.d.ts.map +1 -1
- package/dist/cache/backends/local-fs.js +30 -18
- package/dist/cache/backends/local-fs.js.map +1 -1
- package/dist/cache/backends/memory.d.ts +11 -6
- package/dist/cache/backends/memory.d.ts.map +1 -1
- package/dist/cache/backends/memory.js +13 -5
- package/dist/cache/backends/memory.js.map +1 -1
- package/dist/cache/backends/r2.d.ts +17 -17
- package/dist/cache/backends/r2.d.ts.map +1 -1
- package/dist/cache/backends/r2.js +14 -16
- package/dist/cache/backends/r2.js.map +1 -1
- package/dist/cache/classification-cache.d.ts +76 -34
- package/dist/cache/classification-cache.d.ts.map +1 -1
- package/dist/cache/classification-cache.js +110 -58
- package/dist/cache/classification-cache.js.map +1 -1
- package/dist/cache/coordinate.d.ts +59 -0
- package/dist/cache/coordinate.d.ts.map +1 -0
- package/dist/cache/coordinate.js +240 -0
- package/dist/cache/coordinate.js.map +1 -0
- package/dist/cache/discovery-cache.d.ts +112 -36
- package/dist/cache/discovery-cache.d.ts.map +1 -1
- package/dist/cache/discovery-cache.js +163 -42
- package/dist/cache/discovery-cache.js.map +1 -1
- package/dist/cache/tree-cache.d.ts +48 -53
- package/dist/cache/tree-cache.d.ts.map +1 -1
- package/dist/cache/tree-cache.js +87 -72
- package/dist/cache/tree-cache.js.map +1 -1
- package/dist/cache/verify-cache.d.ts +26 -21
- package/dist/cache/verify-cache.d.ts.map +1 -1
- package/dist/cache/verify-cache.js +37 -29
- package/dist/cache/verify-cache.js.map +1 -1
- package/dist/llm/ax-client.d.ts +3 -16
- package/dist/llm/ax-client.d.ts.map +1 -1
- package/dist/llm/ax-client.js +49 -2
- package/dist/llm/ax-client.js.map +1 -1
- package/dist/llm/batch-classifier.d.ts.map +1 -1
- package/dist/llm/batch-classifier.js +1 -0
- package/dist/llm/batch-classifier.js.map +1 -1
- package/dist/llm/config.d.ts +34 -8
- package/dist/llm/config.d.ts.map +1 -1
- package/dist/llm/config.js +52 -12
- package/dist/llm/config.js.map +1 -1
- package/dist/llm/discovery.d.ts.map +1 -1
- package/dist/llm/discovery.js +50 -4
- package/dist/llm/discovery.js.map +1 -1
- package/dist/llm/verification.d.ts.map +1 -1
- package/dist/llm/verification.js +45 -1
- package/dist/llm/verification.js.map +1 -1
- package/dist/security-scan/scanner.d.ts +15 -0
- package/dist/security-scan/scanner.d.ts.map +1 -1
- package/dist/security-scan/scanner.js +34 -0
- package/dist/security-scan/scanner.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cloudflare R2 (S3-compatible) cache backend (cognium-ai#158).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* The R2 object key is `<prefix>/<key[0:2]>/<key>.json` — same sharding
|
|
6
|
-
* as `LocalFSBackend` so the existing 3-component `ClassificationCache`
|
|
7
|
-
* keys (content + classifier + modelFingerprint hash) just work.
|
|
4
|
+
* Round 2 (2.28.0) layout:
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
6
|
+
* <prefix>/tree/<eco>/<name>@<ver>/<4hex>/<sha>.json (coord present)
|
|
7
|
+
* <prefix>/tree/_unowned/<2hex>/<sha>.json (no coord)
|
|
8
|
+
* <prefix>/verify/<2hex>/<sha>.json (flat)
|
|
9
|
+
* <prefix>/discovery/<2hex>/<sha>.json (flat)
|
|
10
|
+
*
|
|
11
|
+
* The coordinate is only consulted for tree-namespace keys; verify and
|
|
12
|
+
* discovery stay flat because their content already encodes the model
|
|
13
|
+
* fingerprint + per-batch fingerprint, so a coordinate prefix would
|
|
14
|
+
* just bloat the path.
|
|
13
15
|
*
|
|
14
16
|
* Configuration (env vars):
|
|
15
17
|
* LLM_CACHE_BACKEND=r2
|
|
@@ -18,14 +20,13 @@
|
|
|
18
20
|
* R2_ACCESS_KEY_ID=<S3-compat access key>
|
|
19
21
|
* R2_SECRET_ACCESS_KEY=<S3-compat secret>
|
|
20
22
|
* R2_REGION=auto (default; R2 ignores region)
|
|
21
|
-
* R2_PREFIX=
|
|
23
|
+
* R2_PREFIX=v2 (default; namespaces the bucket)
|
|
22
24
|
*
|
|
23
25
|
* Failure mode: R2 errors are swallowed and surfaced as misses so a
|
|
24
26
|
* network blip during scan degrades to local behavior, not a crash.
|
|
25
|
-
* This matches `LocalFSBackend` and the cache-as-optimization contract
|
|
26
|
-
* spelled out in `backend.ts`.
|
|
27
27
|
*/
|
|
28
28
|
import { S3Client, GetObjectCommand, PutObjectCommand, HeadObjectCommand, DeleteObjectCommand, } from '@aws-sdk/client-s3';
|
|
29
|
+
import { renderObjectPath } from './layout.js';
|
|
29
30
|
export class R2Backend {
|
|
30
31
|
kind = 'r2';
|
|
31
32
|
client;
|
|
@@ -33,7 +34,7 @@ export class R2Backend {
|
|
|
33
34
|
prefix;
|
|
34
35
|
constructor(opts) {
|
|
35
36
|
this.bucket = opts.bucket;
|
|
36
|
-
this.prefix = (opts.prefix ?? '
|
|
37
|
+
this.prefix = (opts.prefix ?? 'v2').replace(/^\/+|\/+$/g, '');
|
|
37
38
|
this.client = new S3Client({
|
|
38
39
|
endpoint: opts.endpoint,
|
|
39
40
|
region: opts.region ?? 'auto',
|
|
@@ -46,8 +47,7 @@ export class R2Backend {
|
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
objectKey(key) {
|
|
49
|
-
|
|
50
|
-
return `${this.prefix}/${sub}/${key}.json`;
|
|
50
|
+
return `${this.prefix}/${renderObjectPath(key)}`;
|
|
51
51
|
}
|
|
52
52
|
async get(key) {
|
|
53
53
|
try {
|
|
@@ -57,12 +57,10 @@ export class R2Backend {
|
|
|
57
57
|
}));
|
|
58
58
|
if (!out.Body)
|
|
59
59
|
return null;
|
|
60
|
-
// SDK v3 returns a stream; collect to string.
|
|
61
60
|
const stream = out.Body;
|
|
62
61
|
return await streamToString(stream);
|
|
63
62
|
}
|
|
64
63
|
catch (err) {
|
|
65
|
-
// 404 → miss. Other errors swallowed (cache is best-effort).
|
|
66
64
|
const name = err?.name;
|
|
67
65
|
if (name === 'NoSuchKey' || name === 'NotFound')
|
|
68
66
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r2.js","sourceRoot":"","sources":["../../../src/cache/backends/r2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"r2.js","sourceRoot":"","sources":["../../../src/cache/backends/r2.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,35 +2,65 @@
|
|
|
2
2
|
* Classification Cache
|
|
3
3
|
*
|
|
4
4
|
* Caches LLM classification results by composite key
|
|
5
|
-
* (content hash + classifier name + per-phase model fingerprint
|
|
6
|
-
*
|
|
5
|
+
* (schemaVer + content hash + classifier name + per-phase model fingerprint
|
|
6
|
+
* + optional coordinate hash-prefix).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - Hit/miss tracking for observability
|
|
8
|
+
* Storage is delegated to a `CacheBackend` (cognium-ai#158). Default
|
|
9
|
+
* = `LocalFSBackend`. Selecting `LLM_CACHE_BACKEND=r2` swaps to Cloudflare
|
|
10
|
+
* R2 transparently so two runners on different machines share the same
|
|
11
|
+
* cache.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
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.
|
|
13
|
+
* # Round 2 (2.28.0) key shape
|
|
18
14
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
15
|
+
* sha256(
|
|
16
|
+
* 'cic:v2' // SCHEMA_VER — bump on breaking key changes
|
|
17
|
+
* + '\0' + namespace // 'tree' | 'verify' | 'discovery'
|
|
18
|
+
* + '\0' + classifier
|
|
19
|
+
* + '\0' + content
|
|
20
|
+
* + '\0' + modelFingerprint // per-phase, see ../llm/config.ts
|
|
21
|
+
* + '\0' + coordinateHashPrefix // empty for non-tree, or _unowned
|
|
22
|
+
* )
|
|
23
|
+
*
|
|
24
|
+
* Why `namespace` is in the hash:
|
|
25
|
+
* So a tree-cache entry can never collide with a verify-cache entry
|
|
26
|
+
* even if a future caller happens to choose the same classifier name.
|
|
27
|
+
*
|
|
28
|
+
* Why `coordinate` is in the hash (when present):
|
|
29
|
+
* So `commons-lang3@3.12.0` and `commons-lang3@3.13.0` produce
|
|
30
|
+
* different SHAs for the same byte-identical file (we WANT them
|
|
31
|
+
* distinct because the surrounding rules / version-conditioned
|
|
32
|
+
* profile could differ). The coordinate is also reflected in the
|
|
33
|
+
* backend path layout — see `./backends/layout.ts` — so the cache
|
|
34
|
+
* is browseable in R2 dashboard by package.
|
|
26
35
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
36
|
+
* Model dimension (cognium-ai#151 P0 spike):
|
|
37
|
+
* `modelFingerprint` is the per-phase identity from
|
|
38
|
+
* `getDiscoveryModelFingerprint()` / `getVerifyModelFingerprint()` /
|
|
39
|
+
* `getComponentModelFingerprint()` in `../llm/config.ts`. Each cache
|
|
40
|
+
* instance uses the fingerprint scoped to its phase so swapping the
|
|
41
|
+
* discovery model doesn't invalidate verify results and vice versa
|
|
42
|
+
* (#158 D2).
|
|
43
|
+
*/
|
|
44
|
+
import type { CacheBackend, CacheNamespace } from './backend.js';
|
|
45
|
+
import type { PackageCoordinate } from './coordinate.js';
|
|
46
|
+
/**
|
|
47
|
+
* Wire-format version. Bump on every breaking change to the key composition
|
|
48
|
+
* OR the on-disk envelope shape. Operators rolling back to an older
|
|
49
|
+
* circle-ir-ai will see misses against newer SCHEMA_VER entries and
|
|
50
|
+
* re-populate cleanly.
|
|
31
51
|
*/
|
|
32
|
-
|
|
52
|
+
export declare const SCHEMA_VER = "cic:v2";
|
|
33
53
|
export interface ClassificationCacheOptions {
|
|
54
|
+
/**
|
|
55
|
+
* Logical cache namespace. Determines backend path layout AND whether
|
|
56
|
+
* the cache participates in the coordinate-aware tree layout.
|
|
57
|
+
*
|
|
58
|
+
* Defaults to `'classifier'` for generic LLM classification callers
|
|
59
|
+
* (batch-classifier, trust passes) that don't care about the namespace
|
|
60
|
+
* dimension. The three SAST caches (`tree-cache.ts`, `verify-cache.ts`,
|
|
61
|
+
* `discovery-cache.ts`) always pass their own namespace explicitly.
|
|
62
|
+
*/
|
|
63
|
+
namespace?: CacheNamespace;
|
|
34
64
|
/** Cache directory (default: .cognium/cache/ in project root). For
|
|
35
65
|
* non-fs backends this is interpreted as a sub-prefix only. */
|
|
36
66
|
cacheDir?: string;
|
|
@@ -39,20 +69,28 @@ export interface ClassificationCacheOptions {
|
|
|
39
69
|
/** Enable/disable cache (default: true) */
|
|
40
70
|
enabled?: boolean;
|
|
41
71
|
/**
|
|
42
|
-
* Per-phase model identity for cache namespacing (cognium-ai#151).
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* Defaults to empty string (legacy 2-component key shape).
|
|
72
|
+
* Per-phase model identity for cache namespacing (cognium-ai#151 / #158 D2).
|
|
73
|
+
* Tree-cache passes the discovery+verify joint fingerprint; verify-cache
|
|
74
|
+
* passes the verify-only fingerprint; discovery-cache passes the
|
|
75
|
+
* discovery-only fingerprint. See `../llm/config.ts`.
|
|
47
76
|
*/
|
|
48
77
|
modelFingerprint?: string;
|
|
49
78
|
/**
|
|
50
79
|
* Override the backend instance (tests). When omitted we build one
|
|
51
|
-
* via `makeBackend(cacheDir)` based on `LLM_CACHE_BACKEND` env.
|
|
80
|
+
* via `makeBackend(cacheDir, namespace)` based on `LLM_CACHE_BACKEND` env.
|
|
52
81
|
*/
|
|
53
82
|
backend?: CacheBackend;
|
|
54
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Per-get/set call extras. `coordinate` is honored only for the
|
|
86
|
+
* `tree` namespace; passing it elsewhere is a no-op so callers don't
|
|
87
|
+
* have to branch.
|
|
88
|
+
*/
|
|
89
|
+
export interface CacheCallOptions {
|
|
90
|
+
coordinate?: PackageCoordinate;
|
|
91
|
+
}
|
|
55
92
|
export declare class ClassificationCache<T = unknown> {
|
|
93
|
+
private namespace;
|
|
56
94
|
private cacheDir;
|
|
57
95
|
private maxAge;
|
|
58
96
|
private enabled;
|
|
@@ -66,19 +104,23 @@ export declare class ClassificationCache<T = unknown> {
|
|
|
66
104
|
getModelFingerprint(): string;
|
|
67
105
|
/** Backend kind identifier (`local-fs` | `memory` | `r2`). Observability. */
|
|
68
106
|
getBackendKind(): string;
|
|
69
|
-
|
|
107
|
+
/** Logical namespace (`tree` | `verify` | `discovery`). Observability. */
|
|
108
|
+
getNamespace(): CacheNamespace;
|
|
109
|
+
private computeDigest;
|
|
110
|
+
private toCacheKey;
|
|
70
111
|
private isValid;
|
|
71
|
-
get(content: string, classifier: string): Promise<T | null>;
|
|
72
|
-
set(content: string, classifier: string, result: T): Promise<void>;
|
|
73
|
-
has(content: string, classifier: string): Promise<boolean>;
|
|
112
|
+
get(content: string, classifier: string, opts?: CacheCallOptions): Promise<T | null>;
|
|
113
|
+
set(content: string, classifier: string, result: T, opts?: CacheCallOptions): Promise<void>;
|
|
114
|
+
has(content: string, classifier: string, opts?: CacheCallOptions): Promise<boolean>;
|
|
74
115
|
clear(): Promise<void>;
|
|
75
116
|
stats(): Promise<{
|
|
117
|
+
namespace: CacheNamespace;
|
|
76
118
|
memoryEntries: number;
|
|
77
119
|
diskEntries: number;
|
|
78
120
|
hitRate: number;
|
|
79
121
|
backend: string;
|
|
80
122
|
}>;
|
|
81
123
|
}
|
|
82
|
-
export declare function getClassificationCache(options?: ClassificationCacheOptions): ClassificationCache
|
|
124
|
+
export declare function getClassificationCache<T = unknown>(options?: ClassificationCacheOptions): ClassificationCache<T>;
|
|
83
125
|
export declare function resetClassificationCache(): void;
|
|
84
126
|
//# sourceMappingURL=classification-cache.d.ts.map
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAY,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGzD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,WAAW,CAAC;AAEnC,MAAM,WAAW,0BAA0B;IACzC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;oEACgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAWD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED,qBAAa,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC1C,OAAO,CAAC,SAAS,CAAiB;IAClC,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;IASpD,mEAAmE;IACnE,mBAAmB,IAAI,MAAM;IAI7B,6EAA6E;IAC7E,cAAc,IAAI,MAAM;IAIxB,0EAA0E;IAC1E,YAAY,IAAI,cAAc;IAI9B,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,OAAO;IAKT,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAqCpF,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB3F,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAInF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,KAAK,IAAI,OAAO,CAAC;QACrB,SAAS,EAAE,cAAc,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CAcH;AASD,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,GAAE,0BAA+B,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAUpH;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
|
|
@@ -2,38 +2,59 @@
|
|
|
2
2
|
* Classification Cache
|
|
3
3
|
*
|
|
4
4
|
* Caches LLM classification results by composite key
|
|
5
|
-
* (content hash + classifier name + per-phase model fingerprint
|
|
6
|
-
*
|
|
5
|
+
* (schemaVer + content hash + classifier name + per-phase model fingerprint
|
|
6
|
+
* + optional coordinate hash-prefix).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - Hit/miss tracking for observability
|
|
8
|
+
* Storage is delegated to a `CacheBackend` (cognium-ai#158). Default
|
|
9
|
+
* = `LocalFSBackend`. Selecting `LLM_CACHE_BACKEND=r2` swaps to Cloudflare
|
|
10
|
+
* R2 transparently so two runners on different machines share the same
|
|
11
|
+
* cache.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
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.
|
|
13
|
+
* # Round 2 (2.28.0) key shape
|
|
18
14
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
15
|
+
* sha256(
|
|
16
|
+
* 'cic:v2' // SCHEMA_VER — bump on breaking key changes
|
|
17
|
+
* + '\0' + namespace // 'tree' | 'verify' | 'discovery'
|
|
18
|
+
* + '\0' + classifier
|
|
19
|
+
* + '\0' + content
|
|
20
|
+
* + '\0' + modelFingerprint // per-phase, see ../llm/config.ts
|
|
21
|
+
* + '\0' + coordinateHashPrefix // empty for non-tree, or _unowned
|
|
22
|
+
* )
|
|
23
|
+
*
|
|
24
|
+
* Why `namespace` is in the hash:
|
|
25
|
+
* So a tree-cache entry can never collide with a verify-cache entry
|
|
26
|
+
* even if a future caller happens to choose the same classifier name.
|
|
27
|
+
*
|
|
28
|
+
* Why `coordinate` is in the hash (when present):
|
|
29
|
+
* So `commons-lang3@3.12.0` and `commons-lang3@3.13.0` produce
|
|
30
|
+
* different SHAs for the same byte-identical file (we WANT them
|
|
31
|
+
* distinct because the surrounding rules / version-conditioned
|
|
32
|
+
* profile could differ). The coordinate is also reflected in the
|
|
33
|
+
* backend path layout — see `./backends/layout.ts` — so the cache
|
|
34
|
+
* is browseable in R2 dashboard by package.
|
|
26
35
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
36
|
+
* Model dimension (cognium-ai#151 P0 spike):
|
|
37
|
+
* `modelFingerprint` is the per-phase identity from
|
|
38
|
+
* `getDiscoveryModelFingerprint()` / `getVerifyModelFingerprint()` /
|
|
39
|
+
* `getComponentModelFingerprint()` in `../llm/config.ts`. Each cache
|
|
40
|
+
* instance uses the fingerprint scoped to its phase so swapping the
|
|
41
|
+
* discovery model doesn't invalidate verify results and vice versa
|
|
42
|
+
* (#158 D2).
|
|
31
43
|
*/
|
|
32
44
|
import * as path from 'path';
|
|
33
45
|
import * as crypto from 'crypto';
|
|
34
46
|
import { makeBackend } from './backends/index.js';
|
|
47
|
+
import { coordinateSegments } from './coordinate.js';
|
|
48
|
+
/**
|
|
49
|
+
* Wire-format version. Bump on every breaking change to the key composition
|
|
50
|
+
* OR the on-disk envelope shape. Operators rolling back to an older
|
|
51
|
+
* circle-ir-ai will see misses against newer SCHEMA_VER entries and
|
|
52
|
+
* re-populate cleanly.
|
|
53
|
+
*/
|
|
54
|
+
export const SCHEMA_VER = 'cic:v2';
|
|
35
55
|
const DEFAULT_MAX_AGE = 24 * 60 * 60 * 1000; // 24 hours
|
|
36
56
|
export class ClassificationCache {
|
|
57
|
+
namespace;
|
|
37
58
|
cacheDir;
|
|
38
59
|
maxAge;
|
|
39
60
|
enabled;
|
|
@@ -43,11 +64,12 @@ export class ClassificationCache {
|
|
|
43
64
|
hits = 0;
|
|
44
65
|
misses = 0;
|
|
45
66
|
constructor(options = {}) {
|
|
67
|
+
this.namespace = options.namespace ?? 'classifier';
|
|
46
68
|
this.cacheDir = options.cacheDir || path.join(process.cwd(), '.cognium', 'cache');
|
|
47
69
|
this.maxAge = options.maxAge ?? DEFAULT_MAX_AGE;
|
|
48
70
|
this.enabled = options.enabled ?? true;
|
|
49
71
|
this.modelFingerprint = options.modelFingerprint ?? '';
|
|
50
|
-
this.backend = options.backend ?? makeBackend(this.cacheDir);
|
|
72
|
+
this.backend = options.backend ?? makeBackend(this.cacheDir, this.namespace);
|
|
51
73
|
}
|
|
52
74
|
/** Per-phase model identity used in cache key (cognium-ai#151). */
|
|
53
75
|
getModelFingerprint() {
|
|
@@ -57,43 +79,62 @@ export class ClassificationCache {
|
|
|
57
79
|
getBackendKind() {
|
|
58
80
|
return this.backend.kind;
|
|
59
81
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
/** Logical namespace (`tree` | `verify` | `discovery`). Observability. */
|
|
83
|
+
getNamespace() {
|
|
84
|
+
return this.namespace;
|
|
85
|
+
}
|
|
86
|
+
computeDigest(content, classifier, coordinate) {
|
|
87
|
+
// Coordinate hash-prefix only applies to the `tree` namespace.
|
|
88
|
+
// Including it in the digest (not just the path) is what guarantees
|
|
89
|
+
// a fileSHA collision across packages still produces distinct keys.
|
|
90
|
+
const coordPart = (this.namespace === 'tree' && coordinate)
|
|
91
|
+
? coordinateSegments(coordinate).hashPrefix
|
|
92
|
+
: '';
|
|
93
|
+
const parts = SCHEMA_VER
|
|
94
|
+
+ '\0' + this.namespace
|
|
95
|
+
+ '\0' + classifier
|
|
96
|
+
+ '\0' + content
|
|
97
|
+
+ '\0' + this.modelFingerprint
|
|
98
|
+
+ '\0' + coordPart;
|
|
68
99
|
return crypto.createHash('sha256').update(parts).digest('hex');
|
|
69
100
|
}
|
|
101
|
+
toCacheKey(digest, coordinate) {
|
|
102
|
+
const key = { namespace: this.namespace, digest };
|
|
103
|
+
if (this.namespace === 'tree' && coordinate) {
|
|
104
|
+
key.coordinate = coordinate;
|
|
105
|
+
}
|
|
106
|
+
return key;
|
|
107
|
+
}
|
|
70
108
|
isValid(entry) {
|
|
109
|
+
if (entry.schemaVer !== SCHEMA_VER)
|
|
110
|
+
return false;
|
|
71
111
|
return (Date.now() - entry.timestamp) <= this.maxAge;
|
|
72
112
|
}
|
|
73
|
-
async get(content, classifier) {
|
|
113
|
+
async get(content, classifier, opts) {
|
|
74
114
|
if (!this.enabled)
|
|
75
115
|
return null;
|
|
76
|
-
const
|
|
116
|
+
const digest = this.computeDigest(content, classifier, opts?.coordinate);
|
|
117
|
+
const cacheKey = this.toCacheKey(digest, opts?.coordinate);
|
|
77
118
|
// Memory cache (process-local hot path)
|
|
78
|
-
const memEntry = this.memoryCache.get(
|
|
119
|
+
const memEntry = this.memoryCache.get(digest);
|
|
79
120
|
if (memEntry && this.isValid(memEntry)) {
|
|
80
121
|
this.hits++;
|
|
81
122
|
return memEntry.result;
|
|
82
123
|
}
|
|
83
124
|
// Backend (disk / R2 / etc.)
|
|
84
125
|
try {
|
|
85
|
-
const raw = await this.backend.get(
|
|
126
|
+
const raw = await this.backend.get(cacheKey);
|
|
86
127
|
if (raw !== null) {
|
|
87
128
|
const entry = JSON.parse(raw);
|
|
88
129
|
if (this.isValid(entry)) {
|
|
89
|
-
this.memoryCache.set(
|
|
130
|
+
this.memoryCache.set(digest, entry);
|
|
90
131
|
this.hits++;
|
|
91
132
|
return entry.result;
|
|
92
133
|
}
|
|
93
|
-
// Stale
|
|
94
|
-
// keep the read path fast.
|
|
134
|
+
// Stale OR schemaVer-mismatch — drop. We don't await the deletion
|
|
135
|
+
// to keep the read path fast.
|
|
95
136
|
if (this.backend.delete) {
|
|
96
|
-
void this.backend.delete(
|
|
137
|
+
void this.backend.delete(cacheKey);
|
|
97
138
|
}
|
|
98
139
|
}
|
|
99
140
|
}
|
|
@@ -103,21 +144,27 @@ export class ClassificationCache {
|
|
|
103
144
|
this.misses++;
|
|
104
145
|
return null;
|
|
105
146
|
}
|
|
106
|
-
async set(content, classifier, result) {
|
|
147
|
+
async set(content, classifier, result, opts) {
|
|
107
148
|
if (!this.enabled)
|
|
108
149
|
return;
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
150
|
+
const digest = this.computeDigest(content, classifier, opts?.coordinate);
|
|
151
|
+
const cacheKey = this.toCacheKey(digest, opts?.coordinate);
|
|
152
|
+
const entry = {
|
|
153
|
+
schemaVer: SCHEMA_VER,
|
|
154
|
+
key: digest,
|
|
155
|
+
timestamp: Date.now(),
|
|
156
|
+
result,
|
|
157
|
+
};
|
|
158
|
+
this.memoryCache.set(digest, entry);
|
|
112
159
|
try {
|
|
113
|
-
await this.backend.set(
|
|
160
|
+
await this.backend.set(cacheKey, JSON.stringify(entry));
|
|
114
161
|
}
|
|
115
162
|
catch {
|
|
116
163
|
// Cache write is best-effort; never fail the caller.
|
|
117
164
|
}
|
|
118
165
|
}
|
|
119
|
-
async has(content, classifier) {
|
|
120
|
-
return (await this.get(content, classifier)) !== null;
|
|
166
|
+
async has(content, classifier, opts) {
|
|
167
|
+
return (await this.get(content, classifier, opts)) !== null;
|
|
121
168
|
}
|
|
122
169
|
async clear() {
|
|
123
170
|
this.memoryCache.clear();
|
|
@@ -140,6 +187,7 @@ export class ClassificationCache {
|
|
|
140
187
|
}
|
|
141
188
|
const total = this.hits + this.misses;
|
|
142
189
|
return {
|
|
190
|
+
namespace: this.namespace,
|
|
143
191
|
memoryEntries: this.memoryCache.size,
|
|
144
192
|
diskEntries,
|
|
145
193
|
hitRate: total > 0 ? this.hits / total : 0,
|
|
@@ -147,20 +195,24 @@ export class ClassificationCache {
|
|
|
147
195
|
};
|
|
148
196
|
}
|
|
149
197
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
export function getClassificationCache(options) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Singleton table — keyed on (namespace, modelFingerprint) so two scans
|
|
200
|
+
* with different per-phase models in the same process get distinct
|
|
201
|
+
* caches (cognium-ai#151).
|
|
202
|
+
*/
|
|
203
|
+
const singletons = new Map();
|
|
204
|
+
export function getClassificationCache(options = {}) {
|
|
205
|
+
const fp = options.modelFingerprint ?? '';
|
|
206
|
+
const namespace = options.namespace ?? 'classifier';
|
|
207
|
+
const slot = `${namespace}\0${fp}`;
|
|
208
|
+
let existing = singletons.get(slot);
|
|
209
|
+
if (!existing) {
|
|
210
|
+
existing = new ClassificationCache(options);
|
|
211
|
+
singletons.set(slot, existing);
|
|
160
212
|
}
|
|
161
|
-
return
|
|
213
|
+
return existing;
|
|
162
214
|
}
|
|
163
215
|
export function resetClassificationCache() {
|
|
164
|
-
|
|
216
|
+
singletons.clear();
|
|
165
217
|
}
|
|
166
218
|
//# sourceMappingURL=classification-cache.js.map
|
|
@@ -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,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package coordinate detection for cache path layout.
|
|
3
|
+
*
|
|
4
|
+
* The tree-cache backend (R2 / local-fs) uses the coordinate to build a
|
|
5
|
+
* package-aware key layout:
|
|
6
|
+
*
|
|
7
|
+
* tree/<ecosystem>/<name>@<version>/<4hex>/<sha>.json (published coords)
|
|
8
|
+
* tree/_local/<name>@<version>/<4hex>/<sha>.json (repo-root manifest)
|
|
9
|
+
* tree/_unowned/<4hex>/<sha>.json (no coordinate)
|
|
10
|
+
*
|
|
11
|
+
* Coordinate is resolved ONCE per scan from the repo root. We deliberately
|
|
12
|
+
* keep the extractor minimal (only manifest files we can parse with zero
|
|
13
|
+
* deps) so it can be folded into hot paths without measurable overhead.
|
|
14
|
+
*
|
|
15
|
+
* What's intentionally *not* in scope here:
|
|
16
|
+
* - per-file coordinate from `node_modules/<pkg>/...` paths (deferred until
|
|
17
|
+
* we actually scan transitive deps)
|
|
18
|
+
* - lockfile-derived coordinates
|
|
19
|
+
* - Maven `<artifactId>` resolution from a multi-module reactor
|
|
20
|
+
*
|
|
21
|
+
* For the first-party github sweep use case, every file in a repo shares
|
|
22
|
+
* the same coordinate (or falls through to `_unowned`), which is exactly
|
|
23
|
+
* what we want for cross-runner cache sharing.
|
|
24
|
+
*/
|
|
25
|
+
export type Ecosystem = 'npm' | 'maven' | 'pypi' | 'cargo' | 'go' | '_local' | '_unowned';
|
|
26
|
+
export interface PackageCoordinate {
|
|
27
|
+
ecosystem: Ecosystem;
|
|
28
|
+
/** Package name in its ecosystem's canonical form (lower-case, normalized). */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Resolved version string, or `'HEAD'` for `_local`. */
|
|
31
|
+
version: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The sentinel coordinate emitted when the repo has no detectable manifest.
|
|
35
|
+
*
|
|
36
|
+
* Tree-cache content still gets cached, just under `_unowned/<4hex>/<sha>`.
|
|
37
|
+
* Re-using the singleton means the path layout is constant for un-coord'd
|
|
38
|
+
* scans and avoids per-file object allocation.
|
|
39
|
+
*/
|
|
40
|
+
export declare const UNOWNED: PackageCoordinate;
|
|
41
|
+
/**
|
|
42
|
+
* Render the coordinate as a stable path prefix and a stable hash prefix.
|
|
43
|
+
*
|
|
44
|
+
* `pathSegment` is what the backend uses to build the R2 / local-fs key.
|
|
45
|
+
* `hashPrefix` is what cache-key composers fold into the content hash so
|
|
46
|
+
* the SHA itself encodes the coordinate (no collision across packages).
|
|
47
|
+
*/
|
|
48
|
+
export declare function coordinateSegments(c: PackageCoordinate): {
|
|
49
|
+
pathSegment: string;
|
|
50
|
+
hashPrefix: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Detect a package coordinate from the repo root manifest files.
|
|
54
|
+
*
|
|
55
|
+
* Synchronous because it runs once per scan at scanner entry and the
|
|
56
|
+
* disk reads are tiny (`package.json` / `pom.xml`).
|
|
57
|
+
*/
|
|
58
|
+
export declare function detectCoordinate(repoRoot: string): PackageCoordinate;
|
|
59
|
+
//# sourceMappingURL=coordinate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate.d.ts","sourceRoot":"","sources":["../../src/cache/coordinate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE1F,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,iBAIpB,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,iBAAiB,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CASpG;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAwFpE"}
|