natureco-cli 5.64.6 → 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,16 @@
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
+
5
15
  ## [5.64.6] - 2026-07-13 — Typed MiniMax tool arguments
6
16
 
7
17
  ### Fixed
package/README.md CHANGED
@@ -51,6 +51,7 @@ 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. |
54
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`. |
55
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. |
56
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. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.64.6",
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"
@@ -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 };