scai 0.1.173 → 0.1.174

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.
@@ -198,6 +198,7 @@ export class MainAgent {
198
198
  if (!this.isWorkLoopReady())
199
199
  return;
200
200
  this.ensureTaskForWorkLoop();
201
+ this.recalibrateRoutingAfterVerify();
201
202
  // Research gate is evaluated after runResearch() in run().
202
203
  }
203
204
  /* ───────────── research ───────────── */
@@ -207,8 +208,10 @@ export class MainAgent {
207
208
  */
208
209
  async runResearch() {
209
210
  var _a, _b;
210
- if (!this.canExecuteRoute("research"))
211
+ if (!this.canExecuteRoute("research")) {
212
+ this.logLine("RESEARCH", "taskStepSeed", undefined, "skipped (route disallows research)");
211
213
  return;
214
+ }
212
215
  if (!this.context.task)
213
216
  return;
214
217
  (_a = this.context.task).taskSteps || (_a.taskSteps = []);
@@ -305,9 +308,20 @@ export class MainAgent {
305
308
  .map(step => step.filePath));
306
309
  const selectedFiles = this.context.analysis.focus?.selectedFiles ?? [];
307
310
  const touchedFromResearch = this.context.analysis.researchArtifacts?.touchedFiles ?? [];
308
- const verifyRelevantFiles = Object.entries(this.context.analysis.verify?.byFile ?? {})
309
- .filter(([_, verify]) => verify?.isRelevant)
311
+ const route = this.context.analysis.routingDecision;
312
+ const useFocusedSelectedPlanOnly = this.context.analysis.readiness?.decision === "ready" &&
313
+ (route?.decision === "has-info") &&
314
+ (route?.scopeLocked ?? false) &&
315
+ (route?.allowSearch === false) &&
316
+ selectedFiles.length > 0;
317
+ const verifyMinConfidence = this.getVerifyConfidenceThresholdForPlan();
318
+ const verifyEntries = Object.entries(this.context.analysis.verify?.byFile ?? {});
319
+ const verifyRelevantFiles = verifyEntries
320
+ .filter(([_, verify]) => verify?.isRelevant &&
321
+ (verify.fileConfidence ?? 0) >= verifyMinConfidence)
310
322
  .map(([filePath]) => filePath);
323
+ const verifySkippedLowConfidenceCount = verifyEntries.filter(([_, verify]) => !!verify?.isRelevant &&
324
+ (verify.fileConfidence ?? 0) < verifyMinConfidence).length;
311
325
  const rankPath = (filePath) => {
312
326
  const inSelected = selectedFiles.includes(filePath);
313
327
  const inResearchTouched = touchedFromResearch.includes(filePath);
@@ -322,11 +336,14 @@ export class MainAgent {
322
336
  return 3;
323
337
  return 4;
324
338
  };
325
- const plannedPaths = Array.from(new Set([
326
- ...selectedFiles,
327
- ...touchedFromResearch,
328
- ...verifyRelevantFiles,
329
- ]))
339
+ const plannedPathsSource = useFocusedSelectedPlanOnly
340
+ ? Array.from(new Set(selectedFiles))
341
+ : Array.from(new Set([
342
+ ...selectedFiles,
343
+ ...touchedFromResearch,
344
+ ...verifyRelevantFiles,
345
+ ]));
346
+ const plannedPaths = plannedPathsSource
330
347
  .filter(filePath => !!filePath && !filePath.startsWith("__research__/") && fs.existsSync(filePath))
331
348
  .sort((a, b) => rankPath(a) - rankPath(b))
332
349
  .slice(0, 16);
@@ -363,10 +380,62 @@ export class MainAgent {
363
380
  selectedFileCount: selectedFiles.length,
364
381
  researchTouchedCount: touchedFromResearch.length,
365
382
  verifyRelevantCount: verifyRelevantFiles.length,
383
+ focusedSelectedOnly: useFocusedSelectedPlanOnly,
384
+ verifyMinConfidence,
385
+ verifySkippedLowConfidenceCount,
366
386
  seeded,
367
387
  });
368
388
  this.logLine("PLAN", "taskStepSeed", undefined, `${seededCount} execution step(s) planned`);
369
389
  }
390
+ /**
391
+ * Sets minimum verify confidence before a file can be plan-seeded from verify-only signal.
392
+ * Example: single-file lanes require higher confidence than repo-wide lanes.
393
+ */
394
+ getVerifyConfidenceThresholdForPlan() {
395
+ const scope = this.context.analysis?.scopeType ?? "repo-wide";
396
+ if (scope === "single-file")
397
+ return 0.45;
398
+ if (scope === "multi-file")
399
+ return 0.35;
400
+ return 0.3;
401
+ }
402
+ /**
403
+ * Re-routes after verify when evidence converges on selected files with high confidence.
404
+ * Example: selected files strongly verified => disable expansion/research and lock focused execution.
405
+ */
406
+ recalibrateRoutingAfterVerify() {
407
+ var _a;
408
+ (_a = this.context).analysis || (_a.analysis = {});
409
+ const routing = this.context.analysis.routingDecision;
410
+ if (!routing)
411
+ return;
412
+ const selectedFiles = this.context.analysis.focus?.selectedFiles ?? [];
413
+ if (selectedFiles.length === 0)
414
+ return;
415
+ const readinessConfidence = this.context.analysis.readiness?.confidence ?? 0;
416
+ const intentConfidence = this.context.analysis.intent?.confidence ?? 0;
417
+ const minFileConfidence = 0.28;
418
+ const strongSelected = selectedFiles.filter(filePath => {
419
+ const verify = this.context.analysis?.verify?.byFile?.[filePath];
420
+ return verify?.isRelevant === true && (verify.fileConfidence ?? 0) >= minFileConfidence;
421
+ });
422
+ const convergedSingle = selectedFiles.length === 1 &&
423
+ strongSelected.length === 1 &&
424
+ readinessConfidence >= 0.9 &&
425
+ intentConfidence >= 0.8;
426
+ const convergedMulti = selectedFiles.length >= 2 &&
427
+ strongSelected.length >= 2 &&
428
+ readinessConfidence >= 0.9 &&
429
+ intentConfidence >= 0.75;
430
+ if (!convergedSingle && !convergedMulti)
431
+ return;
432
+ routing.decision = "has-info";
433
+ routing.allowSearch = false;
434
+ routing.allowResearch = false;
435
+ routing.scopeLocked = true;
436
+ routing.rationale = `${routing.rationale}; postVerify=focused-selection(${strongSelected.length})`;
437
+ this.logLine("TASK", "Routing recalibrated", undefined, `focused=${selectedFiles.length} selected, strong=${strongSelected.length}`);
438
+ }
370
439
  /* ───────────── work loop ───────────── */
371
440
  async runWorkLoop() {
372
441
  if (this.context.task.status !== "active")
@@ -39,9 +39,12 @@ export const reasonNextTaskStep = {
39
39
  ...(context.analysis.focus?.selectedFiles ?? []),
40
40
  ...(context.workingFiles?.map(f => f.path).filter(Boolean) ?? []),
41
41
  ]));
42
- const intendedFiles = plannedExecutionFiles.length > 0
43
- ? plannedExecutionFiles
44
- : fallbackIntendedFiles;
42
+ // Use union (not fallback switch) so docs-only or planning-skipped lanes
43
+ // still process all selected files instead of only ad-hoc seeded taskSteps.
44
+ const intendedFiles = Array.from(new Set([
45
+ ...plannedExecutionFiles,
46
+ ...fallbackIntendedFiles,
47
+ ]));
45
48
  // ---------------------------
46
49
  // 2️⃣ Transformed and analyzed files
47
50
  // ---------------------------
@@ -48,6 +48,19 @@ export const resolveExecutionModeStep = {
48
48
  docsOnly: true
49
49
  };
50
50
  break;
51
+ // ───── Generic requests: promote to transform when edit intent is explicit ─────
52
+ case "request":
53
+ if (hasExplicitWriteIntent(normalizedQuery)) {
54
+ mode = "transform";
55
+ rationale = "Request contains explicit edit intent.";
56
+ constraints = {
57
+ allowAnalysis: true,
58
+ allowPlanning: true,
59
+ allowFileWrites: true,
60
+ docsOnly: false
61
+ };
62
+ }
63
+ break;
51
64
  // ───── Everything else is analysis ─────
52
65
  case "debugging":
53
66
  case "planning":
@@ -88,5 +101,5 @@ function isQuestionLikeQuery(query) {
88
101
  }
89
102
  function hasExplicitWriteIntent(query) {
90
103
  const q = query.toLowerCase();
91
- return /\b(add|update|edit|modify|refactor|rewrite|implement|fix|create|remove|delete|replace|rename|write|patch|change)\b/.test(q);
104
+ return /\b(add|update|edit|modify|refactor|rewrite|implement|fix|create|remove|delete|replace|rename|write|patch|change|increment|bump|set)\b/.test(q);
92
105
  }
@@ -23,6 +23,9 @@ export const routingDecisionStep = {
23
23
  const hasExplicitTargets = (context.analysis.intent?.targetFiles?.length ?? 0) > 0;
24
24
  const ambiguousIntent = intentConfidence < 0.45;
25
25
  const isRefactorLike = intentCategory === "refactorTask" || intentCategory === "codingTask";
26
+ const isAnalysisLike = intentCategory === "question" ||
27
+ intentCategory === "analysis" ||
28
+ intentCategory === "explanation";
26
29
  let decision = "has-info";
27
30
  let allowSearch = true;
28
31
  let scopeLocked = false;
@@ -50,8 +53,8 @@ export const routingDecisionStep = {
50
53
  ? "single-file-focused"
51
54
  : "bounded-analysis";
52
55
  const allowResearch = scope !== "none" &&
53
- (isRefactorLike ||
54
- (isResearchScope && (decision === "needs-info" || complexitySignals >= 2)));
56
+ ((isAnalysisLike && isResearchScope && (decision === "needs-info" || complexitySignals >= 2)) ||
57
+ (isRefactorLike && complexitySignals >= 1));
55
58
  const confidence = Math.max(0, Math.min(1, 0.55 + intentConfidence * 0.35 - (ambiguousIntent ? 0.2 : 0)));
56
59
  const routingDecision = {
57
60
  decision,
@@ -3,7 +3,7 @@ import chalk from "chalk";
3
3
  import { RUN_LOG_PATH } from "../constants.js";
4
4
  // ---------------- Test Queries ----------------
5
5
  export const testQueries = [
6
- "Add concise comments to semanticAnalysisModule.ts and MainAgent.ts describing phase boundaries.",
6
+ "Add concise comments to semanticAnalysisModule.ts and finalAnswerModule.ts describing phase boundaries.",
7
7
  "Refactor MainAgent runVerify flow to reduce nesting while preserving behavior.",
8
8
  "Explain how resolveExecutionModeStep, routingDecisionStep, and canExecuteRoute interact.",
9
9
  "Add stronger validation and safer fallback behavior in contextReviewStep.ts.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.173",
3
+ "version": "0.1.174",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"