@hamp10/agentforge 0.2.25 → 0.2.26
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 +36 -0
- package/src/OpenClawCLI.js +15 -2
- package/src/selfUpdate.js +41 -5
package/package.json
CHANGED
|
@@ -668,6 +668,7 @@ try {
|
|
|
668
668
|
const openClawSource = readFileSync(new URL('../src/OpenClawCLI.js', import.meta.url), 'utf-8');
|
|
669
669
|
const workerSource = readFileSync(new URL('../src/worker.js', import.meta.url), 'utf-8');
|
|
670
670
|
const workerBinSource = readFileSync(new URL('../bin/agentforge.js', import.meta.url), 'utf-8');
|
|
671
|
+
const selfUpdateSource = readFileSync(new URL('../src/selfUpdate.js', import.meta.url), 'utf-8');
|
|
671
672
|
const defaultGuidesSource = readFileSync(new URL('../src/default-task-guides.js', import.meta.url), 'utf-8');
|
|
672
673
|
const browserSource = readFileSync(new URL('../src/browser.js', import.meta.url), 'utf-8');
|
|
673
674
|
const previewServerSource = readFileSync(new URL('../src/preview-server.js', import.meta.url), 'utf-8');
|
|
@@ -838,16 +839,41 @@ assert.match(
|
|
|
838
839
|
/app\.delete\('\/api\/agents\/delete\/cleanup-empty'[\s\S]*activeTasks[\s\S]*type: 'agents_pruned'/i,
|
|
839
840
|
'empty-agent cleanup should preserve live tasks and broadcast removed agents'
|
|
840
841
|
);
|
|
842
|
+
assert.match(
|
|
843
|
+
serverSource,
|
|
844
|
+
/CREATE TABLE IF NOT EXISTS agent_flows[\s\S]*config JSONB DEFAULT NULL/i,
|
|
845
|
+
'agent flow emergency schema patch should include config so model routing survives skipped migrations'
|
|
846
|
+
);
|
|
847
|
+
assert.match(
|
|
848
|
+
serverSource,
|
|
849
|
+
/ALTER TABLE agent_flows ADD COLUMN IF NOT EXISTS config JSONB DEFAULT NULL/i,
|
|
850
|
+
'agent flow startup should self-heal missing config column'
|
|
851
|
+
);
|
|
852
|
+
assert.match(
|
|
853
|
+
serverSource,
|
|
854
|
+
/ON CONFLICT \(id\)[\s\S]*WHERE agent_flows\.user_id = EXCLUDED\.user_id[\s\S]*RETURNING id/i,
|
|
855
|
+
'flow upserts should not update another user flow with the same id'
|
|
856
|
+
);
|
|
841
857
|
assert.match(
|
|
842
858
|
dashboardSource,
|
|
843
859
|
/confirmDeleteAgent[\s\S]*await fetch[\s\S]*removeAgentFromLocalState/i,
|
|
844
860
|
'single-agent delete should only remove local UI after the server confirms deletion'
|
|
845
861
|
);
|
|
862
|
+
assert.match(
|
|
863
|
+
dashboardSource,
|
|
864
|
+
/confirmPruneAgents[\s\S]*\/api\/agents\/delete\/prune\?keep=15[\s\S]*removeAgentFromLocalState/i,
|
|
865
|
+
'dashboard should expose old-agent pruning through the safe keep-latest endpoint'
|
|
866
|
+
);
|
|
846
867
|
assert.match(
|
|
847
868
|
dashboardSource,
|
|
848
869
|
/function finalizeLiveFeedBeforeGuide[\s\S]*commitChunkBuffer[\s\S]*hideTypingIndicator/i,
|
|
849
870
|
'live Guide messages should close the current visible agent feed before rendering the user bubble'
|
|
850
871
|
);
|
|
872
|
+
assert.match(
|
|
873
|
+
selfUpdateSource,
|
|
874
|
+
/ensureWritableGlobalInstallEnv[\s\S]*canWriteNpmRoot[\s\S]*configureUserOwnedNpmGlobalEnv/i,
|
|
875
|
+
'self-update should preflight npm global permissions before printing a failed system-prefix install'
|
|
876
|
+
);
|
|
851
877
|
assert.match(
|
|
852
878
|
dashboardSource,
|
|
853
879
|
/async function sendGuideMessage[\s\S]*agent\._guideInflight[\s\S]*finalizeLiveFeedBeforeGuide\(agentId\)[\s\S]*addMessage/i,
|
|
@@ -953,6 +979,16 @@ assert.doesNotMatch(
|
|
|
953
979
|
/taskRequiresVisualVerification\s*&&\s*directIteration\s*<=\s*1\s*&&\s*explicitScopeForTask\.pageOnly/s,
|
|
954
980
|
'comparable UI context gate should apply on retries, not only first iteration'
|
|
955
981
|
);
|
|
982
|
+
assert.match(
|
|
983
|
+
openClawSource,
|
|
984
|
+
/read-only project context, not as a target page/,
|
|
985
|
+
'read-only comparable page inspection should not be narrated as target-page work'
|
|
986
|
+
);
|
|
987
|
+
assert.match(
|
|
988
|
+
openClawSource,
|
|
989
|
+
/verifiedLocalUrl = isLocalUiUrl\(resultUrl \|\| url\)/,
|
|
990
|
+
'scoped auto-verification should count a clean inferred local target URL even when browser output formatting is imperfect'
|
|
991
|
+
);
|
|
956
992
|
assert.match(
|
|
957
993
|
openClawSource,
|
|
958
994
|
/const minRatio = isLargeText \? 3 : 4\.5;/,
|
package/src/OpenClawCLI.js
CHANGED
|
@@ -4629,8 +4629,10 @@ export class OpenClawCLI extends EventEmitter {
|
|
|
4629
4629
|
recordDirectToolSummary('browser', result);
|
|
4630
4630
|
continue;
|
|
4631
4631
|
}
|
|
4632
|
-
|
|
4633
|
-
|
|
4632
|
+
const resultUrl = browserResultUrl(result);
|
|
4633
|
+
rememberLocalScopedUrl(resultUrl);
|
|
4634
|
+
const verifiedLocalUrl = isLocalUiUrl(resultUrl || url);
|
|
4635
|
+
if (isLocalUiVerificationResult(result) || verifiedLocalUrl) {
|
|
4634
4636
|
recordCleanLocalVerification(result, [slug]);
|
|
4635
4637
|
verifiedAny = true;
|
|
4636
4638
|
}
|
|
@@ -4766,6 +4768,13 @@ export class OpenClawCLI extends EventEmitter {
|
|
|
4766
4768
|
return `I am checking the live browser${url} to judge the actual product surface before deciding the next change.`;
|
|
4767
4769
|
}
|
|
4768
4770
|
if (name === 'read_file' || name === 'read') {
|
|
4771
|
+
const scope = extractExplicitScope(task);
|
|
4772
|
+
const targetText = String(targetPath || base || '').toLowerCase();
|
|
4773
|
+
const looksLikePageSource = /\.(html?|css|s[ac]ss|jsx?|tsx?|vue|svelte|astro|mdx?)$/i.test(targetText);
|
|
4774
|
+
const isNamedTarget = scope.slugs.length > 0 && scopeSlugsMatchingText(targetText, scope.slugs).length > 0;
|
|
4775
|
+
if (base && looksLikePageSource && scope.slugs.length > 0 && !isNamedTarget) {
|
|
4776
|
+
return `I am reading ${base} as read-only project context, not as a target page.`;
|
|
4777
|
+
}
|
|
4769
4778
|
return base
|
|
4770
4779
|
? `I am reading ${base} to work from the current implementation instead of guessing.`
|
|
4771
4780
|
: `I am reading the current code before making the next change.`;
|
|
@@ -5097,6 +5106,10 @@ export class OpenClawCLI extends EventEmitter {
|
|
|
5097
5106
|
lastDirectVisualWarning = visualWarning;
|
|
5098
5107
|
} else if (!taskRequiresVisualVerification || isLocalUiVerificationResult(result)) {
|
|
5099
5108
|
recordCleanLocalVerification(result);
|
|
5109
|
+
} else {
|
|
5110
|
+
const verifiedUrl = resultUrl || args?.url || '';
|
|
5111
|
+
const verifiedSlugs = isLocalUiUrl(verifiedUrl) ? scopeSlugsInText(verifiedUrl) : [];
|
|
5112
|
+
if (verifiedSlugs.length > 0) recordCleanLocalVerification(result, verifiedSlugs);
|
|
5100
5113
|
}
|
|
5101
5114
|
}
|
|
5102
5115
|
}
|
package/src/selfUpdate.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* prevent infinite re-exec loops).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { execSync, spawn } from 'child_process';
|
|
10
|
-
import { mkdirSync } from 'fs';
|
|
9
|
+
import { execFileSync, execSync, spawn } from 'child_process';
|
|
10
|
+
import { mkdirSync, rmSync } from 'fs';
|
|
11
11
|
import { homedir } from 'os';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
|
|
@@ -75,22 +75,58 @@ export async function checkAndUpdate(packageName, currentVersion) {
|
|
|
75
75
|
|
|
76
76
|
function installGlobalPackage(pkg, { force = false } = {}) {
|
|
77
77
|
const forceFlag = force ? ' --force' : '';
|
|
78
|
+
const preflightEnv = ensureWritableGlobalInstallEnv();
|
|
78
79
|
try {
|
|
79
|
-
execSync(`npm install -g${forceFlag} ${pkg}`, { stdio: 'inherit' });
|
|
80
|
+
execSync(`npm install -g${forceFlag} ${pkg}`, { stdio: 'inherit', env: preflightEnv });
|
|
80
81
|
return;
|
|
81
82
|
} catch (error) {
|
|
82
83
|
if (!looksLikePermissionFailure(error)) throw error;
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
const env = configureUserOwnedNpmGlobalEnv();
|
|
87
|
+
execSync(`npm install -g${forceFlag} ${pkg}`, { stdio: 'inherit', env });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function npmConfigGet(args) {
|
|
91
|
+
try {
|
|
92
|
+
return execFileSync('npm', args, {
|
|
93
|
+
encoding: 'utf8',
|
|
94
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
95
|
+
}).trim();
|
|
96
|
+
} catch {
|
|
97
|
+
return '';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function canWriteNpmRoot(rootDir) {
|
|
102
|
+
if (!rootDir) return false;
|
|
103
|
+
const testDir = path.join(rootDir, `.agentforge-write-test-${process.pid}`);
|
|
104
|
+
try {
|
|
105
|
+
mkdirSync(testDir, { recursive: true });
|
|
106
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
107
|
+
return true;
|
|
108
|
+
} catch {
|
|
109
|
+
try { rmSync(testDir, { recursive: true, force: true }); } catch {}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function ensureWritableGlobalInstallEnv() {
|
|
115
|
+
const root = npmConfigGet(['root', '-g']);
|
|
116
|
+
if (canWriteNpmRoot(root)) return process.env;
|
|
117
|
+
return configureUserOwnedNpmGlobalEnv();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function configureUserOwnedNpmGlobalEnv() {
|
|
85
121
|
const prefix = path.join(homedir(), '.npm-global');
|
|
86
122
|
mkdirSync(path.join(prefix, 'bin'), { recursive: true });
|
|
87
123
|
mkdirSync(path.join(prefix, 'lib', 'node_modules'), { recursive: true });
|
|
88
|
-
|
|
124
|
+
execFileSync('npm', ['config', 'set', 'prefix', prefix], { stdio: ['ignore', 'ignore', 'inherit'] });
|
|
89
125
|
const env = {
|
|
90
126
|
...process.env,
|
|
91
127
|
PATH: `${path.join(prefix, 'bin')}${path.delimiter}${process.env.PATH || ''}`,
|
|
92
128
|
};
|
|
93
|
-
|
|
129
|
+
return env;
|
|
94
130
|
}
|
|
95
131
|
|
|
96
132
|
function looksLikePermissionFailure(error) {
|