@feng0531/dsh-code-review 0.18.4 → 0.19.0

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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # 变更记录
2
2
 
3
+ ## 0.19.0
4
+
5
+ - 注册入口启用有界业务分组,预检计一次调用并披露动态综合执行前未知/初始上界,报告展示假设状态。
6
+ - 双文件服务预检5实际6调用链通过;全量241项及宿主离线冒烟通过,真实质量未测,其他源码对齐继续。
7
+
3
8
  ## 0.18.4
4
9
 
5
10
  - 可选业务元数据分组闭集每主恰一次/4文件限,共享首主期限/总调用预算,失败保留原组并记限制。
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 当前状态
6
6
 
7
- 当前代码版本为 0.18.4,包含有界覆盖预检、显式项目规则及候选证据与反证复核;发布状态以 npm 和 GitHub Release 为准。已通过真实临时 Git/SVN 仓库测试,以及隔离进程中的真实 DSH ToolRuntime 注册验证。当前开发会话的实际预览已返回0.6.0的plan/rules字段,空变更预览与默认关闭外发已验证;已进行经宿主审批的六次小规模真实试跑,累计12次模型调用;四组合成正负例与历史修复对照符合预期,历史引入方向检出候选但引用复核未完成(partial)。不是独立盲测或整体质量证明。详情见 [试跑结果](evaluation/PILOT-RESULTS.md)。
7
+ 当前代码版本为 0.19.0,包含有界覆盖预检、显式项目规则及候选证据与反证复核;发布状态以 npm 和 GitHub Release 为准。已通过真实临时 Git/SVN 仓库测试,以及隔离进程中的真实 DSH ToolRuntime 注册验证。当前开发会话的实际预览已返回0.6.0的plan/rules字段,空变更预览与默认关闭外发已验证;已进行经宿主审批的六次小规模真实试跑,累计12次模型调用;四组合成正负例与历史修复对照符合预期,历史引入方向检出候选但引用复核未完成(partial)。不是独立盲测或整体质量证明。详情见 [试跑结果](evaluation/PILOT-RESULTS.md)。
8
8
 
9
9
  ## 工作区下多个子仓库(0.11.0)
10
10
 
package/index.mjs CHANGED
@@ -5,7 +5,7 @@ export const inject = ['tools', 'llm'];
5
5
  export function apply(ctx, config = {}) {
6
6
  // This entry runs only when the host invokes the registered tool.
7
7
  // Host tool policy owns permission; do not request a second approval here.
8
- const service = createReviewService(ctx.llm, { allowModelSending: true, enableRiskPlanning: true, authorize: async () => true });
8
+ const service = createReviewService(ctx.llm, { allowModelSending: true, enableRiskPlanning: true, enableBusinessGrouping: true, authorize: async () => true });
9
9
  const output = {
10
10
  schema: { type: 'object', additionalProperties: false, properties: { json: { type: 'string' }, markdown: { type: 'string' } }, required: ['json', 'markdown'] },
11
11
  render: (_args, value) => [{ type: 'text', text: value.markdown || value.json }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feng0531/dsh-code-review",
3
- "version": "0.18.4",
3
+ "version": "0.19.0",
4
4
  "private": false,
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
@@ -19,5 +19,6 @@ export function buildCoveragePlan(input, options) {
19
19
  return { ...item, ...prepared.metadata, status: prepared.budget.fits ? 'ready' : 'input-blocked', minimumCalls: prepared.budget.fits ? ((prepared.batches?.length ?? 1) * (options.enableRiskPlanning === true ? 2 : 1) + (prepared.synthesis?.budget.fits ? 1 : 0)) : 0 };
20
20
  });
21
21
  const interactions = options.scope && options.grouping ? options.grouping.groups.filter(g => g.fileIds.length > 1).map(group => { const prepared = prepareInteractionInput(input, group, payload => initialInputBudget({ instructions: options.instructions, input: payload }, options.scope, options.maxInputBytes)); return { groupId: group.id, fileIds: prepared.fileIds, status: prepared.status, reason: prepared.reason, minimumCalls: prepared.status === 'ready' ? 1 : 0 }; }) : [];
22
- return { interactions, snapshotId: input.id, maxInputBytes: options.maxInputBytes, items, minimumCalls: items.reduce((sum,item) => sum + item.minimumCalls, 0) + interactions.reduce((sum,item) => sum + item.minimumCalls, 0), initialRequestsOnly: true };
22
+ const businessGrouping = options.enableBusinessGrouping === true && input.files.filter(f => f.eligibility === 'reviewable').length > 1 ? { minimumCalls: 1, dynamicInteractionCalls: 'unknown-until-execution', maximumAdditionalInitialCalls: Math.floor(input.files.filter(f => f.eligibility === 'reviewable').length / 2), notice: '模型分组假设仅调度,额外综合动态且不保证预算或覆盖。' } : null;
23
+ return { ...(businessGrouping ? { businessGrouping } : {}), interactions, snapshotId: input.id, maxInputBytes: options.maxInputBytes, items, minimumCalls: (businessGrouping?.minimumCalls ?? 0) + items.reduce((sum,item) => sum + item.minimumCalls, 0) + interactions.reduce((sum,item) => sum + item.minimumCalls, 0), initialRequestsOnly: true };
23
24
  }
package/src/review.mjs CHANGED
@@ -231,6 +231,7 @@ const safe = text => Array.from(String(text ?? '')).map(c => {
231
231
  }).join('');
232
232
  export function markdownReport(report) {
233
233
  const output = ['# Code Review 报告', '', '状态:' + report.status, '快照:' + report.snapshotId, '', '## 覆盖情况'];
234
+ if (report.businessGrouping) output.push('- 业务分组:' + safe(report.businessGrouping.status) + (report.businessGrouping.reason ? ' — ' + safe(report.businessGrouping.reason) : '') + ';仅调度假设,不证明依赖或覆盖');
234
235
  for (const file of report.files) for (const plan of file.riskPlans ?? []) output.push('- 风险计划:' + safe(file.path) + ' · ' + safe(plan.status) + ' · ' + plan.risks.length + '项待证假设;不表示缺陷成立或覆盖完成');
235
236
  for (const file of report.files) output.push('- ' + safe(file.path) + ':' + file.status + (file.reason ? ' — ' + safe(file.reason) : ''));
236
237
  if (report.grouping) {
package/src/service.mjs CHANGED
@@ -14,7 +14,7 @@ function owner(exec) {
14
14
  if (!agent || typeof cwd !== 'string' || !cwd || !agent.options?.provider || !agent.options?.model) throw new Error('Active Agent with cwd and model is required');
15
15
  return { agent, cwd, route: { provider: agent.options.provider, model: agent.options.model, ...(agent.options.reasoningEffort ? { reasoningEffort: agent.options.reasoningEffort } : {}) } };
16
16
  }
17
- export function createReviewService(llm, { capture = captureSnapshot, now = Date.now, ttlMs = 300000, maxPreviews = 8, allowModelSending = false, enableRiskPlanning = false, authorize = async () => false } = {}) {
17
+ export function createReviewService(llm, { capture = captureSnapshot, now = Date.now, ttlMs = 300000, maxPreviews = 8, allowModelSending = false, enableRiskPlanning = false, enableBusinessGrouping = false, authorize = async () => false } = {}) {
18
18
  for (const value of [ttlMs, maxPreviews]) if (!Number.isSafeInteger(value) || value < 1) throw new Error('Invalid preview limit');
19
19
  const lifetime = new AbortController();
20
20
  const bind = exec => ({ ...exec, signal: exec.signal ? AbortSignal.any([exec.signal, lifetime.signal]) : lifetime.signal });
@@ -36,12 +36,12 @@ export function createReviewService(llm, { capture = captureSnapshot, now = Date
36
36
  const snapshot = await capture(target, { ...options, signal: exec.signal });
37
37
  exec.signal?.throwIfAborted();
38
38
  if (owner(exec).cwd !== current.cwd) throw new Error('Agent directory changed; preview again');
39
- const plan = buildCoveragePlan(snapshot, { enableRiskPlanning, instructions: REVIEW_INSTRUCTIONS, scope: createRetrievalScope(snapshot, { signal: exec.signal }), grouping: buildReviewGroups(snapshot.files, inferFileRelations(snapshot.files)), maxInputBytes: 96 * 1024, signal: exec.signal });
39
+ const plan = buildCoveragePlan(snapshot, { enableRiskPlanning, enableBusinessGrouping, instructions: REVIEW_INSTRUCTIONS, scope: createRetrievalScope(snapshot, { signal: exec.signal }), grouping: buildReviewGroups(snapshot.files, inferFileRelations(snapshot.files)), maxInputBytes: 96 * 1024, signal: exec.signal });
40
40
  prune();
41
41
  while (previews.size >= maxPreviews) previews.delete(previews.keys().next().value);
42
42
  const previewId = randomUUID();
43
43
  previews.set(previewId, { ...current, target, options, snapshotId: snapshot.id, expires: now() + ttlMs });
44
- return { previewId, plan, snapshotId: snapshot.id, repositoryRoot: snapshot.root, vcs: snapshot.vcs, model: current.route, modelSendingEnabled: allowModelSending, files: snapshot.files.map(f => ({ path: f.path, eligibility: f.eligibility, reason: f.reason })), contextPaths: snapshot.context.map(c => c.path), rules: (snapshot.rules ?? []).map(({ path, hash }) => ({ path, hash })), notice: (enableRiskPlanning ? '执行先制定风险假设计划,再审查并复核;计划不是证据,所有阶段共享原预算。' : '') + '执行会向上述模型提供方发送可审查文件两侧内容、显式上下文及所选项目规则全文。模型可在该快照范围内多轮只读检索,不读取范围外文件;候选生成后会进行证据与反证复核,两阶段各最多3轮检索,共享每文件120秒和总模型调用100次预算;复核不能证明缺陷成立。plan仅预检初始输入,ready不代表已审查;minimumCalls不含后续检索/复核,实际预算可能不足。请先向用户展示范围,获得确认后执行。' };
44
+ return { previewId, plan, snapshotId: snapshot.id, repositoryRoot: snapshot.root, vcs: snapshot.vcs, model: current.route, modelSendingEnabled: allowModelSending, files: snapshot.files.map(f => ({ path: f.path, eligibility: f.eligibility, reason: f.reason })), contextPaths: snapshot.context.map(c => c.path), rules: (snapshot.rules ?? []).map(({ path, hash }) => ({ path, hash })), notice: (enableRiskPlanning ? '执行先制定风险假设计划,再审查并复核;计划不是证据,所有阶段共享原预算。' : '') + '执行会向上述模型提供方发送可审查文件两侧内容、显式上下文及所选项目规则全文。模型可在该快照范围内多轮只读检索,不读取范围外文件;候选生成后会进行证据与反证复核,两阶段各最多3轮检索,共享每文件120秒和总模型调用100次预算;复核不能证明缺陷成立。plan仅预检初始输入,ready不代表已审查;minimumCalls不含后续检索/复核/动态业务综合;多文件可先进行业务分组,分组失败保留原组并记录限制,动态综合数执行前未知,实际预算可能不足。请先向用户展示范围,获得确认后执行。' };
45
45
  } finally { busy = false; }
46
46
  },
47
47
  async execute(args, exec) {
@@ -64,7 +64,7 @@ export function createReviewService(llm, { capture = captureSnapshot, now = Date
64
64
  if (await authorize({ snapshot, route: preview.route, exec }) !== true) throw new Error('Model sending approval denied or unavailable');
65
65
  exec.signal?.throwIfAborted();
66
66
  if (owner(exec).cwd !== current.cwd || JSON.stringify(owner(exec).route) !== JSON.stringify(preview.route)) throw new Error('Agent changed during approval');
67
- const report = await reviewSnapshot(snapshot, createDshModel(llm, preview.route), { signal: exec.signal, enableRetrieval: true, enableGrouping: true, enableVerification: true, enableRiskPlanning });
67
+ const report = await reviewSnapshot(snapshot, createDshModel(llm, preview.route), { signal: exec.signal, enableRetrieval: true, enableGrouping: true, enableVerification: true, enableRiskPlanning, enableBusinessGrouping });
68
68
  report.model = preview.route;
69
69
  const latest = await resolveWorkspaceRepository(current.cwd, preview.options.repositoryPath).then(target => target === preview.target ? capture(target, { ...preview.options, signal: exec.signal }) : null).catch(() => null);
70
70
  report.outdated = !latest || latest.id !== snapshot.id || owner(exec).cwd !== current.cwd;