@feng0531/dsh-code-review 0.87.0 → 0.87.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/CORE-SOURCE-INVENTORY.md +1 -1
- package/DEVELOPMENT.md +3 -3
- package/README.md +1 -1
- package/RELEASE-NOTES.md +11 -15
- package/package.json +1 -1
- package/src/background-review.mjs +6 -1
- package/src/cpp-migration-bindings.mjs +30 -0
- package/src/group-primary.mjs +6 -2
- package/src/line-diff.mjs +57 -1
- package/src/migration-contracts.mjs +87 -3
- package/src/migration-regression.mjs +71 -7
- package/src/preview-validity.mjs +9 -0
- package/src/primary-input.mjs +2 -14
- package/src/project-review-service.mjs +6 -3
- package/src/prompts.mjs +1 -1
- package/src/retrieval-loop.mjs +26 -5
- package/src/review-change-map.mjs +17 -0
- package/src/review-resume.mjs +95 -9
- package/src/review-stage-state.mjs +7 -2
- package/src/review.mjs +84 -25
- package/src/service.mjs +8 -3
- package/src/stage-continuation.mjs +12 -2
- package/src/stage-generation-cache.mjs +22 -0
package/src/service.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { previewValidity } from './preview-validity.mjs';
|
|
1
2
|
import { configureWireBudget } from './model-wire-budget.mjs';
|
|
2
3
|
import {REVIEW_RUNTIME} from './runtime-identity.mjs';
|
|
3
4
|
import { buildResumeState, validateResumeState, resumePreview } from './review-resume.mjs';
|
|
@@ -101,12 +102,15 @@ function createSessionReviewService(llm, { capture = captureSnapshot, now = Date
|
|
|
101
102
|
prune();
|
|
102
103
|
while (previews.size >= maxPreviews) previews.delete(previews.keys().next().value);
|
|
103
104
|
const previewId = randomUUID();
|
|
104
|
-
|
|
105
|
+
const expires = now() + ttlMs;
|
|
106
|
+
previews.set(previewId, { ...current, target, options, rounds, snapshotId: snapshot.id, resumeState, sourceJobId, expires });
|
|
105
107
|
return {
|
|
106
108
|
...(options.businessRequirement !== undefined
|
|
107
109
|
? { businessRequirement: { text: options.businessRequirement, hash: hash(options.businessRequirement), bytes: Buffer.byteLength(options.businessRequirement) } }
|
|
108
110
|
: {}),
|
|
109
111
|
previewId,
|
|
112
|
+
expiresAt: new Date(expires).toISOString(),
|
|
113
|
+
validForMs: ttlMs,
|
|
110
114
|
...(resumeState ? { resume: resumePreview(resumeState, sourceJobId) } : {}),
|
|
111
115
|
plan,
|
|
112
116
|
snapshotId: snapshot.id,
|
|
@@ -151,9 +155,10 @@ function createSessionReviewService(llm, { capture = captureSnapshot, now = Date
|
|
|
151
155
|
if (busy) throw new Error('Review service busy');
|
|
152
156
|
if (!allowModelSending) throw new Error('Model sending is disabled by plugin configuration');
|
|
153
157
|
if (!args || Object.keys(args).some(k => !['previewId', 'confirmed'].includes(k)) || args.confirmed !== true || typeof args.previewId !== 'string') throw new Error('Confirmed preview is required');
|
|
154
|
-
prune();
|
|
155
158
|
const current = owner(exec), preview = previews.get(args.previewId);
|
|
156
|
-
|
|
159
|
+
try { previewValidity(preview, current, now()); }
|
|
160
|
+
catch (error) { if (error.code === 'PREVIEW_EXPIRED') previews.delete(args.previewId); throw error; }
|
|
161
|
+
prune();
|
|
157
162
|
busy = true;
|
|
158
163
|
previews.delete(args.previewId);
|
|
159
164
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
const KINDS = new Set(['verification', 'synthesis', 'interaction']);
|
|
2
|
+
const KINDS = new Set(['primary', 'verification', 'synthesis', 'interaction']);
|
|
3
3
|
const REASONS = new Set(['READER_CALL_BUDGET_EXCEEDED', 'READER_OUTPUT_BUDGET_EXCEEDED', 'STAGE_INTERRUPTED', 'STAGE_CANCELLED', 'STAGE_TIMEOUT', 'STAGE_NO_PROGRESS', 'STAGE_ATTEMPTS_EXHAUSTED', 'STAGE_FAILED']);
|
|
4
4
|
const fail = code => Object.assign(new Error(code), { code });
|
|
5
5
|
const canonical = value => Array.isArray(value) ? value.map(canonical) : value && typeof value === 'object' ? Object.fromEntries(Object.keys(value).sort().filter(key => value[key] !== undefined).map(key => [key, canonical(value[key])])) : value;
|
|
@@ -40,7 +40,17 @@ export function createStageContinuation(snapshot, options, { completedPrimary =
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
const get = id => { const state=states.get(id); if (!state) throw fail('UNKNOWN_STAGE_UNIT'); return state; };
|
|
43
|
-
|
|
43
|
+
// The completedPrimary baseline is immutable. A pending primary may run without
|
|
44
|
+
// belonging to it; secondary work must name a completed primary unit explicitly
|
|
45
|
+
// when relying on primary work performed by this continuation. Reserved
|
|
46
|
+
// primary:<fileId> dependencies still refer only to the completed baseline.
|
|
47
|
+
const dependencyCompleted = dep => dep.startsWith('primary:') ? primary.includes(dep.slice(8)) : get(dep).status === 'completed';
|
|
48
|
+
const primaryCompleted = (state, fileId) => primary.includes(fileId) || state.dependencies.some(dep => {
|
|
49
|
+
if (dep.startsWith('primary:')) return false;
|
|
50
|
+
const prerequisite = get(dep);
|
|
51
|
+
return prerequisite.kind === 'primary' && prerequisite.status === 'completed' && prerequisite.fileIds.includes(fileId);
|
|
52
|
+
});
|
|
53
|
+
const dependenciesReady = state => (state.kind === 'primary' || state.fileIds.every(id => primaryCompleted(state, id))) && state.dependencies.every(dependencyCompleted);
|
|
44
54
|
for (const state of states.values()) {
|
|
45
55
|
if (state.status === 'completed' && (!dependenciesReady(state) || state.stopped || state.pendingReason !== null)) throw fail('INVALID_STAGE_CHECKPOINT');
|
|
46
56
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { prepareStageGenerations } from './review-stage-state.mjs';
|
|
2
|
+
|
|
3
|
+
// Only host-validated generations enter this cache. Failed admission leaves the
|
|
4
|
+
// last good set unchanged; imported checkpoints still fail closed in the validator.
|
|
5
|
+
export function createStageGenerationCache(snapshot, records = [], { maxBytes = 8 * 1024 * 1024 } = {}) {
|
|
6
|
+
let saved = prepareStageGenerations(snapshot, records, { maxBytes });
|
|
7
|
+
return {
|
|
8
|
+
records() { return structuredClone(saved); },
|
|
9
|
+
admit(record) {
|
|
10
|
+
let normalized;
|
|
11
|
+
try { [normalized] = prepareStageGenerations(snapshot, [record]); }
|
|
12
|
+
catch { return { accepted: false, code: 'STAGE_CACHE_RECORD_REJECTED' }; }
|
|
13
|
+
const next = saved.filter(row => row.key !== normalized.key);
|
|
14
|
+
next.push(normalized);
|
|
15
|
+
let checked;
|
|
16
|
+
try { checked = prepareStageGenerations(snapshot, next, { maxBytes }); }
|
|
17
|
+
catch { return { accepted: false, code: 'STAGE_CACHE_CAPACITY_EXCEEDED' }; }
|
|
18
|
+
saved = checked;
|
|
19
|
+
return { accepted: true, record: structuredClone(normalized) };
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|