@hamp10/agentforge 0.2.32 → 0.2.33

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/worker.js +84 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamp10/agentforge",
3
- "version": "0.2.32",
3
+ "version": "0.2.33",
4
4
  "description": "AgentForge worker — connect your machine to agentforge.ai",
5
5
  "type": "module",
6
6
  "bin": {
package/src/worker.js CHANGED
@@ -854,11 +854,8 @@ export class AgentForgeWorker extends EventEmitter {
854
854
  addLines(this._gitOutput(baseline.root, ['diff', '--name-only'], 10000));
855
855
  addLines(this._gitOutput(baseline.root, ['diff', '--name-only', '--cached'], 10000));
856
856
  addLines(this._gitOutput(baseline.root, ['ls-files', '--others', '--exclude-standard'], 10000));
857
- const status = this._gitStatusPorcelain(baseline.root, 10000);
858
- for (const rawLine of String(status || '').split('\n').filter(Boolean)) {
859
- const rel = rawLine.length >= 3 && rawLine[2] === ' ' ? rawLine.slice(3) : rawLine.trim().replace(/^..\s+/, '');
860
- const pathName = rel.includes(' -> ') ? rel.split(' -> ').pop() : rel;
861
- if (pathName) names.add(pathName);
857
+ for (const pathName of this._parseGitStatusPaths(this._gitStatusPorcelain(baseline.root, 10000))) {
858
+ names.add(pathName);
862
859
  }
863
860
 
864
861
  const initialDirty = new Set(baseline.initialDirtyPaths || []);
@@ -890,6 +887,45 @@ export class AgentForgeWorker extends EventEmitter {
890
887
  return restored;
891
888
  }
892
889
 
890
+ _restoreGeneratedScopedUiTargets(repoBaselines, userMessage) {
891
+ if (!this._isBroadUiQualityTask(userMessage)) return [];
892
+ if (!Array.isArray(repoBaselines) || repoBaselines.length === 0) return [];
893
+ const { slugs: allowedSlugs, pageOnly } = this._extractExplicitScope(userMessage);
894
+ if (allowedSlugs.length === 0 || !pageOnly) return [];
895
+
896
+ const restored = [];
897
+ for (const baseline of repoBaselines) {
898
+ const names = new Set();
899
+ const addLines = (output) => {
900
+ for (const rel of String(output || '').split('\n').map(line => line.trim()).filter(Boolean)) {
901
+ names.add(rel);
902
+ }
903
+ };
904
+ addLines(this._gitOutput(baseline.root, ['diff', '--name-only'], 10000));
905
+ addLines(this._gitOutput(baseline.root, ['diff', '--name-only', '--cached'], 10000));
906
+ addLines(this._gitOutput(baseline.root, ['ls-files', '--others', '--exclude-standard'], 10000));
907
+ for (const pathName of this._parseGitStatusPaths(this._gitStatusPorcelain(baseline.root, 10000))) {
908
+ names.add(pathName);
909
+ }
910
+
911
+ const initialDirty = new Set(baseline.initialDirtyPaths || []);
912
+ const files = [...names]
913
+ .filter(rel => !initialDirty.has(rel))
914
+ .filter(rel => this._scopeAllowsChangedPath(baseline, rel, allowedSlugs, pageOnly, userMessage))
915
+ .filter(rel => !this._gitPathExistsAtRef(baseline.root, baseline.head || 'HEAD', rel))
916
+ .sort();
917
+ if (files.length === 0) continue;
918
+
919
+ this._gitRun(baseline.root, ['restore', '--staged', '--', ...files], 10000);
920
+ const untracked = files.filter(file => this._gitOutput(baseline.root, ['ls-files', '--others', '--exclude-standard', '--', file], 5000));
921
+ if (untracked.length > 0) {
922
+ this._gitRun(baseline.root, ['clean', '-fd', '--', ...untracked], 10000);
923
+ }
924
+ restored.push({ repo: baseline.root, files: files.slice(0, 12), restoredCount: files.length });
925
+ }
926
+ return restored;
927
+ }
928
+
893
929
  _formatScopeDriftRestoreNudge(restored) {
894
930
  const lines = restored.slice(0, 5).flatMap(w => [
895
931
  `- ${w.repo}: restored ${w.restoredCount} out-of-scope file(s); allowed scope tokens: ${w.allowedSlugs.join(', ')}${w.pageOnly ? ' (page/source files only)' : ''}`,
@@ -1294,6 +1330,19 @@ export class AgentForgeWorker extends EventEmitter {
1294
1330
  ].join('\n');
1295
1331
  }
1296
1332
 
1333
+ _formatGeneratedScopedUiResetNudge(restored) {
1334
+ if (!Array.isArray(restored) || restored.length === 0) return '';
1335
+ const lines = restored.slice(0, 5).flatMap(item => [
1336
+ `- ${item.repo}: removed ${item.restoredCount} rejected generated scoped file(s) before retrying`,
1337
+ ...item.files.map(file => ` ${file}`),
1338
+ ]);
1339
+ return [
1340
+ 'AgentForge removed the rejected generated target file(s) before this retry.',
1341
+ ...lines,
1342
+ 'Treat those removed files as failed output, not source material. Rebuild the requested target page-owned file(s) from inspected project context and comparable existing pages, then verify the rebuilt pages visually.',
1343
+ ].join('\n');
1344
+ }
1345
+
1297
1346
  _findUiImplementationArtifactChanges(repoBaselines, userMessage) {
1298
1347
  if (!this._isBroadUiQualityTask(userMessage)) return [];
1299
1348
  if (!Array.isArray(repoBaselines) || repoBaselines.length === 0) return [];
@@ -3469,6 +3518,20 @@ export class AgentForgeWorker extends EventEmitter {
3469
3518
  // generic discovery loop after the user has steered the active task.
3470
3519
  const withTaskContext = (message) => activeGuidePrefix() + retryContextPrefix + taskContextPrefix + message;
3471
3520
  finalMessage = withTaskContext(finalMessage);
3521
+ const applyGuideToSemanticScope = (guideText) => {
3522
+ const text = String(guideText || '').trim();
3523
+ if (!text) return;
3524
+ const before = this._extractExplicitScope(scopeAwareUserMessage);
3525
+ const merged = `${scopeAwareUserMessage}\n\n[Active user guide]\n${text}`;
3526
+ const after = this._extractExplicitScope(merged);
3527
+ if (after.slugs.length === 0) return;
3528
+ scopeAwareUserMessage = merged;
3529
+ const beforeKey = `${before.slugs.join(',')}|${before.pageOnly}`;
3530
+ const afterKey = `${after.slugs.join(',')}|${after.pageOnly}`;
3531
+ if (beforeKey !== afterKey) {
3532
+ agentLog(`[${taskId}] 🎯 Updated active scope from guide: ${after.slugs.join(', ')}${after.pageOnly ? ' (page/source files only)' : ''}`);
3533
+ }
3534
+ };
3472
3535
 
3473
3536
  // If conversation history was loaded from DB (e.g. session expired, worker restarted,
3474
3537
  // or user returning hours later), prepend it so the agent has full context.
@@ -3794,6 +3857,7 @@ export class AgentForgeWorker extends EventEmitter {
3794
3857
  nudgeCount = 0;
3795
3858
  uiRepairNudgeCount = 0;
3796
3859
  uiVerificationRetryCount = 0;
3860
+ applyGuideToSemanticScope(preRunGuide.text);
3797
3861
  agentLog(`[${taskId}] 🧭 Applying ${preRunGuide.count} guide note(s) before iteration ${iteration}`);
3798
3862
  this.send({ type: 'task_progress', taskId, agentId, roomId, output: 'Guide received; applying it to the current task...' });
3799
3863
  iterationMessage = `${preRunGuide.text}\n\n${iterationMessage}`;
@@ -4112,6 +4176,7 @@ export class AgentForgeWorker extends EventEmitter {
4112
4176
  nudgeCount = 0;
4113
4177
  uiRepairNudgeCount = 0;
4114
4178
  uiVerificationRetryCount = 0;
4179
+ applyGuideToSemanticScope(postRunGuide.text);
4115
4180
  agentLog(`[${taskId}] 🧭 Applying ${postRunGuide.count} guide note(s) after iteration ${iteration}`);
4116
4181
  this.send({ type: 'task_progress', taskId, agentId, roomId, output: 'Guide received; continuing with the updated direction...' });
4117
4182
  iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${postRunGuide.text}\n\nContinue with the active task using the guide as the newest user direction. Preserve useful work and context only when it does not conflict with the guide.`);
@@ -4559,9 +4624,15 @@ export class AgentForgeWorker extends EventEmitter {
4559
4624
  throw new Error('UI task failed visual verification after repeated repair attempts');
4560
4625
  }
4561
4626
  const repairBudget = consumeUiRepairNudge('visual verification warnings', visualVerificationFailureNudge);
4627
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4628
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4629
+ );
4562
4630
  nudgeCount = 0;
4563
4631
  console.log(`[${taskId}] UI task visual verification still reported visible issues — retrying (${uiVerificationRetryCount}/${UI_REPAIR_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4564
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${visualVerificationFailureNudge}\n\nContinue from the current changed files and latest browser evidence. Do not restart from scratch. Fix the visible issues reported by the local browser verification, reopen each edited target screen locally after the final edit, and only then end with ✓ TASK_COMPLETE.`);
4632
+ const retryInstruction = generatedResetNudge
4633
+ ? 'Rebuild the removed target page file(s) from already-inspected project context and comparable existing pages. Do not reuse the rejected generated page as the basis for the next attempt. Fix the visible issues reported by local browser verification, reopen each edited target screen locally after the final edit, and only then end with ✓ TASK_COMPLETE.'
4634
+ : 'Continue from the current changed files and latest browser evidence. Fix the visible issues reported by the local browser verification, reopen each edited target screen locally after the final edit, and only then end with ✓ TASK_COMPLETE.';
4635
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\n${[visualVerificationFailureNudge, generatedResetNudge].filter(Boolean).join('\n\n')}\n\n${retryInstruction}`);
4565
4636
  } else if (hasMissingLocalUiVerification) {
4566
4637
  uiVerificationRetryCount++;
4567
4638
  const uiVerificationFailureDetails = this._extractUiVerificationFailureDetails(output);
@@ -4570,9 +4641,15 @@ export class AgentForgeWorker extends EventEmitter {
4570
4641
  throw new Error('UI task failed visual verification after repeated repair attempts');
4571
4642
  }
4572
4643
  const repairBudget = consumeUiRepairNudge('missing local visual verification', uiVerificationFailureDetails);
4644
+ const generatedResetNudge = this._formatGeneratedScopedUiResetNudge(
4645
+ this._restoreGeneratedScopedUiTargets(repoBaselines, scopeAwareUserMessage)
4646
+ );
4573
4647
  nudgeCount = 0;
4574
4648
  console.log(`[${taskId}] UI task missing local visual verification — retrying with local-app repair instruction (${uiVerificationRetryCount}/${UI_REPAIR_NUDGE_LIMIT}, total UI repairs ${repairBudget})`);
4575
- iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\nYour edits are not complete because the changed UI was not successfully loaded and inspected cleanly in its actual local app URL.${uiVerificationFailureDetails ? `\n\nSpecific verification issue(s):\n${uiVerificationFailureDetails}` : ''}\n\nContinue from the current changed files and already-inspected project context. Do not restart from scratch, repeat initial delete/reset/setup steps, discard existing progress, or reread site indexes, shared/global CSS, header/footer partials, or unrelated reference pages unless a specific verification issue requires it. Do not use external sites, site indexes, listing indexes, shared style files, or reference pages as final verification for scoped UI edits. Start or repair the project's local dev/static server if needed, use the port the project actually declares, open the real localhost/127.0.0.1 URL for each edited target screen, inspect the changed screen, fix visible issues, and only then end with ✓ TASK_COMPLETE.`);
4649
+ const retryInstruction = generatedResetNudge
4650
+ ? 'Rebuild the removed target page file(s) from already-inspected project context and comparable existing pages. Do not reuse the rejected generated page as the basis for the next attempt.'
4651
+ : 'Continue from the current changed files and already-inspected project context. Do not restart from scratch, repeat initial delete/reset/setup steps, discard existing progress, or reread site indexes, shared/global CSS, header/footer partials, or unrelated reference pages unless a specific verification issue requires it.';
4652
+ iterationMessage = withTaskContext(`The task is: "${userMessage}"\n\nYour edits are not complete because the changed UI was not successfully loaded and inspected cleanly in its actual local app URL.${uiVerificationFailureDetails ? `\n\nSpecific verification issue(s):\n${uiVerificationFailureDetails}` : ''}${generatedResetNudge ? `\n\n${generatedResetNudge}` : ''}\n\n${retryInstruction} Do not use external sites, site indexes, listing indexes, shared style files, or reference pages as final verification for scoped UI edits. Start or repair the project's local dev/static server if needed, use the port the project actually declares, open the real localhost/127.0.0.1 URL for each edited target screen, inspect the changed screen, fix visible issues, and only then end with ✓ TASK_COMPLETE.`);
4576
4653
  } else if (hasIncompleteTurn) {
4577
4654
  // openclaw reported an incomplete turn (payloads=0) after the agent used a tool.
4578
4655
  // This is a tool timeout, not a narration. Reset nudgeCount and give a targeted retry hint.