@rigour-labs/core 6.8.0 → 6.8.1

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 (326) hide show
  1. package/dist/inference/cloud-provider.js +12 -2
  2. package/package.json +11 -8
  3. package/dist/brief/briefing.test.d.ts +0 -1
  4. package/dist/brief/briefing.test.js +0 -107
  5. package/dist/context/automatic-index-cache.test.d.ts +0 -1
  6. package/dist/context/automatic-index-cache.test.js +0 -30
  7. package/dist/context/automatic-index.test.d.ts +0 -1
  8. package/dist/context/automatic-index.test.js +0 -45
  9. package/dist/context/cache-engine.test.d.ts +0 -1
  10. package/dist/context/cache-engine.test.js +0 -99
  11. package/dist/context/dependency-graph.test.d.ts +0 -1
  12. package/dist/context/dependency-graph.test.js +0 -13
  13. package/dist/context/index-status.test.d.ts +0 -1
  14. package/dist/context/index-status.test.js +0 -26
  15. package/dist/context.test.d.ts +0 -1
  16. package/dist/context.test.js +0 -228
  17. package/dist/deep/agent-review.test.d.ts +0 -1
  18. package/dist/deep/agent-review.test.js +0 -53
  19. package/dist/deep/code-context.test.d.ts +0 -1
  20. package/dist/deep/code-context.test.js +0 -48
  21. package/dist/deep/code-pass.test.d.ts +0 -1
  22. package/dist/deep/code-pass.test.js +0 -112
  23. package/dist/deep/code-review-prompt.test.d.ts +0 -1
  24. package/dist/deep/code-review-prompt.test.js +0 -20
  25. package/dist/deep/code-verifier.test.d.ts +0 -1
  26. package/dist/deep/code-verifier.test.js +0 -54
  27. package/dist/deep/diff-tests/calls.test.d.ts +0 -1
  28. package/dist/deep/diff-tests/calls.test.js +0 -33
  29. package/dist/deep/diff-tests/run.test.d.ts +0 -1
  30. package/dist/deep/diff-tests/run.test.js +0 -58
  31. package/dist/deep/fact-extractor.test.d.ts +0 -1
  32. package/dist/deep/fact-extractor.test.js +0 -581
  33. package/dist/deep/parse-findings.test.d.ts +0 -1
  34. package/dist/deep/parse-findings.test.js +0 -25
  35. package/dist/deep/pr-review.test.d.ts +0 -1
  36. package/dist/deep/pr-review.test.js +0 -80
  37. package/dist/deep/prompts.test.d.ts +0 -1
  38. package/dist/deep/prompts.test.js +0 -235
  39. package/dist/deep/reference-pack.test.d.ts +0 -1
  40. package/dist/deep/reference-pack.test.js +0 -46
  41. package/dist/deep/related-changes.test.d.ts +0 -1
  42. package/dist/deep/related-changes.test.js +0 -33
  43. package/dist/deep/review-context-export.test.d.ts +0 -1
  44. package/dist/deep/review-context-export.test.js +0 -32
  45. package/dist/deep/review-tools.test.d.ts +0 -1
  46. package/dist/deep/review-tools.test.js +0 -39
  47. package/dist/deep/risk.test.d.ts +0 -1
  48. package/dist/deep/risk.test.js +0 -101
  49. package/dist/deep/verifier.test.d.ts +0 -1
  50. package/dist/deep/verifier.test.js +0 -635
  51. package/dist/discovery.test.d.ts +0 -1
  52. package/dist/discovery.test.js +0 -93
  53. package/dist/environment.test.d.ts +0 -1
  54. package/dist/environment.test.js +0 -94
  55. package/dist/firewall/firewall.test.d.ts +0 -1
  56. package/dist/firewall/firewall.test.js +0 -117
  57. package/dist/firewall/trust-boundaries.test.d.ts +0 -1
  58. package/dist/firewall/trust-boundaries.test.js +0 -64
  59. package/dist/firewall/trusted-control.test.d.ts +0 -1
  60. package/dist/firewall/trusted-control.test.js +0 -188
  61. package/dist/gates/agent-team.test.d.ts +0 -1
  62. package/dist/gates/agent-team.test.js +0 -113
  63. package/dist/gates/ast.test.d.ts +0 -1
  64. package/dist/gates/ast.test.js +0 -112
  65. package/dist/gates/checkpoint.test.d.ts +0 -1
  66. package/dist/gates/checkpoint.test.js +0 -105
  67. package/dist/gates/content.test.d.ts +0 -1
  68. package/dist/gates/content.test.js +0 -73
  69. package/dist/gates/coverage.test.d.ts +0 -1
  70. package/dist/gates/coverage.test.js +0 -53
  71. package/dist/gates/dedupe-failures.test.d.ts +0 -1
  72. package/dist/gates/dedupe-failures.test.js +0 -12
  73. package/dist/gates/deep-analysis.test.d.ts +0 -1
  74. package/dist/gates/deep-analysis.test.js +0 -86
  75. package/dist/gates/deep-intent.test.d.ts +0 -1
  76. package/dist/gates/deep-intent.test.js +0 -51
  77. package/dist/gates/deep-timeout.test.d.ts +0 -1
  78. package/dist/gates/deep-timeout.test.js +0 -10
  79. package/dist/gates/deprecated-apis.test.d.ts +0 -1
  80. package/dist/gates/deprecated-apis.test.js +0 -318
  81. package/dist/gates/deprecated-dependencies.test.d.ts +0 -1
  82. package/dist/gates/deprecated-dependencies.test.js +0 -55
  83. package/dist/gates/frontend-secret-exposure.test.d.ts +0 -1
  84. package/dist/gates/frontend-secret-exposure.test.js +0 -148
  85. package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.d.ts +0 -1
  86. package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.js +0 -27
  87. package/dist/gates/hallucinated-imports/js-resolver-types.test.d.ts +0 -1
  88. package/dist/gates/hallucinated-imports/js-resolver-types.test.js +0 -14
  89. package/dist/gates/hallucinated-imports-sveltekit.test.d.ts +0 -1
  90. package/dist/gates/hallucinated-imports-sveltekit.test.js +0 -132
  91. package/dist/gates/hallucinated-imports.test.d.ts +0 -1
  92. package/dist/gates/hallucinated-imports.test.js +0 -1206
  93. package/dist/gates/js-style-context.test.d.ts +0 -1
  94. package/dist/gates/js-style-context.test.js +0 -35
  95. package/dist/gates/logic-drift.test.d.ts +0 -1
  96. package/dist/gates/logic-drift.test.js +0 -52
  97. package/dist/gates/phantom-apis.test.d.ts +0 -1
  98. package/dist/gates/phantom-apis.test.js +0 -396
  99. package/dist/gates/promise-safety.test.d.ts +0 -1
  100. package/dist/gates/promise-safety.test.js +0 -34
  101. package/dist/gates/runner.test.d.ts +0 -1
  102. package/dist/gates/runner.test.js +0 -77
  103. package/dist/gates/scoped-gates.test.d.ts +0 -1
  104. package/dist/gates/scoped-gates.test.js +0 -52
  105. package/dist/gates/security-patterns-owasp.test.d.ts +0 -1
  106. package/dist/gates/security-patterns-owasp.test.js +0 -186
  107. package/dist/gates/security-patterns.test.d.ts +0 -1
  108. package/dist/gates/security-patterns.test.js +0 -194
  109. package/dist/gates/semantic-bugs.test.d.ts +0 -1
  110. package/dist/gates/semantic-bugs.test.js +0 -76
  111. package/dist/gates/side-effect-analysis.test.d.ts +0 -1
  112. package/dist/gates/side-effect-analysis.test.js +0 -162
  113. package/dist/gates/style-drift.test.d.ts +0 -1
  114. package/dist/gates/style-drift.test.js +0 -26
  115. package/dist/gates/test-quality.test.d.ts +0 -1
  116. package/dist/gates/test-quality.test.js +0 -325
  117. package/dist/gates/trusted-reviews.test.d.ts +0 -1
  118. package/dist/gates/trusted-reviews.test.js +0 -16
  119. package/dist/gates/unindexed-reads/queries.test.d.ts +0 -1
  120. package/dist/gates/unindexed-reads/queries.test.js +0 -54
  121. package/dist/gates/unindexed-reads/schema.test.d.ts +0 -1
  122. package/dist/gates/unindexed-reads/schema.test.js +0 -81
  123. package/dist/gates/unindexed-reads/unindexed-reads.test.d.ts +0 -1
  124. package/dist/gates/unindexed-reads/unindexed-reads.test.js +0 -90
  125. package/dist/hooks/checker.test.d.ts +0 -1
  126. package/dist/hooks/checker.test.js +0 -159
  127. package/dist/hooks/dlp-confidence.test.d.ts +0 -1
  128. package/dist/hooks/dlp-confidence.test.js +0 -51
  129. package/dist/hooks/dlp-feedback.test.d.ts +0 -1
  130. package/dist/hooks/dlp-feedback.test.js +0 -131
  131. package/dist/hooks/input-validator.test.d.ts +0 -1
  132. package/dist/hooks/input-validator.test.js +0 -329
  133. package/dist/hooks/templates.test.d.ts +0 -1
  134. package/dist/hooks/templates.test.js +0 -27
  135. package/dist/inference/brain-placeholder.test.d.ts +0 -1
  136. package/dist/inference/brain-placeholder.test.js +0 -28
  137. package/dist/inference/cloud-provider.test.d.ts +0 -1
  138. package/dist/inference/cloud-provider.test.js +0 -139
  139. package/dist/inference/executable.test.d.ts +0 -1
  140. package/dist/inference/executable.test.js +0 -41
  141. package/dist/inference/http-download.test.d.ts +0 -1
  142. package/dist/inference/http-download.test.js +0 -109
  143. package/dist/inference/llama-engine-checksum.test.d.ts +0 -1
  144. package/dist/inference/llama-engine-checksum.test.js +0 -27
  145. package/dist/inference/llama-engine.test.d.ts +0 -1
  146. package/dist/inference/llama-engine.test.js +0 -51
  147. package/dist/inference/llama-process.test.d.ts +0 -1
  148. package/dist/inference/llama-process.test.js +0 -61
  149. package/dist/inference/local-model.test.d.ts +0 -1
  150. package/dist/inference/local-model.test.js +0 -23
  151. package/dist/inference/model-download.test.d.ts +0 -1
  152. package/dist/inference/model-download.test.js +0 -125
  153. package/dist/inference/model-manager.test.d.ts +0 -1
  154. package/dist/inference/model-manager.test.js +0 -24
  155. package/dist/inference/types.test.d.ts +0 -1
  156. package/dist/inference/types.test.js +0 -19
  157. package/dist/memory/recall.test.d.ts +0 -1
  158. package/dist/memory/recall.test.js +0 -36
  159. package/dist/pattern-index/indexer.test.d.ts +0 -6
  160. package/dist/pattern-index/indexer.test.js +0 -197
  161. package/dist/pattern-index/matcher.test.d.ts +0 -6
  162. package/dist/pattern-index/matcher.test.js +0 -238
  163. package/dist/pattern-index/pattern-reuse.test.d.ts +0 -1
  164. package/dist/pattern-index/pattern-reuse.test.js +0 -76
  165. package/dist/pattern-index/semantic-runtime.test.d.ts +0 -1
  166. package/dist/pattern-index/semantic-runtime.test.js +0 -31
  167. package/dist/pattern-index/staleness.test.d.ts +0 -6
  168. package/dist/pattern-index/staleness.test.js +0 -211
  169. package/dist/review/agent-fixes.test.d.ts +0 -1
  170. package/dist/review/agent-fixes.test.js +0 -32
  171. package/dist/review/backtest-init.test.d.ts +0 -1
  172. package/dist/review/backtest-init.test.js +0 -136
  173. package/dist/review/backtest-judges.test.d.ts +0 -1
  174. package/dist/review/backtest-judges.test.js +0 -32
  175. package/dist/review/backtest-last.test.d.ts +0 -1
  176. package/dist/review/backtest-last.test.js +0 -109
  177. package/dist/review/backtest.test.d.ts +0 -1
  178. package/dist/review/backtest.test.js +0 -183
  179. package/dist/review/baseline.test.d.ts +0 -1
  180. package/dist/review/baseline.test.js +0 -22
  181. package/dist/review/branch-checks.test.d.ts +0 -1
  182. package/dist/review/branch-checks.test.js +0 -48
  183. package/dist/review/check-outcomes.test.d.ts +0 -1
  184. package/dist/review/check-outcomes.test.js +0 -56
  185. package/dist/review/code-patterns.test.d.ts +0 -1
  186. package/dist/review/code-patterns.test.js +0 -142
  187. package/dist/review/dead-code.test.d.ts +0 -1
  188. package/dist/review/dead-code.test.js +0 -154
  189. package/dist/review/deep-runs.test.d.ts +0 -1
  190. package/dist/review/deep-runs.test.js +0 -23
  191. package/dist/review/effectiveness.test.d.ts +0 -1
  192. package/dist/review/effectiveness.test.js +0 -42
  193. package/dist/review/fix-scope.test.d.ts +0 -1
  194. package/dist/review/fix-scope.test.js +0 -104
  195. package/dist/review/generated-files.test.d.ts +0 -1
  196. package/dist/review/generated-files.test.js +0 -25
  197. package/dist/review/migration-order.test.d.ts +0 -1
  198. package/dist/review/migration-order.test.js +0 -62
  199. package/dist/review/quiet.test.d.ts +0 -1
  200. package/dist/review/quiet.test.js +0 -38
  201. package/dist/review/receipt.test.d.ts +0 -1
  202. package/dist/review/receipt.test.js +0 -60
  203. package/dist/review/review-task.test.d.ts +0 -1
  204. package/dist/review/review-task.test.js +0 -89
  205. package/dist/review/review.test.d.ts +0 -1
  206. package/dist/review/review.test.js +0 -203
  207. package/dist/review/reviewer/adapters.test.d.ts +0 -1
  208. package/dist/review/reviewer/adapters.test.js +0 -100
  209. package/dist/review/reviewer/api-judge.test.d.ts +0 -1
  210. package/dist/review/reviewer/api-judge.test.js +0 -108
  211. package/dist/review/reviewer/background.test.d.ts +0 -1
  212. package/dist/review/reviewer/background.test.js +0 -118
  213. package/dist/review/reviewer/context.test.d.ts +0 -1
  214. package/dist/review/reviewer/context.test.js +0 -45
  215. package/dist/review/reviewer/exec.test.d.ts +0 -1
  216. package/dist/review/reviewer/exec.test.js +0 -59
  217. package/dist/review/reviewer/inputs.test.d.ts +0 -1
  218. package/dist/review/reviewer/inputs.test.js +0 -52
  219. package/dist/review/reviewer/panel.test.d.ts +0 -1
  220. package/dist/review/reviewer/panel.test.js +0 -110
  221. package/dist/review/reviewer/record.test.d.ts +0 -1
  222. package/dist/review/reviewer/record.test.js +0 -40
  223. package/dist/review/reviewer/rule-writer.test.d.ts +0 -1
  224. package/dist/review/reviewer/rule-writer.test.js +0 -52
  225. package/dist/review/reviewer/settings.test.d.ts +0 -1
  226. package/dist/review/reviewer/settings.test.js +0 -57
  227. package/dist/review/reviewer/usage.test.d.ts +0 -1
  228. package/dist/review/reviewer/usage.test.js +0 -14
  229. package/dist/review/reviewer.test.d.ts +0 -1
  230. package/dist/review/reviewer.test.js +0 -792
  231. package/dist/review/stories.test.d.ts +0 -1
  232. package/dist/review/stories.test.js +0 -58
  233. package/dist/review/toolchain.test.d.ts +0 -1
  234. package/dist/review/toolchain.test.js +0 -108
  235. package/dist/review/typed/redundancy.test.d.ts +0 -1
  236. package/dist/review/typed/redundancy.test.js +0 -315
  237. package/dist/review/typed/schema-nullability.test.d.ts +0 -1
  238. package/dist/review/typed/schema-nullability.test.js +0 -63
  239. package/dist/review-learning/human-edits.test.d.ts +0 -1
  240. package/dist/review-learning/human-edits.test.js +0 -47
  241. package/dist/review-learning/repo-rules.test.d.ts +0 -1
  242. package/dist/review-learning/repo-rules.test.js +0 -93
  243. package/dist/review-learning/review-learning.test.d.ts +0 -1
  244. package/dist/review-learning/review-learning.test.js +0 -249
  245. package/dist/safety.test.d.ts +0 -1
  246. package/dist/safety.test.js +0 -42
  247. package/dist/semantic/benchmark.test.d.ts +0 -1
  248. package/dist/semantic/benchmark.test.js +0 -22
  249. package/dist/semantic/intent/intent.test.d.ts +0 -1
  250. package/dist/semantic/intent/intent.test.js +0 -46
  251. package/dist/semantic/learn/learn.test.d.ts +0 -1
  252. package/dist/semantic/learn/learn.test.js +0 -67
  253. package/dist/semantic/origins.test.d.ts +0 -1
  254. package/dist/semantic/origins.test.js +0 -77
  255. package/dist/semantic/project-facts.test.d.ts +0 -1
  256. package/dist/semantic/project-facts.test.js +0 -45
  257. package/dist/semantic/sites/call-sites.test.d.ts +0 -1
  258. package/dist/semantic/sites/call-sites.test.js +0 -46
  259. package/dist/services/adaptive-thresholds.test.d.ts +0 -1
  260. package/dist/services/adaptive-thresholds.test.js +0 -53
  261. package/dist/services/agent-history.test.d.ts +0 -1
  262. package/dist/services/agent-history.test.js +0 -69
  263. package/dist/services/context-scope-summary.test.d.ts +0 -1
  264. package/dist/services/context-scope-summary.test.js +0 -17
  265. package/dist/services/context-telemetry-service.test.d.ts +0 -1
  266. package/dist/services/context-telemetry-service.test.js +0 -182
  267. package/dist/services/cursor-usage-sync.test.d.ts +0 -1
  268. package/dist/services/cursor-usage-sync.test.js +0 -173
  269. package/dist/services/engineering-knowledge-graph.test.d.ts +0 -1
  270. package/dist/services/engineering-knowledge-graph.test.js +0 -76
  271. package/dist/services/model-pricing.test.d.ts +0 -1
  272. package/dist/services/model-pricing.test.js +0 -44
  273. package/dist/services/observed-savings.test.d.ts +0 -1
  274. package/dist/services/observed-savings.test.js +0 -37
  275. package/dist/services/score-history.test.d.ts +0 -1
  276. package/dist/services/score-history.test.js +0 -61
  277. package/dist/smoke.test.d.ts +0 -1
  278. package/dist/smoke.test.js +0 -17
  279. package/dist/storage/cache-cleanup.test.d.ts +0 -1
  280. package/dist/storage/cache-cleanup.test.js +0 -54
  281. package/dist/storage/context-telemetry.test.d.ts +0 -1
  282. package/dist/storage/context-telemetry.test.js +0 -80
  283. package/dist/storage/db.test.d.ts +0 -1
  284. package/dist/storage/db.test.js +0 -46
  285. package/dist/storage/fix-lessons.test.d.ts +0 -1
  286. package/dist/storage/fix-lessons.test.js +0 -61
  287. package/dist/storage/lessons.test.d.ts +0 -1
  288. package/dist/storage/lessons.test.js +0 -81
  289. package/dist/storage/local-encryption.test.d.ts +0 -1
  290. package/dist/storage/local-encryption.test.js +0 -34
  291. package/dist/storage/local-memory.test.d.ts +0 -1
  292. package/dist/storage/local-memory.test.js +0 -55
  293. package/dist/storage/share-memory.test.d.ts +0 -1
  294. package/dist/storage/share-memory.test.js +0 -34
  295. package/dist/storage/team-diagnostics.test.d.ts +0 -1
  296. package/dist/storage/team-diagnostics.test.js +0 -51
  297. package/dist/storage/team-scope.test.d.ts +0 -1
  298. package/dist/storage/team-scope.test.js +0 -22
  299. package/dist/storage/team-store.test.d.ts +0 -1
  300. package/dist/storage/team-store.test.js +0 -11
  301. package/dist/storage/team-sync-scope.test.d.ts +0 -1
  302. package/dist/storage/team-sync-scope.test.js +0 -100
  303. package/dist/storage/team-vector-store.test.d.ts +0 -1
  304. package/dist/storage/team-vector-store.test.js +0 -56
  305. package/dist/storage/telemetry-scope.test.d.ts +0 -1
  306. package/dist/storage/telemetry-scope.test.js +0 -38
  307. package/dist/task/thread.test.d.ts +0 -1
  308. package/dist/task/thread.test.js +0 -133
  309. package/dist/telemetry/telemetry.test.d.ts +0 -1
  310. package/dist/telemetry/telemetry.test.js +0 -64
  311. package/dist/types/index.test.d.ts +0 -1
  312. package/dist/types/index.test.js +0 -33
  313. package/dist/utils/command-line.test.d.ts +0 -1
  314. package/dist/utils/command-line.test.js +0 -8
  315. package/dist/utils/diff-removed.test.d.ts +0 -1
  316. package/dist/utils/diff-removed.test.js +0 -29
  317. package/dist/utils/diff.test.d.ts +0 -1
  318. package/dist/utils/diff.test.js +0 -38
  319. package/dist/utils/glob-paths.test.d.ts +0 -1
  320. package/dist/utils/glob-paths.test.js +0 -40
  321. package/dist/utils/profile.test.d.ts +0 -1
  322. package/dist/utils/profile.test.js +0 -68
  323. package/dist/utils/scanner.test.d.ts +0 -1
  324. package/dist/utils/scanner.test.js +0 -48
  325. package/dist/utils/scope.test.d.ts +0 -1
  326. package/dist/utils/scope.test.js +0 -38
@@ -1,109 +0,0 @@
1
- import fs from 'fs-extra';
2
- import os from 'os';
3
- import path from 'path';
4
- import { createHash } from 'crypto';
5
- import { Readable, PassThrough } from 'stream';
6
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
- import { downloadToFile } from './http-download.js';
8
- const PAYLOAD = Buffer.from('0123456789abcdefghijklmnopqrstuvwxyz');
9
- const sha = (b) => createHash('sha256').update(b).digest('hex');
10
- function response(statusCode, body = Buffer.alloc(0), headers = {}) {
11
- return { statusCode, headers, body: Buffer.isBuffer(body) ? Readable.from([body]) : body };
12
- }
13
- /** Serves PAYLOAD, honouring Range unless told not to. */
14
- function server(opts = {}) {
15
- const calls = [];
16
- const get = async (url, headers) => {
17
- calls.push({ url, headers });
18
- const range = headers.Range?.match(/bytes=(\d+)-/);
19
- if (range && opts.honourRange !== false) {
20
- const start = Number(range[1]);
21
- if (start >= PAYLOAD.length)
22
- return response(416);
23
- return response(206, PAYLOAD.subarray(start), { 'content-length': String(PAYLOAD.length - start) });
24
- }
25
- return response(200, PAYLOAD, { 'content-length': String(PAYLOAD.length), etag: opts.etag });
26
- };
27
- return { get, calls };
28
- }
29
- describe('downloadToFile', () => {
30
- let dir;
31
- let dest;
32
- beforeEach(async () => {
33
- dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-dl-'));
34
- dest = path.join(dir, 'model.gguf.download');
35
- });
36
- afterEach(async () => { await fs.remove(dir); });
37
- it('downloads the full body and hashes it', async () => {
38
- const { get } = server({ etag: '"abc"' });
39
- const result = await downloadToFile('https://host/model', dest, { get });
40
- expect(await fs.readFile(dest)).toEqual(PAYLOAD);
41
- expect(result).toEqual({ sha256: sha(PAYLOAD), bytes: PAYLOAD.length, etag: '"abc"' });
42
- });
43
- it('follows redirects and keeps the x-linked-etag from the first hop', async () => {
44
- const { get: origin, calls } = server();
45
- const get = async (url, headers, timeout) => {
46
- if (url === 'https://hub/model') {
47
- return response(302, Buffer.alloc(0), { location: 'https://cdn/model', 'x-linked-etag': '"lfs-sha"' });
48
- }
49
- return origin(url, headers, timeout);
50
- };
51
- const result = await downloadToFile('https://hub/model', dest, { get });
52
- expect(calls.map(c => c.url)).toEqual(['https://cdn/model']);
53
- expect(result.etag).toBe('"lfs-sha"');
54
- });
55
- it('resumes a partial file with a Range request and hashes the whole file', async () => {
56
- await fs.writeFile(dest, PAYLOAD.subarray(0, 10));
57
- const { get, calls } = server();
58
- const result = await downloadToFile('https://host/model', dest, { get });
59
- expect(calls[0].headers.Range).toBe('bytes=10-');
60
- expect(await fs.readFile(dest)).toEqual(PAYLOAD);
61
- expect(result.sha256).toBe(sha(PAYLOAD));
62
- expect(result.bytes).toBe(PAYLOAD.length);
63
- });
64
- it('restarts from zero when the server ignores Range', async () => {
65
- await fs.writeFile(dest, Buffer.from('stale-bytes'));
66
- const { get } = server({ honourRange: false });
67
- const result = await downloadToFile('https://host/model', dest, { get });
68
- expect(await fs.readFile(dest)).toEqual(PAYLOAD);
69
- expect(result.sha256).toBe(sha(PAYLOAD));
70
- });
71
- it('restarts when the partial is already past the end (416)', async () => {
72
- await fs.writeFile(dest, Buffer.concat([PAYLOAD, Buffer.from('extra')]));
73
- const { get, calls } = server();
74
- await downloadToFile('https://host/model', dest, { get });
75
- expect(calls).toHaveLength(2);
76
- expect(await fs.readFile(dest)).toEqual(PAYLOAD);
77
- });
78
- it('rejects non-success responses without creating the file', async () => {
79
- const get = async () => response(401);
80
- await expect(downloadToFile('https://host/model', dest, { get })).rejects.toThrow('HTTP 401');
81
- expect(await fs.pathExists(dest)).toBe(false);
82
- });
83
- it('refuses non-https URLs, including redirects to them', async () => {
84
- const { get } = server();
85
- await expect(downloadToFile('http://host/model', dest, { get })).rejects.toThrow('non-https');
86
- const redirecting = async () => response(302, Buffer.alloc(0), { location: 'http://evil/model' });
87
- await expect(downloadToFile('https://host/model', dest, { get: redirecting })).rejects.toThrow('non-https');
88
- });
89
- it('stops after too many redirects', async () => {
90
- const loop = async (url) => response(302, Buffer.alloc(0), { location: `${url}x` });
91
- await expect(downloadToFile('https://host/m', dest, { get: loop })).rejects.toThrow('Too many redirects');
92
- });
93
- it('aborts a stalled body and keeps the partial for the next resume', async () => {
94
- const body = new PassThrough();
95
- body.write(PAYLOAD.subarray(0, 5));
96
- const get = async () => response(200, body, { 'content-length': String(PAYLOAD.length) });
97
- await expect(downloadToFile('https://host/model', dest, { get, stallTimeoutMs: 50 })).rejects.toThrow('stalled');
98
- // The partial file is kept for the next resume (how many bytes reached disk is up to the OS).
99
- expect(await fs.pathExists(dest)).toBe(true);
100
- expect((await fs.stat(dest)).size).toBeLessThanOrEqual(5);
101
- });
102
- it('reports progress against the full size when resuming', async () => {
103
- await fs.writeFile(dest, PAYLOAD.subarray(0, 10));
104
- const { get } = server();
105
- const seen = [];
106
- await downloadToFile('https://host/model', dest, { get, onProgress: (d, t) => seen.push([d, t]) });
107
- expect(seen.at(-1)).toEqual([PAYLOAD.length, PAYLOAD.length]);
108
- });
109
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,27 +0,0 @@
1
- import fs from 'fs';
2
- import os from 'os';
3
- import path from 'path';
4
- import { Readable } from 'stream';
5
- import { afterAll, describe, expect, it, vi } from 'vitest';
6
- /** Install into a scratch folder: the real one is ~/.rigour. */
7
- const home = vi.hoisted(() => {
8
- const p = require('path');
9
- return p.join(require('os').tmpdir(), `rigour-engine-${process.pid}`);
10
- });
11
- vi.mock('../storage/db.js', async (importOriginal) => ({ ...(await importOriginal()), RIGOUR_DIR: home }));
12
- const { installLlamaEngine, managedEngineDir, releaseAssetFor } = await import('./llama-engine.js');
13
- afterAll(() => fs.rmSync(home, { recursive: true, force: true }));
14
- describe('llama.cpp engine install', () => {
15
- it('pins a checksum for every published platform', () => {
16
- for (const key of ['darwin-arm64', 'darwin-x64', 'linux-x64', 'win32-x64']) {
17
- expect(releaseAssetFor(key)?.sha256).toMatch(/^[0-9a-f]{64}$/);
18
- }
19
- expect(releaseAssetFor('linux-arm64')).toBeUndefined();
20
- });
21
- it.runIf(releaseAssetFor(`${os.platform()}-${os.arch()}`))('refuses an archive that does not match its pinned checksum, and installs nothing', async () => {
22
- const tampered = async () => ({ statusCode: 200, headers: {}, body: Readable.from([Buffer.from('not the real llama.cpp archive')]) });
23
- await expect(installLlamaEngine(undefined, tampered)).rejects.toThrow(/does not match its pinned checksum/);
24
- expect(fs.existsSync(managedEngineDir())).toBe(false);
25
- expect(fs.readdirSync(path.dirname(managedEngineDir())).filter(f => f.endsWith('.zip'))).toEqual([]);
26
- });
27
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,51 +0,0 @@
1
- import fs from 'fs-extra';
2
- import os from 'os';
3
- import path from 'path';
4
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
- import { installBinFolder, llamaBinaryName, probeBinary, resetProbeCache } from './llama-engine.js';
6
- const isWindows = process.platform === 'win32';
7
- async function writeScript(file, body) {
8
- await fs.outputFile(file, `#!/bin/sh\n${body}\n`);
9
- await fs.chmod(file, 0o755);
10
- }
11
- describe.skipIf(isWindows)('probeBinary', () => {
12
- let dir;
13
- beforeEach(async () => {
14
- dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-engine-'));
15
- resetProbeCache();
16
- });
17
- afterEach(async () => { await fs.remove(dir); });
18
- it('accepts a binary that reports its version', async () => {
19
- const bin = path.join(dir, 'llama-cli');
20
- await writeScript(bin, 'echo "version: 5604 (228f34c9)" >&2; exit 0');
21
- expect(await probeBinary(bin)).toEqual({ ok: true, version: '5604' });
22
- });
23
- it('rejects the placeholder brain script', async () => {
24
- const bin = path.join(dir, 'rigour-brain');
25
- await writeScript(bin, 'echo "rigour-brain is a placeholder." >&2; exit 1');
26
- const result = await probeBinary(bin);
27
- expect(result.ok).toBe(false);
28
- expect(result.error).toContain('placeholder');
29
- });
30
- it('rejects a missing binary', async () => {
31
- expect((await probeBinary(path.join(dir, 'nope'))).ok).toBe(false);
32
- });
33
- });
34
- describe('installBinFolder', () => {
35
- let dir;
36
- beforeEach(async () => { dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-engine-')); });
37
- afterEach(async () => { await fs.remove(dir); });
38
- it('installs the binary together with the shared libraries next to it', async () => {
39
- const extracted = path.join(dir, 'extract', 'build', 'bin');
40
- await fs.outputFile(path.join(extracted, llamaBinaryName()), 'bin');
41
- await fs.outputFile(path.join(extracted, 'libllama.dylib'), 'lib');
42
- await fs.outputFile(path.join(extracted, 'ggml-metal.metal'), 'metal');
43
- const installDir = path.join(dir, 'llama-b5604');
44
- await installBinFolder(path.join(dir, 'extract'), installDir);
45
- expect((await fs.readdir(installDir)).sort()).toEqual(['ggml-metal.metal', 'libllama.dylib', llamaBinaryName()].sort());
46
- });
47
- it('fails clearly when the archive has no llama-cli', async () => {
48
- await fs.outputFile(path.join(dir, 'extract', 'README.md'), 'x');
49
- await expect(installBinFolder(path.join(dir, 'extract'), path.join(dir, 'out'))).rejects.toThrow('not found');
50
- });
51
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,61 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { buildLlamaArgs, cleanLlamaOutput, contextSizeFor, runProcess } from './llama-process.js';
3
- const base = { modelPath: '/m.gguf', prompt: 'hello', maxTokens: 1024, threads: 4, temperature: 0.1 };
4
- describe('buildLlamaArgs', () => {
5
- it('uses flags llama.cpp b5604 accepts and that keep generated text on stdout', () => {
6
- const args = buildLlamaArgs(base);
7
- expect(args).toContain('-st');
8
- expect(args).toContain('--simple-io');
9
- expect(args).toContain('--no-display-prompt');
10
- expect(args).not.toContain('--json');
11
- expect(args).not.toContain('--log-disable');
12
- });
13
- it('fixes the seed so a run is reproducible', () => {
14
- const args = buildLlamaArgs(base);
15
- expect(args[args.indexOf('--seed') + 1]).toBe('42');
16
- });
17
- it('constrains output with the findings schema file when given', () => {
18
- const args = buildLlamaArgs({ ...base, schemaPath: '/schema.json' });
19
- expect(args.slice(-2)).toEqual(['--json-schema-file', '/schema.json']);
20
- expect(buildLlamaArgs(base)).not.toContain('--json-schema-file');
21
- });
22
- it('passes a context size that fits the prompt', () => {
23
- const args = buildLlamaArgs({ ...base, prompt: 'x'.repeat(30_000) });
24
- expect(Number(args[args.indexOf('--ctx-size') + 1])).toBe(contextSizeFor('x'.repeat(30_000), 1024));
25
- });
26
- });
27
- describe('contextSizeFor', () => {
28
- it('never goes below the 4096 default', () => {
29
- expect(contextSizeFor('short', 512)).toBe(4096);
30
- });
31
- it('grows with the prompt, rounded up to 1024', () => {
32
- const size = contextSizeFor('x'.repeat(30_000), 1024);
33
- expect(size).toBeGreaterThanOrEqual(10_000 + 1024);
34
- expect(size % 1024).toBe(0);
35
- });
36
- it('is capped at the model training context', () => {
37
- expect(contextSizeFor('x'.repeat(1_000_000), 1024)).toBe(32768);
38
- });
39
- });
40
- describe('cleanLlamaOutput', () => {
41
- it('strips the end-of-generation marker', () => {
42
- expect(cleanLlamaOutput('{"findings": []} [end of text]\n\n')).toBe('{"findings": []}');
43
- });
44
- });
45
- describe('runProcess', () => {
46
- it('collects stdout and stderr and returns the exit code', async () => {
47
- const result = await runProcess(process.execPath, ['-e', 'process.stdout.write("out");process.stderr.write("err");process.exit(3)'], { timeoutMs: 10_000 });
48
- expect(result).toEqual({ code: 3, stdout: 'out', stderr: 'err' });
49
- });
50
- it('runs with stdin closed so an interactive binary cannot wait on it', async () => {
51
- const result = await runProcess(process.execPath, ['-e', 'process.stdin.on("end",()=>process.stdout.write("eof")).resume()'], { timeoutMs: 10_000 });
52
- expect(result.stdout).toBe('eof');
53
- });
54
- it('kills and rejects on timeout', async () => {
55
- await expect(runProcess(process.execPath, ['-e', 'setTimeout(()=>{}, 10_000)'], { timeoutMs: 100 }))
56
- .rejects.toThrow('timed out');
57
- });
58
- it('rejects when the binary cannot be spawned', async () => {
59
- await expect(runProcess('/nonexistent/llama-cli', [], { timeoutMs: 1_000 })).rejects.toThrow();
60
- });
61
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,23 +0,0 @@
1
- import fs from 'fs';
2
- import os from 'os';
3
- import path from 'path';
4
- import { afterEach, describe, expect, it } from 'vitest';
5
- import { localModel } from './sidecar-provider.js';
6
- let dir;
7
- afterEach(() => { if (dir)
8
- fs.rmSync(dir, { recursive: true, force: true }); dir = undefined; });
9
- describe('a local GGUF for --model-path', () => {
10
- it('is used as is, never reported as a fallback', async () => {
11
- dir = fs.mkdtempSync(path.join(os.tmpdir(), 'local-model-'));
12
- const file = path.join(dir, 'rigour-max-candidate-q4_k_m.gguf');
13
- fs.writeFileSync(file, Buffer.alloc(2_000_000));
14
- expect(await localModel('max', file)).toEqual({
15
- path: file, fallback: false,
16
- info: { tier: 'max', name: 'rigour-max-candidate-q4_k_m.gguf (local)', filename: 'rigour-max-candidate-q4_k_m.gguf', url: '', sizeBytes: 2_000_000, sizeHuman: '2MB' },
17
- });
18
- });
19
- it('fails clearly when the file is missing, rather than downloading something else', async () => {
20
- const missing = path.resolve('/nonexistent/model.gguf'); // D:\\nonexistent\\model.gguf on Windows
21
- await expect(localModel('max', missing)).rejects.toThrow(`Model file not found: ${missing}`);
22
- });
23
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,125 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { createHash } from 'crypto';
4
- import { Readable } from 'stream';
5
- import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest';
6
- const home = vi.hoisted(() => {
7
- const p = require('path');
8
- const o = require('os');
9
- return p.join(o.tmpdir(), `rigour-models-${process.pid}-${Date.now()}`);
10
- });
11
- vi.mock('../storage/db.js', () => ({ RIGOUR_DIR: home }));
12
- const { checkForUpdates, downloadModel, getCachedModel, normalizeModelVersion } = await import('./model-manager.js');
13
- const { FALLBACK_MODELS, MODELS } = await import('./types.js');
14
- const modelsDir = path.join(home, 'models');
15
- const PAYLOAD = Buffer.from('gguf-model-bytes');
16
- const PAYLOAD_SHA = createHash('sha256').update(PAYLOAD).digest('hex');
17
- function ok(body, sha) {
18
- return Promise.resolve({ statusCode: 200, headers: { 'x-linked-etag': `"${sha}"` }, body: Readable.from([body]) });
19
- }
20
- const unauthorized = Promise.resolve({ statusCode: 401, headers: {}, body: Readable.from([]) });
21
- describe('downloadModel', () => {
22
- beforeEach(async () => {
23
- await fs.remove(home);
24
- // A fresh version check: no network call to Hugging Face.
25
- await fs.outputJson(path.join(modelsDir, '.latest_version.json'), { format: 2, version: '5', checkedAt: new Date().toISOString() });
26
- });
27
- afterAll(async () => { await fs.remove(home); });
28
- it('falls back to the stock model when the fine-tuned model is unavailable, and says so', async () => {
29
- const requested = [];
30
- const get = (url) => {
31
- requested.push(url);
32
- return url.includes('rigour-labs/') ? unauthorized : ok(PAYLOAD, PAYLOAD_SHA);
33
- };
34
- const messages = [];
35
- const modelPath = await downloadModel('lite', m => messages.push(m), get);
36
- expect(requested).toEqual([MODELS.lite.url, FALLBACK_MODELS.lite.url]);
37
- expect(path.basename(modelPath)).toBe(FALLBACK_MODELS.lite.filename);
38
- expect(messages.some(m => m.includes('Fine-tuned model unavailable (HTTP 401'))).toBe(true);
39
- const meta = await fs.readJson(`${modelPath}.meta.json`);
40
- expect(meta).toMatchObject({ sha256: PAYLOAD_SHA, sizeBytes: PAYLOAD.length, sourceUrl: FALLBACK_MODELS.lite.url });
41
- });
42
- it('retries the fine-tuned model once a day when only the stock fallback is cached', async () => {
43
- const failing = (url) => (url.includes('rigour-labs/') ? unauthorized : ok(PAYLOAD, PAYLOAD_SHA));
44
- const stockPath = await downloadModel('lite', undefined, failing);
45
- const requested = [];
46
- const again = await downloadModel('lite', undefined, (url) => { requested.push(url); return unauthorized; });
47
- expect(requested).toEqual([]); // failed less than a day ago: keep the fallback, no request
48
- expect(again).toBe(stockPath);
49
- await fs.writeJson(path.join(modelsDir, '.fine-tuned-unavailable-lite.json'), { failedAt: '2000-01-01T00:00:00Z', url: MODELS.lite.url });
50
- const fineTuned = await downloadModel('lite', undefined, (url) => { requested.push(url); return ok(PAYLOAD, PAYLOAD_SHA); });
51
- expect(requested).toEqual([MODELS.lite.url]);
52
- expect(path.basename(fineTuned)).toBe(MODELS.lite.filename);
53
- expect(await fs.pathExists(path.join(modelsDir, '.fine-tuned-unavailable-lite.json'))).toBe(false);
54
- });
55
- it('rejects a checksum mismatch and deletes the partial download', async () => {
56
- const get = () => ok(PAYLOAD, 'f'.repeat(64));
57
- await expect(downloadModel('lite', undefined, get)).rejects.toThrow('Checksum mismatch');
58
- expect(await fs.pathExists(path.join(modelsDir, `${FALLBACK_MODELS.lite.filename}.download`))).toBe(false);
59
- expect(await fs.pathExists(path.join(modelsDir, FALLBACK_MODELS.lite.filename))).toBe(false);
60
- });
61
- });
62
- describe('getCachedModel', () => {
63
- beforeEach(async () => { await fs.remove(home); });
64
- async function cacheFile(filename, sizeBytes) {
65
- const file = path.join(modelsDir, filename);
66
- await fs.ensureDir(modelsDir);
67
- await fs.writeFile(file, '');
68
- await fs.truncate(file, sizeBytes); // sparse: no real disk use
69
- await fs.writeJson(`${file}.meta.json`, {
70
- sha256: 'a'.repeat(64), sizeBytes, verifiedAt: new Date(Date.now() + 1000).toISOString(), sourceUrl: 'https://x',
71
- });
72
- }
73
- it('returns null when nothing is cached', async () => {
74
- expect(await getCachedModel('deep')).toBeNull();
75
- });
76
- it('reports the stock fallback when only it is cached', async () => {
77
- await cacheFile(FALLBACK_MODELS.deep.filename, FALLBACK_MODELS.deep.sizeBytes);
78
- const cached = await getCachedModel('deep');
79
- expect(cached).toMatchObject({ fallback: true, info: { filename: FALLBACK_MODELS.deep.filename } });
80
- });
81
- it('accepts a verified fine-tuned file smaller than the size estimate', async () => {
82
- await cacheFile(MODELS.lite.filename, 397_807_360); // the published v5 lite model
83
- expect(await getCachedModel('lite')).toMatchObject({ fallback: false, info: { filename: MODELS.lite.filename } });
84
- });
85
- it('accepts a file verified within the same millisecond it was written', async () => {
86
- const file = path.join(modelsDir, FALLBACK_MODELS.deep.filename);
87
- await cacheFile(FALLBACK_MODELS.deep.filename, 10);
88
- const written = new Date('2026-01-01T00:00:00.000Z');
89
- await fs.utimes(file, written, written.getTime() / 1000 + 0.0007); // mtime 0.7 ms into the same millisecond
90
- await fs.writeJson(`${file}.meta.json`, { sha256: 'a'.repeat(64), sizeBytes: 10, verifiedAt: written.toISOString(), sourceUrl: 'https://x' });
91
- expect(await getCachedModel('deep')).toMatchObject({ fallback: true });
92
- });
93
- it('rejects a file modified after it was verified', async () => {
94
- const file = path.join(modelsDir, FALLBACK_MODELS.deep.filename);
95
- await cacheFile(FALLBACK_MODELS.deep.filename, 10);
96
- await fs.writeJson(`${file}.meta.json`, { sha256: 'a'.repeat(64), sizeBytes: 10, verifiedAt: '2000-01-01T00:00:00.000Z', sourceUrl: 'https://x' });
97
- expect(await getCachedModel('deep')).toBeNull();
98
- });
99
- it('ignores a file without verified metadata', async () => {
100
- await fs.ensureDir(modelsDir);
101
- await fs.writeFile(path.join(modelsDir, FALLBACK_MODELS.deep.filename), 'partial');
102
- expect(await getCachedModel('deep')).toBeNull();
103
- });
104
- });
105
- describe('model versions', () => {
106
- beforeEach(async () => { await fs.remove(home); vi.unstubAllGlobals(); });
107
- it('keeps published version strings as they are', () => {
108
- expect(normalizeModelVersion(5)).toBe('5');
109
- expect(normalizeModelVersion('5')).toBe('5');
110
- expect(normalizeModelVersion('v2.1.0')).toBe('2.1.0');
111
- expect(normalizeModelVersion('5.0')).toBeNull();
112
- expect(normalizeModelVersion({})).toBeNull();
113
- });
114
- it('names the published v5 repositories by default', () => {
115
- expect(MODELS.lite.url).toBe('https://huggingface.co/rigour-labs/rigour-lite-v5-gguf/resolve/main/rigour-lite-v5-q4_k_m.gguf');
116
- expect(MODELS.deep.url).toBe('https://huggingface.co/rigour-labs/rigour-deep-v5-gguf/resolve/main/rigour-deep-v5-q4_k_m.gguf');
117
- });
118
- it('ignores a version cache from the old SemVer conversion and re-reads the published version', async () => {
119
- await fs.outputJson(path.join(modelsDir, '.latest_version.json'), { version: '5.0.0', checkedAt: new Date().toISOString() });
120
- vi.stubGlobal('fetch', vi.fn(async () => ({ ok: true, json: async () => ({ version: 5 }) })));
121
- expect(await checkForUpdates()).toBe('5');
122
- expect(await fs.readJson(path.join(modelsDir, '.latest_version.json'))).toMatchObject({ format: 2, version: '5' });
123
- expect(MODELS.deep.filename).toBe('rigour-deep-v5-q4_k_m.gguf');
124
- });
125
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,24 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import fs from 'fs-extra';
3
- import os from 'os';
4
- import path from 'path';
5
- import { extractSha256FromEtag, hashFileSha256 } from './model-manager.js';
6
- describe('model manager integrity helpers', () => {
7
- it('extracts sha256 digest from a strong ETag', () => {
8
- const digest = 'a'.repeat(64);
9
- expect(extractSha256FromEtag(`"${digest}"`)).toBe(digest);
10
- expect(extractSha256FromEtag(`W/"${digest}"`)).toBe(digest);
11
- });
12
- it('returns null for non-sha ETags', () => {
13
- expect(extractSha256FromEtag('"not-a-digest"')).toBeNull();
14
- expect(extractSha256FromEtag(null)).toBeNull();
15
- });
16
- it('hashes file contents with sha256', async () => {
17
- const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-model-hash-'));
18
- const filePath = path.join(dir, 'sample.gguf');
19
- await fs.writeFile(filePath, 'rigour-model-check');
20
- const digest = await hashFileSha256(filePath);
21
- expect(digest).toBe('e123266ea4b37a81948a0a844dd58eddfc81737aa6fdf9dafc818fd23bae75f0');
22
- await fs.remove(dir);
23
- });
24
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,19 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { buildModelInfo, FALLBACK_MODELS, localTier, MODELS } from './types.js';
3
- describe('local model tiers', () => {
4
- it('picks max over pro over the lite default', () => {
5
- expect(localTier({})).toBe('lite');
6
- expect(localTier({ pro: true })).toBe('deep');
7
- expect(localTier({ max: true })).toBe('max');
8
- expect(localTier({ max: true, pro: true })).toBe('max');
9
- });
10
- it('asks for a published max fine-tune and falls back to the stock 7B coder model', () => {
11
- expect(buildModelInfo('max', '6.0.0').url)
12
- .toBe('https://huggingface.co/rigour-labs/rigour-max-v6.0.0-gguf/resolve/main/rigour-max-v6.0.0-q4_k_m.gguf');
13
- expect(MODELS.max.tier).toBe('max');
14
- expect(FALLBACK_MODELS.max).toMatchObject({
15
- filename: 'qwen2.5-coder-7b-instruct-q4_k_m.gguf',
16
- url: 'https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct-GGUF/resolve/main/qwen2.5-coder-7b-instruct-q4_k_m.gguf',
17
- });
18
- });
19
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,36 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { RECALL_LIMIT, rankMemories } from './recall.js';
3
- const memories = [
4
- { scope: 'repo', key: 'retry_policy', value: 'Wrap network calls in withRetry from lib/net; never hand-roll backoff.' },
5
- { scope: 'repo', key: 'db_access', value: 'Reports read from the replica, never the primary.' },
6
- { scope: 'user', key: 'pr_size', value: 'Keep pull requests under 400 lines.' },
7
- ];
8
- /** A stand-in embedding: one dimension per topic word, so similarity is predictable. */
9
- const TOPICS = ['retry', 'backoff', 'network', 'replica', 'report', 'pull', 'request'];
10
- const embed = async (text) => TOPICS.map(topic => (text.toLowerCase().includes(topic) ? 1 : 0));
11
- describe('rankMemories', () => {
12
- it('returns the memories that match by meaning, best first, above the floor', async () => {
13
- const ranked = await rankMemories('add exponential backoff to a flaky network call', memories, embed);
14
- expect(ranked.map(m => [m.key, m.match])).toEqual([['retry_policy', 'semantic']]);
15
- });
16
- it('returns nothing rather than the closest weak match', async () => {
17
- expect(await rankMemories('rename a css class', memories, embed)).toEqual([]);
18
- });
19
- it('falls back to keyword overlap when the model is unavailable, and says so', async () => {
20
- const ranked = await rankMemories('replica reports', memories, async () => []);
21
- expect(ranked.map(m => [m.key, m.match])).toEqual([['db_access', 'keyword']]);
22
- });
23
- it(`returns at most ${RECALL_LIMIT}`, async () => {
24
- const many = Array.from({ length: 10 }, (_, i) => ({ scope: 'repo', key: `retry_${i}`, value: 'network retry backoff' }));
25
- expect(await rankMemories('network retry backoff', many, embed)).toHaveLength(RECALL_LIMIT);
26
- });
27
- it('embeds each text once, not on every recall', async () => {
28
- let calls = 0;
29
- const counting = async (text) => { calls++; return embed(text); };
30
- const fresh = memories.map(m => ({ ...m, value: `${m.value} (cache test)` }));
31
- await rankMemories('network retry', fresh, counting);
32
- const first = calls;
33
- await rankMemories('replica reports', fresh, counting);
34
- expect(calls - first).toBe(1); // only the new query
35
- });
36
- });
@@ -1,6 +0,0 @@
1
- /**
2
- * Pattern Indexer Tests
3
- *
4
- * Comprehensive tests for the pattern indexer.
5
- */
6
- export {};