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.
Files changed (71) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/dist/agents/mastra/workflow.d.ts +15 -0
  3. package/dist/agents/mastra/workflow.d.ts.map +1 -1
  4. package/dist/agents/mastra/workflow.js +33 -12
  5. package/dist/agents/mastra/workflow.js.map +1 -1
  6. package/dist/cache/backend.d.ts +62 -10
  7. package/dist/cache/backend.d.ts.map +1 -1
  8. package/dist/cache/backend.js +20 -4
  9. package/dist/cache/backend.js.map +1 -1
  10. package/dist/cache/backends/index.d.ts +30 -18
  11. package/dist/cache/backends/index.d.ts.map +1 -1
  12. package/dist/cache/backends/index.js +37 -29
  13. package/dist/cache/backends/index.js.map +1 -1
  14. package/dist/cache/backends/layout.d.ts +31 -0
  15. package/dist/cache/backends/layout.d.ts.map +1 -0
  16. package/dist/cache/backends/layout.js +46 -0
  17. package/dist/cache/backends/layout.js.map +1 -0
  18. package/dist/cache/backends/local-fs.d.ts +14 -11
  19. package/dist/cache/backends/local-fs.d.ts.map +1 -1
  20. package/dist/cache/backends/local-fs.js +30 -18
  21. package/dist/cache/backends/local-fs.js.map +1 -1
  22. package/dist/cache/backends/memory.d.ts +11 -6
  23. package/dist/cache/backends/memory.d.ts.map +1 -1
  24. package/dist/cache/backends/memory.js +13 -5
  25. package/dist/cache/backends/memory.js.map +1 -1
  26. package/dist/cache/backends/r2.d.ts +17 -17
  27. package/dist/cache/backends/r2.d.ts.map +1 -1
  28. package/dist/cache/backends/r2.js +14 -16
  29. package/dist/cache/backends/r2.js.map +1 -1
  30. package/dist/cache/classification-cache.d.ts +76 -34
  31. package/dist/cache/classification-cache.d.ts.map +1 -1
  32. package/dist/cache/classification-cache.js +110 -58
  33. package/dist/cache/classification-cache.js.map +1 -1
  34. package/dist/cache/coordinate.d.ts +59 -0
  35. package/dist/cache/coordinate.d.ts.map +1 -0
  36. package/dist/cache/coordinate.js +240 -0
  37. package/dist/cache/coordinate.js.map +1 -0
  38. package/dist/cache/discovery-cache.d.ts +112 -36
  39. package/dist/cache/discovery-cache.d.ts.map +1 -1
  40. package/dist/cache/discovery-cache.js +163 -42
  41. package/dist/cache/discovery-cache.js.map +1 -1
  42. package/dist/cache/tree-cache.d.ts +48 -53
  43. package/dist/cache/tree-cache.d.ts.map +1 -1
  44. package/dist/cache/tree-cache.js +87 -72
  45. package/dist/cache/tree-cache.js.map +1 -1
  46. package/dist/cache/verify-cache.d.ts +26 -21
  47. package/dist/cache/verify-cache.d.ts.map +1 -1
  48. package/dist/cache/verify-cache.js +37 -29
  49. package/dist/cache/verify-cache.js.map +1 -1
  50. package/dist/llm/ax-client.d.ts +3 -16
  51. package/dist/llm/ax-client.d.ts.map +1 -1
  52. package/dist/llm/ax-client.js +49 -2
  53. package/dist/llm/ax-client.js.map +1 -1
  54. package/dist/llm/batch-classifier.d.ts.map +1 -1
  55. package/dist/llm/batch-classifier.js +1 -0
  56. package/dist/llm/batch-classifier.js.map +1 -1
  57. package/dist/llm/config.d.ts +34 -8
  58. package/dist/llm/config.d.ts.map +1 -1
  59. package/dist/llm/config.js +52 -12
  60. package/dist/llm/config.js.map +1 -1
  61. package/dist/llm/discovery.d.ts.map +1 -1
  62. package/dist/llm/discovery.js +50 -4
  63. package/dist/llm/discovery.js.map +1 -1
  64. package/dist/llm/verification.d.ts.map +1 -1
  65. package/dist/llm/verification.js +45 -1
  66. package/dist/llm/verification.js.map +1 -1
  67. package/dist/security-scan/scanner.d.ts +15 -0
  68. package/dist/security-scan/scanner.d.ts.map +1 -1
  69. package/dist/security-scan/scanner.js +34 -0
  70. package/dist/security-scan/scanner.js.map +1 -1
  71. package/package.json +2 -2
@@ -0,0 +1,240 @@
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
+ import * as fs from 'fs';
26
+ import * as path from 'path';
27
+ /**
28
+ * The sentinel coordinate emitted when the repo has no detectable manifest.
29
+ *
30
+ * Tree-cache content still gets cached, just under `_unowned/<4hex>/<sha>`.
31
+ * Re-using the singleton means the path layout is constant for un-coord'd
32
+ * scans and avoids per-file object allocation.
33
+ */
34
+ export const UNOWNED = Object.freeze({
35
+ ecosystem: '_unowned',
36
+ name: '_',
37
+ version: '_',
38
+ });
39
+ /**
40
+ * Render the coordinate as a stable path prefix and a stable hash prefix.
41
+ *
42
+ * `pathSegment` is what the backend uses to build the R2 / local-fs key.
43
+ * `hashPrefix` is what cache-key composers fold into the content hash so
44
+ * the SHA itself encodes the coordinate (no collision across packages).
45
+ */
46
+ export function coordinateSegments(c) {
47
+ if (c.ecosystem === '_unowned') {
48
+ return { pathSegment: '_unowned', hashPrefix: '' };
49
+ }
50
+ const slug = `${c.name}@${c.version}`;
51
+ return {
52
+ pathSegment: `${c.ecosystem}/${slug}`,
53
+ hashPrefix: `${c.ecosystem}\0${slug}`,
54
+ };
55
+ }
56
+ /**
57
+ * Detect a package coordinate from the repo root manifest files.
58
+ *
59
+ * Synchronous because it runs once per scan at scanner entry and the
60
+ * disk reads are tiny (`package.json` / `pom.xml`).
61
+ */
62
+ export function detectCoordinate(repoRoot) {
63
+ // npm — package.json
64
+ const pkgJsonPath = path.join(repoRoot, 'package.json');
65
+ if (fs.existsSync(pkgJsonPath)) {
66
+ try {
67
+ const raw = fs.readFileSync(pkgJsonPath, 'utf8');
68
+ const json = JSON.parse(raw);
69
+ const name = typeof json.name === 'string' ? json.name.trim() : '';
70
+ const version = typeof json.version === 'string' ? json.version.trim() : '';
71
+ if (name && version) {
72
+ // `private: true` packages are by definition not published — use
73
+ // `_local` so the layout still groups them but doesn't pollute
74
+ // an ecosystem namespace with unpublishable coords.
75
+ const isPrivate = json.private === true;
76
+ return {
77
+ ecosystem: isPrivate ? '_local' : 'npm',
78
+ name: name.toLowerCase(),
79
+ version,
80
+ };
81
+ }
82
+ if (name) {
83
+ return { ecosystem: '_local', name: name.toLowerCase(), version: 'HEAD' };
84
+ }
85
+ }
86
+ catch {
87
+ // fallthrough
88
+ }
89
+ }
90
+ // maven — pom.xml (regex-only; no XML parser dep)
91
+ const pomPath = path.join(repoRoot, 'pom.xml');
92
+ if (fs.existsSync(pomPath)) {
93
+ try {
94
+ const raw = fs.readFileSync(pomPath, 'utf8');
95
+ const coord = parsePomCoordinate(raw);
96
+ if (coord)
97
+ return coord;
98
+ }
99
+ catch {
100
+ // fallthrough
101
+ }
102
+ }
103
+ // pyproject.toml — minimal `[project] name = "...", version = "..."` lift
104
+ const pyprojectPath = path.join(repoRoot, 'pyproject.toml');
105
+ if (fs.existsSync(pyprojectPath)) {
106
+ try {
107
+ const raw = fs.readFileSync(pyprojectPath, 'utf8');
108
+ const coord = parsePyprojectCoordinate(raw);
109
+ if (coord)
110
+ return coord;
111
+ }
112
+ catch {
113
+ // fallthrough
114
+ }
115
+ }
116
+ // Cargo.toml — `[package] name = "...", version = "..."`
117
+ const cargoPath = path.join(repoRoot, 'Cargo.toml');
118
+ if (fs.existsSync(cargoPath)) {
119
+ try {
120
+ const raw = fs.readFileSync(cargoPath, 'utf8');
121
+ const coord = parseCargoCoordinate(raw);
122
+ if (coord)
123
+ return coord;
124
+ }
125
+ catch {
126
+ // fallthrough
127
+ }
128
+ }
129
+ // go.mod — `module <path>` (no version in manifest; use HEAD)
130
+ const goModPath = path.join(repoRoot, 'go.mod');
131
+ if (fs.existsSync(goModPath)) {
132
+ try {
133
+ const raw = fs.readFileSync(goModPath, 'utf8');
134
+ const match = /^module\s+(\S+)\s*$/m.exec(raw);
135
+ if (match?.[1]) {
136
+ return { ecosystem: 'go', name: match[1].toLowerCase(), version: 'HEAD' };
137
+ }
138
+ }
139
+ catch {
140
+ // fallthrough
141
+ }
142
+ }
143
+ // Fallback: use repo-root basename as a `_local/<dir>@HEAD` coordinate so
144
+ // re-scans of the same on-disk repo still share cache, even without a
145
+ // manifest. The 4-hex shard under `_unowned` is reserved for truly
146
+ // anonymous content (e.g. ad-hoc file inputs from CI).
147
+ const basename = path.basename(path.resolve(repoRoot));
148
+ if (basename && basename !== '/' && basename !== '.') {
149
+ return { ecosystem: '_local', name: basename.toLowerCase(), version: 'HEAD' };
150
+ }
151
+ return UNOWNED;
152
+ }
153
+ /**
154
+ * Parse `<groupId>:<artifactId>:<version>` from the *top-level* of a pom.
155
+ *
156
+ * We intentionally read the project's own `<groupId>/<artifactId>/<version>`
157
+ * and fall through to `<parent>` only when the child block omits them
158
+ * (standard Maven inheritance). Sub-modules are not recursed; the cache
159
+ * shares the parent coordinate which is fine for cross-runner sharing.
160
+ */
161
+ function parsePomCoordinate(xml) {
162
+ // Strip XML comments to avoid commented-out parent blocks shadowing real ones.
163
+ const cleaned = xml.replace(/<!--[\s\S]*?-->/g, '');
164
+ // Find the top-level <project> children only — i.e. tags that appear
165
+ // before any sub-element opens. Cheap approximation: take the first
166
+ // occurrence of each tag at depth-1 by finding the first match.
167
+ const projectMatch = /<project\b[^>]*>([\s\S]*?)<\/project>/.exec(cleaned);
168
+ if (!projectMatch)
169
+ return null;
170
+ const body = projectMatch[1];
171
+ const direct = readDirectChildren(body);
172
+ let groupId = direct.groupId;
173
+ let artifactId = direct.artifactId;
174
+ let version = direct.version;
175
+ if (!groupId || !version) {
176
+ const parentMatch = /<parent\b[^>]*>([\s\S]*?)<\/parent>/.exec(body);
177
+ if (parentMatch) {
178
+ const parent = readDirectChildren(parentMatch[1]);
179
+ groupId ??= parent.groupId;
180
+ version ??= parent.version;
181
+ }
182
+ }
183
+ if (!groupId || !artifactId || !version)
184
+ return null;
185
+ return {
186
+ ecosystem: 'maven',
187
+ name: `${groupId.toLowerCase()}:${artifactId.toLowerCase()}`,
188
+ version,
189
+ };
190
+ }
191
+ function readDirectChildren(body) {
192
+ const pick = (tag) => {
193
+ const re = new RegExp(`<${tag}>\\s*([^<\\s][^<]*?)\\s*</${tag}>`);
194
+ const m = re.exec(body);
195
+ return m?.[1]?.trim();
196
+ };
197
+ return {
198
+ groupId: pick('groupId'),
199
+ artifactId: pick('artifactId'),
200
+ version: pick('version'),
201
+ };
202
+ }
203
+ function parsePyprojectCoordinate(toml) {
204
+ // Look for `[project]` table, then `name = "..."` and `version = "..."`
205
+ // inside it. Handle `[tool.poetry]` as a fallback for legacy Poetry
206
+ // layouts.
207
+ const tryTable = (header) => {
208
+ const re = new RegExp(`\\[${header}\\]([\\s\\S]*?)(?=\\n\\[|$)`, 'm');
209
+ const m = re.exec(toml);
210
+ if (!m)
211
+ return null;
212
+ const body = m[1];
213
+ const nameMatch = /^\s*name\s*=\s*['"]([^'"]+)['"]/m.exec(body);
214
+ const versionMatch = /^\s*version\s*=\s*['"]([^'"]+)['"]/m.exec(body);
215
+ if (!nameMatch || !versionMatch)
216
+ return null;
217
+ return {
218
+ ecosystem: 'pypi',
219
+ name: nameMatch[1].toLowerCase(),
220
+ version: versionMatch[1],
221
+ };
222
+ };
223
+ return tryTable('project') ?? tryTable('tool\\.poetry');
224
+ }
225
+ function parseCargoCoordinate(toml) {
226
+ const m = /\[package\]([\s\S]*?)(?=\n\[|$)/m.exec(toml);
227
+ if (!m)
228
+ return null;
229
+ const body = m[1];
230
+ const nameMatch = /^\s*name\s*=\s*['"]([^'"]+)['"]/m.exec(body);
231
+ const versionMatch = /^\s*version\s*=\s*['"]([^'"]+)['"]/m.exec(body);
232
+ if (!nameMatch || !versionMatch)
233
+ return null;
234
+ return {
235
+ ecosystem: 'cargo',
236
+ name: nameMatch[1].toLowerCase(),
237
+ version: versionMatch[1],
238
+ };
239
+ }
240
+ //# sourceMappingURL=coordinate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinate.js","sourceRoot":"","sources":["../../src/cache/coordinate.ts"],"names":[],"mappings":"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"}
@@ -1,71 +1,147 @@
1
1
  /**
2
2
  * Discovery Cache
3
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.
4
+ * Persistent on-disk cache for LLM discovery results per method
5
+ * (cognium-ai#134, P2 of epic #155, key reshape #158 Round 2).
10
6
  *
11
- * Built on top of `ClassificationCache<DiscoveryResult>` (which already
12
- * carries the cognium-ai#151 model-fingerprint key dimension), with
13
- * discovery-specific key construction:
7
+ * Sits in front of the in-memory `_discoveryDedupCache` from
8
+ * discovery.ts (#136) so identical method-level discovery requests
9
+ * survive across processes, CI runs, and developer iterations.
14
10
  *
15
- * content = className + '\\0' + methodName + '\\0' +
16
- * SHA-256(methodCode) + '\\0' + sortedCweSet
11
+ * **Key composition (Round 2 2.28.0):**
12
+ *
13
+ * content = methodSha + '\0' + sortedCweSet + '\0' + language
14
+ * + '\0' + PROMPT_VER
17
15
  * classifier = 'discovery-method'
18
- * fingerprint = getPhaseModelFingerprint(config) // includes discovery model
16
+ * fingerprint = getDiscoveryModelFingerprint() // discovery model ONLY
17
+ *
18
+ * **D6 fix:** dropped `className` and `methodName` from the key. They
19
+ * were redundant — if two different method *bodies* hash to the same
20
+ * SHA-256, they are byte-identical and the LLM's answer would be too.
21
+ * Including names also broke cache sharing on refactors that renamed
22
+ * a method without changing its body. (Names are still passed to the
23
+ * LLM as context inside the prompt; we just don't key the cache on
24
+ * them.)
25
+ *
26
+ * **D6 also adds `language`:** the same byte-identical snippet means
27
+ * very different things in Java vs Kotlin vs Scala — without language
28
+ * in the key, a Java result could be reused for a Kotlin call. Likely
29
+ * benign in practice but a correctness gap worth closing.
19
30
  *
20
- * 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).
31
+ * **D2 fix:** uses the discovery-only model fingerprint instead of the
32
+ * joint disc+verify+component fingerprint, so swapping the verify
33
+ * model doesn't invalidate discovery results.
24
34
  *
25
- * 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)
35
+ * Cache namespace: `discovery` (separate from `tree` and `verify`).
32
36
  *
33
37
  * Env toggles:
34
- * LLM_DISCOVERY_CACHE=0 → disable persistent discovery cache
35
- * (in-memory dedup from #136 still applies)
38
+ * LLM_DISCOVERY_CACHE=0 → disable persistent discovery cache.
36
39
  */
37
40
  import { ClassificationCache } from './classification-cache.js';
38
41
  import { type LLMConfig } from '../llm/config.js';
39
42
  import type { DiscoveryResult } from '../llm/discovery.js';
43
+ /** Source item shape returned by `discoverSourcesForLanguage`. */
44
+ export interface LangDiscoveredSource {
45
+ line: number;
46
+ variable: string;
47
+ type: string;
48
+ confidence: number;
49
+ reasoning: string;
50
+ }
51
+ /** Sink item shape returned by `discoverSinksForLanguage`. */
52
+ export interface LangDiscoveredSink {
53
+ line: number;
54
+ method: string;
55
+ type: string;
56
+ cwe: string;
57
+ argPositions: number[];
58
+ confidence: number;
59
+ reasoning: string;
60
+ }
40
61
  /**
41
62
  * 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).
63
+ * fingerprint. When `LLM_DISCOVERY_CACHE=0` returns a disabled cache.
44
64
  */
45
65
  export declare function getDiscoveryCache(config?: LLMConfig): ClassificationCache<DiscoveryResult>;
46
66
  /** Reset the module-scoped singleton — test-only. */
47
67
  export declare function resetDiscoveryCache(): void;
68
+ /**
69
+ * Get the persistent cache for `discoverSourcesForLanguage` (cognium-ai#161).
70
+ * Uses the same `'discovery'` namespace and per-phase discovery model
71
+ * fingerprint as the per-CWE cache, but a different classifier so the two
72
+ * never collide. Honors `LLM_DISCOVERY_CACHE=0` to disable.
73
+ */
74
+ export declare function getDiscoverSourcesLanguageCache(config?: LLMConfig): ClassificationCache<LangDiscoveredSource[]>;
75
+ /**
76
+ * Get the persistent cache for `discoverSinksForLanguage` (cognium-ai#161).
77
+ * See `getDiscoverSourcesLanguageCache` for the namespace/fingerprint
78
+ * rationale.
79
+ */
80
+ export declare function getDiscoverSinksLanguageCache(config?: LLMConfig): ClassificationCache<LangDiscoveredSink[]>;
81
+ /**
82
+ * Compute the `(content, classifier)` tuple for the source-discoverer
83
+ * cache. Components (in order):
84
+ * - language — Java vs Kotlin etc. (same rationale as D6 above)
85
+ * - methodSha — code edit invalidates only that entry
86
+ * - functionName — same body called as different methods is a distinct
87
+ * prompt (the name is templated into the user prompt)
88
+ * - moduleRole — passed verbatim into the prompt
89
+ * - existingSourcesSha — the "what we already found" context changes
90
+ * the prompt, so it must change the key
91
+ * - LANG_DISCOVERER_PROMPT_VER — global invalidator for the language
92
+ * discoverer prompt template
93
+ */
94
+ export declare function computeDiscoverSourcesLanguageCacheKey(input: {
95
+ language: string;
96
+ code: string;
97
+ functionName: string;
98
+ moduleRole: string;
99
+ existingSources: string;
100
+ }): {
101
+ content: string;
102
+ classifier: string;
103
+ };
104
+ /**
105
+ * Compute the `(content, classifier)` tuple for the sink-discoverer
106
+ * cache. Mirrors `computeDiscoverSourcesLanguageCacheKey` but uses the
107
+ * sink-prompt context (`methodCalls`, `existingSinks`) instead of the
108
+ * source-prompt context.
109
+ */
110
+ export declare function computeDiscoverSinksLanguageCacheKey(input: {
111
+ language: string;
112
+ code: string;
113
+ functionName: string;
114
+ methodCalls: string;
115
+ existingSinks: string;
116
+ }): {
117
+ content: string;
118
+ classifier: string;
119
+ };
120
+ /** Test-only: expose the language-discoverer prompt-version sentinel. */
121
+ export declare function getLangDiscovererCachePromptVersion(): string;
48
122
  /**
49
123
  * Compute the `(content, classifier)` tuple for the underlying
50
124
  * ClassificationCache. The fingerprint dimension is folded in by the
51
- * cache itself (see classification-cache.ts:computeKey).
125
+ * cache itself.
52
126
  *
53
127
  * Components:
54
- * - classNamedisambiguates 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
128
+ * - methodCode SHA-256 method edit invalidates only that entry
57
129
  * - 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
130
+ * reuse each other's prompts
131
+ * - languageJava vs Kotlin etc. (D6 fix)
132
+ * - PROMPT_VER — global discovery-prompt invalidator
133
+ *
134
+ * Note: `className` / `methodName` are intentionally NOT in the key
135
+ * (D6 fix). They're prompt-context, not identity.
61
136
  */
62
137
  export declare function computeDiscoveryCacheKey(input: {
63
- className: string;
64
- methodName: string;
65
138
  methodCode: string;
66
139
  targetCWEs: ReadonlyArray<string>;
140
+ language: string;
67
141
  }): {
68
142
  content: string;
69
143
  classifier: string;
70
144
  };
145
+ /** Test-only: expose the prompt-version sentinel. */
146
+ export declare function getDiscoveryCachePromptVersion(): string;
71
147
  //# sourceMappingURL=discovery-cache.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"discovery-cache.d.ts","sourceRoot":"","sources":["../../src/cache/discovery-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAIH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAuB3D,kEAAkE;AAClE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,eAAe,CAAC,CAetC;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,oBAAoB,EAAE,CAAC,CAkB7C;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,GAAE,SAAiC,GACxC,mBAAmB,CAAC,kBAAkB,EAAE,CAAC,CAkB3C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sCAAsC,CAAC,KAAK,EAAE;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAc1C;AAED;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,KAAK,EAAE;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAe1C;AAED,yEAAyE;AACzE,wBAAgB,mCAAmC,IAAI,MAAM,CAE5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAO1C;AAED,qDAAqD;AACrD,wBAAgB,8BAA8B,IAAI,MAAM,CAEvD"}