@hamp10/agentforge 0.2.39 → 0.2.41
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/package.json +1 -1
- package/scripts/check-task-semantics.js +87 -0
- package/src/worker.js +145 -14
package/package.json
CHANGED
|
@@ -296,6 +296,93 @@ try {
|
|
|
296
296
|
'new untracked scoped page files should count as touched UI targets'
|
|
297
297
|
);
|
|
298
298
|
rmSync(path.join(fixture.repo, untrackedGammaRel), { force: true });
|
|
299
|
+
const untrackedShallowGammaHtml = [
|
|
300
|
+
'<!doctype html>',
|
|
301
|
+
'<html>',
|
|
302
|
+
'<head>',
|
|
303
|
+
'<style>',
|
|
304
|
+
...Array.from({ length: 36 }, (_, i) =>
|
|
305
|
+
`.hero-gloss-${i} { color: #fff; background: #${String(120000 + i).slice(0, 6)}; padding: ${i + 4}px; margin: ${i}px; box-shadow: 0 0 ${i + 1}px rgba(0,0,0,.12); }`
|
|
306
|
+
),
|
|
307
|
+
'</style>',
|
|
308
|
+
'</head>',
|
|
309
|
+
'<body>',
|
|
310
|
+
'<main>',
|
|
311
|
+
'<section class="property-hero hero-gloss-1">',
|
|
312
|
+
'<h1 class="property-title">Gamma.ai</h1>',
|
|
313
|
+
'<p class="property-subtitle">Short generated hero copy.</p>',
|
|
314
|
+
'</section>',
|
|
315
|
+
'</main>',
|
|
316
|
+
'</body>',
|
|
317
|
+
'</html>',
|
|
318
|
+
].join('\n');
|
|
319
|
+
writeFileSync(path.join(fixture.repo, untrackedGammaRel), untrackedShallowGammaHtml);
|
|
320
|
+
const generatedQualityMessage = 'Work on the Example.com listing page for Gamma.ai. Make it visually polished.';
|
|
321
|
+
assert.match(
|
|
322
|
+
worker._buildShallowUiSurfaceNudge(
|
|
323
|
+
[{ root: fixture.repo, head: fixture.head, initialDirtyPaths: [] }],
|
|
324
|
+
generatedQualityMessage
|
|
325
|
+
),
|
|
326
|
+
/gamma\.html/i,
|
|
327
|
+
'untracked generated scoped page files should participate in shallow UI quality checks'
|
|
328
|
+
);
|
|
329
|
+
const generatedReset = worker._restoreGeneratedScopedUiTargets(
|
|
330
|
+
[{ root: fixture.repo, head: fixture.head, initialDirtyPaths: [] }],
|
|
331
|
+
generatedQualityMessage
|
|
332
|
+
);
|
|
333
|
+
assert.equal(
|
|
334
|
+
existsSync(path.join(fixture.repo, untrackedGammaRel)),
|
|
335
|
+
false,
|
|
336
|
+
'UI quality retries should remove rejected generated scoped pages before the next attempt'
|
|
337
|
+
);
|
|
338
|
+
assert.match(
|
|
339
|
+
worker._formatGeneratedScopedUiResetNudge(generatedReset),
|
|
340
|
+
/removed 1 rejected generated scoped file/i,
|
|
341
|
+
'retry feedback should tell the agent that generated scoped pages were removed as failed output'
|
|
342
|
+
);
|
|
343
|
+
const generatedDeltaRel = 'public_html/domains/delta.html';
|
|
344
|
+
const generatedEpsilonRel = 'public_html/domains/epsilon.html';
|
|
345
|
+
writeFileSync(
|
|
346
|
+
path.join(fixture.repo, generatedDeltaRel),
|
|
347
|
+
'<!doctype html><html><body><main><section><h1>Delta</h1><p>New scoped page.</p></section></main></body></html>'
|
|
348
|
+
);
|
|
349
|
+
writeFileSync(
|
|
350
|
+
path.join(fixture.repo, generatedEpsilonRel),
|
|
351
|
+
'<!doctype html><html><body><main><section><h1>Epsilon</h1><p>New scoped page.</p></section></main></body></html>'
|
|
352
|
+
);
|
|
353
|
+
const multiGeneratedQualityMessage = 'Work on the Example.com listing pages for Delta.ai and Epsilon.ai. Make both pages visually polished.';
|
|
354
|
+
assert.deepEqual(
|
|
355
|
+
worker._extractUiVerificationFailureSlugs(
|
|
356
|
+
'delta.html: Visual warning: hero text fails local contrast verification.',
|
|
357
|
+
multiGeneratedQualityMessage
|
|
358
|
+
),
|
|
359
|
+
['delta-ai'],
|
|
360
|
+
'visual verification parsing should identify the scoped page named by a page-prefixed warning'
|
|
361
|
+
);
|
|
362
|
+
assert.deepEqual(
|
|
363
|
+
worker._extractUiVerificationFailureSlugs(
|
|
364
|
+
'Visual warning: hero text fails local contrast verification.',
|
|
365
|
+
multiGeneratedQualityMessage
|
|
366
|
+
),
|
|
367
|
+
[],
|
|
368
|
+
'generic visual warnings should not be treated as evidence to reset every generated scoped page'
|
|
369
|
+
);
|
|
370
|
+
worker._restoreGeneratedScopedUiTargets(
|
|
371
|
+
[{ root: fixture.repo, head: fixture.head, initialDirtyPaths: [] }],
|
|
372
|
+
multiGeneratedQualityMessage,
|
|
373
|
+
{ onlySlugs: ['delta-ai'] }
|
|
374
|
+
);
|
|
375
|
+
assert.equal(
|
|
376
|
+
existsSync(path.join(fixture.repo, generatedDeltaRel)),
|
|
377
|
+
false,
|
|
378
|
+
'page-aware generated resets should remove the rejected generated page'
|
|
379
|
+
);
|
|
380
|
+
assert.equal(
|
|
381
|
+
existsSync(path.join(fixture.repo, generatedEpsilonRel)),
|
|
382
|
+
true,
|
|
383
|
+
'page-aware generated resets should preserve unrelated generated scoped pages'
|
|
384
|
+
);
|
|
385
|
+
rmSync(path.join(fixture.repo, generatedEpsilonRel), { force: true });
|
|
299
386
|
const projectsRoot = mkdtempSync(path.join(tmpdir(), 'agentforge-project-list-'));
|
|
300
387
|
let agentWorkspace = null;
|
|
301
388
|
try {
|
package/src/worker.js
CHANGED
|
@@ -530,6 +530,24 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
530
530
|
return stats;
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
+
_listUntrackedUiFiles(baseline, userMessage, { scopedOnly = false } = {}) {
|
|
534
|
+
if (!baseline?.root) return [];
|
|
535
|
+
const uiFileRe = /\.(html?|css|s[ac]ss|jsx?|tsx?|vue|svelte|astro|mdx?)$/i;
|
|
536
|
+
const initialDirty = new Set(baseline.initialDirtyPaths || []);
|
|
537
|
+
const { slugs: allowedSlugs, pageOnly } = this._extractExplicitScope(userMessage);
|
|
538
|
+
return String(this._gitOutput(baseline.root, ['ls-files', '--others', '--exclude-standard'], 10000) || '')
|
|
539
|
+
.split('\n')
|
|
540
|
+
.map(line => line.trim())
|
|
541
|
+
.filter(Boolean)
|
|
542
|
+
.filter(rel => uiFileRe.test(rel))
|
|
543
|
+
.filter(rel => !initialDirty.has(rel))
|
|
544
|
+
.filter(rel => !this._isNestedProjectCopyRel(baseline.root, rel, baseline.head || 'HEAD'))
|
|
545
|
+
.filter(rel => {
|
|
546
|
+
if (!scopedOnly || allowedSlugs.length === 0) return true;
|
|
547
|
+
return this._scopeAllowsChangedPath(baseline, rel, allowedSlugs, pageOnly, userMessage);
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
|
|
533
551
|
_findDestructiveRepoChanges(repoBaselines, userMessage) {
|
|
534
552
|
if (!Array.isArray(repoBaselines) || repoBaselines.length === 0) return [];
|
|
535
553
|
if (this._allowsLargeDestructiveChange(userMessage)) return [];
|
|
@@ -927,11 +945,19 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
927
945
|
return restored;
|
|
928
946
|
}
|
|
929
947
|
|
|
930
|
-
|
|
948
|
+
_scopedUiPathMatchesAnySlug(relativePath, slugs) {
|
|
949
|
+
const matches = this._scopeSlugsMatchingText(relativePath, slugs);
|
|
950
|
+
return matches.length > 0;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
_restoreGeneratedScopedUiTargets(repoBaselines, userMessage, options = {}) {
|
|
931
954
|
if (!this._isBroadUiQualityTask(userMessage)) return [];
|
|
932
955
|
if (!Array.isArray(repoBaselines) || repoBaselines.length === 0) return [];
|
|
933
956
|
const { slugs: allowedSlugs, pageOnly } = this._extractExplicitScope(userMessage);
|
|
934
957
|
if (allowedSlugs.length === 0 || !pageOnly) return [];
|
|
958
|
+
const onlySlugs = Array.isArray(options.onlySlugs)
|
|
959
|
+
? [...new Set(options.onlySlugs.map(slug => this._scopeSlug(slug)).filter(Boolean))]
|
|
960
|
+
: [];
|
|
935
961
|
|
|
936
962
|
const restored = [];
|
|
937
963
|
for (const baseline of repoBaselines) {
|
|
@@ -952,6 +978,7 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
952
978
|
const files = [...names]
|
|
953
979
|
.filter(rel => !initialDirty.has(rel))
|
|
954
980
|
.filter(rel => this._scopeAllowsChangedPath(baseline, rel, allowedSlugs, pageOnly, userMessage))
|
|
981
|
+
.filter(rel => onlySlugs.length === 0 || this._scopedUiPathMatchesAnySlug(rel, onlySlugs))
|
|
955
982
|
.filter(rel => !this._gitPathExistsAtRef(baseline.root, baseline.head || 'HEAD', rel))
|
|
956
983
|
.sort();
|
|
957
984
|
if (files.length === 0) continue;
|
|
@@ -1158,7 +1185,20 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
1158
1185
|
this._gitOutput(baseline.root, ['diff', '--numstat'], 10000),
|
|
1159
1186
|
'working tree'
|
|
1160
1187
|
);
|
|
1161
|
-
const
|
|
1188
|
+
const untracked = this._listUntrackedUiFiles(baseline, userMessage, { scopedOnly: true })
|
|
1189
|
+
.map(rel => {
|
|
1190
|
+
let additions = 0;
|
|
1191
|
+
try {
|
|
1192
|
+
additions = String(readFileSync(path.join(baseline.root, rel), 'utf-8') || '')
|
|
1193
|
+
.split('\n')
|
|
1194
|
+
.filter(line => line.trim())
|
|
1195
|
+
.length;
|
|
1196
|
+
} catch {
|
|
1197
|
+
additions = 1;
|
|
1198
|
+
}
|
|
1199
|
+
return { additions, deletions: 0, path: rel, source: 'untracked' };
|
|
1200
|
+
});
|
|
1201
|
+
const combined = [...committed, ...working, ...untracked]
|
|
1162
1202
|
.filter(stat => /\.(html?|css|s[ac]ss|jsx?|tsx?|vue|svelte|astro|mdx?)$/i.test(stat.path));
|
|
1163
1203
|
if (combined.length === 0) continue;
|
|
1164
1204
|
|
|
@@ -1272,10 +1312,45 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
1272
1312
|
}
|
|
1273
1313
|
};
|
|
1274
1314
|
|
|
1315
|
+
const scanUntracked = (baseline, byPath) => {
|
|
1316
|
+
for (const currentFile of this._listUntrackedUiFiles(baseline, userMessage, { scopedOnly: true })) {
|
|
1317
|
+
let content = '';
|
|
1318
|
+
try {
|
|
1319
|
+
content = readFileSync(path.join(baseline.root, currentFile), 'utf-8');
|
|
1320
|
+
} catch {
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
for (const line of String(content || '').split('\n')) {
|
|
1324
|
+
const raw = line.trim();
|
|
1325
|
+
if (!raw || /^(?:\/\*|\*|\/\/|<!--|-->|})$/.test(raw)) continue;
|
|
1326
|
+
const stat = byPath.get(currentFile) || {
|
|
1327
|
+
path: currentFile,
|
|
1328
|
+
changed: 0,
|
|
1329
|
+
shallow: 0,
|
|
1330
|
+
semanticAdds: 0,
|
|
1331
|
+
inlineStyleAdds: 0,
|
|
1332
|
+
sources: new Set(),
|
|
1333
|
+
};
|
|
1334
|
+
stat.changed += 1;
|
|
1335
|
+
stat.sources.add('untracked');
|
|
1336
|
+
if (shallowLineRe.test(raw)) stat.shallow += 1;
|
|
1337
|
+
if (/\bstyle=["'][^"']+["']/i.test(raw)) stat.inlineStyleAdds += 1;
|
|
1338
|
+
const text = visibleText(raw);
|
|
1339
|
+
const hasStructuralMarkup = /<(?:section|article|aside|ul|ol|li|h2|h3|p|form|table|figure)\b/i.test(raw);
|
|
1340
|
+
const hasSubstantiveClassWithText = structuralClassRe.test(raw) && /<[^>]+>/i.test(raw) && text.length >= 24;
|
|
1341
|
+
if ((hasStructuralMarkup && text.length >= 35) || hasSubstantiveClassWithText) {
|
|
1342
|
+
stat.semanticAdds += 1;
|
|
1343
|
+
}
|
|
1344
|
+
byPath.set(currentFile, stat);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
|
|
1275
1349
|
for (const baseline of repoBaselines) {
|
|
1276
1350
|
const byPath = new Map();
|
|
1277
1351
|
scanDiff(baseline, ['diff', '--unified=0', `${baseline.head}..HEAD`], 'committed', byPath);
|
|
1278
1352
|
scanDiff(baseline, ['diff', '--unified=0'], 'working tree', byPath);
|
|
1353
|
+
scanUntracked(baseline, byPath);
|
|
1279
1354
|
const changedFiles = [...byPath.values()];
|
|
1280
1355
|
if (changedFiles.length === 0) continue;
|
|
1281
1356
|
|
|
@@ -4160,12 +4235,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4160
4235
|
: '';
|
|
4161
4236
|
if (interimUiImplementationArtifactNudge) {
|
|
4162
4237
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimUiImplementationArtifactWarnings), nudgeCount + 1);
|
|
4238
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4239
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4240
|
+
);
|
|
4163
4241
|
const interimScopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
|
|
4164
4242
|
nudgeCount++;
|
|
4165
4243
|
const repairBudget = consumeUiRepairNudge('patch artifacts', interimUiImplementationArtifactNudge);
|
|
4166
4244
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4167
4245
|
console.log(`[${taskId}] Iteration had patch artifacts — correcting (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4168
|
-
const combinedNudge = [interimUiImplementationArtifactNudge, restoreNudge, interimScopedUiTargetSetNudge].filter(Boolean).join('\n\n');
|
|
4246
|
+
const combinedNudge = [interimUiImplementationArtifactNudge, restoreNudge, generatedResetNudge, interimScopedUiTargetSetNudge].filter(Boolean).join('\n\n');
|
|
4169
4247
|
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${combinedNudge}\n\nContinue from the restored files and already-inspected context. Clean the implementation, address every requested scoped target page, and verify each edited target screen after the final edit.`);
|
|
4170
4248
|
continue;
|
|
4171
4249
|
}
|
|
@@ -4190,12 +4268,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4190
4268
|
: '';
|
|
4191
4269
|
if (interimShallowUiSurfaceNudge) {
|
|
4192
4270
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimShallowUiSurfaceWarnings), nudgeCount + 1);
|
|
4271
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4272
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4273
|
+
);
|
|
4193
4274
|
const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
|
|
4194
4275
|
nudgeCount++;
|
|
4195
4276
|
const repairBudget = consumeUiRepairNudge('shallow UI surface changes', interimShallowUiSurfaceNudge);
|
|
4196
4277
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4197
4278
|
console.log(`[${taskId}] Iteration had shallow UI surface changes — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4198
|
-
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimShallowUiSurfaceNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better across the page-owned surface, then verify the edited target screens after the final edit.`);
|
|
4279
|
+
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimShallowUiSurfaceNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better across the page-owned surface, then verify the edited target screens after the final edit.`);
|
|
4199
4280
|
continue;
|
|
4200
4281
|
}
|
|
4201
4282
|
throw new Error(`Task produced shallow UI surface changes after repeated retries:\n${interimShallowUiSurfaceNudge}`);
|
|
@@ -4207,12 +4288,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4207
4288
|
: '';
|
|
4208
4289
|
if (interimThinUiChangeNudge) {
|
|
4209
4290
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(interimThinUiChangeWarnings), nudgeCount + 1);
|
|
4291
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4292
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4293
|
+
);
|
|
4210
4294
|
const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
|
|
4211
4295
|
nudgeCount++;
|
|
4212
4296
|
const repairBudget = consumeUiRepairNudge('too-thin UI changes', interimThinUiChangeNudge);
|
|
4213
4297
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4214
4298
|
console.log(`[${taskId}] Iteration had too-thin UI changes — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4215
|
-
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimThinUiChangeNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better, then verify the edited target screens after the final edit.`);
|
|
4299
|
+
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[interimThinUiChangeNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context. Make the requested scoped UI materially better, then verify the edited target screens after the final edit.`);
|
|
4216
4300
|
continue;
|
|
4217
4301
|
}
|
|
4218
4302
|
throw new Error(`Task produced too-thin UI changes after repeated retries:\n${interimThinUiChangeNudge}`);
|
|
@@ -4314,12 +4398,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4314
4398
|
: '';
|
|
4315
4399
|
if (uiImplementationArtifactNudge) {
|
|
4316
4400
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(uiImplementationArtifactWarnings), nudgeCount + 1);
|
|
4401
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4402
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4403
|
+
);
|
|
4317
4404
|
const scopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
|
|
4318
4405
|
nudgeCount++;
|
|
4319
4406
|
const repairBudget = consumeUiRepairNudge('completion patch artifacts', uiImplementationArtifactNudge);
|
|
4320
4407
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4321
4408
|
console.log(`[${taskId}] UI completion had patch artifacts — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4322
|
-
const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
|
|
4409
|
+
const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, generatedResetNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
|
|
4323
4410
|
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${combinedNudge}\n\nContinue from the restored files and already-inspected context. Do not mark complete until the UI source reads like finished product code and every requested scoped target screen is visually verified.`);
|
|
4324
4411
|
continue;
|
|
4325
4412
|
}
|
|
@@ -4342,12 +4429,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4342
4429
|
: '';
|
|
4343
4430
|
if (shallowUiSurfaceNudge) {
|
|
4344
4431
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(shallowUiSurfaceWarnings), nudgeCount + 1);
|
|
4432
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4433
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4434
|
+
);
|
|
4345
4435
|
const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
|
|
4346
4436
|
nudgeCount++;
|
|
4347
4437
|
const repairBudget = consumeUiRepairNudge('completion shallow UI surface changes', shallowUiSurfaceNudge);
|
|
4348
4438
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4349
4439
|
console.log(`[${taskId}] UI completion had shallow surface diff — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4350
|
-
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is improved across the page-owned product surface and visually verified.`);
|
|
4440
|
+
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is improved across the page-owned product surface and visually verified.`);
|
|
4351
4441
|
continue;
|
|
4352
4442
|
}
|
|
4353
4443
|
throw new Error(`UI task claimed completion with shallow UI surface changes after repeated retries:\n${shallowUiSurfaceNudge}`);
|
|
@@ -4358,12 +4448,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4358
4448
|
: '';
|
|
4359
4449
|
if (thinUiChangeNudge) {
|
|
4360
4450
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(thinUiChangeWarnings), nudgeCount + 1);
|
|
4451
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4452
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4453
|
+
);
|
|
4361
4454
|
const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
|
|
4362
4455
|
nudgeCount++;
|
|
4363
4456
|
const repairBudget = consumeUiRepairNudge('completion too-thin UI changes', thinUiChangeNudge);
|
|
4364
4457
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4365
4458
|
console.log(`[${taskId}] UI completion had too-thin diff — nudging (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4366
|
-
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is substantively improved and visually verified.`);
|
|
4459
|
+
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nDo not mark complete until the requested scoped UI is substantively improved and visually verified.`);
|
|
4367
4460
|
continue;
|
|
4368
4461
|
}
|
|
4369
4462
|
throw new Error(`UI task claimed completion with too-thin diff after repeated retries:\n${thinUiChangeNudge}`);
|
|
@@ -4572,12 +4665,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4572
4665
|
: '';
|
|
4573
4666
|
if (uiImplementationArtifactNudge) {
|
|
4574
4667
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(uiImplementationArtifactWarnings), nudgeCount + 1);
|
|
4668
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4669
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4670
|
+
);
|
|
4575
4671
|
const scopedUiTargetSetNudge = this._buildScopedUiTargetSetNudge(repoBaselines, scopeAwareUserMessage);
|
|
4576
4672
|
nudgeCount++;
|
|
4577
4673
|
const repairBudget = consumeUiRepairNudge('publish patch artifacts', uiImplementationArtifactNudge);
|
|
4578
4674
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4579
4675
|
console.log(`[${taskId}] Publish evidence had patch artifacts — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4580
|
-
const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
|
|
4676
|
+
const combinedNudge = [uiImplementationArtifactNudge, restoreNudge, generatedResetNudge, scopedUiTargetSetNudge].filter(Boolean).join('\n\n');
|
|
4581
4677
|
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${combinedNudge}\n\nContinue from the restored files and already-inspected context, clean up the UI implementation, address every requested scoped target page, verify each target screen visually, then commit/push any additional changes before reporting delivery complete.`);
|
|
4582
4678
|
continue;
|
|
4583
4679
|
}
|
|
@@ -4600,12 +4696,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4600
4696
|
: '';
|
|
4601
4697
|
if (shallowUiSurfaceNudge) {
|
|
4602
4698
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(shallowUiSurfaceWarnings), nudgeCount + 1);
|
|
4699
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4700
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4701
|
+
);
|
|
4603
4702
|
const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
|
|
4604
4703
|
nudgeCount++;
|
|
4605
4704
|
const repairBudget = consumeUiRepairNudge('publish shallow UI surface changes', shallowUiSurfaceNudge);
|
|
4606
4705
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4607
4706
|
console.log(`[${taskId}] Publish evidence had shallow UI surface diff — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4608
|
-
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, improve the requested scoped UI across the page-owned product surface, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
|
|
4707
|
+
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[shallowUiSurfaceNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, improve the requested scoped UI across the page-owned product surface, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
|
|
4609
4708
|
continue;
|
|
4610
4709
|
}
|
|
4611
4710
|
throw new Error('Publish task had shallow UI surface changes after repeated retries');
|
|
@@ -4616,12 +4715,15 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4616
4715
|
: '';
|
|
4617
4716
|
if (thinUiChangeNudge) {
|
|
4618
4717
|
const restoreNudge = this._formatUiQualityRestoreNudge(this._restoreUiQualityWarningPaths(thinUiChangeWarnings), nudgeCount + 1);
|
|
4718
|
+
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4719
|
+
this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
|
|
4720
|
+
);
|
|
4619
4721
|
const scopedTargetSetNudge = this._formatScopedUiTargetSetReminder(scopeAwareUserMessage);
|
|
4620
4722
|
nudgeCount++;
|
|
4621
4723
|
const repairBudget = consumeUiRepairNudge('publish too-thin UI changes', thinUiChangeNudge);
|
|
4622
4724
|
if (nudgeCount < UI_QUALITY_NUDGE_LIMIT) {
|
|
4623
4725
|
console.log(`[${taskId}] Publish evidence had too-thin UI diff — continuing (${nudgeCount}/${UI_QUALITY_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
4624
|
-
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, make the requested scoped UI improvement substantive, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
|
|
4726
|
+
iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[thinUiChangeNudge, restoreNudge, generatedResetNudge, scopedTargetSetNudge].filter(Boolean).join('\n\n')}\n\nContinue from the restored files and already-inspected context, make the requested scoped UI improvement substantive, verify it visually, then commit/push any additional changes before reporting delivery complete.`);
|
|
4625
4727
|
continue;
|
|
4626
4728
|
}
|
|
4627
4729
|
throw new Error('Publish task had too-thin UI diff after repeated retries');
|
|
@@ -4672,8 +4774,11 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4672
4774
|
throw new Error('UI task failed visual verification after repeated repair attempts');
|
|
4673
4775
|
}
|
|
4674
4776
|
const repairBudget = consumeUiRepairNudge('visual verification warnings', visualVerificationFailureNudge);
|
|
4777
|
+
const visualFailureSlugs = this._extractUiVerificationFailureSlugs(output, scopeAwareUserMessage);
|
|
4675
4778
|
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4676
|
-
|
|
4779
|
+
visualFailureSlugs.length > 0
|
|
4780
|
+
? this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage, { onlySlugs: visualFailureSlugs })
|
|
4781
|
+
: []
|
|
4677
4782
|
);
|
|
4678
4783
|
nudgeCount = 0;
|
|
4679
4784
|
console.log(`[${taskId}] UI task visual verification still reported visible issues — retrying (${uiVerificationRetryCount}/${UI_REPAIR_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
@@ -4689,8 +4794,11 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
4689
4794
|
throw new Error('UI task failed visual verification after repeated repair attempts');
|
|
4690
4795
|
}
|
|
4691
4796
|
const repairBudget = consumeUiRepairNudge('missing local visual verification', uiVerificationFailureDetails);
|
|
4797
|
+
const visualFailureSlugs = this._extractUiVerificationFailureSlugs(output, scopeAwareUserMessage);
|
|
4692
4798
|
const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
|
|
4693
|
-
|
|
4799
|
+
visualFailureSlugs.length > 0
|
|
4800
|
+
? this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage, { onlySlugs: visualFailureSlugs })
|
|
4801
|
+
: []
|
|
4694
4802
|
);
|
|
4695
4803
|
nudgeCount = 0;
|
|
4696
4804
|
console.log(`[${taskId}] UI task missing local visual verification — retrying with local-app repair instruction (${uiVerificationRetryCount}/${UI_REPAIR_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
|
|
@@ -5783,7 +5891,7 @@ end tell`.trim().replace(/\n/g, '\n');
|
|
|
5783
5891
|
.split('\n')
|
|
5784
5892
|
.map(line => line.trim())
|
|
5785
5893
|
.filter(line =>
|
|
5786
|
-
|
|
5894
|
+
/(?:^|[\s:])(?:Style|Visual) warning:/i.test(line) ||
|
|
5787
5895
|
/Browser failed to load/i.test(line) ||
|
|
5788
5896
|
/changed UI was not successfully loaded and inspected/i.test(line) ||
|
|
5789
5897
|
/latest visual verification still reported/i.test(line) ||
|
|
@@ -5793,6 +5901,29 @@ end tell`.trim().replace(/\n/g, '\n');
|
|
|
5793
5901
|
return unique.join('\n').slice(0, 1800);
|
|
5794
5902
|
}
|
|
5795
5903
|
|
|
5904
|
+
_extractUiVerificationFailureSlugs(output, userMessage) {
|
|
5905
|
+
const { slugs: allowedSlugs } = this._extractExplicitScope(userMessage);
|
|
5906
|
+
if (allowedSlugs.length === 0) return [];
|
|
5907
|
+
const lines = String(output || '')
|
|
5908
|
+
.split('\n')
|
|
5909
|
+
.map(line => line.trim())
|
|
5910
|
+
.filter(line =>
|
|
5911
|
+
/(?:^|[\s:])(?:Style|Visual) warning:/i.test(line) ||
|
|
5912
|
+
/Browser failed to load/i.test(line) ||
|
|
5913
|
+
/changed UI was not successfully loaded and inspected/i.test(line) ||
|
|
5914
|
+
/latest visual verification still reported/i.test(line) ||
|
|
5915
|
+
/This is not complete/i.test(line)
|
|
5916
|
+
);
|
|
5917
|
+
if (lines.length === 0) return [];
|
|
5918
|
+
const matched = new Set();
|
|
5919
|
+
for (const line of lines) {
|
|
5920
|
+
for (const slug of this._scopeSlugsMatchingText(line, allowedSlugs)) {
|
|
5921
|
+
matched.add(slug);
|
|
5922
|
+
}
|
|
5923
|
+
}
|
|
5924
|
+
return [...matched];
|
|
5925
|
+
}
|
|
5926
|
+
|
|
5796
5927
|
_buildUiVerificationFailureNudge(output) {
|
|
5797
5928
|
const details = this._extractUiVerificationFailureDetails(output);
|
|
5798
5929
|
const genericFailure =
|