natureco-cli 5.64.5 → 5.64.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to NatureCo CLI will be documented in this file.
4
4
 
5
+ ## [5.64.7] - 2026-07-13 — Resilient GUI vision decisions
6
+
7
+ ### Fixed
8
+ - GUI actions now validate required coordinates, text, and keys before generating macOS AppleScript, preventing `undefined değişkeni tanımlanmamış` failures.
9
+ - Truncated, malformed, empty, or Markdown-wrapped vision JSON is handled safely; invalid decisions trigger another visual-analysis step instead of crashing the GUI loop.
10
+ - The independent completion verifier now treats malformed JSON as failed evidence rather than throwing.
11
+
12
+ ### Tests
13
+ - Added regressions for missing GUI parameters, Markdown JSON responses, and the exact unterminated-string response reported with MiniMax vision.
14
+
15
+ ## [5.64.6] - 2026-07-13 — Typed MiniMax tool arguments
16
+
17
+ ### Fixed
18
+ - MiniMax XML tool parameters are now coerced from text to their declared JSON Schema types before validation and execution.
19
+ - `computer_use_loop` now accepts XML values such as `<parameter name="maxSteps">30</parameter>` as the number `30`, so visible GUI automation starts instead of failing with `expected number, got string`.
20
+ - Conversion also covers integer, boolean, object, and array parameters while leaving invalid values untouched for normal schema validation.
21
+
22
+ ### Tests
23
+ - Added regressions for valid type conversion, invalid-value preservation, and the exact `computer_use_loop` `maxSteps` failure reported on macOS.
24
+
5
25
  ## [5.64.5] - 2026-07-13 — Deterministic visible-browser recovery
6
26
 
7
27
  ### Fixed
package/README.md CHANGED
@@ -51,6 +51,8 @@ natureco code
51
51
 
52
52
  | Version | Highlights |
53
53
  |---------|-----------|
54
+ | **v5.64.7** | **Resilient GUI vision:** invalid action parameters and truncated vision JSON are rejected safely and retried instead of crashing macOS automation. |
55
+ | **v5.64.6** | **Reliable MiniMax tool calls:** XML parameters are converted to their declared schema types, fixing `computer_use_loop` failures such as `maxSteps: expected number, got string`. |
54
56
  | **v5.64.5** | **Deterministic GUI recovery:** visible web tasks use one verified GUI loop; failures expose their concrete reason and stop blind browser/AppleScript fallback chains. |
55
57
  | **v5.64.4** | **Unified MiniMax media:** the existing MiniMax key now powers `MiniMax-VL-01` analysis, `image-01` generation, `MiniMax-Hailuo-2.3` video, and verified GUI vision—no second key required. |
56
58
  | **v5.64.3** | **Evidence-based GUI completion:** desktop actions require a changed screen plus independent visual evidence before success. MiniMax screenshots use the Token Plan VLM with the existing provider key. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.64.5",
3
+ "version": "5.64.7",
4
4
  "description": "Terminal-native AI agent CLI with bilingual TR/EN UI, multi-agent orchestration, persistent memory, secure tools and messaging integrations.",
5
5
  "bin": {
6
6
  "natureco": "bin/natureco.js"
@@ -288,6 +288,37 @@ function buildFeedback(norm, res) {
288
288
  return body ? `${head}:\n${body}` : head;
289
289
  }
290
290
 
291
+ function coerceArgsForSchema(args, schema) {
292
+ if (!args || typeof args !== 'object' || !schema || typeof schema !== 'object') return { ...(args || {}) };
293
+ const properties = schema.properties || {};
294
+ const result = { ...args };
295
+
296
+ for (const [key, property] of Object.entries(properties)) {
297
+ const value = result[key];
298
+ if (typeof value !== 'string' || !property) continue;
299
+ const types = Array.isArray(property.type) ? property.type : [property.type];
300
+ const trimmed = value.trim();
301
+
302
+ if (types.includes('integer') && /^[-+]?\d+$/.test(trimmed)) {
303
+ const number = Number(trimmed);
304
+ if (Number.isSafeInteger(number)) result[key] = number;
305
+ } else if (types.includes('number') && trimmed !== '' && Number.isFinite(Number(trimmed))) {
306
+ result[key] = Number(trimmed);
307
+ } else if (types.includes('boolean') && /^(true|false)$/i.test(trimmed)) {
308
+ result[key] = trimmed.toLowerCase() === 'true';
309
+ } else if (types.includes('object') || types.includes('array')) {
310
+ try {
311
+ const parsed = JSON.parse(trimmed);
312
+ if ((types.includes('array') && Array.isArray(parsed)) ||
313
+ (types.includes('object') && parsed && typeof parsed === 'object' && !Array.isArray(parsed))) {
314
+ result[key] = parsed;
315
+ }
316
+ } catch {}
317
+ }
318
+ }
319
+ return result;
320
+ }
321
+
291
322
  /**
292
323
  * Tek bir parse edilmis cagriyi calistir.
293
324
  * Donus: { records: [...], feedback: '...' }
@@ -389,9 +420,11 @@ async function executeCall(call, opts = {}) {
389
420
  records.push({ tool: norm, status: 'error', error: 'execute yok' });
390
421
  return { records, feedback: `${norm} HATA: execute fonksiyonu yok` };
391
422
  }
423
+ const schema = mod.inputSchema || mod.parameters || (mod.default && (mod.default.inputSchema || mod.default.parameters));
424
+ const typedArgs = coerceArgsForSchema(args, schema);
392
425
  const res = await executeThroughGateway({
393
426
  toolName: norm,
394
- args,
427
+ args: typedArgs,
395
428
  resolveTool: () => mod,
396
429
  execute: fn,
397
430
  normalizeSuccess: value => value,
@@ -401,7 +434,7 @@ async function executeCall(call, opts = {}) {
401
434
  const ok = typeof res === 'string' ? true : (res && res.success !== false);
402
435
  const status = ok ? 'done' : 'error';
403
436
  const feedback = buildFeedback(norm, res);
404
- records.push({ tool: norm, status, args: sanitizeArgs(args), result: res, error: ok ? undefined : res.error });
437
+ records.push({ tool: norm, status, args: sanitizeArgs(typedArgs), result: res, error: ok ? undefined : res.error });
405
438
  return { records, feedback };
406
439
  }
407
440
 
@@ -462,4 +495,4 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
462
495
  return { records: allRecords, reply: finalReply, iterations };
463
496
  }
464
497
 
465
- module.exports = { parseAgenticCalls, stripProtocolTokens, executeCall, runAgentic, expandHome, makeStreamFilter, makeSanitizeStream, agentExecAllowed, buildFeedback, TOOL_ALIASES, DEFAULT_ALLOWED };
498
+ module.exports = { parseAgenticCalls, stripProtocolTokens, executeCall, runAgentic, expandHome, makeStreamFilter, makeSanitizeStream, agentExecAllowed, buildFeedback, coerceArgsForSchema, TOOL_ALIASES, DEFAULT_ALLOWED };
@@ -133,17 +133,41 @@ function evaluateCompletionEvidence({ mutationCount, initialHash, currentHash, v
133
133
  async function verifyGoal(providerUrl, apiKey, model, goal, screenshot) {
134
134
  const prompt = `You are a strict independent verifier. Inspect only the screenshot. Goal: ${goal}\nReturn JSON only: {"verified":boolean,"confidence":0..1,"evidence":"exact visible evidence","reason":"why not"}. Never infer success from the goal text. If the requested sent message, booking, purchase, or confirmation is not visibly present, verified must be false.`;
135
135
  const reply = await visionCall(providerUrl, apiKey, model, prompt, screenshot);
136
- try { return JSON.parse(reply); }
137
- catch {
138
- const match = reply.match(/\{[\s\S]*\}/);
139
- return match ? JSON.parse(match[0]) : { verified: false, confidence: 0, reason: 'Verifier returned invalid JSON' };
136
+ const parsed = parseVisionDecision(reply);
137
+ return parsed.success ? parsed.value : { verified: false, confidence: 0, reason: 'Verifier returned invalid JSON: ' + parsed.error };
138
+ }
139
+
140
+ function parseVisionDecision(reply) {
141
+ const raw = String(reply || '').trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
142
+ const candidates = [raw];
143
+ const start = raw.indexOf('{');
144
+ const end = raw.lastIndexOf('}');
145
+ if (start >= 0 && end > start) candidates.push(raw.slice(start, end + 1));
146
+ for (const candidate of candidates) {
147
+ try {
148
+ const value = JSON.parse(candidate);
149
+ if (value && typeof value === 'object' && !Array.isArray(value)) return { success: true, value };
150
+ } catch {}
140
151
  }
152
+ return { success: false, error: raw ? 'malformed or truncated JSON' : 'empty vision response' };
153
+ }
154
+
155
+ function validateAction(action, params = {}) {
156
+ const finite = value => typeof value === 'number' && Number.isFinite(value);
157
+ if (!['click', 'type', 'keypress', 'mouse_move', 'scroll', 'wait', 'done'].includes(action)) return 'Unknown action: ' + action;
158
+ if ((action === 'click' || action === 'mouse_move') && (!finite(params.x) || !finite(params.y))) return action + ' requires finite numeric x and y';
159
+ if (action === 'scroll' && !finite(params.y)) return 'scroll requires a finite numeric y';
160
+ if (action === 'type' && typeof params.text !== 'string') return 'type requires text';
161
+ if (action === 'keypress' && (typeof params.key !== 'string' || !params.key.trim())) return 'keypress requires key';
162
+ return null;
141
163
  }
142
164
 
143
165
  function executeAction(action, params) {
144
166
  const { spawnSync } = require('child_process');
145
167
  const PLATFORM = os.platform();
146
168
 
169
+ const invalid = validateAction(action, params);
170
+ if (invalid) return { success: false, error: invalid };
147
171
  const ESC = (s) => s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
148
172
 
149
173
  function osaScript(script, timeoutMs = 10000) {
@@ -232,15 +256,17 @@ async function loop(goal, maxSteps) {
232
256
  const history = steps.filter(s => s.action && s.action !== 'screenshot' && s.action !== 'done').slice(-5);
233
257
  const historyText = history.length > 0 ? '\nOnceki adimlar:\n' + history.map(h => 'Adim ' + h.step + ': ' + JSON.stringify(h)).join('\n') : '';
234
258
  const reply = await visionCall(providerUrl, apiKey, model, SYSTEM_PROMPT + '\n\nGorev: ' + goal + historyText + '\n\nEkran goruntusunu analiz et. Siradaki action ne?', screenshot);
235
- let decision;
236
- try {
237
- decision = JSON.parse(reply);
238
- } catch {
239
- const m = reply.match(/\{[\s\S]*\}/);
240
- decision = m ? JSON.parse(m[0]) : { action: 'wait' };
259
+ const parsedDecision = parseVisionDecision(reply);
260
+ if (!parsedDecision.success) {
261
+ steps.push({ step: i + 1, action: 'vision_retry', error: parsedDecision.error });
262
+ continue;
263
+ }
264
+ const decision = parsedDecision.value;
265
+ const invalidDecision = validateAction(decision.action, decision);
266
+ if (invalidDecision) {
267
+ steps.push({ step: i + 1, action: 'vision_retry', error: invalidDecision });
268
+ continue;
241
269
  }
242
-
243
- if (!decision.action) decision.action = 'wait';
244
270
 
245
271
  // 3. Execute
246
272
  if (decision.action === 'done') {
@@ -307,4 +333,4 @@ async function execute(params) {
307
333
  return await loop(params.goal, params.maxSteps || 30);
308
334
  }
309
335
 
310
- module.exports = { name, description, parameters, execute, executeAction, evaluateCompletionEvidence, resolveVisionConfig, visionCall };
336
+ module.exports = { name, description, parameters, execute, executeAction, evaluateCompletionEvidence, resolveVisionConfig, visionCall, parseVisionDecision, validateAction };