scai 0.1.177 → 0.1.178
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/agents/MainAgent.js
CHANGED
|
@@ -61,6 +61,13 @@ export class MainAgent {
|
|
|
61
61
|
*/
|
|
62
62
|
constructor(context, ui) {
|
|
63
63
|
this.runCount = 0;
|
|
64
|
+
this.verboseAgentLogs = process.env.SCAI_VERBOSE_AGENT_LOGS === "1";
|
|
65
|
+
this.hasPrintedHeader = false;
|
|
66
|
+
this.activity = {
|
|
67
|
+
searches: [],
|
|
68
|
+
lists: [],
|
|
69
|
+
reads: [],
|
|
70
|
+
};
|
|
64
71
|
this.context = context;
|
|
65
72
|
this.query = context.initContext?.userQuery ?? "";
|
|
66
73
|
this.ui = ui;
|
|
@@ -69,16 +76,23 @@ export class MainAgent {
|
|
|
69
76
|
async run() {
|
|
70
77
|
try {
|
|
71
78
|
this.runCount = 0;
|
|
79
|
+
this.printHeader("Boot & Scope");
|
|
72
80
|
await this.runBoot();
|
|
73
81
|
await this.runScope();
|
|
82
|
+
this.printHeader("Search");
|
|
74
83
|
await this.runSearch();
|
|
84
|
+
this.printHeader("Verify");
|
|
75
85
|
await this.runVerify();
|
|
86
|
+
this.printHeader("Research");
|
|
76
87
|
await this.runResearch();
|
|
77
88
|
const canProceedToExecution = this.isResearchGateSatisfied();
|
|
78
89
|
if (canProceedToExecution) {
|
|
90
|
+
this.printHeader("Plan");
|
|
79
91
|
await this.runPlan();
|
|
92
|
+
this.printHeader("Execute");
|
|
80
93
|
await this.runWorkLoop();
|
|
81
94
|
}
|
|
95
|
+
this.printHeader("Finalize");
|
|
82
96
|
await this.runFinalize();
|
|
83
97
|
}
|
|
84
98
|
finally {
|
|
@@ -101,7 +115,7 @@ export class MainAgent {
|
|
|
101
115
|
taskSteps: [],
|
|
102
116
|
});
|
|
103
117
|
this.context.task.id = this.taskId;
|
|
104
|
-
this.
|
|
118
|
+
this.userOutput(`Boot complete (taskId=${this.taskId}).`);
|
|
105
119
|
}
|
|
106
120
|
/* ───────────── scope ───────────── */
|
|
107
121
|
async runScope() {
|
|
@@ -109,10 +123,11 @@ export class MainAgent {
|
|
|
109
123
|
await resolveExecutionModeStep.run(this.context);
|
|
110
124
|
await routingDecisionStep.run(this.context);
|
|
111
125
|
const routing = this.context.analysis?.routingDecision;
|
|
126
|
+
const scope = this.context.analysis?.scopeType ?? "unknown";
|
|
112
127
|
if (routing) {
|
|
113
|
-
this.
|
|
128
|
+
this.userOutput(`Routing: ${routing.decision} (scope=${scope}, search=${routing.allowSearch}, research=${routing.allowResearch}, transform=${routing.allowTransform}, scopeLocked=${routing.scopeLocked}).`);
|
|
114
129
|
}
|
|
115
|
-
this.
|
|
130
|
+
this.userOutput("Scope classification complete.");
|
|
116
131
|
}
|
|
117
132
|
/* ───────────── search ───────────── */
|
|
118
133
|
/**
|
|
@@ -121,6 +136,7 @@ export class MainAgent {
|
|
|
121
136
|
*/
|
|
122
137
|
async runSearch() {
|
|
123
138
|
const { rawUserQuery, retrievalQuery } = this.resolveInitialRetrievalQueries();
|
|
139
|
+
this.userOutput("I’ll start by identifying likely files from semantic search before analyzing content.");
|
|
124
140
|
const t = this.startTimer();
|
|
125
141
|
try {
|
|
126
142
|
const results = await this.fetchInitialRetrievalResults(retrievalQuery);
|
|
@@ -129,7 +145,12 @@ export class MainAgent {
|
|
|
129
145
|
const mergedRelatedCount = this.mergeSeededInitialContext(rawUserQuery, seededContext);
|
|
130
146
|
const prefilter = this.applyDeterministicPreGroundingPrefilter(retrievalQuery);
|
|
131
147
|
const repoDefaults = this.injectWellKnownRepoFiles(prefilter.after);
|
|
148
|
+
this.recordSearch(retrievalQuery, path.basename(process.cwd()));
|
|
149
|
+
this.recordListEntries(Array.from(new Set(results.map(result => path.dirname(this.formatPathForLog(result.path)))))
|
|
150
|
+
.slice(0, 2)
|
|
151
|
+
.map(dir => dir === "." ? process.cwd() : dir));
|
|
132
152
|
this.logLine("ANALYSIS", "initialRetrieval", t(), `${results.length} result(s), ${mergedRelatedCount} candidate file(s), prefilter ${prefilter.before} -> ${prefilter.after}, defaults +${repoDefaults.added} (${repoDefaults.reason})`);
|
|
153
|
+
this.flushActivity();
|
|
133
154
|
}
|
|
134
155
|
catch (err) {
|
|
135
156
|
this.logLine("ANALYSIS", "initialRetrieval", t(), `failed: ${String(err)}`);
|
|
@@ -151,24 +172,52 @@ export class MainAgent {
|
|
|
151
172
|
this.pruneMissingVerifyPaths();
|
|
152
173
|
this.logLine("ANALYSIS", "groundingWave", undefined, `wave ${groundingWave}/${maxGroundingWaves}`);
|
|
153
174
|
const beforeFocus = this.captureVerifyFocusSnapshot();
|
|
175
|
+
const beforeWorking = new Set((this.context.workingFiles ?? []).map(file => file.path));
|
|
176
|
+
const beforeSelected = new Set(this.context.analysis?.focus?.selectedFiles ?? []);
|
|
177
|
+
const beforeCandidate = new Set(this.context.analysis?.focus?.candidateFiles ?? []);
|
|
178
|
+
const structuralBefore = new Set(Object.entries(this.context.analysis?.fileAnalysis ?? {})
|
|
179
|
+
.filter(([_, analysis]) => !!analysis?.structural)
|
|
180
|
+
.map(([filePath]) => filePath));
|
|
154
181
|
// ---------------- EVIDENCE PIPELINE ----------------
|
|
155
182
|
// -------- STRUCTURAL PRELOAD --------
|
|
156
183
|
const t0 = this.startTimer();
|
|
157
184
|
await structuralPreloadStep.run({ query: this.query, context: this.context });
|
|
185
|
+
const structuralAfter = Object.entries(this.context.analysis?.fileAnalysis ?? {})
|
|
186
|
+
.filter(([_, analysis]) => !!analysis?.structural)
|
|
187
|
+
.map(([filePath]) => filePath);
|
|
188
|
+
const newStructural = structuralAfter.filter(filePath => !structuralBefore.has(filePath));
|
|
158
189
|
this.logLine("ANALYSIS", "structuralPreload", t0());
|
|
159
190
|
const t1 = this.startTimer();
|
|
160
191
|
await evidenceVerifierStep.run({ query: this.query, context: this.context });
|
|
192
|
+
const verifyEntries = Object.entries(this.context.analysis?.verify?.byFile ?? {});
|
|
161
193
|
this.logLine("ANALYSIS", "collectAnalysisEvidence", t1());
|
|
162
194
|
const t2 = this.startTimer();
|
|
163
195
|
await fileCheckStep(this.context);
|
|
196
|
+
const selectedAfterCheck = this.context.analysis?.focus?.selectedFiles ?? [];
|
|
197
|
+
const candidateAfterCheck = this.context.analysis?.focus?.candidateFiles ?? [];
|
|
198
|
+
const addedSelected = selectedAfterCheck.filter(filePath => !beforeSelected.has(filePath));
|
|
199
|
+
const addedCandidate = candidateAfterCheck.filter(filePath => !beforeCandidate.has(filePath));
|
|
164
200
|
this.logLine("ANALYSIS", "fileCheckStep", t2());
|
|
165
201
|
const t3 = this.startTimer();
|
|
166
202
|
await selectRelevantSourcesStep.run({ query: this.query, context: this.context });
|
|
203
|
+
const workingAfter = this.context.workingFiles ?? [];
|
|
204
|
+
const newWorking = workingAfter
|
|
205
|
+
.map(file => file.path)
|
|
206
|
+
.filter(filePath => !beforeWorking.has(filePath));
|
|
167
207
|
this.logLine("ANALYSIS", "selectRelevantSources", t3());
|
|
168
208
|
// ---------------- READINESS GATE ----------------
|
|
169
209
|
const t4 = this.startTimer();
|
|
170
210
|
await readinessGateStep.run(this.context);
|
|
171
|
-
this.
|
|
211
|
+
const readiness = this.context.analysis?.readiness;
|
|
212
|
+
const selectedCount = this.context.analysis?.focus?.selectedFiles?.length ?? 0;
|
|
213
|
+
this.logLine("ANALYSIS", "readinessGate", t4(), `decision=${readiness?.decision ?? "unknown"}, confidence=${(readiness?.confidence ?? 0).toFixed(2)}, selected=${selectedCount}`);
|
|
214
|
+
this.userOutput("I mapped the verify wave. Next I’ll analyze selected files and refine scope if needed.");
|
|
215
|
+
this.recordReadEntries(newStructural);
|
|
216
|
+
this.recordReadEntries(verifyEntries.map(([filePath]) => filePath));
|
|
217
|
+
this.recordReadEntries(addedSelected);
|
|
218
|
+
this.recordListEntries(addedCandidate);
|
|
219
|
+
this.recordReadEntries(newWorking);
|
|
220
|
+
this.flushActivity();
|
|
172
221
|
ready = this.context.analysis?.readiness?.decision === "ready";
|
|
173
222
|
if (ready) {
|
|
174
223
|
break;
|
|
@@ -389,6 +438,8 @@ export class MainAgent {
|
|
|
389
438
|
seeded,
|
|
390
439
|
});
|
|
391
440
|
this.logLine("PLAN", "taskStepSeed", undefined, `${seededCount} execution step(s) planned`);
|
|
441
|
+
this.recordReadEntries(seeded.map(step => step.filePath));
|
|
442
|
+
this.flushActivity();
|
|
392
443
|
}
|
|
393
444
|
/**
|
|
394
445
|
* Sets minimum verify confidence before a file can be plan-seeded from verify-only signal.
|
|
@@ -973,7 +1024,8 @@ export class MainAgent {
|
|
|
973
1024
|
return { query: input.query, content: input.content, data: { skipped: true } };
|
|
974
1025
|
}
|
|
975
1026
|
try {
|
|
976
|
-
this.
|
|
1027
|
+
const stepTarget = step.targetFile ? this.formatPathForLog(step.targetFile) : undefined;
|
|
1028
|
+
this.ui.update(stepTarget ? `Running ${step.action} on ${stepTarget}` : `Running ${step.action}`);
|
|
977
1029
|
const output = await mod.run({ query: step.description ?? input.query, content: input.data ?? input.content, context: this.context });
|
|
978
1030
|
const errors = Array.isArray(output.data?.errors)
|
|
979
1031
|
? output.data.errors.filter((e) => typeof e === "string" && e.trim().length > 0)
|
|
@@ -1182,22 +1234,31 @@ export class MainAgent {
|
|
|
1182
1234
|
let removedSelected = 0;
|
|
1183
1235
|
let removedCandidate = 0;
|
|
1184
1236
|
if (init?.relatedFiles?.length) {
|
|
1185
|
-
const
|
|
1186
|
-
init.relatedFiles =
|
|
1187
|
-
|
|
1237
|
+
const existing = init.relatedFiles;
|
|
1238
|
+
init.relatedFiles = existing.filter(filePath => {
|
|
1239
|
+
const keep = existsOrResearch(filePath);
|
|
1240
|
+
return keep;
|
|
1241
|
+
});
|
|
1242
|
+
removedRelated = existing.length - init.relatedFiles.length;
|
|
1188
1243
|
if (removedRelated > 0 && init.relatedFileScores) {
|
|
1189
1244
|
init.relatedFileScores = Object.fromEntries(Object.entries(init.relatedFileScores).filter(([filePath]) => init.relatedFiles?.includes(filePath)));
|
|
1190
1245
|
}
|
|
1191
1246
|
}
|
|
1192
1247
|
if (focus?.selectedFiles?.length) {
|
|
1193
|
-
const
|
|
1194
|
-
focus.selectedFiles =
|
|
1195
|
-
|
|
1248
|
+
const existing = focus.selectedFiles;
|
|
1249
|
+
focus.selectedFiles = existing.filter(filePath => {
|
|
1250
|
+
const keep = existsOrResearch(filePath);
|
|
1251
|
+
return keep;
|
|
1252
|
+
});
|
|
1253
|
+
removedSelected = existing.length - focus.selectedFiles.length;
|
|
1196
1254
|
}
|
|
1197
1255
|
if (focus?.candidateFiles?.length) {
|
|
1198
|
-
const
|
|
1199
|
-
focus.candidateFiles =
|
|
1200
|
-
|
|
1256
|
+
const existing = focus.candidateFiles;
|
|
1257
|
+
focus.candidateFiles = existing.filter(filePath => {
|
|
1258
|
+
const keep = existsOrResearch(filePath);
|
|
1259
|
+
return keep;
|
|
1260
|
+
});
|
|
1261
|
+
removedCandidate = existing.length - focus.candidateFiles.length;
|
|
1201
1262
|
}
|
|
1202
1263
|
if (removedRelated + removedSelected + removedCandidate > 0) {
|
|
1203
1264
|
this.logLine("ANALYSIS", "verifyPruneMissing", undefined, `removed related=${removedRelated}, selected=${removedSelected}, candidate=${removedCandidate}`);
|
|
@@ -1326,7 +1387,7 @@ export class MainAgent {
|
|
|
1326
1387
|
this.logLine("TASK", "Route updated from step reasoning", undefined, "expand-scope requested; re-enabled search expansion");
|
|
1327
1388
|
}
|
|
1328
1389
|
startTaskStep(taskStep, stepCount) {
|
|
1329
|
-
this.ensureWorkingFilesLoaded([taskStep.filePath], "Current task step");
|
|
1390
|
+
const hydration = this.ensureWorkingFilesLoaded([taskStep.filePath], "Current task step");
|
|
1330
1391
|
const db = getDbForRepo();
|
|
1331
1392
|
this.ensureTaskIdentityForPersistence(db);
|
|
1332
1393
|
this.context.task.currentStep = taskStep;
|
|
@@ -1335,7 +1396,10 @@ export class MainAgent {
|
|
|
1335
1396
|
taskStep.status = "pending";
|
|
1336
1397
|
persistTaskStepInsert(taskStep, db);
|
|
1337
1398
|
const displayPath = this.formatTaskStepDisplayPath(taskStep.filePath);
|
|
1338
|
-
this.
|
|
1399
|
+
this.printHeader(`Step ${stepCount}`);
|
|
1400
|
+
this.userOutput(`Now processing ${this.formatPathForLog(displayPath)}.`);
|
|
1401
|
+
this.recordReadEntries(hydration.loadedFromDisk);
|
|
1402
|
+
this.flushActivity();
|
|
1339
1403
|
taskStep.startTime = Date.now();
|
|
1340
1404
|
persistTaskStepStart(taskStep, db);
|
|
1341
1405
|
}
|
|
@@ -1386,6 +1450,31 @@ export class MainAgent {
|
|
|
1386
1450
|
this.ui.pause(() => { process.stdout.write('\r\x1b[K'); fn(); });
|
|
1387
1451
|
}
|
|
1388
1452
|
logLine(phase, step, ms, desc, options) {
|
|
1453
|
+
if (!this.verboseAgentLogs) {
|
|
1454
|
+
// Keep only key transitions when verbose logging is disabled.
|
|
1455
|
+
if (phase === "STEP-DONE" && desc) {
|
|
1456
|
+
this.userOutput(`Completed ${this.formatPathForLog(desc)}.`);
|
|
1457
|
+
}
|
|
1458
|
+
else if (phase === "TASK" && step.includes("Execution paused")) {
|
|
1459
|
+
this.userOutput("Execution paused; waiting for user clarification.");
|
|
1460
|
+
}
|
|
1461
|
+
else if (phase === "TASK" && step.includes("All selected files processed")) {
|
|
1462
|
+
this.userOutput("All selected files processed.");
|
|
1463
|
+
}
|
|
1464
|
+
else if (phase === "TASK" && step.includes("No eligible taskStep found")) {
|
|
1465
|
+
this.userOutput("No eligible task step found.");
|
|
1466
|
+
}
|
|
1467
|
+
else if (phase === "TASK" && step.includes("Finalize complete")) {
|
|
1468
|
+
this.userOutput("Finalize complete.");
|
|
1469
|
+
}
|
|
1470
|
+
else if (phase === "RESEARCH" && step === "taskStepSeed" && desc?.includes("skipped")) {
|
|
1471
|
+
this.userOutput("Research skipped for this route.");
|
|
1472
|
+
}
|
|
1473
|
+
else if (phase === "RESEARCH" && step === "taskStepSeed" && desc) {
|
|
1474
|
+
this.userOutput(desc);
|
|
1475
|
+
}
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1389
1478
|
this.withSpinnerPaused(() => {
|
|
1390
1479
|
const suffix = desc ? ` — ${desc}` : "";
|
|
1391
1480
|
const timing = typeof ms === "number" ? ` (${ms}ms)` : "";
|
|
@@ -1400,12 +1489,21 @@ export class MainAgent {
|
|
|
1400
1489
|
// Print a blank line **before** NEW STEP only
|
|
1401
1490
|
if (phase === "NEW STEP")
|
|
1402
1491
|
console.log();
|
|
1492
|
+
process.stdout.write("\r\x1b[K");
|
|
1403
1493
|
console.log(line);
|
|
1404
1494
|
});
|
|
1405
1495
|
}
|
|
1406
1496
|
userOutput(message) {
|
|
1407
1497
|
this.withSpinnerPaused(() => {
|
|
1408
|
-
console.log(
|
|
1498
|
+
console.log(message);
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
printHeader(title) {
|
|
1502
|
+
this.withSpinnerPaused(() => {
|
|
1503
|
+
if (this.hasPrintedHeader)
|
|
1504
|
+
console.log("");
|
|
1505
|
+
console.log(chalk.bold(`== ${title} ==`));
|
|
1506
|
+
this.hasPrintedHeader = true;
|
|
1409
1507
|
});
|
|
1410
1508
|
}
|
|
1411
1509
|
/**
|
|
@@ -1416,18 +1514,25 @@ export class MainAgent {
|
|
|
1416
1514
|
var _a;
|
|
1417
1515
|
(_a = this.context).workingFiles || (_a.workingFiles = []);
|
|
1418
1516
|
const indexByPath = new Map(this.context.workingFiles.map(file => [file.path, file]));
|
|
1517
|
+
const loadedFromDisk = [];
|
|
1518
|
+
const alreadyLoaded = [];
|
|
1519
|
+
const skippedMissing = [];
|
|
1419
1520
|
for (const filePath of paths) {
|
|
1420
1521
|
if (!filePath || filePath.startsWith("__research__/"))
|
|
1421
1522
|
continue;
|
|
1422
|
-
if (!fs.existsSync(filePath))
|
|
1523
|
+
if (!fs.existsSync(filePath)) {
|
|
1524
|
+
skippedMissing.push(filePath);
|
|
1423
1525
|
continue;
|
|
1526
|
+
}
|
|
1424
1527
|
const existing = indexByPath.get(filePath);
|
|
1425
1528
|
if (existing && typeof existing.code === "string" && existing.code.length > 0) {
|
|
1529
|
+
alreadyLoaded.push(filePath);
|
|
1426
1530
|
continue;
|
|
1427
1531
|
}
|
|
1428
1532
|
let code;
|
|
1429
1533
|
try {
|
|
1430
1534
|
code = fs.readFileSync(filePath, "utf-8");
|
|
1535
|
+
loadedFromDisk.push(filePath);
|
|
1431
1536
|
}
|
|
1432
1537
|
catch {
|
|
1433
1538
|
code = undefined;
|
|
@@ -1442,6 +1547,77 @@ export class MainAgent {
|
|
|
1442
1547
|
indexByPath.set(filePath, capsule);
|
|
1443
1548
|
}
|
|
1444
1549
|
}
|
|
1550
|
+
return { loadedFromDisk, alreadyLoaded, skippedMissing };
|
|
1551
|
+
}
|
|
1552
|
+
formatPathForLog(filePath) {
|
|
1553
|
+
if (!filePath)
|
|
1554
|
+
return "(none)";
|
|
1555
|
+
if (filePath.startsWith("__research__/")) {
|
|
1556
|
+
return filePath.replace("__research__/", "research/");
|
|
1557
|
+
}
|
|
1558
|
+
const normalized = path.normalize(filePath);
|
|
1559
|
+
const relative = path.relative(process.cwd(), normalized);
|
|
1560
|
+
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
1561
|
+
return relative || path.basename(normalized);
|
|
1562
|
+
}
|
|
1563
|
+
return normalized;
|
|
1564
|
+
}
|
|
1565
|
+
describeFileList(paths, maxItems = 4) {
|
|
1566
|
+
const unique = Array.from(new Set(paths.filter(Boolean)));
|
|
1567
|
+
if (unique.length === 0)
|
|
1568
|
+
return "none";
|
|
1569
|
+
const shown = unique.slice(0, maxItems).map(filePath => this.formatPathForLog(filePath));
|
|
1570
|
+
const extra = unique.length - shown.length;
|
|
1571
|
+
if (extra > 0)
|
|
1572
|
+
shown.push(`+${extra} more`);
|
|
1573
|
+
return shown.join(", ");
|
|
1574
|
+
}
|
|
1575
|
+
recordSearch(query, scope) {
|
|
1576
|
+
const normalizedScope = scope || "repo";
|
|
1577
|
+
this.activity.searches.push(`Searched for ${query} in ${normalizedScope}`);
|
|
1578
|
+
}
|
|
1579
|
+
recordListEntries(entries) {
|
|
1580
|
+
for (const entry of entries) {
|
|
1581
|
+
const normalized = entry.replace(/\\/g, "/");
|
|
1582
|
+
const asDir = normalized.endsWith("/") || path.extname(normalized) === ""
|
|
1583
|
+
? normalized
|
|
1584
|
+
: path.dirname(normalized);
|
|
1585
|
+
const display = asDir === "." ? "repo root" : this.formatPathForLog(asDir);
|
|
1586
|
+
this.activity.lists.push(`Listed files in ${display}`);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
recordReadEntries(paths) {
|
|
1590
|
+
for (const filePath of paths) {
|
|
1591
|
+
if (!filePath || filePath.startsWith("__research__/"))
|
|
1592
|
+
continue;
|
|
1593
|
+
this.activity.reads.push(`Read ${this.formatPathForLog(filePath)}`);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
flushActivity() {
|
|
1597
|
+
const searches = Array.from(new Set(this.activity.searches));
|
|
1598
|
+
const lists = Array.from(new Set(this.activity.lists));
|
|
1599
|
+
const reads = Array.from(new Set(this.activity.reads));
|
|
1600
|
+
if (searches.length === 0 && lists.length === 0 && reads.length === 0) {
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
const parts = [];
|
|
1604
|
+
if (searches.length > 0) {
|
|
1605
|
+
parts.push(`${searches.length} search${searches.length === 1 ? "" : "es"}`);
|
|
1606
|
+
}
|
|
1607
|
+
if (lists.length > 0) {
|
|
1608
|
+
parts.push(`${lists.length} director${lists.length === 1 ? "y" : "ies"}`);
|
|
1609
|
+
}
|
|
1610
|
+
if (reads.length > 0) {
|
|
1611
|
+
parts.push(`${reads.length} file${reads.length === 1 ? "" : "s"}`);
|
|
1612
|
+
}
|
|
1613
|
+
this.userOutput(`Explored ${parts.join(", ")}`);
|
|
1614
|
+
for (const line of searches.slice(0, 2))
|
|
1615
|
+
this.userOutput(line);
|
|
1616
|
+
for (const line of lists.slice(0, 3))
|
|
1617
|
+
this.userOutput(line);
|
|
1618
|
+
for (const line of reads.slice(0, 6))
|
|
1619
|
+
this.userOutput(line);
|
|
1620
|
+
this.activity = { searches: [], lists: [], reads: [] };
|
|
1445
1621
|
}
|
|
1446
1622
|
/**
|
|
1447
1623
|
* Ensures the current task id exists in the active DB before step persistence.
|
|
@@ -103,10 +103,14 @@ Return a valid JSON object in this format:
|
|
|
103
103
|
});
|
|
104
104
|
context.analysis.planSuggestion = { plan: { steps: finalSteps } };
|
|
105
105
|
logInputOutput("analysisPlanGen", "output", finalSteps);
|
|
106
|
-
|
|
106
|
+
if (process.env.SCAI_VERBOSE_AGENT_LOGS === "1") {
|
|
107
|
+
console.log("🧠 [analysisPlanGenStep] EXIT (success)");
|
|
108
|
+
}
|
|
107
109
|
}
|
|
108
110
|
catch (err) {
|
|
109
|
-
|
|
111
|
+
if (process.env.SCAI_VERBOSE_AGENT_LOGS === "1") {
|
|
112
|
+
console.warn("⚠️ [analysisPlanGenStep] FAILED to generate analysis plan:", err);
|
|
113
|
+
}
|
|
110
114
|
context.analysis.planSuggestion = { plan: { steps: [] } };
|
|
111
115
|
logInputOutput("analysisPlanGen", "output", { steps: [] });
|
|
112
116
|
}
|
|
@@ -176,8 +176,8 @@ function pickRandom(items) {
|
|
|
176
176
|
return items[Math.floor(Math.random() * items.length)];
|
|
177
177
|
}
|
|
178
178
|
function logHighContrastQuery(prefix, query) {
|
|
179
|
-
//
|
|
180
|
-
console.log(chalk.bold
|
|
179
|
+
// Neutral bold so it is readable on both light and dark terminal themes.
|
|
180
|
+
console.log(chalk.bold(`${prefix} ${query}`));
|
|
181
181
|
}
|
|
182
182
|
function countOccurrences(haystack, needle) {
|
|
183
183
|
if (!needle)
|