@hamp10/agentforge 0.2.18 → 0.2.19
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/src/OpenClawCLI.js +32 -17
- package/src/hampagent/browser.js +4 -4
- package/src/worker.js +24 -4
package/package.json
CHANGED
package/src/OpenClawCLI.js
CHANGED
|
@@ -817,7 +817,7 @@ export class OpenClawCLI extends EventEmitter {
|
|
|
817
817
|
* Run an agent task
|
|
818
818
|
* Images are saved to workspace and referenced in message for vision model analysis
|
|
819
819
|
*/
|
|
820
|
-
async runAgentTask(agentId, task, workDir, sessionId = null, image = null, browserProfile = null, imageWorkDir = null, agentModel = null) {
|
|
820
|
+
async runAgentTask(agentId, task, workDir, sessionId = null, image = null, browserProfile = null, imageWorkDir = null, agentModel = null, customSystemPrompt = null, conversationHistory = null, allImages = null) {
|
|
821
821
|
// Apply per-agent model override before running (writes to openclaw.json + signals gateway)
|
|
822
822
|
if (agentModel) {
|
|
823
823
|
await this.setAgentModel(agentId, agentModel);
|
|
@@ -825,7 +825,7 @@ export class OpenClawCLI extends EventEmitter {
|
|
|
825
825
|
// ── Gateway path disabled — subprocess shows live tool activity ──────────
|
|
826
826
|
// Gateway path: SSE token streaming — tokens arrive live as the model generates.
|
|
827
827
|
// Dashboard buffers tokens into sentences before showing each as a complete bubble.
|
|
828
|
-
if (!image && this.gatewayPort && this.gatewayToken) {
|
|
828
|
+
if (!(allImages?.length > 0 || image) && this.gatewayPort && this.gatewayToken) {
|
|
829
829
|
console.log(`\n🤖 Running agent (streaming): ${agentId}`);
|
|
830
830
|
console.log(` Task: ${task.slice(0, 120)}${task.length > 120 ? '…' : ''}`);
|
|
831
831
|
try {
|
|
@@ -885,34 +885,49 @@ export class OpenClawCLI extends EventEmitter {
|
|
|
885
885
|
let imagePath = null;
|
|
886
886
|
let modifiedTask = task;
|
|
887
887
|
|
|
888
|
-
// Handle image by saving to workspace
|
|
889
|
-
if
|
|
888
|
+
// Handle image(s) by saving to workspace
|
|
889
|
+
// Build canonical list: allImages[] wins if provided, else fall back to single image
|
|
890
|
+
const imagesToSave = (allImages && allImages.length > 0)
|
|
891
|
+
? allImages
|
|
892
|
+
: (image ? [image] : []);
|
|
893
|
+
|
|
894
|
+
if (imagesToSave.length > 0) {
|
|
890
895
|
try {
|
|
891
896
|
const fs = await import('fs/promises');
|
|
892
897
|
const path = await import('path');
|
|
893
898
|
|
|
894
899
|
// Use imageWorkDir (agent's actual workspace) if provided, otherwise fall back to workDir
|
|
895
900
|
const imageDir = imageWorkDir || workDir;
|
|
896
|
-
|
|
901
|
+
|
|
897
902
|
// Ensure workspace directory exists before saving image
|
|
898
903
|
await fs.mkdir(imageDir, { recursive: true });
|
|
899
904
|
|
|
900
|
-
|
|
901
|
-
const base64Data = image.replace(/^data:image\/\w+;base64,/, '');
|
|
902
|
-
const buffer = Buffer.from(base64Data, 'base64');
|
|
903
|
-
|
|
904
|
-
// Save to workspace with timestamp to avoid conflicts
|
|
905
|
+
const savedNames = [];
|
|
905
906
|
const timestamp = Date.now();
|
|
906
|
-
const imageFileName = `uploaded_image_${timestamp}.png`;
|
|
907
|
-
imagePath = path.join(imageDir, imageFileName);
|
|
908
907
|
|
|
909
|
-
|
|
910
|
-
|
|
908
|
+
for (let i = 0; i < imagesToSave.length; i++) {
|
|
909
|
+
const imgData = imagesToSave[i];
|
|
910
|
+
// Extract base64 data (remove data:image/png;base64, prefix if present)
|
|
911
|
+
const base64Data = imgData.replace(/^data:image\/\w+;base64,/, '');
|
|
912
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
913
|
+
// Use suffix for multiple images so names don't collide
|
|
914
|
+
const suffix = imagesToSave.length > 1 ? `_${i + 1}` : '';
|
|
915
|
+
const imageFileName = `uploaded_image_${timestamp}${suffix}.png`;
|
|
916
|
+
const imgPath = path.join(imageDir, imageFileName);
|
|
917
|
+
await fs.writeFile(imgPath, buffer);
|
|
918
|
+
console.log(` 📷 Image ${i + 1}/${imagesToSave.length} saved to: ${imgPath}`);
|
|
919
|
+
savedNames.push(imageFileName);
|
|
920
|
+
if (i === 0) imagePath = imgPath; // track first for cleanup
|
|
921
|
+
}
|
|
911
922
|
|
|
912
|
-
// Modify task to reference the image
|
|
913
|
-
|
|
923
|
+
// Modify task to reference the image file(s)
|
|
924
|
+
if (savedNames.length === 1) {
|
|
925
|
+
modifiedTask = `${task}\n\n[There is an image file in the workspace: ${savedNames[0]}]`;
|
|
926
|
+
} else {
|
|
927
|
+
modifiedTask = `${task}\n\n[There are ${savedNames.length} image files in the workspace: ${savedNames.join(', ')}]`;
|
|
928
|
+
}
|
|
914
929
|
} catch (error) {
|
|
915
|
-
console.error(` ⚠️ Failed to save image: ${error.message}`);
|
|
930
|
+
console.error(` ⚠️ Failed to save image(s): ${error.message}`);
|
|
916
931
|
}
|
|
917
932
|
}
|
|
918
933
|
|
package/src/hampagent/browser.js
CHANGED
|
@@ -151,8 +151,8 @@ export async function browserAction(input) {
|
|
|
151
151
|
const page = await getPage();
|
|
152
152
|
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
153
153
|
_activePage = page;
|
|
154
|
-
//
|
|
155
|
-
|
|
154
|
+
// Brief pause for initial render, then snapshot
|
|
155
|
+
await _wait(400);
|
|
156
156
|
return await _snapshot(page);
|
|
157
157
|
}
|
|
158
158
|
|
|
@@ -215,8 +215,8 @@ export async function browserAction(input) {
|
|
|
215
215
|
: `No visible element found matching "${input.text || input.selector}"`;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
//
|
|
219
|
-
|
|
218
|
+
// Brief pause for dynamic content to render
|
|
219
|
+
await _wait(300);
|
|
220
220
|
|
|
221
221
|
// Always return a fresh snapshot after clicking so agent immediately sees what changed
|
|
222
222
|
const snap = await _snapshot(page);
|
package/src/worker.js
CHANGED
|
@@ -782,14 +782,16 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
782
782
|
}
|
|
783
783
|
}
|
|
784
784
|
|
|
785
|
-
async executeTaskNow({ taskId, agentId, sessionId, message: userMessage, workDir, defaultProjectsPath, image, roomId, roomContext, isMaestro, conversationHistory, browserProfile, agentName, agentEmoji, runnerType, agentModel, customSystemPrompt }) {
|
|
785
|
+
async executeTaskNow({ taskId, agentId, sessionId, message: userMessage, workDir, defaultProjectsPath, image, images: extraImages, roomId, roomContext, isMaestro, conversationHistory, browserProfile, agentName, agentEmoji, runnerType, agentModel, customSystemPrompt }) {
|
|
786
|
+
// Build canonical images array: images[] takes priority, fall back to single image field
|
|
787
|
+
const allImages = extraImages && extraImages.length > 0 ? extraImages : (image ? [image] : []);
|
|
786
788
|
const isMaestroTask = isMaestro || agentId === 'maestro';
|
|
787
789
|
console.log(`🤖 Executing task ${taskId} for agent ${agentId}${isMaestroTask ? ' (MAESTRO)' : ''}${browserProfile ? ` [browser: ${browserProfile}]` : ''}`);
|
|
788
790
|
if (sessionId) {
|
|
789
791
|
console.log(` Session: ${sessionId} (resuming conversation)`);
|
|
790
792
|
}
|
|
791
|
-
if (
|
|
792
|
-
console.log(` Image:
|
|
793
|
+
if (allImages.length > 0) {
|
|
794
|
+
console.log(` Image${allImages.length > 1 ? 's' : ''}: ${allImages.length} provided`);
|
|
793
795
|
}
|
|
794
796
|
if (roomId) {
|
|
795
797
|
console.log(` Room: ${roomId}`);
|
|
@@ -1321,7 +1323,7 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
1321
1323
|
console.log(`[${taskId}] 🏃 Runner: ${useHampagent ? '⚡ HAMPAGENT' : '🔧 OPENCLAW'} — agent ${agentId} iteration ${iteration}`);
|
|
1322
1324
|
const runAgentStart = Date.now();
|
|
1323
1325
|
taskResult = await activeRunner.runAgentTask(
|
|
1324
|
-
agentId, iterationMessage, taskCwd, sessionId, iteration === 1 ?
|
|
1326
|
+
agentId, iterationMessage, taskCwd, sessionId, iteration === 1 ? (allImages[0] || null) : null, browserProfile, actualWorkDir, agentModel || null, customSystemPrompt || null, conversationHistory || null, iteration === 1 ? allImages : null
|
|
1325
1327
|
);
|
|
1326
1328
|
const runAgentDuration = Date.now() - runAgentStart;
|
|
1327
1329
|
console.log(`[${taskId}] runAgentTask iteration ${iteration} returned after ${runAgentDuration}ms, success=${taskResult?.success}`);
|
|
@@ -1432,6 +1434,24 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
1432
1434
|
}
|
|
1433
1435
|
|
|
1434
1436
|
console.log(`[${taskId}] Sending completion message...`);
|
|
1437
|
+
|
|
1438
|
+
// ── Native macOS notification (no Chrome required) ────────────────────
|
|
1439
|
+
try {
|
|
1440
|
+
const { exec: execChild } = await import('child_process');
|
|
1441
|
+
|
|
1442
|
+
const agentLabel = (identity?.name || agentId || 'Agent')
|
|
1443
|
+
.replace(/\\/g, '').replace(/"/g, '').replace(/'/g, '');
|
|
1444
|
+
const preview = (finalOutput || 'Task complete')
|
|
1445
|
+
.replace(/✓\s*TASK_COMPLETE/g, '').replace(/[\r\n]+/g, ' ').trim().slice(0, 60)
|
|
1446
|
+
.replace(/\\/g, '').replace(/"/g, '').replace(/'/g, '');
|
|
1447
|
+
|
|
1448
|
+
// osascript is reliable and already authorized on this machine
|
|
1449
|
+
const script = `display notification "${preview}" with title "AgentForge" subtitle "${agentLabel}"`;
|
|
1450
|
+
execChild(`osascript -e '${script}'`, { timeout: 3000 }, (err) => {
|
|
1451
|
+
if (err) console.warn(`[${taskId}] macOS notification failed: ${err.message}`);
|
|
1452
|
+
});
|
|
1453
|
+
} catch (_) {}
|
|
1454
|
+
|
|
1435
1455
|
this.send(completionMessage);
|
|
1436
1456
|
|
|
1437
1457
|
console.log(`✅ Task ${taskId} completed (${identity.identityName})`);
|