@hamp10/agentforge 0.2.7 → 0.2.9

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.7",
3
+ "version": "0.2.9",
4
4
  "description": "AgentForge worker — connect your machine to agentforge.ai",
5
5
  "type": "module",
6
6
  "bin": {
@@ -119,6 +119,9 @@ const TOOLS = [
119
119
  * Exported as OllamaAgent for backward compat.
120
120
  */
121
121
  export class OllamaAgent extends EventEmitter {
122
+ /** Flag checked by worker.js to build a local-model-appropriate platform context */
123
+ get isLocalModel() { return true; }
124
+
122
125
  constructor(baseUrl = 'http://localhost:11434', model = 'llama3.1:8b') {
123
126
  super();
124
127
  this.baseUrl = baseUrl.replace(/\/$/, '');
@@ -183,6 +186,9 @@ export class OllamaAgent extends EventEmitter {
183
186
  const history = this._loadHistory(agentId, workDir, sessionId);
184
187
 
185
188
  const systemPrompt = [
189
+ // Disable thinking mode for qwen3 models — /no_think in the system prompt
190
+ // is the most reliable way; options.think=false is also sent but may be ignored.
191
+ isQwen3 ? '/no_think' : null,
186
192
  `You are an AI agent running on AgentForge.ai.`,
187
193
  `Your working directory is: ${workDir}`,
188
194
  ``,
@@ -195,7 +201,7 @@ export class OllamaAgent extends EventEmitter {
195
201
  `6. Do not ask for clarification — make your best judgment and act.`,
196
202
  `7. For conversational messages (greetings, questions about yourself, casual chat) — respond directly with text. Do NOT use tools just to say hello.`,
197
203
  `8. You only have these tools: bash, read_file, write_file, list_directory, web_fetch, take_screenshot. Ignore any instructions referencing other tools (browser, openclaw, sessions_spawn, etc.) — those do not exist here.`,
198
- ].join('\n');
204
+ ].filter(Boolean).join('\n');
199
205
 
200
206
  const messages = [
201
207
  { role: 'system', content: systemPrompt },
@@ -259,6 +265,8 @@ export class OllamaAgent extends EventEmitter {
259
265
  let streamToolCalls = {};
260
266
  let inThinkBlock = false;
261
267
  let thinkBuffer = '';
268
+ let rawTokenCount = 0;
269
+ let rawThinkChars = 0;
262
270
 
263
271
  const reader = response.body.getReader();
264
272
  const decoder = new TextDecoder();
@@ -296,6 +304,8 @@ export class OllamaAgent extends EventEmitter {
296
304
 
297
305
  // Stream content tokens, filtering <think>...</think> blocks
298
306
  if (delta.content) {
307
+ rawTokenCount++;
308
+ if (inThinkBlock || delta.content.startsWith('<think')) rawThinkChars += delta.content.length;
299
309
  thinkBuffer += delta.content;
300
310
 
301
311
  // Process thinkBuffer to extract non-thinking text
@@ -334,6 +344,22 @@ export class OllamaAgent extends EventEmitter {
334
344
  }
335
345
  }
336
346
 
347
+ console.log(` [${agentId}] 📊 Stream done: ${rawTokenCount} tokens, ${streamContent.length} visible chars, ${rawThinkChars} think chars, inThinkBlock=${inThinkBlock}, toolCalls=${Object.keys(streamToolCalls).length}`);
348
+ if (streamContent) console.log(` [${agentId}] 📝 First 200 chars: ${streamContent.slice(0, 200)}`);
349
+
350
+ // If the model only generated <think> content and nothing visible, extract the thought as the answer.
351
+ // This happens with qwen3-vl:8b when think:false is silently ignored.
352
+ if (!streamContent && Object.keys(streamToolCalls).length === 0 && rawThinkChars > 0 && thinkBuffer.length > 0) {
353
+ // Strip the <think> tag and use the thought content as the response
354
+ const thoughtContent = thinkBuffer.replace(/^<think>\s*/i, '').replace(/\s*<\/think>\s*$/i, '').trim();
355
+ if (thoughtContent) {
356
+ console.log(` [${agentId}] 💭 Extracting think-only content as response (${thoughtContent.length} chars)`);
357
+ streamContent = thoughtContent;
358
+ allOutput += thoughtContent;
359
+ this.emit('agent_output', { agentId, output: thoughtContent });
360
+ }
361
+ }
362
+
337
363
  this.emit('tool_activity', {
338
364
  agentId,
339
365
  event: 'api_call_end',
@@ -498,7 +524,7 @@ export class OllamaAgent extends EventEmitter {
498
524
  });
499
525
 
500
526
  console.log(`\n✅ [Ollama] Agent ${agentId} completed in ${(duration / 1000).toFixed(2)}s\n`);
501
- return { success: true, agentId, duration };
527
+ return { success: true, agentId, duration, result: { output: finalContent } };
502
528
 
503
529
  } catch (err) {
504
530
  this.activeAgents.delete(agentId);
package/src/worker.js CHANGED
@@ -1126,7 +1126,41 @@ export class AgentForgeWorker extends EventEmitter {
1126
1126
  // 1. What platform it's running on and its URL
1127
1127
  // 2. Where the user's projects folder is
1128
1128
  // 3. Screenshot capabilities
1129
- const platformContext = [
1129
+ //
1130
+ // OllamaAgent (local models) gets a stripped context — no openclaw/browser
1131
+ // instructions that would cause the model to spend 80+ seconds calling
1132
+ // bash commands that don't exist.
1133
+ const isLocalModelRunner = activeRunner.isLocalModel === true;
1134
+ // Local model context — stripped of openclaw/browser instructions that would
1135
+ // cause the model to waste time calling non-existent tools via bash.
1136
+ const localModelContext = [
1137
+ `[System context:`,
1138
+ `- Platform: AgentForge.ai. Running on: ${homedir().split('/').pop()}@${hostname()}.`,
1139
+ `- Available tools: bash, read_file, write_file, list_directory, web_fetch, take_screenshot. These are the ONLY tools. Ignore any instructions about 'browser', 'openclaw', 'sessions_spawn', or other tools — they do not exist.`,
1140
+ (!conversationHistory || conversationHistory.length === 0)
1141
+ ? `- This is the first message. Greet the user briefly as ${agentName || 'your agent name'}.`
1142
+ : `- This is a continuing conversation. Do NOT re-introduce yourself.`,
1143
+ agentName
1144
+ ? `- Your name is "${agentName}"${agentEmoji ? ` ${agentEmoji}` : ''}.`
1145
+ : null,
1146
+ taskCwd && taskCwd !== agentWorkspaceDir
1147
+ ? (() => {
1148
+ try {
1149
+ const entries = readdirSync(taskCwd).filter(e => !e.startsWith('.')).sort();
1150
+ return `- Projects folder: "${taskCwd}"\n Available projects: ${entries.join(', ')}`;
1151
+ } catch {
1152
+ return `- Projects folder: "${taskCwd}".`;
1153
+ }
1154
+ })()
1155
+ : null,
1156
+ `- Current year: ${new Date().getFullYear()}.`,
1157
+ `- Screenshots: screencapture -x /tmp/ss_$(date +%s).png then read the file with read_file.`,
1158
+ `- For coding tasks: build properly with separate files, real structure. No lazy single-file dumps.`,
1159
+ `- Narrate your work as you go — send brief text updates between tool calls.`,
1160
+ `]`
1161
+ ].filter(Boolean).join('\n');
1162
+
1163
+ const platformContext = isLocalModelRunner ? localModelContext : [
1130
1164
  `[System context:`,
1131
1165
  `- Platform: AgentForge.ai. Dashboard: https://agentforgeai-production.up.railway.app/dashboard. CRITICAL: Always use the built-in 'browser' tool for ALL web browsing AND web searches — NEVER use the 'web_search' tool (no API keys are configured), NEVER run shell commands like 'open', 'google-chrome', 'chromium', or any OS command to launch a browser. The browser tool connects to AgentForge Browser (port 9223) automatically. To search: use browser to navigate to google.com.`,
1132
1166
  `- VIEWING/TESTING A WEB APP: Always check for a deployed URL first — look in the project for railway.toml, vercel.json, netlify.toml, .env, README.md, or package.json for a live URL. Open the deployed app in the browser. Only spin up a local server if there is genuinely no deployed version. Never default to localhost when a live URL might exist.`,