@rigour-labs/core 6.8.0 → 6.8.1-rc.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.
- package/dist/inference/cloud-provider.js +12 -2
- package/package.json +11 -8
- package/dist/brief/briefing.test.d.ts +0 -1
- package/dist/brief/briefing.test.js +0 -107
- package/dist/context/automatic-index-cache.test.d.ts +0 -1
- package/dist/context/automatic-index-cache.test.js +0 -30
- package/dist/context/automatic-index.test.d.ts +0 -1
- package/dist/context/automatic-index.test.js +0 -45
- package/dist/context/cache-engine.test.d.ts +0 -1
- package/dist/context/cache-engine.test.js +0 -99
- package/dist/context/dependency-graph.test.d.ts +0 -1
- package/dist/context/dependency-graph.test.js +0 -13
- package/dist/context/index-status.test.d.ts +0 -1
- package/dist/context/index-status.test.js +0 -26
- package/dist/context.test.d.ts +0 -1
- package/dist/context.test.js +0 -228
- package/dist/deep/agent-review.test.d.ts +0 -1
- package/dist/deep/agent-review.test.js +0 -53
- package/dist/deep/code-context.test.d.ts +0 -1
- package/dist/deep/code-context.test.js +0 -48
- package/dist/deep/code-pass.test.d.ts +0 -1
- package/dist/deep/code-pass.test.js +0 -112
- package/dist/deep/code-review-prompt.test.d.ts +0 -1
- package/dist/deep/code-review-prompt.test.js +0 -20
- package/dist/deep/code-verifier.test.d.ts +0 -1
- package/dist/deep/code-verifier.test.js +0 -54
- package/dist/deep/diff-tests/calls.test.d.ts +0 -1
- package/dist/deep/diff-tests/calls.test.js +0 -33
- package/dist/deep/diff-tests/run.test.d.ts +0 -1
- package/dist/deep/diff-tests/run.test.js +0 -58
- package/dist/deep/fact-extractor.test.d.ts +0 -1
- package/dist/deep/fact-extractor.test.js +0 -581
- package/dist/deep/parse-findings.test.d.ts +0 -1
- package/dist/deep/parse-findings.test.js +0 -25
- package/dist/deep/pr-review.test.d.ts +0 -1
- package/dist/deep/pr-review.test.js +0 -80
- package/dist/deep/prompts.test.d.ts +0 -1
- package/dist/deep/prompts.test.js +0 -235
- package/dist/deep/reference-pack.test.d.ts +0 -1
- package/dist/deep/reference-pack.test.js +0 -46
- package/dist/deep/related-changes.test.d.ts +0 -1
- package/dist/deep/related-changes.test.js +0 -33
- package/dist/deep/review-context-export.test.d.ts +0 -1
- package/dist/deep/review-context-export.test.js +0 -32
- package/dist/deep/review-tools.test.d.ts +0 -1
- package/dist/deep/review-tools.test.js +0 -39
- package/dist/deep/risk.test.d.ts +0 -1
- package/dist/deep/risk.test.js +0 -101
- package/dist/deep/verifier.test.d.ts +0 -1
- package/dist/deep/verifier.test.js +0 -635
- package/dist/discovery.test.d.ts +0 -1
- package/dist/discovery.test.js +0 -93
- package/dist/environment.test.d.ts +0 -1
- package/dist/environment.test.js +0 -94
- package/dist/firewall/firewall.test.d.ts +0 -1
- package/dist/firewall/firewall.test.js +0 -117
- package/dist/firewall/trust-boundaries.test.d.ts +0 -1
- package/dist/firewall/trust-boundaries.test.js +0 -64
- package/dist/firewall/trusted-control.test.d.ts +0 -1
- package/dist/firewall/trusted-control.test.js +0 -188
- package/dist/gates/agent-team.test.d.ts +0 -1
- package/dist/gates/agent-team.test.js +0 -113
- package/dist/gates/ast.test.d.ts +0 -1
- package/dist/gates/ast.test.js +0 -112
- package/dist/gates/checkpoint.test.d.ts +0 -1
- package/dist/gates/checkpoint.test.js +0 -105
- package/dist/gates/content.test.d.ts +0 -1
- package/dist/gates/content.test.js +0 -73
- package/dist/gates/coverage.test.d.ts +0 -1
- package/dist/gates/coverage.test.js +0 -53
- package/dist/gates/dedupe-failures.test.d.ts +0 -1
- package/dist/gates/dedupe-failures.test.js +0 -12
- package/dist/gates/deep-analysis.test.d.ts +0 -1
- package/dist/gates/deep-analysis.test.js +0 -86
- package/dist/gates/deep-intent.test.d.ts +0 -1
- package/dist/gates/deep-intent.test.js +0 -51
- package/dist/gates/deep-timeout.test.d.ts +0 -1
- package/dist/gates/deep-timeout.test.js +0 -10
- package/dist/gates/deprecated-apis.test.d.ts +0 -1
- package/dist/gates/deprecated-apis.test.js +0 -318
- package/dist/gates/deprecated-dependencies.test.d.ts +0 -1
- package/dist/gates/deprecated-dependencies.test.js +0 -55
- package/dist/gates/frontend-secret-exposure.test.d.ts +0 -1
- package/dist/gates/frontend-secret-exposure.test.js +0 -148
- package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.js +0 -27
- package/dist/gates/hallucinated-imports/js-resolver-types.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports/js-resolver-types.test.js +0 -14
- package/dist/gates/hallucinated-imports-sveltekit.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports-sveltekit.test.js +0 -132
- package/dist/gates/hallucinated-imports.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports.test.js +0 -1206
- package/dist/gates/js-style-context.test.d.ts +0 -1
- package/dist/gates/js-style-context.test.js +0 -35
- package/dist/gates/logic-drift.test.d.ts +0 -1
- package/dist/gates/logic-drift.test.js +0 -52
- package/dist/gates/phantom-apis.test.d.ts +0 -1
- package/dist/gates/phantom-apis.test.js +0 -396
- package/dist/gates/promise-safety.test.d.ts +0 -1
- package/dist/gates/promise-safety.test.js +0 -34
- package/dist/gates/runner.test.d.ts +0 -1
- package/dist/gates/runner.test.js +0 -77
- package/dist/gates/scoped-gates.test.d.ts +0 -1
- package/dist/gates/scoped-gates.test.js +0 -52
- package/dist/gates/security-patterns-owasp.test.d.ts +0 -1
- package/dist/gates/security-patterns-owasp.test.js +0 -186
- package/dist/gates/security-patterns.test.d.ts +0 -1
- package/dist/gates/security-patterns.test.js +0 -194
- package/dist/gates/semantic-bugs.test.d.ts +0 -1
- package/dist/gates/semantic-bugs.test.js +0 -76
- package/dist/gates/side-effect-analysis.test.d.ts +0 -1
- package/dist/gates/side-effect-analysis.test.js +0 -162
- package/dist/gates/style-drift.test.d.ts +0 -1
- package/dist/gates/style-drift.test.js +0 -26
- package/dist/gates/test-quality.test.d.ts +0 -1
- package/dist/gates/test-quality.test.js +0 -325
- package/dist/gates/trusted-reviews.test.d.ts +0 -1
- package/dist/gates/trusted-reviews.test.js +0 -16
- package/dist/gates/unindexed-reads/queries.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/queries.test.js +0 -54
- package/dist/gates/unindexed-reads/schema.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/schema.test.js +0 -81
- package/dist/gates/unindexed-reads/unindexed-reads.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/unindexed-reads.test.js +0 -90
- package/dist/hooks/checker.test.d.ts +0 -1
- package/dist/hooks/checker.test.js +0 -159
- package/dist/hooks/dlp-confidence.test.d.ts +0 -1
- package/dist/hooks/dlp-confidence.test.js +0 -51
- package/dist/hooks/dlp-feedback.test.d.ts +0 -1
- package/dist/hooks/dlp-feedback.test.js +0 -131
- package/dist/hooks/input-validator.test.d.ts +0 -1
- package/dist/hooks/input-validator.test.js +0 -329
- package/dist/hooks/templates.test.d.ts +0 -1
- package/dist/hooks/templates.test.js +0 -27
- package/dist/inference/brain-placeholder.test.d.ts +0 -1
- package/dist/inference/brain-placeholder.test.js +0 -28
- package/dist/inference/cloud-provider.test.d.ts +0 -1
- package/dist/inference/cloud-provider.test.js +0 -139
- package/dist/inference/executable.test.d.ts +0 -1
- package/dist/inference/executable.test.js +0 -41
- package/dist/inference/http-download.test.d.ts +0 -1
- package/dist/inference/http-download.test.js +0 -109
- package/dist/inference/llama-engine-checksum.test.d.ts +0 -1
- package/dist/inference/llama-engine-checksum.test.js +0 -27
- package/dist/inference/llama-engine.test.d.ts +0 -1
- package/dist/inference/llama-engine.test.js +0 -51
- package/dist/inference/llama-process.test.d.ts +0 -1
- package/dist/inference/llama-process.test.js +0 -61
- package/dist/inference/local-model.test.d.ts +0 -1
- package/dist/inference/local-model.test.js +0 -23
- package/dist/inference/model-download.test.d.ts +0 -1
- package/dist/inference/model-download.test.js +0 -125
- package/dist/inference/model-manager.test.d.ts +0 -1
- package/dist/inference/model-manager.test.js +0 -24
- package/dist/inference/types.test.d.ts +0 -1
- package/dist/inference/types.test.js +0 -19
- package/dist/memory/recall.test.d.ts +0 -1
- package/dist/memory/recall.test.js +0 -36
- package/dist/pattern-index/indexer.test.d.ts +0 -6
- package/dist/pattern-index/indexer.test.js +0 -197
- package/dist/pattern-index/matcher.test.d.ts +0 -6
- package/dist/pattern-index/matcher.test.js +0 -238
- package/dist/pattern-index/pattern-reuse.test.d.ts +0 -1
- package/dist/pattern-index/pattern-reuse.test.js +0 -76
- package/dist/pattern-index/semantic-runtime.test.d.ts +0 -1
- package/dist/pattern-index/semantic-runtime.test.js +0 -31
- package/dist/pattern-index/staleness.test.d.ts +0 -6
- package/dist/pattern-index/staleness.test.js +0 -211
- package/dist/review/agent-fixes.test.d.ts +0 -1
- package/dist/review/agent-fixes.test.js +0 -32
- package/dist/review/backtest-init.test.d.ts +0 -1
- package/dist/review/backtest-init.test.js +0 -136
- package/dist/review/backtest-judges.test.d.ts +0 -1
- package/dist/review/backtest-judges.test.js +0 -32
- package/dist/review/backtest-last.test.d.ts +0 -1
- package/dist/review/backtest-last.test.js +0 -109
- package/dist/review/backtest.test.d.ts +0 -1
- package/dist/review/backtest.test.js +0 -183
- package/dist/review/baseline.test.d.ts +0 -1
- package/dist/review/baseline.test.js +0 -22
- package/dist/review/branch-checks.test.d.ts +0 -1
- package/dist/review/branch-checks.test.js +0 -48
- package/dist/review/check-outcomes.test.d.ts +0 -1
- package/dist/review/check-outcomes.test.js +0 -56
- package/dist/review/code-patterns.test.d.ts +0 -1
- package/dist/review/code-patterns.test.js +0 -142
- package/dist/review/dead-code.test.d.ts +0 -1
- package/dist/review/dead-code.test.js +0 -154
- package/dist/review/deep-runs.test.d.ts +0 -1
- package/dist/review/deep-runs.test.js +0 -23
- package/dist/review/effectiveness.test.d.ts +0 -1
- package/dist/review/effectiveness.test.js +0 -42
- package/dist/review/fix-scope.test.d.ts +0 -1
- package/dist/review/fix-scope.test.js +0 -104
- package/dist/review/generated-files.test.d.ts +0 -1
- package/dist/review/generated-files.test.js +0 -25
- package/dist/review/migration-order.test.d.ts +0 -1
- package/dist/review/migration-order.test.js +0 -62
- package/dist/review/quiet.test.d.ts +0 -1
- package/dist/review/quiet.test.js +0 -38
- package/dist/review/receipt.test.d.ts +0 -1
- package/dist/review/receipt.test.js +0 -60
- package/dist/review/review-task.test.d.ts +0 -1
- package/dist/review/review-task.test.js +0 -89
- package/dist/review/review.test.d.ts +0 -1
- package/dist/review/review.test.js +0 -203
- package/dist/review/reviewer/adapters.test.d.ts +0 -1
- package/dist/review/reviewer/adapters.test.js +0 -100
- package/dist/review/reviewer/api-judge.test.d.ts +0 -1
- package/dist/review/reviewer/api-judge.test.js +0 -108
- package/dist/review/reviewer/background.test.d.ts +0 -1
- package/dist/review/reviewer/background.test.js +0 -118
- package/dist/review/reviewer/context.test.d.ts +0 -1
- package/dist/review/reviewer/context.test.js +0 -45
- package/dist/review/reviewer/exec.test.d.ts +0 -1
- package/dist/review/reviewer/exec.test.js +0 -59
- package/dist/review/reviewer/inputs.test.d.ts +0 -1
- package/dist/review/reviewer/inputs.test.js +0 -52
- package/dist/review/reviewer/panel.test.d.ts +0 -1
- package/dist/review/reviewer/panel.test.js +0 -110
- package/dist/review/reviewer/record.test.d.ts +0 -1
- package/dist/review/reviewer/record.test.js +0 -40
- package/dist/review/reviewer/rule-writer.test.d.ts +0 -1
- package/dist/review/reviewer/rule-writer.test.js +0 -52
- package/dist/review/reviewer/settings.test.d.ts +0 -1
- package/dist/review/reviewer/settings.test.js +0 -57
- package/dist/review/reviewer/usage.test.d.ts +0 -1
- package/dist/review/reviewer/usage.test.js +0 -14
- package/dist/review/reviewer.test.d.ts +0 -1
- package/dist/review/reviewer.test.js +0 -792
- package/dist/review/stories.test.d.ts +0 -1
- package/dist/review/stories.test.js +0 -58
- package/dist/review/toolchain.test.d.ts +0 -1
- package/dist/review/toolchain.test.js +0 -108
- package/dist/review/typed/redundancy.test.d.ts +0 -1
- package/dist/review/typed/redundancy.test.js +0 -315
- package/dist/review/typed/schema-nullability.test.d.ts +0 -1
- package/dist/review/typed/schema-nullability.test.js +0 -63
- package/dist/review-learning/human-edits.test.d.ts +0 -1
- package/dist/review-learning/human-edits.test.js +0 -47
- package/dist/review-learning/repo-rules.test.d.ts +0 -1
- package/dist/review-learning/repo-rules.test.js +0 -93
- package/dist/review-learning/review-learning.test.d.ts +0 -1
- package/dist/review-learning/review-learning.test.js +0 -249
- package/dist/safety.test.d.ts +0 -1
- package/dist/safety.test.js +0 -42
- package/dist/semantic/benchmark.test.d.ts +0 -1
- package/dist/semantic/benchmark.test.js +0 -22
- package/dist/semantic/intent/intent.test.d.ts +0 -1
- package/dist/semantic/intent/intent.test.js +0 -46
- package/dist/semantic/learn/learn.test.d.ts +0 -1
- package/dist/semantic/learn/learn.test.js +0 -67
- package/dist/semantic/origins.test.d.ts +0 -1
- package/dist/semantic/origins.test.js +0 -77
- package/dist/semantic/project-facts.test.d.ts +0 -1
- package/dist/semantic/project-facts.test.js +0 -45
- package/dist/semantic/sites/call-sites.test.d.ts +0 -1
- package/dist/semantic/sites/call-sites.test.js +0 -46
- package/dist/services/adaptive-thresholds.test.d.ts +0 -1
- package/dist/services/adaptive-thresholds.test.js +0 -53
- package/dist/services/agent-history.test.d.ts +0 -1
- package/dist/services/agent-history.test.js +0 -69
- package/dist/services/context-scope-summary.test.d.ts +0 -1
- package/dist/services/context-scope-summary.test.js +0 -17
- package/dist/services/context-telemetry-service.test.d.ts +0 -1
- package/dist/services/context-telemetry-service.test.js +0 -182
- package/dist/services/cursor-usage-sync.test.d.ts +0 -1
- package/dist/services/cursor-usage-sync.test.js +0 -173
- package/dist/services/engineering-knowledge-graph.test.d.ts +0 -1
- package/dist/services/engineering-knowledge-graph.test.js +0 -76
- package/dist/services/model-pricing.test.d.ts +0 -1
- package/dist/services/model-pricing.test.js +0 -44
- package/dist/services/observed-savings.test.d.ts +0 -1
- package/dist/services/observed-savings.test.js +0 -37
- package/dist/services/score-history.test.d.ts +0 -1
- package/dist/services/score-history.test.js +0 -61
- package/dist/smoke.test.d.ts +0 -1
- package/dist/smoke.test.js +0 -17
- package/dist/storage/cache-cleanup.test.d.ts +0 -1
- package/dist/storage/cache-cleanup.test.js +0 -54
- package/dist/storage/context-telemetry.test.d.ts +0 -1
- package/dist/storage/context-telemetry.test.js +0 -80
- package/dist/storage/db.test.d.ts +0 -1
- package/dist/storage/db.test.js +0 -46
- package/dist/storage/fix-lessons.test.d.ts +0 -1
- package/dist/storage/fix-lessons.test.js +0 -61
- package/dist/storage/lessons.test.d.ts +0 -1
- package/dist/storage/lessons.test.js +0 -81
- package/dist/storage/local-encryption.test.d.ts +0 -1
- package/dist/storage/local-encryption.test.js +0 -34
- package/dist/storage/local-memory.test.d.ts +0 -1
- package/dist/storage/local-memory.test.js +0 -55
- package/dist/storage/share-memory.test.d.ts +0 -1
- package/dist/storage/share-memory.test.js +0 -34
- package/dist/storage/team-diagnostics.test.d.ts +0 -1
- package/dist/storage/team-diagnostics.test.js +0 -51
- package/dist/storage/team-scope.test.d.ts +0 -1
- package/dist/storage/team-scope.test.js +0 -22
- package/dist/storage/team-store.test.d.ts +0 -1
- package/dist/storage/team-store.test.js +0 -11
- package/dist/storage/team-sync-scope.test.d.ts +0 -1
- package/dist/storage/team-sync-scope.test.js +0 -100
- package/dist/storage/team-vector-store.test.d.ts +0 -1
- package/dist/storage/team-vector-store.test.js +0 -56
- package/dist/storage/telemetry-scope.test.d.ts +0 -1
- package/dist/storage/telemetry-scope.test.js +0 -38
- package/dist/task/thread.test.d.ts +0 -1
- package/dist/task/thread.test.js +0 -133
- package/dist/telemetry/telemetry.test.d.ts +0 -1
- package/dist/telemetry/telemetry.test.js +0 -64
- package/dist/types/index.test.d.ts +0 -1
- package/dist/types/index.test.js +0 -33
- package/dist/utils/command-line.test.d.ts +0 -1
- package/dist/utils/command-line.test.js +0 -8
- package/dist/utils/diff-removed.test.d.ts +0 -1
- package/dist/utils/diff-removed.test.js +0 -29
- package/dist/utils/diff.test.d.ts +0 -1
- package/dist/utils/diff.test.js +0 -38
- package/dist/utils/glob-paths.test.d.ts +0 -1
- package/dist/utils/glob-paths.test.js +0 -40
- package/dist/utils/profile.test.d.ts +0 -1
- package/dist/utils/profile.test.js +0 -68
- package/dist/utils/scanner.test.d.ts +0 -1
- package/dist/utils/scanner.test.js +0 -48
- package/dist/utils/scope.test.d.ts +0 -1
- package/dist/utils/scope.test.js +0 -38
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import fs from 'fs-extra';
|
|
5
|
-
vi.mock('../storage/db.js', () => ({
|
|
6
|
-
isSQLiteAvailable: () => false,
|
|
7
|
-
openDatabase: async () => null,
|
|
8
|
-
DB_PATH: '/tmp/rigour-test.db',
|
|
9
|
-
RIGOUR_DIR: '/tmp/.rigour',
|
|
10
|
-
}));
|
|
11
|
-
import { fetchCursorUsageFromAdminApi, syncCursorUsageFromAdminApi, getTaskCostStats, } from './context-telemetry-service.js';
|
|
12
|
-
import { getModelUsages } from '../storage/context-telemetry.js';
|
|
13
|
-
import { centsToUsd, normalizeCursorUsageEvent } from './cursor-usage-normalizer.js';
|
|
14
|
-
import { CURSOR_FILTERED_USAGE_URL } from './cursor-usage-client.js';
|
|
15
|
-
function buildUsageEvent(overrides = {}) {
|
|
16
|
-
return {
|
|
17
|
-
timestamp: '1750979225854',
|
|
18
|
-
userEmail: 'dev@example.com',
|
|
19
|
-
conversationId: 'conv-1',
|
|
20
|
-
model: 'composer-2.5-standard',
|
|
21
|
-
isTokenBasedCall: true,
|
|
22
|
-
tokenUsage: {
|
|
23
|
-
inputTokens: 100,
|
|
24
|
-
outputTokens: 50,
|
|
25
|
-
cacheWriteTokens: 10,
|
|
26
|
-
cacheReadTokens: 20,
|
|
27
|
-
totalCents: 12.5,
|
|
28
|
-
},
|
|
29
|
-
chargedCents: 15.25,
|
|
30
|
-
...overrides,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
describe('cursor admin usage sync', () => {
|
|
34
|
-
const testCwd = path.join(os.tmpdir(), `rigour-cursor-sync-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
35
|
-
const apiKey = 'test-cursor-admin-key';
|
|
36
|
-
beforeEach(async () => {
|
|
37
|
-
await fs.ensureDir(path.join(testCwd, '.rigour'));
|
|
38
|
-
});
|
|
39
|
-
afterEach(async () => {
|
|
40
|
-
await fs.remove(testCwd);
|
|
41
|
-
vi.restoreAllMocks();
|
|
42
|
-
});
|
|
43
|
-
it('imports a single-page response with basic auth header', async () => {
|
|
44
|
-
const fetchImpl = vi.fn(async (_url, init) => ({
|
|
45
|
-
ok: true,
|
|
46
|
-
status: 200,
|
|
47
|
-
json: async () => ({
|
|
48
|
-
usageEvents: [buildUsageEvent()],
|
|
49
|
-
pagination: { hasNextPage: false, currentPage: 1 },
|
|
50
|
-
}),
|
|
51
|
-
}));
|
|
52
|
-
const imported = await fetchCursorUsageFromAdminApi({
|
|
53
|
-
cwd: testCwd,
|
|
54
|
-
apiKey,
|
|
55
|
-
fetchImpl,
|
|
56
|
-
});
|
|
57
|
-
expect(imported).toBe(1);
|
|
58
|
-
expect(fetchImpl).toHaveBeenCalledTimes(1);
|
|
59
|
-
const [, init] = fetchImpl.mock.calls[0];
|
|
60
|
-
expect(init.method).toBe('POST');
|
|
61
|
-
expect(init.headers.Authorization).toBe(`Basic ${Buffer.from(`${apiKey}:`, 'utf8').toString('base64')}`);
|
|
62
|
-
expect(init.headers['Content-Type']).toBe('application/json');
|
|
63
|
-
const body = JSON.parse(init.body);
|
|
64
|
-
expect(body.pageSize).toBeGreaterThan(0);
|
|
65
|
-
expect(body.startDate).toBeLessThan(body.endDate);
|
|
66
|
-
});
|
|
67
|
-
it('paginates until hasNextPage is false', async () => {
|
|
68
|
-
let page = 0;
|
|
69
|
-
const fetchImpl = vi.fn(async () => {
|
|
70
|
-
page += 1;
|
|
71
|
-
return {
|
|
72
|
-
ok: true,
|
|
73
|
-
status: 200,
|
|
74
|
-
json: async () => ({
|
|
75
|
-
usageEvents: [buildUsageEvent({ conversationId: `conv-${page}` })],
|
|
76
|
-
pagination: { hasNextPage: page < 2, currentPage: page },
|
|
77
|
-
}),
|
|
78
|
-
};
|
|
79
|
-
});
|
|
80
|
-
const result = await syncCursorUsageFromAdminApi({ cwd: testCwd, apiKey, fetchImpl });
|
|
81
|
-
expect(result.importedCount).toBe(2);
|
|
82
|
-
expect(result.totalEvents).toBe(2);
|
|
83
|
-
expect(fetchImpl).toHaveBeenCalledTimes(2);
|
|
84
|
-
});
|
|
85
|
-
it('converts cents to USD and stores token fields', async () => {
|
|
86
|
-
const normalized = normalizeCursorUsageEvent(buildUsageEvent());
|
|
87
|
-
expect(normalized?.usage.observedCostUsd).toBe(centsToUsd(15.25));
|
|
88
|
-
expect(normalized?.usage.inputTokens).toBe(100);
|
|
89
|
-
expect(normalized?.usage.outputTokens).toBe(50);
|
|
90
|
-
expect(normalized?.usage.cachedInputTokens).toBe(20);
|
|
91
|
-
expect(normalized?.usage.cacheWriteTokens).toBe(10);
|
|
92
|
-
expect(normalized?.usage.source).toBe('cursor-admin-api');
|
|
93
|
-
});
|
|
94
|
-
it('deduplicates repeated syncs', async () => {
|
|
95
|
-
const fetchImpl = vi.fn(async () => ({
|
|
96
|
-
ok: true,
|
|
97
|
-
status: 200,
|
|
98
|
-
json: async () => ({
|
|
99
|
-
usageEvents: [buildUsageEvent()],
|
|
100
|
-
pagination: { hasNextPage: false },
|
|
101
|
-
}),
|
|
102
|
-
}));
|
|
103
|
-
const first = await fetchCursorUsageFromAdminApi({ cwd: testCwd, apiKey, fetchImpl });
|
|
104
|
-
const second = await fetchCursorUsageFromAdminApi({ cwd: testCwd, apiKey, fetchImpl });
|
|
105
|
-
expect(first).toBe(1);
|
|
106
|
-
expect(second).toBe(0);
|
|
107
|
-
const usages = await getModelUsages(undefined, testCwd);
|
|
108
|
-
expect(usages.filter(u => u.source === 'cursor-admin-api')).toHaveLength(1);
|
|
109
|
-
});
|
|
110
|
-
it('surfaces actionable non-2xx errors without exposing the API key', async () => {
|
|
111
|
-
const fetchImpl = vi.fn(async () => ({
|
|
112
|
-
ok: false,
|
|
113
|
-
status: 401,
|
|
114
|
-
text: async () => `Unauthorized for key ${apiKey}`,
|
|
115
|
-
}));
|
|
116
|
-
await expect(fetchCursorUsageFromAdminApi({ cwd: testCwd, apiKey, fetchImpl })).rejects.toThrow(/401/);
|
|
117
|
-
await expect(fetchCursorUsageFromAdminApi({ cwd: testCwd, apiKey, fetchImpl })).rejects.not.toThrow(apiKey);
|
|
118
|
-
});
|
|
119
|
-
it('skips malformed events and handles partial fields', async () => {
|
|
120
|
-
const fetchImpl = vi.fn(async () => ({
|
|
121
|
-
ok: true,
|
|
122
|
-
status: 200,
|
|
123
|
-
json: async () => ({
|
|
124
|
-
usageEvents: [
|
|
125
|
-
null,
|
|
126
|
-
{ model: 'composer-2.5-fast', chargedCents: 5 },
|
|
127
|
-
buildUsageEvent(),
|
|
128
|
-
],
|
|
129
|
-
pagination: { hasNextPage: false },
|
|
130
|
-
}),
|
|
131
|
-
}));
|
|
132
|
-
const imported = await fetchCursorUsageFromAdminApi({ cwd: testCwd, apiKey, fetchImpl });
|
|
133
|
-
expect(imported).toBe(2);
|
|
134
|
-
});
|
|
135
|
-
it('marks observed cost stats from imported admin API events', async () => {
|
|
136
|
-
const fetchImpl = vi.fn(async () => ({
|
|
137
|
-
ok: true,
|
|
138
|
-
status: 200,
|
|
139
|
-
json: async () => ({
|
|
140
|
-
usageEvents: [buildUsageEvent({ conversationId: 'cost-task' })],
|
|
141
|
-
pagination: { hasNextPage: false },
|
|
142
|
-
}),
|
|
143
|
-
}));
|
|
144
|
-
await fetchCursorUsageFromAdminApi({ cwd: testCwd, apiKey, fetchImpl });
|
|
145
|
-
const cost = await getTaskCostStats('cost-task', testCwd);
|
|
146
|
-
expect(cost.actual.isEstimated).toBe(false);
|
|
147
|
-
expect(cost.actual.costUsd).toBeGreaterThan(0);
|
|
148
|
-
expect(cost.actual.pricingBasis).toBe('observed-cursor-charged-cents');
|
|
149
|
-
});
|
|
150
|
-
it('targets the filtered usage events endpoint', () => {
|
|
151
|
-
expect(CURSOR_FILTERED_USAGE_URL).toBe('https://api.cursor.com/teams/filtered-usage-events');
|
|
152
|
-
});
|
|
153
|
-
it('errors when pagination exceeds the defensive page limit', async () => {
|
|
154
|
-
let page = 0;
|
|
155
|
-
const fetchImpl = vi.fn(async () => {
|
|
156
|
-
page += 1;
|
|
157
|
-
return {
|
|
158
|
-
ok: true,
|
|
159
|
-
status: 200,
|
|
160
|
-
json: async () => ({
|
|
161
|
-
usageEvents: [buildUsageEvent({ conversationId: `conv-page-${page}` })],
|
|
162
|
-
pagination: { hasNextPage: true, currentPage: page },
|
|
163
|
-
}),
|
|
164
|
-
};
|
|
165
|
-
});
|
|
166
|
-
await expect(fetchCursorUsageFromAdminApi({
|
|
167
|
-
cwd: testCwd,
|
|
168
|
-
apiKey,
|
|
169
|
-
fetchImpl,
|
|
170
|
-
maxPages: 2,
|
|
171
|
-
})).rejects.toThrow(/truncated after 2 pages/);
|
|
172
|
-
});
|
|
173
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { buildEngineeringKnowledgeGraph } from './engineering-knowledge-graph.js';
|
|
3
|
-
describe('engineering knowledge graph', () => {
|
|
4
|
-
it('connects code, agent outcomes and cross-repository lessons', () => {
|
|
5
|
-
const graph = buildEngineeringKnowledgeGraph({
|
|
6
|
-
repository: { id: 'repo-a', name: 'payments' },
|
|
7
|
-
dependencyGraph: { version: 1, generatedAt: '', nodes: ['src/a.ts', 'src/b.ts'], edges: [{ from: 'src/a.ts', to: 'src/b.ts' }] },
|
|
8
|
-
events: [],
|
|
9
|
-
runs: [{ id: 'run-1', agentId: 'agent-1', startedAt: '', endedAt: '', status: 'success', eventCount: 2, files: ['src/a.ts'], tools: ['check'] }],
|
|
10
|
-
lessons: [{ id: 'lesson-1', repositoryId: 'repo-b', repositoryName: 'platform', visibility: 'team', state: 'promoted', kind: 'interaction', subject: 'Use bounded retries', evidence: {}, confidence: 0.9, source: 'mcp', createdAt: 1, updatedAt: 1 }],
|
|
11
|
-
patterns: [{ id: 'pattern-1', name: 'withTransaction', type: 'function', file: 'src/a.ts', usageCount: 8 }],
|
|
12
|
-
memories: [{ id: 'memory-1', label: 'migration-owner', source: 'project' }],
|
|
13
|
-
});
|
|
14
|
-
expect(graph.counts.repository).toBe(2);
|
|
15
|
-
expect(graph.nodes.some(node => node.type === 'outcome' && node.label === 'success')).toBe(true);
|
|
16
|
-
expect(graph.edges.some(edge => edge.type === 'imports')).toBe(true);
|
|
17
|
-
expect(graph.edges.some(edge => edge.type === 'produced')).toBe(true);
|
|
18
|
-
expect(graph.nodes.some(node => node.type === 'pattern')).toBe(true);
|
|
19
|
-
expect(graph.nodes.some(node => node.type === 'memory')).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
it('represents prevented agent actions as policy evidence', () => {
|
|
22
|
-
const graph = buildEngineeringKnowledgeGraph({
|
|
23
|
-
repository: { id: 'repo-a', name: 'payments' },
|
|
24
|
-
events: [{ id: 'event-1', timestamp: '', type: 'tool_denied', agentId: 'agent-1', outcome: 'rejected', files: [], summary: 'write denied' }],
|
|
25
|
-
runs: [],
|
|
26
|
-
lessons: [],
|
|
27
|
-
});
|
|
28
|
-
expect(graph.nodes).toContainEqual(expect.objectContaining({ type: 'policy', state: 'active' }));
|
|
29
|
-
expect(graph.nodes).toContainEqual(expect.objectContaining({ type: 'outcome', state: 'verified' }));
|
|
30
|
-
expect(graph.edges.some(edge => edge.type === 'protects')).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
it('connects issued advice to its run and supporting knowledge', () => {
|
|
33
|
-
const event = {
|
|
34
|
-
id: 'event-1', requestId: 'run-1', timestamp: '2026-01-01T00:00:00Z', type: 'tool_response',
|
|
35
|
-
agentId: 'agent-1', outcome: 'success', files: ['src/a.ts'], summary: 'scope returned',
|
|
36
|
-
guidance: {
|
|
37
|
-
kind: 'context-scope',
|
|
38
|
-
recommendation: 'Read only src/a.ts.',
|
|
39
|
-
patternRefs: [{ id: 'pattern-1', label: 'withTransaction' }],
|
|
40
|
-
lessonRefs: [], memoryRefs: [], selectedFiles: ['src/a.ts'], excludedFileCount: 9, conflicts: 0,
|
|
41
|
-
},
|
|
42
|
-
telemetry: {
|
|
43
|
-
candidateTokens: 1200, returnedTokens: 200, candidateFiles: 10, returnedFiles: 1,
|
|
44
|
-
cacheStatus: 'exact-hit', classification: 'measured estimate',
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
const graph = buildEngineeringKnowledgeGraph({
|
|
48
|
-
repository: { id: 'repo-a', name: 'payments' },
|
|
49
|
-
dependencyGraph: { version: 1, generatedAt: '', nodes: ['src/a.ts'], edges: [] },
|
|
50
|
-
events: [event],
|
|
51
|
-
runs: [{ id: 'run-1', agentId: 'agent-1', startedAt: event.timestamp, endedAt: event.timestamp, status: 'success', eventCount: 1, files: ['src/a.ts'], tools: ['rigour_context_scope'] }],
|
|
52
|
-
lessons: [],
|
|
53
|
-
patterns: [{ id: 'pattern-1', name: 'withTransaction', type: 'function', file: 'src/a.ts' }],
|
|
54
|
-
});
|
|
55
|
-
expect(graph.nodes).toContainEqual(expect.objectContaining({ type: 'advice', evidence: expect.objectContaining({ avoidedTokens: 1000 }) }));
|
|
56
|
-
expect(graph.edges).toContainEqual(expect.objectContaining({ from: 'run:run-1', to: 'advice:run-1', type: 'guided' }));
|
|
57
|
-
expect(graph.edges).toContainEqual(expect.objectContaining({ from: 'pattern:pattern-1', to: 'advice:run-1', type: 'informed' }));
|
|
58
|
-
});
|
|
59
|
-
it('shows trusted gateway receipts, simulated denials and capability lineage', () => {
|
|
60
|
-
const graph = buildEngineeringKnowledgeGraph({
|
|
61
|
-
repository: { id: 'repo-a', name: 'payments' }, events: [], runs: [], lessons: [],
|
|
62
|
-
gateway: { mode: 'observe', agentId: 'agent-1', taskId: 'task-1', serverCount: 1, toolCount: 2, chainValid: true, receiptCount: 1 },
|
|
63
|
-
capabilities: [{ id: 'child', action: 'mcp.call', resource: 'mcp://github/get_issue', expiresAt: Date.now() + 1000, policyHash: 'hash', used: false, issuerId: 'owner', subjectId: 'agent-1', parentCapabilityId: 'parent' }],
|
|
64
|
-
receipts: [{
|
|
65
|
-
version: 1, id: 'receipt-1', repositoryId: 'repo-a', mode: 'observe', decision: 'allow', simulatedDecision: 'deny',
|
|
66
|
-
reason: 'would deny', policyHash: 'hash', outcome: 'forwarded', createdAt: '2026-01-01T00:00:00Z', digest: 'digest', signature: 'signature',
|
|
67
|
-
action: { version: 1, actorId: 'agent-1', taskId: 'task-1', channel: 'mcp', operation: 'github__get_issue', resource: 'mcp://github/get_issue', environment: 'local', sideEffect: 'read', reversibility: 'high', sensitivity: 'normal', blastRadius: 'single-resource' },
|
|
68
|
-
capabilityId: 'child',
|
|
69
|
-
}],
|
|
70
|
-
});
|
|
71
|
-
expect(graph.nodes).toContainEqual(expect.objectContaining({ type: 'gateway', evidence: expect.objectContaining({ chainValid: true }) }));
|
|
72
|
-
expect(graph.nodes).toContainEqual(expect.objectContaining({ type: 'action', state: 'would deny' }));
|
|
73
|
-
expect(graph.edges).toContainEqual(expect.objectContaining({ from: 'capability:child', to: 'capability:parent', type: 'delegated_from' }));
|
|
74
|
-
expect(graph.edges).toContainEqual(expect.objectContaining({ from: 'action:receipt-1', to: 'gateway:rigour-mcp', type: 'routes_through' }));
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { normalizeModelKey, getInputPricePerMillionUsd, computeWeightedInputPricePerMillion, estimateAvoidedContextCostUsd, estimateTokenCostUsd, FALLBACK_INPUT_PRICE_PER_MILLION_USD, } from './model-pricing.js';
|
|
3
|
-
describe('model-pricing', () => {
|
|
4
|
-
it('resolves Composer 2.5 Standard input pricing', () => {
|
|
5
|
-
expect(getInputPricePerMillionUsd('Composer 2.5 Standard')).toBe(0.5);
|
|
6
|
-
expect(normalizeModelKey('composer-2-5-standard')).toBe('composer-2.5-standard');
|
|
7
|
-
});
|
|
8
|
-
it('resolves Composer 2.5 Fast input pricing', () => {
|
|
9
|
-
expect(getInputPricePerMillionUsd('composer-2.5-fast')).toBe(3.0);
|
|
10
|
-
expect(normalizeModelKey('Composer 2.5 Fast')).toBe('composer-2.5-fast');
|
|
11
|
-
});
|
|
12
|
-
it('normalizes aliases with spaces and hyphens', () => {
|
|
13
|
-
expect(normalizeModelKey('Composer_2.5 FAST')).toBe('composer-2.5-fast');
|
|
14
|
-
expect(normalizeModelKey('composer 25 standard')).toBe('composer-2.5-standard');
|
|
15
|
-
});
|
|
16
|
-
it('uses conservative fallback for unknown models', () => {
|
|
17
|
-
expect(getInputPricePerMillionUsd('totally-unknown-model')).toBe(FALLBACK_INPUT_PRICE_PER_MILLION_USD);
|
|
18
|
-
});
|
|
19
|
-
it('computes mixed-model input-token-weighted pricing', () => {
|
|
20
|
-
const usages = [
|
|
21
|
-
{ source: 'cursor-admin-api', model: 'composer-2.5-standard', inputTokens: 1000, outputTokens: 0 },
|
|
22
|
-
{ source: 'cursor-admin-api', model: 'composer-2.5-fast', inputTokens: 3000, outputTokens: 0 },
|
|
23
|
-
];
|
|
24
|
-
const weighted = computeWeightedInputPricePerMillion(usages);
|
|
25
|
-
expect(weighted.inputPricePerMillionUsd).toBeCloseTo(2.375, 3);
|
|
26
|
-
expect(weighted.pricingBasis).toContain('mixed-model-weighted');
|
|
27
|
-
});
|
|
28
|
-
it('prefers observed cost when estimating actual spend is unnecessary', () => {
|
|
29
|
-
const observed = 4.25;
|
|
30
|
-
expect(observed).toBeGreaterThan(0);
|
|
31
|
-
});
|
|
32
|
-
it('estimates actual token cost when observed cost is absent', () => {
|
|
33
|
-
const estimated = estimateTokenCostUsd(1_000_000, 0, 'composer-2.5-standard');
|
|
34
|
-
expect(estimated).toBe(0.5);
|
|
35
|
-
});
|
|
36
|
-
it('estimates avoided context using input pricing only', () => {
|
|
37
|
-
const usages = [
|
|
38
|
-
{ source: 'cursor-admin-api', model: 'composer-2.5-standard', inputTokens: 100, outputTokens: 0 },
|
|
39
|
-
];
|
|
40
|
-
const { costUsd, pricing } = estimateAvoidedContextCostUsd(2_000_000, usages);
|
|
41
|
-
expect(pricing.inputPricePerMillionUsd).toBe(0.5);
|
|
42
|
-
expect(costUsd).toBe(1.0);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { observedSavings, recordAgentActivity, recordHookPayload, recordScopeOffer } from './observed-savings.js';
|
|
6
|
-
let cwd;
|
|
7
|
-
beforeEach(() => { cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'observed-savings-')); });
|
|
8
|
-
afterEach(() => { fs.rmSync(cwd, { recursive: true, force: true }); });
|
|
9
|
-
const T = 1_000_000;
|
|
10
|
-
describe('observedSavings', () => {
|
|
11
|
-
it('counts only scoped files the agent did not read afterwards, less the summary', () => {
|
|
12
|
-
recordScopeOffer(cwd, [{ path: 'src/a.ts', tokens: 3000 }, { path: 'src/b.ts', tokens: 2000 }], 400, T);
|
|
13
|
-
recordAgentActivity(cwd, 'Read', path.join(cwd, 'src/a.ts'), T + 60_000); // read anyway
|
|
14
|
-
recordAgentActivity(cwd, 'Edit', path.join(cwd, 'src/b.ts'), T + 90_000); // an edit is not a read
|
|
15
|
-
expect(observedSavings(cwd)).toEqual({ scopes: 1, trackedScopes: 1, offeredTokens: 5000, readBackTokens: 3000, avoidedTokens: 1600 });
|
|
16
|
-
});
|
|
17
|
-
it('claims nothing for a scope no hook observed, so a missing hook never reads as a saving', () => {
|
|
18
|
-
recordScopeOffer(cwd, [{ path: 'src/a.ts', tokens: 3000 }], 400, T);
|
|
19
|
-
expect(observedSavings(cwd)).toMatchObject({ scopes: 1, trackedScopes: 0, avoidedTokens: 0 });
|
|
20
|
-
});
|
|
21
|
-
it('ignores activity outside the observation window', () => {
|
|
22
|
-
recordScopeOffer(cwd, [{ path: 'src/a.ts', tokens: 3000 }], 400, T);
|
|
23
|
-
recordAgentActivity(cwd, 'Read', 'src/a.ts', T + 2 * 60 * 60 * 1000);
|
|
24
|
-
expect(observedSavings(cwd).trackedScopes).toBe(0);
|
|
25
|
-
});
|
|
26
|
-
it('never claims more than was offered when the summary outweighs unread files', () => {
|
|
27
|
-
recordScopeOffer(cwd, [{ path: 'src/a.ts', tokens: 100 }], 400, T);
|
|
28
|
-
recordAgentActivity(cwd, 'Grep', undefined, T + 1000);
|
|
29
|
-
expect(observedSavings(cwd).avoidedTokens).toBe(0);
|
|
30
|
-
});
|
|
31
|
-
it('reads a Claude Code PreToolUse payload: the tool, the file it reads, and its working directory', () => {
|
|
32
|
-
recordScopeOffer(cwd, [{ path: 'src/a.ts', tokens: 3000 }], 400, T);
|
|
33
|
-
recordHookPayload({ tool_name: 'Read', tool_input: { file_path: path.join(cwd, 'src/a.ts') }, cwd }, '/elsewhere', T + 1000);
|
|
34
|
-
recordHookPayload({ hello: 'not a tool call' }, cwd, T + 2000);
|
|
35
|
-
expect(observedSavings(cwd)).toMatchObject({ trackedScopes: 1, readBackTokens: 3000, avoidedTokens: 0 });
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as os from 'os';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { getScoreTrend } from './score-history.js';
|
|
6
|
-
describe('getScoreTrend', () => {
|
|
7
|
-
let testDir;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'score-history-test-'));
|
|
10
|
-
fs.mkdirSync(path.join(testDir, '.rigour'));
|
|
11
|
-
});
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
14
|
-
});
|
|
15
|
-
function writeScores(scores) {
|
|
16
|
-
const entries = scores.map((score, index) => ({
|
|
17
|
-
timestamp: new Date(index * 1000).toISOString(),
|
|
18
|
-
status: 'FAIL',
|
|
19
|
-
score,
|
|
20
|
-
failureCount: 1,
|
|
21
|
-
severity_breakdown: {},
|
|
22
|
-
provenance_breakdown: {},
|
|
23
|
-
}));
|
|
24
|
-
fs.writeFileSync(path.join(testDir, '.rigour', 'score-history.jsonl'), entries.map(entry => JSON.stringify(entry)).join('\n') + '\n');
|
|
25
|
-
}
|
|
26
|
-
it('labels the displayed rising scores as improving despite a higher older window', () => {
|
|
27
|
-
writeScores([90, 92, 94, 96, 98, 34, 29, 29, 52, 59]);
|
|
28
|
-
expect(getScoreTrend(testDir)).toMatchObject({
|
|
29
|
-
direction: 'improving',
|
|
30
|
-
delta: -53.4,
|
|
31
|
-
visibleDelta: 25,
|
|
32
|
-
recentScores: [34, 29, 29, 52, 59],
|
|
33
|
-
recentAvg: 41,
|
|
34
|
-
previousAvg: 94,
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
it('labels 41 → 41 → 41 → 41 → 52 as improving', () => {
|
|
38
|
-
writeScores([41, 41, 41, 41, 52]);
|
|
39
|
-
expect(getScoreTrend(testDir)).toMatchObject({
|
|
40
|
-
direction: 'improving',
|
|
41
|
-
visibleDelta: 11,
|
|
42
|
-
recentScores: [41, 41, 41, 41, 52],
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
it('labels a falling displayed series as degrading', () => {
|
|
46
|
-
writeScores([80, 75, 70, 64, 59]);
|
|
47
|
-
expect(getScoreTrend(testDir)).toMatchObject({
|
|
48
|
-
direction: 'degrading',
|
|
49
|
-
delta: 0,
|
|
50
|
-
visibleDelta: -21,
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
it('treats small visible changes as stable', () => {
|
|
54
|
-
writeScores([50, 49, 52, 51, 53]);
|
|
55
|
-
expect(getScoreTrend(testDir)).toMatchObject({
|
|
56
|
-
direction: 'stable',
|
|
57
|
-
delta: 0,
|
|
58
|
-
visibleDelta: 3,
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
});
|
package/dist/smoke.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/smoke.test.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { GateRunner } from '../src/gates/runner.js';
|
|
3
|
-
describe('GateRunner Smoke Test', () => {
|
|
4
|
-
it('should initialize with empty config', async () => {
|
|
5
|
-
const config = {
|
|
6
|
-
version: 1,
|
|
7
|
-
commands: {},
|
|
8
|
-
gates: {
|
|
9
|
-
max_file_lines: 500,
|
|
10
|
-
forbid_todos: true,
|
|
11
|
-
forbid_fixme: true,
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
const runner = new GateRunner(config);
|
|
15
|
-
expect(runner).toBeDefined();
|
|
16
|
-
});
|
|
17
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { cleanContextCache, deadCacheRows } from './cache-cleanup.js';
|
|
6
|
-
import { openDatabase } from './db.js';
|
|
7
|
-
let dir;
|
|
8
|
-
let dbPath;
|
|
9
|
-
async function seed(rows) {
|
|
10
|
-
const db = (await openDatabase(dbPath));
|
|
11
|
-
// One transaction: row-at-a-time commits are slow enough on Windows to time the test out.
|
|
12
|
-
await db.exec('BEGIN');
|
|
13
|
-
for (const [key, type] of rows) {
|
|
14
|
-
await db.run(`INSERT INTO context_cache (cache_key, cache_type, repo, branch, dependency_fingerprint, payload_json, payload_tokens, created_at)
|
|
15
|
-
VALUES (?, ?, 'r', 'main', 'f', ?, 1, 0)`, key, type, JSON.stringify({ padding: 'x'.repeat(2000) }));
|
|
16
|
-
}
|
|
17
|
-
await db.exec('COMMIT');
|
|
18
|
-
await db.close();
|
|
19
|
-
}
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cache-cleanup-')); // a temporary database: never ~/.rigour
|
|
22
|
-
dbPath = path.join(dir, 'rigour.db');
|
|
23
|
-
});
|
|
24
|
-
// Windows keeps a just-closed SQLite file locked for a moment: retry the removal instead of failing.
|
|
25
|
-
afterEach(() => fs.rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
26
|
-
// Real SQLite files on disk: slower on Windows runners than the 5 s default allows.
|
|
27
|
-
describe('cleanContextCache', { timeout: 30_000 }, () => {
|
|
28
|
-
it('removes the unread layers, keeps the readable rows and the indexes, and shrinks the file', async () => {
|
|
29
|
-
await seed([
|
|
30
|
-
...Array.from({ length: 200 }, (_, i) => [`static:${i}`, 'static']),
|
|
31
|
-
...Array.from({ length: 200 }, (_, i) => [`component:${i}`, 'component']),
|
|
32
|
-
['semantic:a', 'semantic'], ['checkpoint:b', 'checkpoint'],
|
|
33
|
-
]);
|
|
34
|
-
expect(await deadCacheRows(dbPath)).toMatchObject({ rows: 400 });
|
|
35
|
-
const report = await cleanContextCache(dbPath, () => Number.MAX_SAFE_INTEGER);
|
|
36
|
-
expect(report).toMatchObject({ removed: 400, kept: 2, vacuumed: true });
|
|
37
|
-
expect(report.sizeAfter).toBeLessThan(report.sizeBefore);
|
|
38
|
-
const db = (await openDatabase(dbPath));
|
|
39
|
-
expect((await db.all('SELECT cache_key FROM context_cache ORDER BY cache_key')).map(r => r.cache_key)).toEqual(['checkpoint:b', 'semantic:a']);
|
|
40
|
-
expect((await db.all("SELECT name FROM sqlite_master WHERE tbl_name = 'context_cache' AND type = 'index'")).map(r => r.name)).toContain('idx_context_cache_type');
|
|
41
|
-
await db.close();
|
|
42
|
-
expect(await deadCacheRows(dbPath)).toMatchObject({ rows: 0 });
|
|
43
|
-
});
|
|
44
|
-
it('still removes the rows but leaves the file size alone when the disk lacks room to shrink it', async () => {
|
|
45
|
-
await seed([['static:1', 'static'], ['semantic:a', 'semantic']]);
|
|
46
|
-
const report = await cleanContextCache(dbPath, () => 0);
|
|
47
|
-
expect(report).toMatchObject({ removed: 1, kept: 1, vacuumed: false });
|
|
48
|
-
expect(report.vacuumSkipped).toContain('free to shrink the file');
|
|
49
|
-
});
|
|
50
|
-
it('does nothing when there is nothing to remove', async () => {
|
|
51
|
-
await seed([['semantic:a', 'semantic']]);
|
|
52
|
-
expect(await cleanContextCache(dbPath, () => 0)).toMatchObject({ removed: 0, vacuumed: false, vacuumSkipped: 'nothing to remove' });
|
|
53
|
-
});
|
|
54
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import fs from 'fs-extra';
|
|
5
|
-
vi.mock('./db.js', () => ({
|
|
6
|
-
isSQLiteAvailable: () => false,
|
|
7
|
-
openDatabase: async () => null,
|
|
8
|
-
DB_PATH: '/tmp/rigour-test.db',
|
|
9
|
-
RIGOUR_DIR: '/tmp/.rigour',
|
|
10
|
-
}));
|
|
11
|
-
import { recordContextEvent, recordModelUsage, setContextCacheRecord, getContextCacheRecord, recordCheckpointMetric, getContextEvents, getModelUsages, getCheckpointMetrics, } from './context-telemetry.js';
|
|
12
|
-
describe('context-telemetry storage', () => {
|
|
13
|
-
const testCwd = path.join(os.tmpdir(), `rigour-ctx-telemetry-${Date.now()}`);
|
|
14
|
-
beforeEach(async () => {
|
|
15
|
-
await fs.ensureDir(path.join(testCwd, '.rigour'));
|
|
16
|
-
});
|
|
17
|
-
afterEach(async () => {
|
|
18
|
-
await fs.remove(testCwd);
|
|
19
|
-
});
|
|
20
|
-
it('records and retrieves context events via JSON fallback', async () => {
|
|
21
|
-
const id = await recordContextEvent({
|
|
22
|
-
taskId: 'task-1',
|
|
23
|
-
toolName: 'rigour_recall',
|
|
24
|
-
cacheStatus: 'semantic-hit',
|
|
25
|
-
candidateTokens: 4000,
|
|
26
|
-
returnedTokens: 800,
|
|
27
|
-
deduplicatedTokens: 1200,
|
|
28
|
-
}, testCwd);
|
|
29
|
-
expect(id).toMatch(/^evt-/);
|
|
30
|
-
const events = await getContextEvents('task-1', testCwd);
|
|
31
|
-
expect(events).toHaveLength(1);
|
|
32
|
-
expect(events[0].toolName).toBe('rigour_recall');
|
|
33
|
-
expect(events[0].candidateTokens).toBe(4000);
|
|
34
|
-
expect(events[0].returnedTokens).toBe(800);
|
|
35
|
-
});
|
|
36
|
-
it('records and retrieves model usage via JSON fallback', async () => {
|
|
37
|
-
await recordModelUsage({
|
|
38
|
-
taskId: 'cursor-imported',
|
|
39
|
-
provider: 'cursor',
|
|
40
|
-
model: 'claude-3-5-sonnet',
|
|
41
|
-
inputTokens: 12000,
|
|
42
|
-
outputTokens: 1500,
|
|
43
|
-
observedCostUsd: 1.25,
|
|
44
|
-
source: 'cursor-dashboard-csv',
|
|
45
|
-
}, testCwd);
|
|
46
|
-
const usages = await getModelUsages(undefined, testCwd);
|
|
47
|
-
expect(usages).toHaveLength(1);
|
|
48
|
-
expect(usages[0].inputTokens).toBe(12000);
|
|
49
|
-
expect(usages[0].source).toBe('cursor-dashboard-csv');
|
|
50
|
-
});
|
|
51
|
-
it('stores cache records and increments hit count', async () => {
|
|
52
|
-
await setContextCacheRecord({
|
|
53
|
-
cacheKey: 'static:repo:main:file.ts:abc123:v1',
|
|
54
|
-
cacheType: 'static',
|
|
55
|
-
repo: 'repo',
|
|
56
|
-
branch: 'main',
|
|
57
|
-
dependencyFingerprint: 'dep-1',
|
|
58
|
-
payloadJson: JSON.stringify({ exports: ['foo'] }),
|
|
59
|
-
payloadTokens: 42,
|
|
60
|
-
}, testCwd);
|
|
61
|
-
const first = await getContextCacheRecord('static:repo:main:file.ts:abc123:v1', testCwd);
|
|
62
|
-
const second = await getContextCacheRecord('static:repo:main:file.ts:abc123:v1', testCwd);
|
|
63
|
-
expect(first?.payloadTokens).toBe(42);
|
|
64
|
-
expect(second?.hitCount).toBe(2);
|
|
65
|
-
});
|
|
66
|
-
it('records and retrieves checkpoint metrics via JSON fallback', async () => {
|
|
67
|
-
await recordCheckpointMetric({
|
|
68
|
-
checkpointId: 'checkpoint:CTP-1:agent-a:implementation',
|
|
69
|
-
taskId: 'CTP-1',
|
|
70
|
-
agentId: 'agent-a',
|
|
71
|
-
rawStateTokens: 50000,
|
|
72
|
-
checkpointTokens: 400,
|
|
73
|
-
replayTokensAvoided: 49600,
|
|
74
|
-
}, testCwd);
|
|
75
|
-
const metrics = await getCheckpointMetrics('CTP-1', testCwd);
|
|
76
|
-
expect(metrics).toHaveLength(1);
|
|
77
|
-
expect(metrics[0].rawStateTokens).toBe(50000);
|
|
78
|
-
expect(metrics[0].replayTokensAvoided).toBe(49600);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/storage/db.test.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { isSQLiteAvailable, openDatabase } from './db.js';
|
|
6
|
-
let dir;
|
|
7
|
-
let db;
|
|
8
|
-
beforeEach(async () => {
|
|
9
|
-
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rigour-db-'));
|
|
10
|
-
db = (await openDatabase(path.join(dir, 'rigour.db')));
|
|
11
|
-
});
|
|
12
|
-
afterEach(async () => {
|
|
13
|
-
await db?.close();
|
|
14
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
15
|
-
});
|
|
16
|
-
describe('the database (node:sqlite)', () => {
|
|
17
|
-
it('is built in: no package to install', () => {
|
|
18
|
-
expect(isSQLiteAvailable()).toBe(true);
|
|
19
|
-
expect(db).not.toBeNull();
|
|
20
|
-
});
|
|
21
|
-
it('stores what callers pass the way sqlite3 did: undefined as NULL, booleans as 1 or 0, a Date as epoch ms', async () => {
|
|
22
|
-
await db.exec('CREATE TABLE t (a, b, c, d)');
|
|
23
|
-
const when = new Date('2026-01-02T03:04:05Z');
|
|
24
|
-
const written = await db.run('INSERT INTO t VALUES (?, ?, ?, ?)', undefined, true, false, when);
|
|
25
|
-
expect(written).toEqual({ changes: 1, lastID: 1 });
|
|
26
|
-
const row = await db.get('SELECT * FROM t');
|
|
27
|
-
expect(row).toEqual({ a: null, b: 1, c: 0, d: when.getTime() });
|
|
28
|
-
expect(Object.getPrototypeOf(row)).toBe(Object.prototype); // a plain object, as callers spread and compare
|
|
29
|
-
expect(await db.get('SELECT * FROM t WHERE a = 1')).toBeUndefined();
|
|
30
|
-
});
|
|
31
|
-
it('rolls a failed transaction back whole', async () => {
|
|
32
|
-
await db.exec('CREATE TABLE t (a UNIQUE)');
|
|
33
|
-
await expect(db.transaction(async (tx) => {
|
|
34
|
-
await tx.run('INSERT INTO t VALUES (?)', 1);
|
|
35
|
-
await tx.run('INSERT INTO t VALUES (?)', 1); // violates UNIQUE
|
|
36
|
-
})).rejects.toThrow(/UNIQUE/);
|
|
37
|
-
expect(await db.all('SELECT * FROM t')).toEqual([]);
|
|
38
|
-
});
|
|
39
|
-
it('opens an existing database again with its schema and data, in WAL mode', async () => {
|
|
40
|
-
await db.run("INSERT INTO meta (key, value) VALUES ('probe', 'kept')");
|
|
41
|
-
await db.close();
|
|
42
|
-
db = (await openDatabase(path.join(dir, 'rigour.db')));
|
|
43
|
-
expect(await db.get("SELECT value FROM meta WHERE key = 'probe'")).toEqual({ value: 'kept' });
|
|
44
|
-
expect(await db.get('PRAGMA journal_mode')).toEqual({ journal_mode: 'wal' });
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|