@hamp10/agentforge 0.2.40 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamp10/agentforge",
3
- "version": "0.2.40",
3
+ "version": "0.2.41",
4
4
  "description": "AgentForge worker — connect your machine to agentforge.ai",
5
5
  "type": "module",
6
6
  "bin": {
@@ -340,6 +340,49 @@ try {
340
340
  /removed 1 rejected generated scoped file/i,
341
341
  'retry feedback should tell the agent that generated scoped pages were removed as failed output'
342
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 });
343
386
  const projectsRoot = mkdtempSync(path.join(tmpdir(), 'agentforge-project-list-'));
344
387
  let agentWorkspace = null;
345
388
  try {
package/src/worker.js CHANGED
@@ -945,11 +945,19 @@ export class AgentForgeWorker extends EventEmitter {
945
945
  return restored;
946
946
  }
947
947
 
948
- _restoreGeneratedScopedUiTargets(repoBaselines, userMessage) {
948
+ _scopedUiPathMatchesAnySlug(relativePath, slugs) {
949
+ const matches = this._scopeSlugsMatchingText(relativePath, slugs);
950
+ return matches.length > 0;
951
+ }
952
+
953
+ _restoreGeneratedScopedUiTargets(repoBaselines, userMessage, options = {}) {
949
954
  if (!this._isBroadUiQualityTask(userMessage)) return [];
950
955
  if (!Array.isArray(repoBaselines) || repoBaselines.length === 0) return [];
951
956
  const { slugs: allowedSlugs, pageOnly } = this._extractExplicitScope(userMessage);
952
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
+ : [];
953
961
 
954
962
  const restored = [];
955
963
  for (const baseline of repoBaselines) {
@@ -970,6 +978,7 @@ export class AgentForgeWorker extends EventEmitter {
970
978
  const files = [...names]
971
979
  .filter(rel => !initialDirty.has(rel))
972
980
  .filter(rel => this._scopeAllowsChangedPath(baseline, rel, allowedSlugs, pageOnly, userMessage))
981
+ .filter(rel => onlySlugs.length === 0 || this._scopedUiPathMatchesAnySlug(rel, onlySlugs))
973
982
  .filter(rel => !this._gitPathExistsAtRef(baseline.root, baseline.head || 'HEAD', rel))
974
983
  .sort();
975
984
  if (files.length === 0) continue;
@@ -4765,8 +4774,11 @@ export class AgentForgeWorker extends EventEmitter {
4765
4774
  throw new Error('UI task failed visual verification after repeated repair attempts');
4766
4775
  }
4767
4776
  const repairBudget = consumeUiRepairNudge('visual verification warnings', visualVerificationFailureNudge);
4777
+ const visualFailureSlugs = this._extractUiVerificationFailureSlugs(output, scopeAwareUserMessage);
4768
4778
  const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4769
- this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4779
+ visualFailureSlugs.length > 0
4780
+ ? this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage, { onlySlugs: visualFailureSlugs })
4781
+ : []
4770
4782
  );
4771
4783
  nudgeCount = 0;
4772
4784
  console.log(`[${taskId}] UI task visual verification still reported visible issues — retrying (${uiVerificationRetryCount}/${UI_REPAIR_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
@@ -4782,8 +4794,11 @@ export class AgentForgeWorker extends EventEmitter {
4782
4794
  throw new Error('UI task failed visual verification after repeated repair attempts');
4783
4795
  }
4784
4796
  const repairBudget = consumeUiRepairNudge('missing local visual verification', uiVerificationFailureDetails);
4797
+ const visualFailureSlugs = this._extractUiVerificationFailureSlugs(output, scopeAwareUserMessage);
4785
4798
  const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4786
- this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4799
+ visualFailureSlugs.length > 0
4800
+ ? this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage, { onlySlugs: visualFailureSlugs })
4801
+ : []
4787
4802
  );
4788
4803
  nudgeCount = 0;
4789
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})`);
@@ -5876,7 +5891,7 @@ end tell`.trim().replace(/\n/g, '\n');
5876
5891
  .split('\n')
5877
5892
  .map(line => line.trim())
5878
5893
  .filter(line =>
5879
- /^(?:Style|Visual) warning:/i.test(line) ||
5894
+ /(?:^|[\s:])(?:Style|Visual) warning:/i.test(line) ||
5880
5895
  /Browser failed to load/i.test(line) ||
5881
5896
  /changed UI was not successfully loaded and inspected/i.test(line) ||
5882
5897
  /latest visual verification still reported/i.test(line) ||
@@ -5886,6 +5901,29 @@ end tell`.trim().replace(/\n/g, '\n');
5886
5901
  return unique.join('\n').slice(0, 1800);
5887
5902
  }
5888
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
+
5889
5927
  _buildUiVerificationFailureNudge(output) {
5890
5928
  const details = this._extractUiVerificationFailureDetails(output);
5891
5929
  const genericFailure =