@rigour-labs/core 6.2.4 → 6.2.5

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 (80) hide show
  1. package/dist/deep/code-context.d.ts +28 -0
  2. package/dist/deep/code-context.js +93 -0
  3. package/dist/deep/code-context.test.d.ts +1 -0
  4. package/dist/deep/code-context.test.js +48 -0
  5. package/dist/deep/code-pass.d.ts +15 -0
  6. package/dist/deep/code-pass.js +44 -0
  7. package/dist/deep/code-review-prompt.d.ts +17 -0
  8. package/dist/deep/code-review-prompt.js +30 -0
  9. package/dist/deep/code-review-prompt.test.d.ts +1 -0
  10. package/dist/deep/code-review-prompt.test.js +20 -0
  11. package/dist/deep/code-verifier.d.ts +19 -0
  12. package/dist/deep/code-verifier.js +48 -0
  13. package/dist/deep/code-verifier.test.d.ts +1 -0
  14. package/dist/deep/code-verifier.test.js +46 -0
  15. package/dist/deep/fact-extractor.d.ts +7 -1
  16. package/dist/deep/fact-extractor.js +16 -8
  17. package/dist/deep/fact-extractor.test.js +34 -1
  18. package/dist/deep/facts-pass.d.ts +24 -0
  19. package/dist/deep/facts-pass.js +69 -0
  20. package/dist/deep/index.d.ts +6 -1
  21. package/dist/deep/index.js +5 -1
  22. package/dist/deep/parse-findings.d.ts +12 -0
  23. package/dist/deep/parse-findings.js +73 -0
  24. package/dist/deep/parse-findings.test.d.ts +1 -0
  25. package/dist/deep/parse-findings.test.js +25 -0
  26. package/dist/deep/prompts.d.ts +1 -1
  27. package/dist/deep/prompts.js +7 -8
  28. package/dist/deep/prompts.test.js +15 -0
  29. package/dist/gates/context.js +7 -0
  30. package/dist/gates/deep-analysis.d.ts +31 -8
  31. package/dist/gates/deep-analysis.js +165 -306
  32. package/dist/gates/deep-analysis.test.d.ts +1 -0
  33. package/dist/gates/deep-analysis.test.js +86 -0
  34. package/dist/gates/deep-runner.d.ts +15 -0
  35. package/dist/gates/deep-runner.js +65 -0
  36. package/dist/gates/dependency.js +4 -0
  37. package/dist/gates/logic-drift.js +7 -1
  38. package/dist/gates/runner.js +5 -40
  39. package/dist/gates/runner.test.js +37 -25
  40. package/dist/gates/scoped-gates.test.d.ts +1 -0
  41. package/dist/gates/scoped-gates.test.js +52 -0
  42. package/dist/gates/style-drift.js +3 -0
  43. package/dist/index.d.ts +6 -1
  44. package/dist/index.js +6 -1
  45. package/dist/inference/http-download.d.ts +22 -0
  46. package/dist/inference/http-download.js +132 -0
  47. package/dist/inference/http-download.test.d.ts +1 -0
  48. package/dist/inference/http-download.test.js +109 -0
  49. package/dist/inference/index.d.ts +5 -1
  50. package/dist/inference/index.js +2 -1
  51. package/dist/inference/llama-engine.d.ts +27 -0
  52. package/dist/inference/llama-engine.js +152 -0
  53. package/dist/inference/llama-engine.test.d.ts +1 -0
  54. package/dist/inference/llama-engine.test.js +51 -0
  55. package/dist/inference/llama-process.d.ts +72 -0
  56. package/dist/inference/llama-process.js +126 -0
  57. package/dist/inference/llama-process.test.d.ts +1 -0
  58. package/dist/inference/llama-process.test.js +61 -0
  59. package/dist/inference/model-download.test.d.ts +1 -0
  60. package/dist/inference/model-download.test.js +73 -0
  61. package/dist/inference/model-manager.d.ts +14 -1
  62. package/dist/inference/model-manager.js +65 -80
  63. package/dist/inference/sidecar-provider.d.ts +19 -10
  64. package/dist/inference/sidecar-provider.js +115 -343
  65. package/dist/templates/universal-config.js +0 -2
  66. package/dist/types/index.d.ts +58 -14
  67. package/dist/types/index.js +9 -2
  68. package/dist/utils/deep-status.d.ts +7 -0
  69. package/dist/utils/deep-status.js +11 -0
  70. package/dist/utils/diff.d.ts +3 -0
  71. package/dist/utils/diff.js +52 -0
  72. package/dist/utils/diff.test.d.ts +1 -0
  73. package/dist/utils/diff.test.js +38 -0
  74. package/dist/utils/scanner.js +13 -2
  75. package/dist/utils/scanner.test.js +22 -8
  76. package/dist/utils/scope.d.ts +15 -0
  77. package/dist/utils/scope.js +51 -0
  78. package/dist/utils/scope.test.d.ts +1 -0
  79. package/dist/utils/scope.test.js +38 -0
  80. package/package.json +6 -6
@@ -6,6 +6,7 @@ import path from 'path';
6
6
  import fs from 'fs-extra';
7
7
  import { createHash } from 'crypto';
8
8
  import { RIGOUR_DIR } from '../storage/db.js';
9
+ import { downloadToFile } from './http-download.js';
9
10
  import { MODELS, FALLBACK_MODELS, VERSION_CHECK_URL, BUNDLED_MODEL_VERSION, updateModelVersion } from './types.js';
10
11
  const MODELS_DIR = path.join(RIGOUR_DIR, 'models');
11
12
  const VERSION_CACHE_PATH = path.join(MODELS_DIR, '.latest_version.json');
@@ -76,13 +77,26 @@ async function isFileCached(model) {
76
77
  * Check if any model for this tier is cached (fine-tuned or fallback).
77
78
  */
78
79
  export async function isModelCached(tier) {
79
- if (await isFileCached(MODELS[tier]))
80
- return true;
81
- const fb = FALLBACK_MODELS[tier];
82
- return fb.url !== MODELS[tier].url && await isFileCached(fb);
80
+ return (await getCachedModel(tier)) !== null;
81
+ }
82
+ /**
83
+ * The verified model file for a tier, preferring the fine-tuned model over
84
+ * the stock fallback. Null when neither is cached and verified.
85
+ */
86
+ export async function getCachedModel(tier) {
87
+ const primary = MODELS[tier];
88
+ if (await isFileCached(primary)) {
89
+ return { path: path.join(MODELS_DIR, primary.filename), info: primary, fallback: false };
90
+ }
91
+ const fallback = FALLBACK_MODELS[tier];
92
+ if (fallback.url !== primary.url && await isFileCached(fallback)) {
93
+ return { path: path.join(MODELS_DIR, fallback.filename), info: fallback, fallback: true };
94
+ }
95
+ return null;
83
96
  }
84
97
  /**
85
98
  * Get the path to a cached model (prefers fine-tuned over fallback).
99
+ * Existence only; use getCachedModel() for a verified file.
86
100
  */
87
101
  export function getModelPath(tier) {
88
102
  const primary = path.join(MODELS_DIR, MODELS[tier].filename);
@@ -97,84 +111,55 @@ export function getModelInfo(tier) {
97
111
  return MODELS[tier];
98
112
  }
99
113
  /**
100
- * Stream a response body to disk with progress + SHA256.
101
- * Returns { sha256, downloaded } on success.
102
- */
103
- async function streamToDisk(response, tempPath, model, onProgress) {
104
- const contentLength = parseInt(response.headers.get('content-length') || '0', 10);
105
- const reader = response.body?.getReader();
106
- if (!reader)
107
- throw new Error('No response body');
108
- const writeStream = fs.createWriteStream(tempPath);
109
- const hash = createHash('sha256');
110
- let downloaded = 0;
111
- let lastPct = 0;
112
- while (true) {
113
- const { done, value } = await reader.read();
114
- if (done)
115
- break;
116
- const chunk = Buffer.from(value);
117
- writeStream.write(chunk);
118
- hash.update(chunk);
119
- downloaded += value.length;
120
- if (contentLength > 0) {
121
- const pct = Math.round((downloaded / contentLength) * 100);
122
- if (pct >= lastPct + 5) {
123
- lastPct = pct;
124
- onProgress?.(`Downloading ${model.name}: ${pct}%`, pct);
125
- }
126
- }
127
- }
128
- writeStream.end();
129
- await new Promise((resolve, reject) => {
130
- writeStream.on('finish', resolve);
131
- writeStream.on('error', reject);
132
- });
133
- return { sha256: hash.digest('hex'), downloaded };
134
- }
135
- /**
136
- * Verify SHA256 against ETag, allowing LFS OID mismatches
137
- * if the download size is reasonable.
114
+ * Reject a download whose SHA-256 differs from the one the server published.
115
+ * Hugging Face publishes the LFS object's SHA-256, which is the content hash,
116
+ * so any mismatch means a corrupt or tampered file.
138
117
  */
139
- function verifySha256(expectedSha256, actualSha256, downloaded, model) {
118
+ function verifySha256(expectedSha256, actualSha256, model) {
140
119
  if (!expectedSha256 || actualSha256 === expectedSha256)
141
120
  return;
142
- const tolerance = model.sizeBytes * 0.1;
143
- if (downloaded < model.sizeBytes - tolerance) {
144
- throw new Error(`Checksum mismatch for ${model.name}: ` +
145
- `expected ${expectedSha256}, got ${actualSha256} ` +
146
- `(undersized: ${downloaded} bytes)`);
147
- }
148
- // Size OK — ETag likely a Git LFS OID, not content SHA256
121
+ throw new Error(`Checksum mismatch for ${model.name}: expected ${expectedSha256}, got ${actualSha256}`);
122
+ }
123
+ function progressReporter(model, onProgress) {
124
+ let lastPct = -5;
125
+ return (downloaded, total) => {
126
+ const size = total ?? model.sizeBytes;
127
+ const pct = Math.min(99, Math.round((downloaded / size) * 100));
128
+ if (pct >= lastPct + 5) {
129
+ lastPct = pct;
130
+ onProgress?.(`Downloading ${model.name}: ${pct}%`, pct);
131
+ }
132
+ };
149
133
  }
150
134
  /**
151
- * Download a specific model from its URL, write to disk, save metadata.
135
+ * Download a model to `<file>.download`, verify it, then move it into place
136
+ * with its metadata. A partial file survives network errors so the next run
137
+ * resumes it; a checksum mismatch deletes it.
152
138
  */
153
- async function downloadFromUrl(tier, model, onProgress) {
139
+ async function downloadFromUrl(model, onProgress, get) {
154
140
  const destPath = path.join(MODELS_DIR, model.filename);
155
141
  const tempPath = destPath + '.download';
142
+ const { sha256, bytes, etag } = await downloadToFile(model.url, tempPath, {
143
+ onProgress: progressReporter(model, onProgress),
144
+ get,
145
+ });
156
146
  try {
157
- const response = await fetch(model.url);
158
- if (!response.ok) {
159
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
160
- }
161
- const expectedSha = extractSha256FromEtag(response.headers.get('etag'));
162
- const { sha256, downloaded } = await streamToDisk(response, tempPath, model, onProgress);
163
- verifySha256(expectedSha, sha256, downloaded, model);
164
- fs.renameSync(tempPath, destPath);
165
- await writeModelMeta(model.filename, {
166
- sha256, sizeBytes: downloaded,
167
- verifiedAt: new Date().toISOString(),
168
- sourceUrl: model.url,
169
- sourceEtag: response.headers.get('etag') || undefined,
170
- });
171
- onProgress?.(`Model ${model.name} ready`, 100);
172
- return destPath;
147
+ verifySha256(extractSha256FromEtag(etag), sha256, model);
173
148
  }
174
149
  catch (error) {
175
- fs.removeSync(tempPath);
150
+ await fs.remove(tempPath);
176
151
  throw error;
177
152
  }
153
+ await fs.move(tempPath, destPath, { overwrite: true });
154
+ await writeModelMeta(model.filename, {
155
+ sha256,
156
+ sizeBytes: bytes,
157
+ verifiedAt: new Date().toISOString(),
158
+ sourceUrl: model.url,
159
+ sourceEtag: etag || undefined,
160
+ });
161
+ onProgress?.(`Model ${model.name} ready`, 100);
162
+ return destPath;
178
163
  }
179
164
  /**
180
165
  * Check HuggingFace for a newer model version (like antivirus signature updates).
@@ -236,25 +221,25 @@ export async function checkForUpdates(onProgress) {
236
221
  * Download a model from HuggingFace CDN.
237
222
  * Checks for updates first, then tries fine-tuned model, falls back to stock Qwen.
238
223
  */
239
- export async function downloadModel(tier, onProgress) {
224
+ export async function downloadModel(tier, onProgress, get) {
240
225
  fs.ensureDirSync(MODELS_DIR);
241
226
  // Check for newer model version (non-blocking, cached 24h)
242
227
  await checkForUpdates(onProgress);
243
- if (await isModelCached(tier)) {
244
- onProgress?.(`Model ${MODELS[tier].name} already cached`, 100);
245
- return getModelPath(tier);
228
+ const cached = await getCachedModel(tier);
229
+ if (cached) {
230
+ onProgress?.(`Model ${cached.info.name} already cached`, 100);
231
+ return cached.path;
246
232
  }
247
233
  const model = MODELS[tier];
248
234
  onProgress?.(`Downloading ${model.name} (${model.sizeHuman})...`, 0);
249
235
  try {
250
- return await downloadFromUrl(tier, model, onProgress);
236
+ return await downloadFromUrl(model, onProgress, get);
251
237
  }
252
238
  catch (error) {
253
- // Fine-tuned model not available — try stock fallback
254
239
  const fallback = FALLBACK_MODELS[tier];
255
240
  if (fallback && fallback.url !== model.url) {
256
- onProgress?.(`Fine-tuned model unavailable, using ${fallback.name}`, 0);
257
- return downloadFromUrl(tier, fallback, onProgress);
241
+ onProgress?.(`Fine-tuned model unavailable (${error?.message ?? 'unknown error'}); using stock ${fallback.name}`, 0);
242
+ return downloadFromUrl(fallback, onProgress, get);
258
243
  }
259
244
  throw error;
260
245
  }
@@ -263,9 +248,9 @@ export async function downloadModel(tier, onProgress) {
263
248
  * Ensure a model is available, downloading if needed.
264
249
  */
265
250
  export async function ensureModel(tier, onProgress) {
266
- if (await isModelCached(tier)) {
267
- return getModelPath(tier);
268
- }
251
+ const cached = await getCachedModel(tier);
252
+ if (cached)
253
+ return cached.path;
269
254
  return downloadModel(tier, onProgress);
270
255
  }
271
256
  /**
@@ -1,23 +1,32 @@
1
1
  import type { InferenceProvider, InferenceOptions, ModelTier } from './types.js';
2
+ export interface ActiveModel {
3
+ name: string;
4
+ filename: string;
5
+ fallback: boolean;
6
+ }
2
7
  export declare class SidecarProvider implements InferenceProvider {
3
8
  readonly name = "sidecar";
4
9
  private binaryPath;
5
- private modelPath;
10
+ private model;
11
+ private schemaPath;
6
12
  private tier;
7
13
  private threads;
8
14
  constructor(tier?: ModelTier, threads?: number);
15
+ /** True when a llama-cli that actually runs is installed. */
9
16
  isAvailable(): Promise<boolean>;
17
+ /** The llama-cli this provider would use and its version, or null if none runs. */
18
+ findEngine(): Promise<{
19
+ path: string;
20
+ version?: string;
21
+ } | null>;
22
+ /** Install the engine and model for this tier without running inference. */
23
+ prepare(onProgress?: (message: string) => void): Promise<void>;
10
24
  setup(onProgress?: (message: string) => void): Promise<void>;
25
+ /** The model file setup() loaded, or null before setup. */
26
+ getActiveModel(): ActiveModel | null;
11
27
  analyze(prompt: string, options?: InferenceOptions): Promise<string>;
12
28
  dispose(): void;
13
- private getPlatformKey;
14
- private getPlatformPackageName;
29
+ /** First candidate binary that exists, is executable and passes the probe. */
15
30
  private resolveBinaryPath;
16
- /**
17
- * Download llama-cli directly from llama.cpp GitHub releases.
18
- * Extracts the binary to ~/.rigour/bin/llama-cli
19
- */
20
- private downloadLlamaCli;
21
- /** Legacy: install via npm brain package */
22
- private installSidecarBinary;
31
+ private candidateBinaries;
23
32
  }